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