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  * $FreeBSD: src/sys/boot/common/interp_backslash.c,v 1.6 2003/08/25 23:30:41 obrien Exp $
17  * $DragonFly: src/sys/boot/common/interp_backslash.c,v 1.3 2003/11/10 06:08:31 dillon Exp $
18  */
19 
20 #include <stand.h>
21 #include <string.h>
22 #include "bootstrap.h"
23 
24 #define DIGIT(x) (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.  Also escape as needed.
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     int j = 0;
45 
46     for (i = 0; str[i]; ++i) {
47 	if (str[i] == '\'' ||
48 	    str[i] == '"' ||
49 	    str[i] == '$') {
50 		++j;
51 	}
52     }
53     j += i + 2;	/* terminator + possible final backslash */
54 
55     new_str = malloc(j);
56 
57     i = 0;
58     while (*str) {
59 	if (seenbs) {
60 	    seenbs = 0;
61 	    switch (*str) {
62 	    case '\\':
63 		new_str[i++] = '\\';
64 		str++;
65 		break;
66 
67 	    /* preserve backslashed quotes, dollar signs */
68 	    case '\'':
69 	    case '"':
70 	    case '$':
71 		new_str[i++] = '\\';
72 		new_str[i++] = *str++;
73 		break;
74 
75 	    case 'b':
76 		new_str[i++] = '\b';
77 		str++;
78 		break;
79 
80 	    case 'f':
81 		new_str[i++] = '\f';
82 		str++;
83 		break;
84 
85 	    case 'r':
86 		new_str[i++] = '\r';
87 		str++;
88 		break;
89 
90 	    case 'n':
91 		new_str[i++] = '\n';
92 		str++;
93 		break;
94 
95 	    case 's':
96 		new_str[i++] = ' ';
97 		str++;
98 		break;
99 
100 	    case 't':
101 		new_str[i++] = '\t';
102 		str++;
103 		break;
104 
105 	    case 'v':
106 		new_str[i++] = '\13';
107 		str++;
108 		break;
109 
110 	    case 'z':
111 		str++;
112 		break;
113 
114 	    case '0': case '1': case '2': case '3': case '4':
115 	    case '5': case '6': case '7': case '8': case '9': {
116 		char val;
117 
118 		/* Three digit octal constant? */
119 		if (*str >= '0' && *str <= '3' &&
120 		    *(str + 1) >= '0' && *(str + 1) <= '7' &&
121 		    *(str + 2) >= '0' && *(str + 2) <= '7') {
122 
123 		    val = (DIGIT(*str) << 6) + (DIGIT(*(str + 1)) << 3) +
124 			DIGIT(*(str + 2));
125 
126 		    /* Allow null value if user really wants to shoot
127                        at feet, but beware! */
128 		    new_str[i++] = val;
129 		    str += 3;
130 		    break;
131 		}
132 
133 		/* One or two digit hex constant?
134 		 * If two are there they will both be taken.
135 		 * Use \z to split them up if this is not wanted.
136 		 */
137 		if (*str == '0' &&
138 		    (*(str + 1) == 'x' || *(str + 1) == 'X') &&
139 		    isxdigit(*(str + 2))) {
140 		    val = DIGIT(*(str + 2));
141 		    if (isxdigit(*(str + 3))) {
142 			val = (val << 4) + DIGIT(*(str + 3));
143 			str += 4;
144 		    }
145 		    else
146 			str += 3;
147 		    /* Yep, allow null value here too */
148 		    new_str[i++] = val;
149 		    break;
150 		}
151 	    }
152 	    break;
153 
154 	    default:
155 		new_str[i++] = *str++;
156 		break;
157 	    }
158 	}
159         else {
160             if (*str == '\\') {
161                 seenbs = 1;
162                 str++;
163             } else {
164 		new_str[i++] = *str++;
165 	    }
166         }
167     }
168 
169     if (seenbs) {
170         /*
171          * The final character was a '\'. Put it in as a single backslash.
172          */
173 	new_str[i++] = '\\';
174     }
175     new_str[i] = 0;
176     if (i >= j)
177 	panic("bls");
178 
179     return new_str;
180 }
181