Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / sh / strtrans.c
1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
2
3 /* Copyright (C) 2000-2011 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    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.
11
12    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.
16
17    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 */
20
21 #include <config.h>
22
23 #if defined (HAVE_UNISTD_H)
24 #  include <unistd.h>
25 #endif
26
27 #include <bashansi.h>
28 #include <stdio.h>
29 #include <chartypes.h>
30
31 #include "shell.h"
32
33 #include "shmbchar.h"
34 #include "shmbutil.h"
35
36 #ifdef ESC
37 #undef ESC
38 #endif
39 #define ESC '\033'      /* ASCII */
40
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;
54 {
55   int c, temp;
56   char *ret, *r, *s;
57   unsigned long v;
58
59   if (string == 0 || *string == '\0')
60     return ((char *)NULL);
61
62 #if defined (HANDLE_MULTIBYTE)
63   ret = (char *)xmalloc (4*len + 1);
64 #else
65   ret = (char *)xmalloc (2*len + 1);    /* 2*len for possible CTLESC */
66 #endif
67   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
80             case 'a': c = (int) 0x07; break;
81             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;
90             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':
102               /* 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++)
108                 c = (c * 8) + OCTVALUE (*s);
109               c &= 0xFF;
110               break;
111             case 'x':                   /* Hex digit -- non-ANSI */
112               if ((flags & 2) && *s == '{')
113                 {
114                   flags |= 16;          /* internal flag value */
115                   s++;
116                 }
117               /* Consume at least two hex characters */
118               for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
119                 c = (c * 16) + HEXVALUE (*s);
120               /* 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                 }
131               /* \x followed by non-hex digits is passed through unchanged */
132               else if (temp == 2)
133                 {
134                   *r++ = '\\';
135                   c = 'x';
136                 }
137               c &= 0xFF;
138               break;
139 #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                 }
150               else if (v <= 0x7f)       /* <= 0x7f translates directly */
151                 {
152                   c = v;
153                   break;
154                 }
155               else
156                 {
157                   temp = u32cconv (v, r);
158                   r += temp;
159                   continue;
160                 }
161 #endif
162             case '\\':
163               break;
164             case '\'': case '"': case '?':
165               if (flags & 1)
166                 *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                 }
177               else if ((flags & 1) == 0 && *s == 0)
178                 ;               /* pass \c through */
179               else if ((flags & 1) == 0 && (c = *s))
180                 {
181                   s++;
182                   if ((flags & 2) && c == '\\' && c == *s)
183                     s++;        /* Posix requires $'\c\\' do backslash escaping */
184                   c = TOCTRL(c);
185                   break;
186                 }
187                 /*FALLTHROUGH*/
188             default:
189                 if ((flags & 4) == 0)
190                   *r++ = '\\';
191                 break;
192             }
193           if ((flags & 2) && (c == CTLESC || c == CTLNUL))
194             *r++ = CTLESC;
195           *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. */
206 char *
207 ansic_quote (str, flags, rlen)
208      char *str;
209      int flags, *rlen;
210 {
211   char *r, *ret, *s;
212   int l, rsize;
213   unsigned char c;
214   size_t clen;
215   int b;
216 #if defined (HANDLE_MULTIBYTE)
217   wchar_t wc;
218 #endif
219
220   if (str == 0 || *str == 0)
221     return ((char *)0);
222
223   l = strlen (str);
224   rsize = 4 * l + 4;
225   r = ret = (char *)xmalloc (rsize);
226
227   *r++ = '$';
228   *r++ = '\'';
229
230   s = str;
231
232   for (s = str; c = *s; s++)
233     {
234       b = l = 1;                /* 1 == add backslash; 0 == no backslash */
235       clen = 1;
236
237       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
244         case 0x07: c = 'a'; break;
245         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:
257 #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
263           if (ISPRINT (c) == 0)
264 #endif
265             {
266               *r++ = '\\';
267               *r++ = TOCHAR ((c >> 6) & 07);
268               *r++ = TOCHAR ((c >> 3) & 07);
269               *r++ = TOCHAR (c & 07);
270               continue;
271             }
272           l = 0;
273           break;
274         }
275       if (b == 0 && clen == 0)
276         break;
277
278       if (l)
279         *r++ = '\\';
280
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         }
289     }
290
291   *r++ = '\'';
292   *r = '\0';
293   if (rlen)
294     *rlen = r - ret;
295   return ret;
296 }
297
298 #if defined (HANDLE_MULTIBYTE)
299 int
300 ansic_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
329 /* return 1 if we need to quote with $'...' because of non-printing chars. */
330 int
331 ansic_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++)
341     {
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     }
349
350   return 0;
351 }
352
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. */
355 char *
356 ansiexpand (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 }