xref: /freebsd/stand/common/interp_backslash.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * Redistribution and use in source and binary forms, with or without
3  * modification, are permitted provided that the following conditions
4  * are met:
5  * 1. Redistributions of source code must retain the above copyright
6  *    notice, this list of conditions and the following disclaimer.
7  * 2. Redistributions in binary form must reproduce the above copyright
8  *    notice, this list of conditions and the following disclaimer in the
9  *    documentation and/or other materials provided with the distribution.
10  *
11  * Jordan K. Hubbard
12  * 29 August 1998
13  *
14  * Routine for doing backslash elimination.
15  */
16 
17 #include <sys/cdefs.h>
18 #include <stand.h>
19 #include <string.h>
20 #include "bootstrap.h"
21 
22 #define	DIGIT(x) (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
23 
24 /*
25  * backslash: Return malloc'd copy of str with all standard "backslash
26  * processing" done on it.  Original can be free'd if desired.
27  */
28 char *
29 backslash(const char *str)
30 {
31 	/*
32 	 * Remove backslashes from the strings. Turn \040 etc. into a single
33 	 * character (we allow eight bit values). Currently NUL is not
34 	 * allowed.
35 	 *
36 	 * Turn "\n" and "\t" into '\n' and '\t' characters. Etc.
37 	 *
38 	 */
39 	char *new_str;
40 	int seenbs = 0;
41 	int i = 0;
42 
43 	if ((new_str = strdup(str)) == NULL)
44 		return NULL;
45 
46 	while (*str) {
47 		if (seenbs) {
48 			seenbs = 0;
49 			switch (*str) {
50 			case '\\':
51 				new_str[i++] = '\\';
52 				str++;
53 				break;
54 
55 				/* preserve backslashed quotes, dollar signs */
56 			case '\'':
57 			case '"':
58 			case '$':
59 				new_str[i++] = '\\';
60 				new_str[i++] = *str++;
61 				break;
62 
63 			case 'b':
64 				new_str[i++] = '\b';
65 				str++;
66 				break;
67 
68 			case 'f':
69 				new_str[i++] = '\f';
70 				str++;
71 				break;
72 
73 			case 'r':
74 				new_str[i++] = '\r';
75 				str++;
76 				break;
77 
78 			case 'n':
79 				new_str[i++] = '\n';
80 				str++;
81 				break;
82 
83 			case 's':
84 				new_str[i++] = ' ';
85 				str++;
86 				break;
87 
88 			case 't':
89 				new_str[i++] = '\t';
90 				str++;
91 				break;
92 
93 			case 'v':
94 				new_str[i++] = '\13';
95 				str++;
96 				break;
97 
98 			case 'z':
99 				str++;
100 				break;
101 
102 			case '0': case '1': case '2': case '3': case '4':
103 			case '5': case '6': case '7': case '8': case '9': {
104 				char val;
105 
106 				/* Three digit octal constant? */
107 				if (*str >= '0' && *str <= '3' &&
108 				    *(str + 1) >= '0' && *(str + 1) <= '7' &&
109 				    *(str + 2) >= '0' && *(str + 2) <= '7') {
110 
111 					val = (DIGIT(*str) << 6) + (DIGIT(*(str + 1)) << 3) +
112 					    DIGIT(*(str + 2));
113 
114 					/* Allow null value if user really wants to shoot
115 					   at feet, but beware! */
116 					new_str[i++] = val;
117 					str += 3;
118 					break;
119 				}
120 
121 				/* One or two digit hex constant?
122 				 * If two are there they will both be taken.
123 				 * Use \z to split them up if this is not wanted.
124 				 */
125 				if (*str == '0' &&
126 				    (*(str + 1) == 'x' || *(str + 1) == 'X') &&
127 				    isxdigit(*(str + 2))) {
128 					val = DIGIT(*(str + 2));
129 					if (isxdigit(*(str + 3))) {
130 						val = (val << 4) + DIGIT(*(str + 3));
131 						str += 4;
132 					}
133 					else
134 						str += 3;
135 					/* Yep, allow null value here too */
136 					new_str[i++] = val;
137 					break;
138 				}
139 			}
140 				break;
141 
142 			default:
143 				new_str[i++] = *str++;
144 				break;
145 			}
146 		}
147 		else {
148 			if (*str == '\\') {
149 				seenbs = 1;
150 				str++;
151 			}
152 			else
153 				new_str[i++] = *str++;
154 		}
155 	}
156 
157 	if (seenbs) {
158 		/*
159 		 * The final character was a '\'. Put it in as a single backslash.
160 		 */
161 		new_str[i++] = '\\';
162 	}
163 	new_str[i] = '\0';
164 	return new_str;
165 }
166