1 /* strtrans.c - Translate and untranslate strings with ANSI-C escape sequences. */
3 /* Copyright (C) 2000-2015 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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 Bush 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 Bush. If not, see <http://www.gnu.org/licenses/>.
23 #if defined (HAVE_UNISTD_H)
29 #include <chartypes.h>
39 #define ESC '\033' /* ASCII */
41 /* Convert STRING by expanding the escape sequences specified by the
42 ANSI C standard. If SAWC is non-null, recognize `\c' and use that
43 as a string terminator. If we see \c, set *SAWC to 1 before
44 returning. LEN is the length of STRING. If (FLAGS&1) is non-zero,
45 that we're translating a string for `echo -e', and therefore should not
46 treat a single quote as a character that may be escaped with a backslash.
47 If (FLAGS&2) is non-zero, we're expanding for the parser and want to
48 quote CTLESC and CTLNUL with CTLESC. If (flags&4) is non-zero, we want
49 to remove the backslash before any unrecognized escape sequence. */
51 ansicstr (string
, len
, flags
, sawc
, rlen
)
53 int len
, flags
, *sawc
, *rlen
;
60 #if defined (HANDLE_MULTIBYTE)
64 if (string
== 0 || *string
== '\0')
65 return ((char *)NULL
);
67 mb_cur_max
= MB_CUR_MAX
;
68 #if defined (HANDLE_MULTIBYTE)
71 temp
= 12; /* ensure enough for eventual u32cesc */
72 ret
= (char *)xmalloc (temp
);
74 ret
= (char *)xmalloc (2*len
+ 1); /* 2*len for possible CTLESC */
76 for (r
= ret
, s
= string
; s
&& *s
; )
79 if (c
!= '\\' || *s
== '\0')
82 #if defined (HANDLE_MULTIBYTE)
83 if ((locale_utf8locale
&& (c
& 0x80)) ||
84 (locale_utf8locale
== 0 && mb_cur_max
> 0 && is_basic (c
) == 0))
86 clen
= mbrtowc (&wc
, s
- 1, mb_cur_max
, 0);
87 if (MB_INVALIDCH (clen
))
92 for (--clen
; clen
> 0; clen
--)
99 #if defined (__STDC__)
100 case 'a': c
= '\a'; break;
101 case 'v': c
= '\v'; break;
103 case 'a': c
= (int) 0x07; break;
104 case 'v': c
= (int) 0x0B; break;
106 case 'b': c
= '\b'; break;
107 case 'e': case 'E': /* ESC -- non-ANSI */
109 case 'f': c
= '\f'; break;
110 case 'n': c
= '\n'; break;
111 case 'r': c
= '\r'; break;
112 case 't': c
= '\t'; break;
113 case '1': case '2': case '3':
114 case '4': case '5': case '6':
125 /* If (FLAGS & 1), we're translating a string for echo -e (or
126 the equivalent xpg_echo option), so we obey the SUSv3/
127 POSIX-2001 requirement and accept 0-3 octal digits after
129 temp
= 2 + ((flags
& 1) && (c
== '0'));
130 for (c
-= '0'; ISOCTAL (*s
) && temp
--; s
++)
131 c
= (c
* 8) + OCTVALUE (*s
);
134 case 'x': /* Hex digit -- non-ANSI */
135 if ((flags
& 2) && *s
== '{')
137 flags
|= 16; /* internal flag value */
140 /* Consume at least two hex characters */
141 for (temp
= 2, c
= 0; ISXDIGIT ((unsigned char)*s
) && temp
--; s
++)
142 c
= (c
* 16) + HEXVALUE (*s
);
143 /* DGK says that after a `\x{' ksh93 consumes ISXDIGIT chars
144 until a non-xdigit or `}', so potentially more than two
145 chars are consumed. */
148 for ( ; ISXDIGIT ((unsigned char)*s
); s
++)
149 c
= (c
* 16) + HEXVALUE (*s
);
154 /* \x followed by non-hex digits is passed through unchanged */
162 #if defined (HANDLE_MULTIBYTE)
165 temp
= (c
== 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */
166 for (v
= 0; ISXDIGIT ((unsigned char)*s
) && temp
--; s
++)
167 v
= (v
* 16) + HEXVALUE (*s
);
168 if (temp
== ((c
== 'u') ? 4 : 8))
170 *r
++ = '\\'; /* c remains unchanged */
173 else if (v
<= 0x7f) /* <= 0x7f translates directly */
180 temp
= u32cconv (v
, r
);
187 case '\'': case '"': case '?':
200 else if ((flags
& 1) == 0 && *s
== 0)
201 ; /* pass \c through */
202 else if ((flags
& 1) == 0 && (c
= *s
))
205 if ((flags
& 2) && c
== '\\' && c
== *s
)
206 s
++; /* Posix requires $'\c\\' do backslash escaping */
212 if ((flags
& 4) == 0)
216 if ((flags
& 2) && (c
== CTLESC
|| c
== CTLNUL
))
227 /* Take a string STR, possibly containing non-printing characters, and turn it
228 into a $'...' ANSI-C style quoted string. Returns a new string. */
230 ansic_quote (str
, flags
, rlen
)
239 #if defined (HANDLE_MULTIBYTE)
243 if (str
== 0 || *str
== 0)
248 r
= ret
= (char *)xmalloc (rsize
);
253 for (s
= str
; c
= *s
; s
++)
255 b
= l
= 1; /* 1 == add backslash; 0 == no backslash */
260 case ESC
: c
= 'E'; break;
262 case '\a': c
= 'a'; break;
263 case '\v': c
= 'v'; break;
265 case 0x07: c
= 'a'; break;
266 case 0x0b: c
= 'v'; break;
269 case '\b': c
= 'b'; break;
270 case '\f': c
= 'f'; break;
271 case '\n': c
= 'n'; break;
272 case '\r': c
= 'r'; break;
273 case '\t': c
= 't'; break;
278 #if defined (HANDLE_MULTIBYTE)
280 /* XXX - clen comparison to 0 is dicey */
281 if ((b
== 0 && ((clen
= mbrtowc (&wc
, s
, MB_CUR_MAX
, 0)) < 0 || MB_INVALIDCH (clen
) || iswprint (wc
) == 0)) ||
282 (b
== 1 && ISPRINT (c
) == 0))
284 if (ISPRINT (c
) == 0)
288 *r
++ = TOCHAR ((c
>> 6) & 07);
289 *r
++ = TOCHAR ((c
>> 3) & 07);
290 *r
++ = TOCHAR (c
& 07);
296 if (b
== 0 && clen
== 0)
306 for (b
= 0; b
< (int)clen
; b
++)
307 *r
++ = (unsigned char)s
[b
];
308 s
+= clen
- 1; /* -1 because of the increment above */
319 #if defined (HANDLE_MULTIBYTE)
321 ansic_wshouldquote (string
)
326 wchar_t *wcstr
= NULL
;
329 slen
= mbstowcs (wcstr
, string
, 0);
331 if (slen
== (size_t)-1)
334 wcstr
= (wchar_t *)xmalloc (sizeof (wchar_t) * (slen
+ 1));
335 mbstowcs (wcstr
, string
, slen
+ 1);
337 for (wcs
= wcstr
; wcc
= *wcs
; wcs
++)
338 if (iswprint(wcc
) == 0)
349 /* return 1 if we need to quote with $'...' because of non-printing chars. */
351 ansic_shouldquote (string
)
360 for (s
= string
; c
= *s
; s
++)
362 #if defined (HANDLE_MULTIBYTE)
363 if (is_basic (c
) == 0)
364 return (ansic_wshouldquote (s
));
366 if (ISPRINT (c
) == 0)
373 /* $'...' ANSI-C expand the portion of STRING between START and END and
374 return the result. The result cannot be longer than the input string. */
376 ansiexpand (string
, start
, end
, lenp
)
378 int start
, end
, *lenp
;
383 temp
= (char *)xmalloc (end
- start
+ 1);
384 for (tlen
= 0, len
= start
; len
< end
; )
385 temp
[tlen
++] = string
[len
++];
390 t
= ansicstr (temp
, tlen
, 2, (int *)NULL
, lenp
);