init version.
[bush.git] / lib / sh / strtrans.c
blobc8390f96228f5f8b0c4cc69fccfcd40e1b5a45a3
1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
3 /* Copyright (C) 2000-2015 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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.
12 Bush 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.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 #include <config.h>
23 #if defined (HAVE_UNISTD_H)
24 # include <unistd.h>
25 #endif
27 #include <bushansi.h>
28 #include <stdio.h>
29 #include <chartypes.h>
31 #include "shell.h"
33 #include "shmbchar.h"
34 #include "shmbutil.h"
36 #ifdef ESC
37 #undef ESC
38 #endif
39 #define ESC '\033' /* ASCII */
41 /* 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
44 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
48 quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
49 to remove the backslash before any unrecognized escape sequence. */
50 char *
51 ansicstr (string, len, flags, sawc, rlen)
52 char *string;
53 int len, flags, *sawc, *rlen;
55 int c, temp;
56 char *ret, *r, *s;
57 unsigned long v;
58 size_t clen;
59 int b, mb_cur_max;
60 #if defined (HANDLE_MULTIBYTE)
61 wchar_t wc;
62 #endif
64 if (string == 0 || *string == '\0')
65 return ((char *)NULL);
67 mb_cur_max = MB_CUR_MAX;
68 #if defined (HANDLE_MULTIBYTE)
69 temp = 4*len + 4;
70 if (temp < 12)
71 temp = 12; /* ensure enough for eventual u32cesc */
72 ret = (char *)xmalloc (temp);
73 #else
74 ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
75 #endif
76 for (r = ret, s = string; s && *s; )
78 c = *s++;
79 if (c != '\\' || *s == '\0')
81 clen = 1;
82 #if defined (HANDLE_MULTIBYTE)
83 if ((locale_utf8locale && (c & 0x80)) ||
84 (locale_utf8locale == 0 && mb_cur_max > 0 && is_basic (c) == 0))
86 clen = mbrtowc (&wc, s - 1, mb_cur_max, 0);
87 if (MB_INVALIDCH (clen))
88 clen = 1;
90 #endif
91 *r++ = c;
92 for (--clen; clen > 0; clen--)
93 *r++ = *s++;
95 else
97 switch (c = *s++)
99 #if defined (__STDC__)
100 case 'a': c = '\a'; break;
101 case 'v': c = '\v'; break;
102 #else
103 case 'a': c = (int) 0x07; break;
104 case 'v': c = (int) 0x0B; break;
105 #endif
106 case 'b': c = '\b'; break;
107 case 'e': case 'E': /* ESC -- non-ANSI */
108 c = ESC; break;
109 case 'f': c = '\f'; break;
110 case 'n': c = '\n'; break;
111 case 'r': c = '\r'; break;
112 case 't': c = '\t'; break;
113 case '1': case '2': case '3':
114 case '4': case '5': case '6':
115 case '7':
116 #if 1
117 if (flags & 1)
119 *r++ = '\\';
120 break;
122 /*FALLTHROUGH*/
123 #endif
124 case '0':
125 /* If (FLAGS & 1), we're translating a string for echo -e (or
126 the equivalent xpg_echo option), so we obey the SUSv3/
127 POSIX-2001 requirement and accept 0-3 octal digits after
128 a leading `0'. */
129 temp = 2 + ((flags & 1) && (c == '0'));
130 for (c -= '0'; ISOCTAL (*s) && temp--; s++)
131 c = (c * 8) + OCTVALUE (*s);
132 c &= 0xFF;
133 break;
134 case 'x': /* Hex digit -- non-ANSI */
135 if ((flags & 2) && *s == '{')
137 flags |= 16; /* internal flag value */
138 s++;
140 /* Consume at least two hex characters */
141 for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
142 c = (c * 16) + HEXVALUE (*s);
143 /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
144 until a non-xdigit or `}', so potentially more than two
145 chars are consumed. */
146 if (flags & 16)
148 for ( ; ISXDIGIT ((unsigned char)*s); s++)
149 c = (c * 16) + HEXVALUE (*s);
150 flags &= ~16;
151 if (*s == '}')
152 s++;
154 /* \x followed by non-hex digits is passed through unchanged */
155 else if (temp == 2)
157 *r++ = '\\';
158 c = 'x';
160 c &= 0xFF;
161 break;
162 #if defined (HANDLE_MULTIBYTE)
163 case 'u':
164 case 'U':
165 temp = (c == 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */
166 for (v = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
167 v = (v * 16) + HEXVALUE (*s);
168 if (temp == ((c == 'u') ? 4 : 8))
170 *r++ = '\\'; /* c remains unchanged */
171 break;
173 else if (v <= 0x7f) /* <= 0x7f translates directly */
175 c = v;
176 break;
178 else
180 temp = u32cconv (v, r);
181 r += temp;
182 continue;
184 #endif
185 case '\\':
186 break;
187 case '\'': case '"': case '?':
188 if (flags & 1)
189 *r++ = '\\';
190 break;
191 case 'c':
192 if (sawc)
194 *sawc = 1;
195 *r = '\0';
196 if (rlen)
197 *rlen = r - ret;
198 return ret;
200 else if ((flags & 1) == 0 && *s == 0)
201 ; /* pass \c through */
202 else if ((flags & 1) == 0 && (c = *s))
204 s++;
205 if ((flags & 2) && c == '\\' && c == *s)
206 s++; /* Posix requires $'\c\\' do backslash escaping */
207 c = TOCTRL(c);
208 break;
210 /*FALLTHROUGH*/
211 default:
212 if ((flags & 4) == 0)
213 *r++ = '\\';
214 break;
216 if ((flags & 2) && (c == CTLESC || c == CTLNUL))
217 *r++ = CTLESC;
218 *r++ = c;
221 *r = '\0';
222 if (rlen)
223 *rlen = r - ret;
224 return ret;
227 /* Take a string STR, possibly containing non-printing characters, and turn it
228 into a $'...' ANSI-C style quoted string. Returns a new string. */
229 char *
230 ansic_quote (str, flags, rlen)
231 char *str;
232 int flags, *rlen;
234 char *r, *ret, *s;
235 int l, rsize;
236 unsigned char c;
237 size_t clen;
238 int b;
239 #if defined (HANDLE_MULTIBYTE)
240 wchar_t wc;
241 #endif
243 if (str == 0 || *str == 0)
244 return ((char *)0);
246 l = strlen (str);
247 rsize = 4 * l + 4;
248 r = ret = (char *)xmalloc (rsize);
250 *r++ = '$';
251 *r++ = '\'';
253 for (s = str; c = *s; s++)
255 b = l = 1; /* 1 == add backslash; 0 == no backslash */
256 clen = 1;
258 switch (c)
260 case ESC: c = 'E'; break;
261 #ifdef __STDC__
262 case '\a': c = 'a'; break;
263 case '\v': c = 'v'; break;
264 #else
265 case 0x07: c = 'a'; break;
266 case 0x0b: c = 'v'; break;
267 #endif
269 case '\b': c = 'b'; break;
270 case '\f': c = 'f'; break;
271 case '\n': c = 'n'; break;
272 case '\r': c = 'r'; break;
273 case '\t': c = 't'; break;
274 case '\\':
275 case '\'':
276 break;
277 default:
278 #if defined (HANDLE_MULTIBYTE)
279 b = is_basic (c);
280 /* XXX - clen comparison to 0 is dicey */
281 if ((b == 0 && ((clen = mbrtowc (&wc, s, MB_CUR_MAX, 0)) < 0 || MB_INVALIDCH (clen) || iswprint (wc) == 0)) ||
282 (b == 1 && ISPRINT (c) == 0))
283 #else
284 if (ISPRINT (c) == 0)
285 #endif
287 *r++ = '\\';
288 *r++ = TOCHAR ((c >> 6) & 07);
289 *r++ = TOCHAR ((c >> 3) & 07);
290 *r++ = TOCHAR (c & 07);
291 continue;
293 l = 0;
294 break;
296 if (b == 0 && clen == 0)
297 break;
299 if (l)
300 *r++ = '\\';
302 if (clen == 1)
303 *r++ = c;
304 else
306 for (b = 0; b < (int)clen; b++)
307 *r++ = (unsigned char)s[b];
308 s += clen - 1; /* -1 because of the increment above */
312 *r++ = '\'';
313 *r = '\0';
314 if (rlen)
315 *rlen = r - ret;
316 return ret;
319 #if defined (HANDLE_MULTIBYTE)
321 ansic_wshouldquote (string)
322 const char *string;
324 const wchar_t *wcs;
325 wchar_t wcc;
326 wchar_t *wcstr = NULL;
327 size_t slen;
329 slen = mbstowcs (wcstr, string, 0);
331 if (slen == (size_t)-1)
332 return 1;
334 wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
335 mbstowcs (wcstr, string, slen + 1);
337 for (wcs = wcstr; wcc = *wcs; wcs++)
338 if (iswprint(wcc) == 0)
340 free (wcstr);
341 return 1;
344 free (wcstr);
345 return 0;
347 #endif
349 /* return 1 if we need to quote with $'...' because of non-printing chars. */
351 ansic_shouldquote (string)
352 const char *string;
354 const char *s;
355 unsigned char c;
357 if (string == 0)
358 return 0;
360 for (s = string; c = *s; s++)
362 #if defined (HANDLE_MULTIBYTE)
363 if (is_basic (c) == 0)
364 return (ansic_wshouldquote (s));
365 #endif
366 if (ISPRINT (c) == 0)
367 return 1;
370 return 0;
373 /* $'...' ANSI-C expand the portion of STRING between START and END and
374 return the result. The result cannot be longer than the input string. */
375 char *
376 ansiexpand (string, start, end, lenp)
377 char *string;
378 int start, end, *lenp;
380 char *temp, *t;
381 int len, tlen;
383 temp = (char *)xmalloc (end - start + 1);
384 for (tlen = 0, len = start; len < end; )
385 temp[tlen++] = string[len++];
386 temp[tlen] = '\0';
388 if (*temp)
390 t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
391 free (temp);
392 return (t);
394 else
396 if (lenp)
397 *lenp = 0;
398 return (temp);