]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/sh/shquote.c
final set of ANSI C changes
[thirdparty/bash.git] / lib / sh / shquote.c
CommitLineData
2e4498b3
CR
1/* shquote - functions to quote and dequote strings */
2
81e3a4fb 3/* Copyright (C) 1999-2020,2022 Free Software Foundation, Inc.
bb70624e
JA
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
2e4498b3
CR
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.
11
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.
16
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*/
bb70624e
JA
20
21#include <config.h>
22
23#if defined (HAVE_UNISTD_H)
24# ifdef _MINIX
25# include <sys/types.h>
26# endif
27# include <unistd.h>
28#endif
29
30#include <stdio.h>
09f70f2f 31#include <stdc.h>
bb70624e 32
28ef6c31 33#include "syntax.h"
f73dda09 34#include <xmalloc.h>
bb70624e 35
c4c90ef8
CR
36#include "shmbchar.h"
37#include "shmbutil.h"
38
81e3a4fb
CR
39extern char *ansic_quote (char *, int, int *);
40extern int ansic_shouldquote (const char *);
09f70f2f 41
ddef12ff
CR
42/* Default set of characters that should be backslash-quoted in strings */
43static const char bstab[256] =
44 {
45 0, 0, 0, 0, 0, 0, 0, 0,
46 0, 1, 1, 0, 0, 0, 0, 0, /* TAB, NL */
47 0, 0, 0, 0, 0, 0, 0, 0,
48 0, 0, 0, 0, 0, 0, 0, 0,
49
50 1, 1, 1, 0, 1, 0, 1, 1, /* SPACE, !, DQUOTE, DOL, AMP, SQUOTE */
51 1, 1, 1, 0, 1, 0, 0, 0, /* LPAR, RPAR, STAR, COMMA */
52 0, 0, 0, 0, 0, 0, 0, 0,
53 0, 0, 0, 1, 1, 0, 1, 1, /* SEMI, LESSTHAN, GREATERTHAN, QUEST */
54
55 0, 0, 0, 0, 0, 0, 0, 0,
56 0, 0, 0, 0, 0, 0, 0, 0,
57 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 1, 1, 1, 1, 0, /* LBRACK, BS, RBRACK, CARAT */
59
60 1, 0, 0, 0, 0, 0, 0, 0, /* BACKQ */
61 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 1, 1, 1, 0, 0, /* LBRACE, BAR, RBRACE */
64
65 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0,
69
70 0, 0, 0, 0, 0, 0, 0, 0,
71 0, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0,
74
75 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0,
79
80 0, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0,
84 };
85
bb70624e
JA
86/* **************************************************************** */
87/* */
88/* Functions for quoting strings to be re-read as input */
89/* */
90/* **************************************************************** */
91
92/* Return a new string which is the single-quoted version of STRING.
93 Used by alias and trap, among others. */
94char *
81e3a4fb 95sh_single_quote (const char *string)
bb70624e 96{
2e725f73 97 int c;
89c77bc7
CR
98 char *result, *r;
99 const char *s;
bb70624e 100
f73dda09 101 result = (char *)xmalloc (3 + (4 * strlen (string)));
bb70624e 102 r = result;
1e8fce0f
CR
103
104 if (string[0] == '\'' && string[1] == 0)
105 {
106 *r++ = '\\';
107 *r++ = '\'';
108 *r++ = 0;
109 return result;
110 }
111
bb70624e
JA
112 *r++ = '\'';
113
114 for (s = string; s && (c = *s); s++)
115 {
116 *r++ = c;
117
118 if (c == '\'')
119 {
120 *r++ = '\\'; /* insert escaped single quote */
121 *r++ = '\'';
122 *r++ = '\''; /* start new quoted string */
123 }
124 }
125
126 *r++ = '\'';
127 *r = '\0';
128
129 return (result);
130}
131
132/* Quote STRING using double quotes. Return a new string. */
133char *
81e3a4fb 134sh_double_quote (const char *string)
bb70624e 135{
2e725f73 136 unsigned char c;
203f479c 137 int mb_cur_max;
89c77bc7 138 char *result, *r;
203f479c
CR
139 size_t slen;
140 const char *s, *send;
141 DECLARE_MBSTATE;
142
143 slen = strlen (string);
144 send = string + slen;
145 mb_cur_max = MB_CUR_MAX;
bb70624e 146
f73dda09 147 result = (char *)xmalloc (3 + (2 * strlen (string)));
bb70624e
JA
148 r = result;
149 *r++ = '"';
150
151 for (s = string; s && (c = *s); s++)
152 {
da719982
CR
153 /* Backslash-newline disappears within double quotes, so don't add one. */
154 if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
28ef6c31 155 *r++ = '\\';
203f479c
CR
156
157#if defined (HANDLE_MULTIBYTE)
158 if ((locale_utf8locale && (c & 0x80)) ||
159 (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
160 {
161 COPY_CHAR_P (r, s, send);
162 s--; /* compensate for auto-increment in loop above */
163 continue;
164 }
7afeb718 165#endif
28ef6c31 166
203f479c
CR
167 /* Assume that the string will not be further expanded, so no need to
168 add CTLESC to protect CTLESC or CTLNUL. */
28ef6c31 169 *r++ = c;
bb70624e
JA
170 }
171
172 *r++ = '"';
173 *r = '\0';
174
175 return (result);
176}
177
c3271763
CR
178/* Turn S into a simple double-quoted string. If FLAGS is non-zero, quote
179 double quote characters in S with backslashes. */
180char *
2e725f73 181sh_mkdoublequoted (const char *s, size_t slen, int flags)
c3271763
CR
182{
183 char *r, *ret;
203f479c 184 const char *send;
2e725f73
CR
185 int mb_cur_max;
186 size_t rlen;
203f479c 187 DECLARE_MBSTATE;
c3271763 188
203f479c
CR
189 send = s + slen;
190 mb_cur_max = flags ? MB_CUR_MAX : 1;
c3271763
CR
191 rlen = (flags == 0) ? slen + 3 : (2 * slen) + 1;
192 ret = r = (char *)xmalloc (rlen);
203f479c 193
c3271763
CR
194 *r++ = '"';
195 while (*s)
196 {
197 if (flags && *s == '"')
198 *r++ = '\\';
203f479c
CR
199
200#if defined (HANDLE_MULTIBYTE)
201 if (flags && ((locale_utf8locale && (*s & 0x80)) ||
202 (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (*s) == 0)))
203 {
204 COPY_CHAR_P (r, s, send);
205 continue;
206 }
207#endif
c3271763
CR
208 *r++ = *s++;
209 }
210 *r++ = '"';
211 *r = '\0';
212
213 return ret;
214}
215
bb70624e 216/* Remove backslashes that are quoting characters that are special between
d3a24ed2
CR
217 double quotes. Return a new string. XXX - should this handle CTLESC
218 and CTLNUL? */
bb70624e 219char *
81e3a4fb 220sh_un_double_quote (char *string)
bb70624e 221{
2e725f73 222 int c, pass_next;
bb70624e
JA
223 char *result, *r, *s;
224
f73dda09 225 r = result = (char *)xmalloc (strlen (string) + 1);
bb70624e
JA
226
227 for (pass_next = 0, s = string; s && (c = *s); s++)
228 {
229 if (pass_next)
230 {
231 *r++ = c;
232 pass_next = 0;
233 continue;
234 }
f73dda09 235 if (c == '\\' && (sh_syntaxtab[(unsigned char) s[1]] & CBSDQUOTE))
bb70624e
JA
236 {
237 pass_next = 1;
238 continue;
239 }
240 *r++ = c;
241 }
242
243 *r = '\0';
244 return result;
245}
246
247/* Quote special characters in STRING using backslashes. Return a new
633e5c6d
CR
248 string. NOTE: if the string is to be further expanded, we need a
249 way to protect the CTLESC and CTLNUL characters. As I write this,
250 the current callers will never cause the string to be expanded without
251 going through the shell parser, which will protect the internal
8360b906
CR
252 quoting characters. TABLE, if set, points to a map of the ascii code
253 set with char needing to be backslash-quoted if table[char]==1. FLAGS,
a57ed9e9
CR
254 if 1, causes tildes to be quoted as well. If FLAGS&2, backslash-quote
255 other shell blank characters. */
8360b906 256
bb70624e 257char *
81e3a4fb 258sh_backslash_quote (char *string, char *table, int flags)
bb70624e 259{
3bf257a5 260 int c, mb_cur_max;
c4c90ef8
CR
261 size_t slen;
262 char *result, *r, *s, *backslash_table, *send;
263 DECLARE_MBSTATE;
bb70624e 264
c4c90ef8
CR
265 slen = strlen (string);
266 send = string + slen;
267 result = (char *)xmalloc (2 * slen + 1);
bb70624e 268
f14388d3 269 backslash_table = table ? table : (char *)bstab;
3bf257a5
CR
270 mb_cur_max = MB_CUR_MAX;
271
bb70624e
JA
272 for (r = result, s = string; s && (c = *s); s++)
273 {
c4c90ef8 274#if defined (HANDLE_MULTIBYTE)
74b8cbb4
CR
275 /* XXX - isascii, even if is_basic(c) == 0 - works in most cases. */
276 if (c >= 0 && c <= 127 && backslash_table[(unsigned char)c] == 1)
277 {
278 *r++ = '\\';
279 *r++ = c;
280 continue;
281 }
203f479c
CR
282 if ((locale_utf8locale && (c & 0x80)) ||
283 (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
c4c90ef8
CR
284 {
285 COPY_CHAR_P (r, s, send);
286 s--; /* compensate for auto-increment in loop above */
287 continue;
288 }
289#endif
2171061f 290 if (backslash_table[(unsigned char)c] == 1)
ddef12ff
CR
291 *r++ = '\\';
292 else if (c == '#' && s == string) /* comment char */
293 *r++ = '\\';
8360b906 294 else if ((flags&1) && c == '~' && (s == string || s[-1] == ':' || s[-1] == '='))
863d31ae
CR
295 /* Tildes are special at the start of a word or after a `:' or `='
296 (technically unquoted, but it doesn't make a difference in practice) */
297 *r++ = '\\';
a57ed9e9
CR
298 else if ((flags&2) && shellblank((unsigned char)c))
299 *r++ = '\\';
ddef12ff 300 *r++ = c;
bb70624e
JA
301 }
302
303 *r = '\0';
304 return (result);
305}
306
006856ed 307#if defined (PROMPT_STRING_DECODE) || defined (TRANSLATABLE_STRINGS)
28ef6c31 308/* Quote characters that get special treatment when in double quotes in STRING
68ab281e 309 using backslashes. FLAGS is reserved for future use. Return a new string. */
28ef6c31 310char *
81e3a4fb 311sh_backslash_quote_for_double_quotes (char *string, int flags)
28ef6c31 312{
f73dda09 313 unsigned char c;
68ab281e
CR
314 char *result, *r, *s, *send;
315 size_t slen;
203f479c
CR
316 int mb_cur_max;
317 DECLARE_MBSTATE;
318
319 slen = strlen (string);
320 send = string + slen;
321 mb_cur_max = MB_CUR_MAX;
68ab281e 322 result = (char *)xmalloc (2 * slen + 1);
28ef6c31 323
68ab281e 324 for (r = result, s = string; s && (c = *s); s++)
28ef6c31 325 {
203f479c
CR
326 /* Backslash-newline disappears within double quotes, so don't add one. */
327 if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
68ab281e 328 *r++ = '\\';
203f479c 329 /* I should probably use the CSPECL flag for these in sh_syntaxtab[] */
d3a24ed2 330 else if (c == CTLESC || c == CTLNUL)
68ab281e 331 *r++ = CTLESC; /* could be '\\'? */
28ef6c31 332
203f479c
CR
333#if defined (HANDLE_MULTIBYTE)
334 if ((locale_utf8locale && (c & 0x80)) ||
335 (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0))
336 {
68ab281e
CR
337 COPY_CHAR_P (r, s, send);
338 s--; /* compensate for auto-increment in loop above */
203f479c
CR
339 continue;
340 }
341#endif
342
68ab281e 343 *r++ = c;
28ef6c31
JA
344 }
345
68ab281e 346 *r = '\0';
28ef6c31
JA
347 return (result);
348}
349#endif /* PROMPT_STRING_DECODE */
350
09f70f2f 351char *
81e3a4fb 352sh_quote_reusable (char *s, int flags)
09f70f2f
CR
353{
354 char *ret;
355
356 if (s == 0)
357 return s;
358 else if (*s == 0)
359 {
360 ret = (char *)xmalloc (3);
361 ret[0] = ret[1] = '\'';
362 ret[2] = '\0';
363 }
364 else if (ansic_shouldquote (s))
365 ret = ansic_quote (s, 0, (int *)0);
366 else if (flags)
367 ret = sh_backslash_quote (s, 0, 1);
368 else
369 ret = sh_single_quote (s);
370
371 return ret;
372}
373
bb70624e 374int
81e3a4fb 375sh_contains_shell_metas (const char *string)
bb70624e 376{
8f50a023 377 const char *s;
bb70624e
JA
378
379 for (s = string; s && *s; s++)
380 {
381 switch (*s)
382 {
383 case ' ': case '\t': case '\n': /* IFS white space */
384 case '\'': case '"': case '\\': /* quoting chars */
385 case '|': case '&': case ';': /* shell metacharacters */
386 case '(': case ')': case '<': case '>':
387 case '!': case '{': case '}': /* reserved words */
388 case '*': case '[': case '?': case ']': /* globbing chars */
389 case '^':
390 case '$': case '`': /* expansion chars */
391 return (1);
392 case '~': /* tilde expansion */
393 if (s == string || s[-1] == '=' || s[-1] == ':')
394 return (1);
f73dda09 395 break;
bb70624e
JA
396 case '#':
397 if (s == string) /* comment char */
398 return (1);
399 /* FALLTHROUGH */
400 default:
401 break;
402 }
403 }
404
405 return (0);
406}
939d190e
CR
407
408int
81e3a4fb 409sh_contains_quotes (const char *string)
939d190e 410{
8f50a023 411 const char *s;
939d190e
CR
412
413 for (s = string; s && *s; s++)
414 {
415 if (*s == '\'' || *s == '"' || *s == '\\')
416 return 1;
417 }
418 return 0;
419}