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