blob: ae88e694db67e6e9349c318a5de736873d6e60b7 [file] [log] [blame]
Jari Aalto31859422009-01-12 13:36:28 +00001/* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
Jari Aalto28ef6c32001-04-06 19:14:31 +00002
Chet Rameyac50fba2014-02-26 09:36:43 -05003/* Copyright (C) 2000-2011 Free Software Foundation, Inc.
Jari Aalto28ef6c32001-04-06 19:14:31 +00004
5 This file is part of GNU Bash, the Bourne Again SHell.
6
Jari Aalto31859422009-01-12 13:36:28 +00007 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
Jari Aalto28ef6c32001-04-06 19:14:31 +000011
Jari Aalto31859422009-01-12 13:36:28 +000012 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
Jari Aalto28ef6c32001-04-06 19:14:31 +000016
Jari Aalto31859422009-01-12 13:36:28 +000017 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
Jari Aalto28ef6c32001-04-06 19:14:31 +000020
21#include <config.h>
22
23#if defined (HAVE_UNISTD_H)
24# include <unistd.h>
25#endif
26
Jari Aaltof73dda02001-11-13 17:56:06 +000027#include <bashansi.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000028#include <stdio.h>
Jari Aaltof73dda02001-11-13 17:56:06 +000029#include <chartypes.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000030
31#include "shell.h"
32
Chet Rameyac50fba2014-02-26 09:36:43 -050033#include "shmbchar.h"
34#include "shmbutil.h"
35
Jari Aalto28ef6c32001-04-06 19:14:31 +000036#ifdef ESC
37#undef ESC
38#endif
39#define ESC '\033' /* ASCII */
40
Jari Aalto28ef6c32001-04-06 19:14:31 +000041/* Convert STRING by expanding the escape sequences specified by the
42 ANSI C standard. If SAWC is non-null, recognize `\c' and use that
43 as a string terminator. If we see \c, set *SAWC to 1 before
Jari Aalto7117c2d2002-07-17 14:10:11 +000044 returning. LEN is the length of STRING. If (FLAGS&1) is non-zero,
45 that we're translating a string for `echo -e', and therefore should not
46 treat a single quote as a character that may be escaped with a backslash.
47 If (FLAGS&2) is non-zero, we're expanding for the parser and want to
Jari Aalto95732b42005-12-07 14:08:12 +000048 quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
49 to remove the backslash before any unrecognized escape sequence. */
Jari Aalto28ef6c32001-04-06 19:14:31 +000050char *
Jari Aalto7117c2d2002-07-17 14:10:11 +000051ansicstr (string, len, flags, sawc, rlen)
Jari Aalto28ef6c32001-04-06 19:14:31 +000052 char *string;
Jari Aalto7117c2d2002-07-17 14:10:11 +000053 int len, flags, *sawc, *rlen;
Jari Aalto28ef6c32001-04-06 19:14:31 +000054{
55 int c, temp;
56 char *ret, *r, *s;
Chet Ramey495aee42011-11-22 19:11:26 -050057 unsigned long v;
Jari Aalto28ef6c32001-04-06 19:14:31 +000058
59 if (string == 0 || *string == '\0')
60 return ((char *)NULL);
61
Chet Ramey495aee42011-11-22 19:11:26 -050062#if defined (HANDLE_MULTIBYTE)
63 ret = (char *)xmalloc (4*len + 1);
64#else
Jari Aalto7117c2d2002-07-17 14:10:11 +000065 ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
Chet Ramey495aee42011-11-22 19:11:26 -050066#endif
Jari Aalto28ef6c32001-04-06 19:14:31 +000067 for (r = ret, s = string; s && *s; )
68 {
69 c = *s++;
70 if (c != '\\' || *s == '\0')
71 *r++ = c;
72 else
73 {
74 switch (c = *s++)
75 {
76#if defined (__STDC__)
77 case 'a': c = '\a'; break;
78 case 'v': c = '\v'; break;
79#else
Chet Rameyac50fba2014-02-26 09:36:43 -050080 case 'a': c = (int) 0x07; break;
Jari Aalto28ef6c32001-04-06 19:14:31 +000081 case 'v': c = (int) 0x0B; break;
82#endif
83 case 'b': c = '\b'; break;
84 case 'e': case 'E': /* ESC -- non-ANSI */
85 c = ESC; break;
86 case 'f': c = '\f'; break;
87 case 'n': c = '\n'; break;
88 case 'r': c = '\r'; break;
89 case 't': c = '\t'; break;
Jari Aalto06285672006-10-10 14:15:34 +000090 case '1': case '2': case '3':
91 case '4': case '5': case '6':
92 case '7':
93#if 1
94 if (flags & 1)
95 {
96 *r++ = '\\';
97 break;
98 }
99 /*FALLTHROUGH*/
100#endif
101 case '0':
Jari Aalto7117c2d2002-07-17 14:10:11 +0000102 /* If (FLAGS & 1), we're translating a string for echo -e (or
103 the equivalent xpg_echo option), so we obey the SUSv3/
104 POSIX-2001 requirement and accept 0-3 octal digits after
105 a leading `0'. */
106 temp = 2 + ((flags & 1) && (c == '0'));
107 for (c -= '0'; ISOCTAL (*s) && temp--; s++)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000108 c = (c * 8) + OCTVALUE (*s);
Jari Aaltof73dda02001-11-13 17:56:06 +0000109 c &= 0xFF;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000110 break;
111 case 'x': /* Hex digit -- non-ANSI */
Jari Aaltob80f6442004-07-27 13:29:18 +0000112 if ((flags & 2) && *s == '{')
113 {
114 flags |= 16; /* internal flag value */
115 s++;
116 }
117 /* Consume at least two hex characters */
Jari Aaltof73dda02001-11-13 17:56:06 +0000118 for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000119 c = (c * 16) + HEXVALUE (*s);
Jari Aaltob80f6442004-07-27 13:29:18 +0000120 /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
121 until a non-xdigit or `}', so potentially more than two
122 chars are consumed. */
123 if (flags & 16)
124 {
125 for ( ; ISXDIGIT ((unsigned char)*s); s++)
126 c = (c * 16) + HEXVALUE (*s);
127 flags &= ~16;
128 if (*s == '}')
129 s++;
130 }
Jari Aalto28ef6c32001-04-06 19:14:31 +0000131 /* \x followed by non-hex digits is passed through unchanged */
Jari Aaltob80f6442004-07-27 13:29:18 +0000132 else if (temp == 2)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000133 {
134 *r++ = '\\';
135 c = 'x';
136 }
Jari Aaltof73dda02001-11-13 17:56:06 +0000137 c &= 0xFF;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000138 break;
Chet Ramey495aee42011-11-22 19:11:26 -0500139#if defined (HANDLE_MULTIBYTE)
140 case 'u':
141 case 'U':
142 temp = (c == 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */
143 for (v = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
144 v = (v * 16) + HEXVALUE (*s);
145 if (temp == ((c == 'u') ? 4 : 8))
146 {
147 *r++ = '\\'; /* c remains unchanged */
148 break;
149 }
Chet Rameyac50fba2014-02-26 09:36:43 -0500150 else if (v <= 0x7f) /* <= 0x7f translates directly */
Chet Ramey495aee42011-11-22 19:11:26 -0500151 {
152 c = v;
153 break;
154 }
155 else
156 {
157 temp = u32cconv (v, r);
158 r += temp;
159 continue;
160 }
161#endif
Jari Aalto28ef6c32001-04-06 19:14:31 +0000162 case '\\':
163 break;
Jari Aaltob80f6442004-07-27 13:29:18 +0000164 case '\'': case '"': case '?':
Jari Aalto7117c2d2002-07-17 14:10:11 +0000165 if (flags & 1)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000166 *r++ = '\\';
167 break;
168 case 'c':
169 if (sawc)
170 {
171 *sawc = 1;
172 *r = '\0';
173 if (rlen)
174 *rlen = r - ret;
175 return ret;
176 }
Chet Ramey495aee42011-11-22 19:11:26 -0500177 else if ((flags & 1) == 0 && *s == 0)
178 ; /* pass \c through */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000179 else if ((flags & 1) == 0 && (c = *s))
180 {
181 s++;
Chet Ramey495aee42011-11-22 19:11:26 -0500182 if ((flags & 2) && c == '\\' && c == *s)
183 s++; /* Posix requires $'\c\\' do backslash escaping */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000184 c = TOCTRL(c);
185 break;
186 }
187 /*FALLTHROUGH*/
Jari Aalto95732b42005-12-07 14:08:12 +0000188 default:
189 if ((flags & 4) == 0)
190 *r++ = '\\';
191 break;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000192 }
Jari Aalto7117c2d2002-07-17 14:10:11 +0000193 if ((flags & 2) && (c == CTLESC || c == CTLNUL))
194 *r++ = CTLESC;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000195 *r++ = c;
196 }
197 }
198 *r = '\0';
199 if (rlen)
200 *rlen = r - ret;
201 return ret;
202}
203
204/* Take a string STR, possibly containing non-printing characters, and turn it
205 into a $'...' ANSI-C style quoted string. Returns a new string. */
206char *
207ansic_quote (str, flags, rlen)
208 char *str;
209 int flags, *rlen;
210{
Jari Aalto7117c2d2002-07-17 14:10:11 +0000211 char *r, *ret, *s;
Jari Aalto06285672006-10-10 14:15:34 +0000212 int l, rsize;
Jari Aaltof73dda02001-11-13 17:56:06 +0000213 unsigned char c;
Chet Rameyac50fba2014-02-26 09:36:43 -0500214 size_t clen;
215 int b;
216#if defined (HANDLE_MULTIBYTE)
217 wchar_t wc;
218#endif
Jari Aalto28ef6c32001-04-06 19:14:31 +0000219
220 if (str == 0 || *str == 0)
221 return ((char *)0);
222
223 l = strlen (str);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000224 rsize = 4 * l + 4;
Jari Aaltof73dda02001-11-13 17:56:06 +0000225 r = ret = (char *)xmalloc (rsize);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000226
227 *r++ = '$';
228 *r++ = '\'';
229
Chet Rameyac50fba2014-02-26 09:36:43 -0500230 s = str;
231
232 for (s = str; c = *s; s++)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000233 {
Chet Rameyac50fba2014-02-26 09:36:43 -0500234 b = l = 1; /* 1 == add backslash; 0 == no backslash */
235 clen = 1;
236
Jari Aalto28ef6c32001-04-06 19:14:31 +0000237 switch (c)
238 {
239 case ESC: c = 'E'; break;
240#ifdef __STDC__
241 case '\a': c = 'a'; break;
242 case '\v': c = 'v'; break;
243#else
Chet Rameyac50fba2014-02-26 09:36:43 -0500244 case 0x07: c = 'a'; break;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000245 case 0x0b: c = 'v'; break;
246#endif
247
248 case '\b': c = 'b'; break;
249 case '\f': c = 'f'; break;
250 case '\n': c = 'n'; break;
251 case '\r': c = 'r'; break;
252 case '\t': c = 't'; break;
253 case '\\':
254 case '\'':
255 break;
256 default:
Chet Rameyac50fba2014-02-26 09:36:43 -0500257#if defined (HANDLE_MULTIBYTE)
258 b = is_basic (c);
259 /* XXX - clen comparison to 0 is dicey */
260 if ((b == 0 && ((clen = mbrtowc (&wc, s, MB_CUR_MAX, 0)) < 0 || MB_INVALIDCH (clen) || iswprint (wc) == 0)) ||
261 (b == 1 && ISPRINT (c) == 0))
262#else
Jari Aaltof73dda02001-11-13 17:56:06 +0000263 if (ISPRINT (c) == 0)
Chet Rameyac50fba2014-02-26 09:36:43 -0500264#endif
Jari Aalto28ef6c32001-04-06 19:14:31 +0000265 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000266 *r++ = '\\';
267 *r++ = TOCHAR ((c >> 6) & 07);
268 *r++ = TOCHAR ((c >> 3) & 07);
269 *r++ = TOCHAR (c & 07);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000270 continue;
271 }
272 l = 0;
273 break;
274 }
Chet Rameyac50fba2014-02-26 09:36:43 -0500275 if (b == 0 && clen == 0)
276 break;
277
Jari Aalto28ef6c32001-04-06 19:14:31 +0000278 if (l)
279 *r++ = '\\';
Chet Rameyac50fba2014-02-26 09:36:43 -0500280
281 if (clen == 1)
282 *r++ = c;
283 else
284 {
285 for (b = 0; b < (int)clen; b++)
286 *r++ = (unsigned char)s[b];
287 s += clen - 1; /* -1 because of the increment above */
288 }
Jari Aalto28ef6c32001-04-06 19:14:31 +0000289 }
290
291 *r++ = '\'';
292 *r = '\0';
293 if (rlen)
294 *rlen = r - ret;
295 return ret;
296}
Jari Aaltof73dda02001-11-13 17:56:06 +0000297
Chet Rameyac50fba2014-02-26 09:36:43 -0500298#if defined (HANDLE_MULTIBYTE)
299int
300ansic_wshouldquote (string)
301 const char *string;
302{
303 const wchar_t *wcs;
304 wchar_t wcc;
305
306 wchar_t *wcstr = NULL;
307 size_t slen;
308
309
310 slen = mbstowcs (wcstr, string, 0);
311
312 if (slen == -1)
313 slen = 0;
314 wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
315 mbstowcs (wcstr, string, slen + 1);
316
317 for (wcs = wcstr; wcc = *wcs; wcs++)
318 if (iswprint(wcc) == 0)
319 {
320 free (wcstr);
321 return 1;
322 }
323
324 free (wcstr);
325 return 0;
326}
327#endif
328
Jari Aaltof73dda02001-11-13 17:56:06 +0000329/* return 1 if we need to quote with $'...' because of non-printing chars. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000330int
Jari Aaltof73dda02001-11-13 17:56:06 +0000331ansic_shouldquote (string)
332 const char *string;
333{
334 const char *s;
335 unsigned char c;
336
337 if (string == 0)
338 return 0;
339
340 for (s = string; c = *s; s++)
Chet Rameyac50fba2014-02-26 09:36:43 -0500341 {
342#if defined (HANDLE_MULTIBYTE)
343 if (is_basic (c) == 0)
344 return (ansic_wshouldquote (s));
345#endif
346 if (ISPRINT (c) == 0)
347 return 1;
348 }
Jari Aaltof73dda02001-11-13 17:56:06 +0000349
350 return 0;
351}
Jari Aalto7117c2d2002-07-17 14:10:11 +0000352
353/* $'...' ANSI-C expand the portion of STRING between START and END and
354 return the result. The result cannot be longer than the input string. */
355char *
356ansiexpand (string, start, end, lenp)
357 char *string;
358 int start, end, *lenp;
359{
360 char *temp, *t;
361 int len, tlen;
362
363 temp = (char *)xmalloc (end - start + 1);
364 for (tlen = 0, len = start; len < end; )
365 temp[tlen++] = string[len++];
366 temp[tlen] = '\0';
367
368 if (*temp)
369 {
370 t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
371 free (temp);
372 return (t);
373 }
374 else
375 {
376 if (lenp)
377 *lenp = 0;
378 return (temp);
379 }
380}