355a3061ee593f2c026668c11d0831f369559b81
[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 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
55   if (string == 0 || *string == '\0')
56     return ((char *)NULL);
57
58   ret = (char *)xmalloc (2*len + 1);    /* 2*len for possible CTLESC */
59   for (r = ret, s = string; s && *s; )
60     {
61       c = *s++;
62       if (c != '\\' || *s == '\0')
63         *r++ = c;
64       else
65         {
66           switch (c = *s++)
67             {
68 #if defined (__STDC__)
69             case 'a': c = '\a'; break;
70             case 'v': c = '\v'; break;
71 #else
72             case 'a': c = '\007'; break;
73             case 'v': c = (int) 0x0B; break;
74 #endif
75             case 'b': c = '\b'; break;
76             case 'e': case 'E':         /* ESC -- non-ANSI */
77               c = ESC; break;
78             case 'f': c = '\f'; break;
79             case 'n': c = '\n'; break;
80             case 'r': c = '\r'; break;
81             case 't': c = '\t'; break;
82             case '1': case '2': case '3':
83             case '4': case '5': case '6':
84             case '7':
85 #if 1
86               if (flags & 1)
87                 {
88                   *r++ = '\\';
89                   break;
90                 }
91             /*FALLTHROUGH*/
92 #endif
93             case '0':
94               /* If (FLAGS & 1), we're translating a string for echo -e (or
95                  the equivalent xpg_echo option), so we obey the SUSv3/
96                  POSIX-2001 requirement and accept 0-3 octal digits after
97                  a leading `0'. */
98               temp = 2 + ((flags & 1) && (c == '0'));
99               for (c -= '0'; ISOCTAL (*s) && temp--; s++)
100                 c = (c * 8) + OCTVALUE (*s);
101               c &= 0xFF;
102               break;
103             case 'x':                   /* Hex digit -- non-ANSI */
104               if ((flags & 2) && *s == '{')
105                 {
106                   flags |= 16;          /* internal flag value */
107                   s++;
108                 }
109               /* Consume at least two hex characters */
110               for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
111                 c = (c * 16) + HEXVALUE (*s);
112               /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
113                  until a non-xdigit or `}', so potentially more than two
114                  chars are consumed. */
115               if (flags & 16)
116                 {
117                   for ( ; ISXDIGIT ((unsigned char)*s); s++)
118                     c = (c * 16) + HEXVALUE (*s);
119                   flags &= ~16;
120                   if (*s == '}')
121                     s++;
122                 }
123               /* \x followed by non-hex digits is passed through unchanged */
124               else if (temp == 2)
125                 {
126                   *r++ = '\\';
127                   c = 'x';
128                 }
129               c &= 0xFF;
130               break;
131             case '\\':
132               break;
133             case '\'': case '"': case '?':
134               if (flags & 1)
135                 *r++ = '\\';
136               break;
137             case 'c':
138               if (sawc)
139                 {
140                   *sawc = 1;
141                   *r = '\0';
142                   if (rlen)
143                     *rlen = r - ret;
144                   return ret;
145                 }
146               else if ((flags & 1) == 0 && (c = *s))
147                 {
148                   s++;
149                   c = TOCTRL(c);
150                   break;
151                 }
152                 /*FALLTHROUGH*/
153             default:
154                 if ((flags & 4) == 0)
155                   *r++ = '\\';
156                 break;
157             }
158           if ((flags & 2) && (c == CTLESC || c == CTLNUL))
159             *r++ = CTLESC;
160           *r++ = c;
161         }
162     }
163   *r = '\0';
164   if (rlen)
165     *rlen = r - ret;
166   return ret;
167 }
168
169 /* Take a string STR, possibly containing non-printing characters, and turn it
170    into a $'...' ANSI-C style quoted string.  Returns a new string. */
171 char *
172 ansic_quote (str, flags, rlen)
173      char *str;
174      int flags, *rlen;
175 {
176   char *r, *ret, *s;
177   int l, rsize;
178   unsigned char c;
179
180   if (str == 0 || *str == 0)
181     return ((char *)0);
182
183   l = strlen (str);
184   rsize = 4 * l + 4;
185   r = ret = (char *)xmalloc (rsize);
186
187   *r++ = '$';
188   *r++ = '\'';
189
190   for (s = str, l = 0; *s; s++)
191     {
192       c = *s;
193       l = 1;            /* 1 == add backslash; 0 == no backslash */
194       switch (c)
195         {
196         case ESC: c = 'E'; break;
197 #ifdef __STDC__
198         case '\a': c = 'a'; break;
199         case '\v': c = 'v'; break;
200 #else
201         case '\007': c = 'a'; break;
202         case 0x0b: c = 'v'; break;
203 #endif
204
205         case '\b': c = 'b'; break;
206         case '\f': c = 'f'; break;
207         case '\n': c = 'n'; break;
208         case '\r': c = 'r'; break;
209         case '\t': c = 't'; break;
210         case '\\':
211         case '\'':
212           break;
213         default:
214           if (ISPRINT (c) == 0)
215             {
216               *r++ = '\\';
217               *r++ = TOCHAR ((c >> 6) & 07);
218               *r++ = TOCHAR ((c >> 3) & 07);
219               *r++ = TOCHAR (c & 07);
220               continue;
221             }
222           l = 0;
223           break;
224         }
225       if (l)
226         *r++ = '\\';
227       *r++ = c;
228     }
229
230   *r++ = '\'';
231   *r = '\0';
232   if (rlen)
233     *rlen = r - ret;
234   return ret;
235 }
236
237 /* return 1 if we need to quote with $'...' because of non-printing chars. */
238 int
239 ansic_shouldquote (string)
240      const char *string;
241 {
242   const char *s;
243   unsigned char c;
244
245   if (string == 0)
246     return 0;
247
248   for (s = string; c = *s; s++)
249     if (ISPRINT (c) == 0)
250       return 1;
251
252   return 0;
253 }
254
255 /* $'...' ANSI-C expand the portion of STRING between START and END and
256    return the result.  The result cannot be longer than the input string. */
257 char *
258 ansiexpand (string, start, end, lenp)
259      char *string;
260      int start, end, *lenp;
261 {
262   char *temp, *t;
263   int len, tlen;
264
265   temp = (char *)xmalloc (end - start + 1);
266   for (tlen = 0, len = start; len < end; )
267     temp[tlen++] = string[len++];
268   temp[tlen] = '\0';
269
270   if (*temp)
271     {
272       t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
273       free (temp);
274       return (t);
275     }
276   else
277     {
278       if (lenp)
279         *lenp = 0;
280       return (temp);
281     }
282 }