xref: /freebsd/stand/common/interp_backslash.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
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 __FBSDID("$FreeBSD$");
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.
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) + (DIGIT(*(str + 1)) << 3) +
114 			DIGIT(*(str + 2));
115 
116 		    /* Allow null value if user really wants to shoot
117                        at feet, but beware! */
118 		    new_str[i++] = val;
119 		    str += 3;
120 		    break;
121 		}
122 
123 		/* One or two digit hex constant?
124 		 * If two are there they will both be taken.
125 		 * Use \z to split them up if this is not wanted.
126 		 */
127 		if (*str == '0' &&
128 		    (*(str + 1) == 'x' || *(str + 1) == 'X') &&
129 		    isxdigit(*(str + 2))) {
130 		    val = DIGIT(*(str + 2));
131 		    if (isxdigit(*(str + 3))) {
132 			val = (val << 4) + DIGIT(*(str + 3));
133 			str += 4;
134 		    }
135 		    else
136 			str += 3;
137 		    /* Yep, allow null value here too */
138 		    new_str[i++] = val;
139 		    break;
140 		}
141 	    }
142 	    break;
143 
144 	    default:
145 		new_str[i++] = *str++;
146 		break;
147 	    }
148 	}
149 	else {
150 	    if (*str == '\\') {
151 		seenbs = 1;
152 		str++;
153 	    }
154 	    else
155 		new_str[i++] = *str++;
156 	}
157     }
158 
159     if (seenbs) {
160 	/*
161 	 * The final character was a '\'. Put it in as a single backslash.
162 	 */
163 	new_str[i++] = '\\';
164     }
165     new_str[i] = '\0';
166     return new_str;
167 }
168