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