]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/sh/strtrans.c
Imported from ../bash-4.0-rc1.tar.gz.
[thirdparty/bash.git] / lib / sh / strtrans.c
CommitLineData
3185942a 1/* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
28ef6c31 2
3185942a 3/* Copyright (C) 2000 Free Software Foundation, Inc.
28ef6c31
JA
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
3185942a
JA
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.
28ef6c31 11
3185942a
JA
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.
28ef6c31 16
3185942a
JA
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*/
28ef6c31
JA
20
21#include <config.h>
22
23#if defined (HAVE_UNISTD_H)
24# include <unistd.h>
25#endif
26
f73dda09 27#include <bashansi.h>
28ef6c31 28#include <stdio.h>
f73dda09 29#include <chartypes.h>
28ef6c31
JA
30
31#include "shell.h"
32
33#ifdef ESC
34#undef ESC
35#endif
36#define ESC '\033' /* ASCII */
37
28ef6c31
JA
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
7117c2d2
JA
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
95732b49
JA
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. */
28ef6c31 47char *
7117c2d2 48ansicstr (string, len, flags, sawc, rlen)
28ef6c31 49 char *string;
7117c2d2 50 int len, flags, *sawc, *rlen;
28ef6c31
JA
51{
52 int c, temp;
53 char *ret, *r, *s;
54
55 if (string == 0 || *string == '\0')
56 return ((char *)NULL);
57
7117c2d2 58 ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
28ef6c31
JA
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;
0628567a
JA
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':
7117c2d2
JA
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++)
28ef6c31 100 c = (c * 8) + OCTVALUE (*s);
f73dda09 101 c &= 0xFF;
28ef6c31
JA
102 break;
103 case 'x': /* Hex digit -- non-ANSI */
b80f6443
JA
104 if ((flags & 2) && *s == '{')
105 {
106 flags |= 16; /* internal flag value */
107 s++;
108 }
109 /* Consume at least two hex characters */
f73dda09 110 for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
28ef6c31 111 c = (c * 16) + HEXVALUE (*s);
b80f6443
JA
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 }
28ef6c31 123 /* \x followed by non-hex digits is passed through unchanged */
b80f6443 124 else if (temp == 2)
28ef6c31
JA
125 {
126 *r++ = '\\';
127 c = 'x';
128 }
f73dda09 129 c &= 0xFF;
28ef6c31
JA
130 break;
131 case '\\':
132 break;
b80f6443 133 case '\'': case '"': case '?':
7117c2d2 134 if (flags & 1)
28ef6c31
JA
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 }
7117c2d2
JA
146 else if ((flags & 1) == 0 && (c = *s))
147 {
148 s++;
149 c = TOCTRL(c);
150 break;
151 }
152 /*FALLTHROUGH*/
95732b49
JA
153 default:
154 if ((flags & 4) == 0)
155 *r++ = '\\';
156 break;
28ef6c31 157 }
7117c2d2
JA
158 if ((flags & 2) && (c == CTLESC || c == CTLNUL))
159 *r++ = CTLESC;
28ef6c31
JA
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. */
171char *
172ansic_quote (str, flags, rlen)
173 char *str;
174 int flags, *rlen;
175{
7117c2d2 176 char *r, *ret, *s;
0628567a 177 int l, rsize;
f73dda09 178 unsigned char c;
28ef6c31
JA
179
180 if (str == 0 || *str == 0)
181 return ((char *)0);
182
183 l = strlen (str);
7117c2d2 184 rsize = 4 * l + 4;
f73dda09 185 r = ret = (char *)xmalloc (rsize);
28ef6c31
JA
186
187 *r++ = '$';
188 *r++ = '\'';
189
190 for (s = str, l = 0; *s; s++)
191 {
f73dda09 192 c = *s;
28ef6c31
JA
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:
f73dda09 214 if (ISPRINT (c) == 0)
28ef6c31 215 {
7117c2d2
JA
216 *r++ = '\\';
217 *r++ = TOCHAR ((c >> 6) & 07);
218 *r++ = TOCHAR ((c >> 3) & 07);
219 *r++ = TOCHAR (c & 07);
28ef6c31
JA
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}
f73dda09
JA
236
237/* return 1 if we need to quote with $'...' because of non-printing chars. */
7117c2d2 238int
f73dda09
JA
239ansic_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}
7117c2d2
JA
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. */
257char *
258ansiexpand (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}