]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/sh/strtrans.c
Imported from ../bash-3.2.tar.gz.
[thirdparty/bash.git] / lib / sh / strtrans.c
1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape
2 sequences. */
3
4 /* Copyright (C) 2000
5 Free Software Foundation, Inc.
6
7 This file is part of GNU Bash, the Bourne Again SHell.
8
9 Bash is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13
14 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with Bash; see the file COPYING. If not, write to the Free Software
21 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
23 #include <config.h>
24
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
28
29 #include <bashansi.h>
30 #include <stdio.h>
31 #include <chartypes.h>
32
33 #include "shell.h"
34
35 #ifdef ESC
36 #undef ESC
37 #endif
38 #define ESC '\033' /* ASCII */
39
40 /* Convert STRING by expanding the escape sequences specified by the
41 ANSI C standard. If SAWC is non-null, recognize `\c' and use that
42 as a string terminator. If we see \c, set *SAWC to 1 before
43 returning. LEN is the length of STRING. If (FLAGS&1) is non-zero,
44 that we're translating a string for `echo -e', and therefore should not
45 treat a single quote as a character that may be escaped with a backslash.
46 If (FLAGS&2) is non-zero, we're expanding for the parser and want to
47 quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
48 to remove the backslash before any unrecognized escape sequence. */
49 char *
50 ansicstr (string, len, flags, sawc, rlen)
51 char *string;
52 int len, flags, *sawc, *rlen;
53 {
54 int c, temp;
55 char *ret, *r, *s;
56
57 if (string == 0 || *string == '\0')
58 return ((char *)NULL);
59
60 ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
61 for (r = ret, s = string; s && *s; )
62 {
63 c = *s++;
64 if (c != '\\' || *s == '\0')
65 *r++ = c;
66 else
67 {
68 switch (c = *s++)
69 {
70 #if defined (__STDC__)
71 case 'a': c = '\a'; break;
72 case 'v': c = '\v'; break;
73 #else
74 case 'a': c = '\007'; break;
75 case 'v': c = (int) 0x0B; break;
76 #endif
77 case 'b': c = '\b'; break;
78 case 'e': case 'E': /* ESC -- non-ANSI */
79 c = ESC; break;
80 case 'f': c = '\f'; break;
81 case 'n': c = '\n'; break;
82 case 'r': c = '\r'; break;
83 case 't': c = '\t'; break;
84 case '1': case '2': case '3':
85 case '4': case '5': case '6':
86 case '7':
87 #if 1
88 if (flags & 1)
89 {
90 *r++ = '\\';
91 break;
92 }
93 /*FALLTHROUGH*/
94 #endif
95 case '0':
96 /* If (FLAGS & 1), we're translating a string for echo -e (or
97 the equivalent xpg_echo option), so we obey the SUSv3/
98 POSIX-2001 requirement and accept 0-3 octal digits after
99 a leading `0'. */
100 temp = 2 + ((flags & 1) && (c == '0'));
101 for (c -= '0'; ISOCTAL (*s) && temp--; s++)
102 c = (c * 8) + OCTVALUE (*s);
103 c &= 0xFF;
104 break;
105 case 'x': /* Hex digit -- non-ANSI */
106 if ((flags & 2) && *s == '{')
107 {
108 flags |= 16; /* internal flag value */
109 s++;
110 }
111 /* Consume at least two hex characters */
112 for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
113 c = (c * 16) + HEXVALUE (*s);
114 /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
115 until a non-xdigit or `}', so potentially more than two
116 chars are consumed. */
117 if (flags & 16)
118 {
119 for ( ; ISXDIGIT ((unsigned char)*s); s++)
120 c = (c * 16) + HEXVALUE (*s);
121 flags &= ~16;
122 if (*s == '}')
123 s++;
124 }
125 /* \x followed by non-hex digits is passed through unchanged */
126 else if (temp == 2)
127 {
128 *r++ = '\\';
129 c = 'x';
130 }
131 c &= 0xFF;
132 break;
133 case '\\':
134 break;
135 case '\'': case '"': case '?':
136 if (flags & 1)
137 *r++ = '\\';
138 break;
139 case 'c':
140 if (sawc)
141 {
142 *sawc = 1;
143 *r = '\0';
144 if (rlen)
145 *rlen = r - ret;
146 return ret;
147 }
148 else if ((flags & 1) == 0 && (c = *s))
149 {
150 s++;
151 c = TOCTRL(c);
152 break;
153 }
154 /*FALLTHROUGH*/
155 default:
156 if ((flags & 4) == 0)
157 *r++ = '\\';
158 break;
159 }
160 if ((flags & 2) && (c == CTLESC || c == CTLNUL))
161 *r++ = CTLESC;
162 *r++ = c;
163 }
164 }
165 *r = '\0';
166 if (rlen)
167 *rlen = r - ret;
168 return ret;
169 }
170
171 /* Take a string STR, possibly containing non-printing characters, and turn it
172 into a $'...' ANSI-C style quoted string. Returns a new string. */
173 char *
174 ansic_quote (str, flags, rlen)
175 char *str;
176 int flags, *rlen;
177 {
178 char *r, *ret, *s;
179 int l, rsize;
180 unsigned char c;
181
182 if (str == 0 || *str == 0)
183 return ((char *)0);
184
185 l = strlen (str);
186 rsize = 4 * l + 4;
187 r = ret = (char *)xmalloc (rsize);
188
189 *r++ = '$';
190 *r++ = '\'';
191
192 for (s = str, l = 0; *s; s++)
193 {
194 c = *s;
195 l = 1; /* 1 == add backslash; 0 == no backslash */
196 switch (c)
197 {
198 case ESC: c = 'E'; break;
199 #ifdef __STDC__
200 case '\a': c = 'a'; break;
201 case '\v': c = 'v'; break;
202 #else
203 case '\007': c = 'a'; break;
204 case 0x0b: c = 'v'; break;
205 #endif
206
207 case '\b': c = 'b'; break;
208 case '\f': c = 'f'; break;
209 case '\n': c = 'n'; break;
210 case '\r': c = 'r'; break;
211 case '\t': c = 't'; break;
212 case '\\':
213 case '\'':
214 break;
215 default:
216 if (ISPRINT (c) == 0)
217 {
218 *r++ = '\\';
219 *r++ = TOCHAR ((c >> 6) & 07);
220 *r++ = TOCHAR ((c >> 3) & 07);
221 *r++ = TOCHAR (c & 07);
222 continue;
223 }
224 l = 0;
225 break;
226 }
227 if (l)
228 *r++ = '\\';
229 *r++ = c;
230 }
231
232 *r++ = '\'';
233 *r = '\0';
234 if (rlen)
235 *rlen = r - ret;
236 return ret;
237 }
238
239 /* return 1 if we need to quote with $'...' because of non-printing chars. */
240 int
241 ansic_shouldquote (string)
242 const char *string;
243 {
244 const char *s;
245 unsigned char c;
246
247 if (string == 0)
248 return 0;
249
250 for (s = string; c = *s; s++)
251 if (ISPRINT (c) == 0)
252 return 1;
253
254 return 0;
255 }
256
257 /* $'...' ANSI-C expand the portion of STRING between START and END and
258 return the result. The result cannot be longer than the input string. */
259 char *
260 ansiexpand (string, start, end, lenp)
261 char *string;
262 int start, end, *lenp;
263 {
264 char *temp, *t;
265 int len, tlen;
266
267 temp = (char *)xmalloc (end - start + 1);
268 for (tlen = 0, len = start; len < end; )
269 temp[tlen++] = string[len++];
270 temp[tlen] = '\0';
271
272 if (*temp)
273 {
274 t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
275 free (temp);
276 return (t);
277 }
278 else
279 {
280 if (lenp)
281 *lenp = 0;
282 return (temp);
283 }
284 }