interp_backslash.c revision 329183
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: stable/11/stand/common/interp_backslash.c 329183 2018-02-12 20:51:28Z kevans $");
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 */
30char *
31backslash(const 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