1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
3 /* Copyright (C) 2000-2010 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
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/>.
23 #if defined (HAVE_UNISTD_H)
29 #include <chartypes.h>
36 #define ESC '\033' /* ASCII */
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. */
48 ansicstr (string, len, flags, sawc, rlen)
50 int len, flags, *sawc, *rlen;
56 if (string == 0 || *string == '\0')
57 return ((char *)NULL);
59 #if defined (HANDLE_MULTIBYTE)
60 ret = (char *)xmalloc (4*len + 1);
62 ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */
64 for (r = ret, s = string; s && *s; )
67 if (c != '\\' || *s == '\0')
73 #if defined (__STDC__)
74 case 'a': c = '\a'; break;
75 case 'v': c = '\v'; break;
77 case 'a': c = '\007'; break;
78 case 'v': c = (int) 0x0B; break;
80 case 'b': c = '\b'; break;
81 case 'e': case 'E': /* ESC -- non-ANSI */
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':
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
103 temp = 2 + ((flags & 1) && (c == '0'));
104 for (c -= '0'; ISOCTAL (*s) && temp--; s++)
105 c = (c * 8) + OCTVALUE (*s);
108 case 'x': /* Hex digit -- non-ANSI */
109 if ((flags & 2) && *s == '{')
111 flags |= 16; /* internal flag value */
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. */
122 for ( ; ISXDIGIT ((unsigned char)*s); s++)
123 c = (c * 16) + HEXVALUE (*s);
128 /* \x followed by non-hex digits is passed through unchanged */
136 #if defined (HANDLE_MULTIBYTE)
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))
144 *r++ = '\\'; /* c remains unchanged */
147 else if (v <= UCHAR_MAX)
154 temp = u32cconv (v, r);
161 case '\'': case '"': case '?':
174 else if ((flags & 1) == 0 && *s == 0)
175 ; /* pass \c through */
176 else if ((flags & 1) == 0 && (c = *s))
179 if ((flags & 2) && c == '\\' && c == *s)
180 s++; /* Posix requires $'\c\\' do backslash escaping */
186 if ((flags & 4) == 0)
190 if ((flags & 2) && (c == CTLESC || c == CTLNUL))
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. */
204 ansic_quote (str, flags, rlen)
212 if (str == 0 || *str == 0)
217 r = ret = (char *)xmalloc (rsize);
222 for (s = str, l = 0; *s; s++)
225 l = 1; /* 1 == add backslash; 0 == no backslash */
228 case ESC: c = 'E'; break;
230 case '\a': c = 'a'; break;
231 case '\v': c = 'v'; break;
233 case '\007': c = 'a'; break;
234 case 0x0b: c = 'v'; break;
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;
246 if (ISPRINT (c) == 0)
249 *r++ = TOCHAR ((c >> 6) & 07);
250 *r++ = TOCHAR ((c >> 3) & 07);
251 *r++ = TOCHAR (c & 07);
269 /* return 1 if we need to quote with $'...' because of non-printing chars. */
271 ansic_shouldquote (string)
280 for (s = string; c = *s; s++)
281 if (ISPRINT (c) == 0)
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. */
290 ansiexpand (string, start, end, lenp)
292 int start, end, *lenp;
297 temp = (char *)xmalloc (end - start + 1);
298 for (tlen = 0, len = start; len < end; )
299 temp[tlen++] = string[len++];
304 t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);