1f0290e3d676a3d9cce19bf2fd3d995a31f0428e
[platform/upstream/bash.git] / lib / sh / strtrans.c
1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape
2                 sequences. */
3
4 /* Copyright (C) 2000
5    Free Software Foundation, Inc.
6
7    This file is part of GNU Bash, the Bourne Again SHell.
8
9    Bash is free software; you can redistribute it and/or modify it under
10    the terms of the GNU General Public License as published by the Free
11    Software Foundation; either version 2, or (at your option) any later
12    version.
13
14    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
15    WARRANTY; without even the implied warranty of MERCHANTABILITY or
16    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17    for more details.
18
19    You should have received a copy of the GNU General Public License along
20    with Bash; see the file COPYING.  If not, write to the Free Software
21    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
23 #include <config.h>
24
25 #if defined (HAVE_UNISTD_H)
26 #  include <unistd.h>
27 #endif
28
29 #include <bashansi.h>
30 #include <stdio.h>
31 #include <chartypes.h>
32
33 #include "shell.h"
34
35 #ifdef ESC
36 #undef ESC
37 #endif
38 #define ESC '\033'      /* ASCII */
39
40 /* Convert STRING by expanding the escape sequences specified by the
41    ANSI C standard.  If SAWC is non-null, recognize `\c' and use that
42    as a string terminator.  If we see \c, set *SAWC to 1 before
43    returning.  LEN is the length of STRING.  If (FLAGS&1) is non-zero,
44    that we're translating a string for `echo -e', and therefore should not
45    treat a single quote as a character that may be escaped with a backslash.
46    If (FLAGS&2) is non-zero, we're expanding for the parser and want to
47    quote CTLESC and CTLNUL with CTLESC.  If (flags&4) is non-zero, we want
48    to remove the backslash before any unrecognized escape sequence. */
49 char *
50 ansicstr (string, len, flags, sawc, rlen)
51      char *string;
52      int len, flags, *sawc, *rlen;
53 {
54   int c, temp;
55   char *ret, *r, *s;
56
57   if (string == 0 || *string == '\0')
58     return ((char *)NULL);
59
60   ret = (char *)xmalloc (2*len + 1);    /* 2*len for possible CTLESC */
61   for (r = ret, s = string; s && *s; )
62     {
63       c = *s++;
64       if (c != '\\' || *s == '\0')
65         *r++ = c;
66       else
67         {
68           switch (c = *s++)
69             {
70 #if defined (__STDC__)
71             case 'a': c = '\a'; break;
72             case 'v': c = '\v'; break;
73 #else
74             case 'a': c = '\007'; break;
75             case 'v': c = (int) 0x0B; break;
76 #endif
77             case 'b': c = '\b'; break;
78             case 'e': case 'E':         /* ESC -- non-ANSI */
79               c = ESC; break;
80             case 'f': c = '\f'; break;
81             case 'n': c = '\n'; break;
82             case 'r': c = '\r'; break;
83             case 't': c = '\t'; break;
84             case '1': case '2': case '3':
85             case '4': case '5': case '6':
86             case '7':
87 #if 1
88               if (flags & 1)
89                 {
90                   *r++ = '\\';
91                   break;
92                 }
93             /*FALLTHROUGH*/
94 #endif
95             case '0':
96               /* If (FLAGS & 1), we're translating a string for echo -e (or
97                  the equivalent xpg_echo option), so we obey the SUSv3/
98                  POSIX-2001 requirement and accept 0-3 octal digits after
99                  a leading `0'. */
100               temp = 2 + ((flags & 1) && (c == '0'));
101               for (c -= '0'; ISOCTAL (*s) && temp--; s++)
102                 c = (c * 8) + OCTVALUE (*s);
103               c &= 0xFF;
104               break;
105             case 'x':                   /* Hex digit -- non-ANSI */
106               if ((flags & 2) && *s == '{')
107                 {
108                   flags |= 16;          /* internal flag value */
109                   s++;
110                 }
111               /* Consume at least two hex characters */
112               for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
113                 c = (c * 16) + HEXVALUE (*s);
114               /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
115                  until a non-xdigit or `}', so potentially more than two
116                  chars are consumed. */
117               if (flags & 16)
118                 {
119                   for ( ; ISXDIGIT ((unsigned char)*s); s++)
120                     c = (c * 16) + HEXVALUE (*s);
121                   flags &= ~16;
122                   if (*s == '}')
123                     s++;
124                 }
125               /* \x followed by non-hex digits is passed through unchanged */
126               else if (temp == 2)
127                 {
128                   *r++ = '\\';
129                   c = 'x';
130                 }
131               c &= 0xFF;
132               break;
133             case '\\':
134               break;
135             case '\'': case '"': case '?':
136               if (flags & 1)
137                 *r++ = '\\';
138               break;
139             case 'c':
140               if (sawc)
141                 {
142                   *sawc = 1;
143                   *r = '\0';
144                   if (rlen)
145                     *rlen = r - ret;
146                   return ret;
147                 }
148               else if ((flags & 1) == 0 && (c = *s))
149                 {
150                   s++;
151                   c = TOCTRL(c);
152                   break;
153                 }
154                 /*FALLTHROUGH*/
155             default:
156                 if ((flags & 4) == 0)
157                   *r++ = '\\';
158                 break;
159             }
160           if ((flags & 2) && (c == CTLESC || c == CTLNUL))
161             *r++ = CTLESC;
162           *r++ = c;
163         }
164     }
165   *r = '\0';
166   if (rlen)
167     *rlen = r - ret;
168   return ret;
169 }
170
171 /* Take a string STR, possibly containing non-printing characters, and turn it
172    into a $'...' ANSI-C style quoted string.  Returns a new string. */
173 char *
174 ansic_quote (str, flags, rlen)
175      char *str;
176      int flags, *rlen;
177 {
178   char *r, *ret, *s;
179   int l, rsize;
180   unsigned char c;
181
182   if (str == 0 || *str == 0)
183     return ((char *)0);
184
185   l = strlen (str);
186   rsize = 4 * l + 4;
187   r = ret = (char *)xmalloc (rsize);
188
189   *r++ = '$';
190   *r++ = '\'';
191
192   for (s = str, l = 0; *s; s++)
193     {
194       c = *s;
195       l = 1;            /* 1 == add backslash; 0 == no backslash */
196       switch (c)
197         {
198         case ESC: c = 'E'; break;
199 #ifdef __STDC__
200         case '\a': c = 'a'; break;
201         case '\v': c = 'v'; break;
202 #else
203         case '\007': c = 'a'; break;
204         case 0x0b: c = 'v'; break;
205 #endif
206
207         case '\b': c = 'b'; break;
208         case '\f': c = 'f'; break;
209         case '\n': c = 'n'; break;
210         case '\r': c = 'r'; break;
211         case '\t': c = 't'; break;
212         case '\\':
213         case '\'':
214           break;
215         default:
216           if (ISPRINT (c) == 0)
217             {
218               *r++ = '\\';
219               *r++ = TOCHAR ((c >> 6) & 07);
220               *r++ = TOCHAR ((c >> 3) & 07);
221               *r++ = TOCHAR (c & 07);
222               continue;
223             }
224           l = 0;
225           break;
226         }
227       if (l)
228         *r++ = '\\';
229       *r++ = c;
230     }
231
232   *r++ = '\'';
233   *r = '\0';
234   if (rlen)
235     *rlen = r - ret;
236   return ret;
237 }
238
239 /* return 1 if we need to quote with $'...' because of non-printing chars. */
240 int
241 ansic_shouldquote (string)
242      const char *string;
243 {
244   const char *s;
245   unsigned char c;
246
247   if (string == 0)
248     return 0;
249
250   for (s = string; c = *s; s++)
251     if (ISPRINT (c) == 0)
252       return 1;
253
254   return 0;
255 }
256
257 /* $'...' ANSI-C expand the portion of STRING between START and END and
258    return the result.  The result cannot be longer than the input string. */
259 char *
260 ansiexpand (string, start, end, lenp)
261      char *string;
262      int start, end, *lenp;
263 {
264   char *temp, *t;
265   int len, tlen;
266
267   temp = (char *)xmalloc (end - start + 1);
268   for (tlen = 0, len = start; len < end; )
269     temp[tlen++] = string[len++];
270   temp[tlen] = '\0';
271
272   if (*temp)
273     {
274       t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
275       free (temp);
276       return (t);
277     }
278   else
279     {
280       if (lenp)
281         *lenp = 0;
282       return (temp);
283     }
284 }