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 */
30char *
31backslash(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