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