2265782f8ee8d41f19e5b557527dff19e9355ead
[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-2010 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 #ifdef ESC
34 #undef ESC
35 #endif
36 #define ESC '\033'      /* ASCII */
37
38 /* Convert STRING by expanding the escape sequences specified by the
39    ANSI C standard.  If SAWC is non-null, recognize `\c' and use that
40    as a string terminator.  If we see \c, set *SAWC to 1 before
41    returning.  LEN is the length of STRING.  If (FLAGS&1) is non-zero,
42    that we're translating a string for `echo -e', and therefore should not
43    treat a single quote as a character that may be escaped with a backslash.
44    If (FLAGS&2) is non-zero, we're expanding for the parser and want to
45    quote CTLESC and CTLNUL with CTLESC.  If (flags&4) is non-zero, we want
46    to remove the backslash before any unrecognized escape sequence. */
47 char *
48 ansicstr (string, len, flags, sawc, rlen)
49      char *string;
50      int len, flags, *sawc, *rlen;
51 {
52   int c, temp;
53   char *ret, *r, *s;
54   unsigned long v;
55
56   if (string == 0 || *string == '\0')
57     return ((char *)NULL);
58
59 #if defined (HANDLE_MULTIBYTE)
60   ret = (char *)xmalloc (4*len + 1);
61 #else
62   ret = (char *)xmalloc (2*len + 1);    /* 2*len for possible CTLESC */
63 #endif
64   for (r = ret, s = string; s && *s; )
65     {
66       c = *s++;
67       if (c != '\\' || *s == '\0')
68         *r++ = c;
69       else
70         {
71           switch (c = *s++)
72             {
73 #if defined (__STDC__)
74             case 'a': c = '\a'; break;
75             case 'v': c = '\v'; break;
76 #else
77             case 'a': c = '\007'; break;
78             case 'v': c = (int) 0x0B; break;
79 #endif
80             case 'b': c = '\b'; break;
81             case 'e': case 'E':         /* ESC -- non-ANSI */
82               c = ESC; break;
83             case 'f': c = '\f'; break;
84             case 'n': c = '\n'; break;
85             case 'r': c = '\r'; break;
86             case 't': c = '\t'; break;
87             case '1': case '2': case '3':
88             case '4': case '5': case '6':
89             case '7':
90 #if 1
91               if (flags & 1)
92                 {
93                   *r++ = '\\';
94                   break;
95                 }
96             /*FALLTHROUGH*/
97 #endif
98             case '0':
99               /* If (FLAGS & 1), we're translating a string for echo -e (or
100                  the equivalent xpg_echo option), so we obey the SUSv3/
101                  POSIX-2001 requirement and accept 0-3 octal digits after
102                  a leading `0'. */
103               temp = 2 + ((flags & 1) && (c == '0'));
104               for (c -= '0'; ISOCTAL (*s) && temp--; s++)
105                 c = (c * 8) + OCTVALUE (*s);
106               c &= 0xFF;
107               break;
108             case 'x':                   /* Hex digit -- non-ANSI */
109               if ((flags & 2) && *s == '{')
110                 {
111                   flags |= 16;          /* internal flag value */
112                   s++;
113                 }
114               /* Consume at least two hex characters */
115               for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
116                 c = (c * 16) + HEXVALUE (*s);
117               /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
118                  until a non-xdigit or `}', so potentially more than two
119                  chars are consumed. */
120               if (flags & 16)
121                 {
122                   for ( ; ISXDIGIT ((unsigned char)*s); s++)
123                     c = (c * 16) + HEXVALUE (*s);
124                   flags &= ~16;
125                   if (*s == '}')
126                     s++;
127                 }
128               /* \x followed by non-hex digits is passed through unchanged */
129               else if (temp == 2)
130                 {
131                   *r++ = '\\';
132                   c = 'x';
133                 }
134               c &= 0xFF;
135               break;
136 #if defined (HANDLE_MULTIBYTE)
137             case 'u':
138             case 'U':
139               temp = (c == 'u') ? 4 : 8;        /* \uNNNN \UNNNNNNNN */
140               for (v = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
141                 v = (v * 16) + HEXVALUE (*s);
142               if (temp == ((c == 'u') ? 4 : 8))
143                 {
144                   *r++ = '\\';  /* c remains unchanged */
145                   break;
146                 }
147               else if (v <= UCHAR_MAX)
148                 {
149                   c = v;
150                   break;
151                 }
152               else
153                 {
154                   temp = u32cconv (v, r);
155                   r += temp;
156                   continue;
157                 }
158 #endif
159             case '\\':
160               break;
161             case '\'': case '"': case '?':
162               if (flags & 1)
163                 *r++ = '\\';
164               break;
165             case 'c':
166               if (sawc)
167                 {
168                   *sawc = 1;
169                   *r = '\0';
170                   if (rlen)
171                     *rlen = r - ret;
172                   return ret;
173                 }
174               else if ((flags & 1) == 0 && *s == 0)
175                 ;               /* pass \c through */
176               else if ((flags & 1) == 0 && (c = *s))
177                 {
178                   s++;
179                   if ((flags & 2) && c == '\\' && c == *s)
180                     s++;        /* Posix requires $'\c\\' do backslash escaping */
181                   c = TOCTRL(c);
182                   break;
183                 }
184                 /*FALLTHROUGH*/
185             default:
186                 if ((flags & 4) == 0)
187                   *r++ = '\\';
188                 break;
189             }
190           if ((flags & 2) && (c == CTLESC || c == CTLNUL))
191             *r++ = CTLESC;
192           *r++ = c;
193         }
194     }
195   *r = '\0';
196   if (rlen)
197     *rlen = r - ret;
198   return ret;
199 }
200
201 /* Take a string STR, possibly containing non-printing characters, and turn it
202    into a $'...' ANSI-C style quoted string.  Returns a new string. */
203 char *
204 ansic_quote (str, flags, rlen)
205      char *str;
206      int flags, *rlen;
207 {
208   char *r, *ret, *s;
209   int l, rsize;
210   unsigned char c;
211
212   if (str == 0 || *str == 0)
213     return ((char *)0);
214
215   l = strlen (str);
216   rsize = 4 * l + 4;
217   r = ret = (char *)xmalloc (rsize);
218
219   *r++ = '$';
220   *r++ = '\'';
221
222   for (s = str, l = 0; *s; s++)
223     {
224       c = *s;
225       l = 1;            /* 1 == add backslash; 0 == no backslash */
226       switch (c)
227         {
228         case ESC: c = 'E'; break;
229 #ifdef __STDC__
230         case '\a': c = 'a'; break;
231         case '\v': c = 'v'; break;
232 #else
233         case '\007': c = 'a'; break;
234         case 0x0b: c = 'v'; break;
235 #endif
236
237         case '\b': c = 'b'; break;
238         case '\f': c = 'f'; break;
239         case '\n': c = 'n'; break;
240         case '\r': c = 'r'; break;
241         case '\t': c = 't'; break;
242         case '\\':
243         case '\'':
244           break;
245         default:
246           if (ISPRINT (c) == 0)
247             {
248               *r++ = '\\';
249               *r++ = TOCHAR ((c >> 6) & 07);
250               *r++ = TOCHAR ((c >> 3) & 07);
251               *r++ = TOCHAR (c & 07);
252               continue;
253             }
254           l = 0;
255           break;
256         }
257       if (l)
258         *r++ = '\\';
259       *r++ = c;
260     }
261
262   *r++ = '\'';
263   *r = '\0';
264   if (rlen)
265     *rlen = r - ret;
266   return ret;
267 }
268
269 /* return 1 if we need to quote with $'...' because of non-printing chars. */
270 int
271 ansic_shouldquote (string)
272      const char *string;
273 {
274   const char *s;
275   unsigned char c;
276
277   if (string == 0)
278     return 0;
279
280   for (s = string; c = *s; s++)
281     if (ISPRINT (c) == 0)
282       return 1;
283
284   return 0;
285 }
286
287 /* $'...' ANSI-C expand the portion of STRING between START and END and
288    return the result.  The result cannot be longer than the input string. */
289 char *
290 ansiexpand (string, start, end, lenp)
291      char *string;
292      int start, end, *lenp;
293 {
294   char *temp, *t;
295   int len, tlen;
296
297   temp = (char *)xmalloc (end - start + 1);
298   for (tlen = 0, len = start; len < end; )
299     temp[tlen++] = string[len++];
300   temp[tlen] = '\0';
301
302   if (*temp)
303     {
304       t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
305       free (temp);
306       return (t);
307     }
308   else
309     {
310       if (lenp)
311         *lenp = 0;
312       return (temp);
313     }
314 }