aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2015-05-18 09:59:14 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2015-05-18 09:59:14 +0200
commit417622cc2e1ce0f3bc797b3997f5728433c8ccee (patch)
tree796ecf5c5ee3850e9391bf9915723a068c3d3788 /shell
parent549deab5abd59c1ab752754170f69aa2248e72c9 (diff)
downloadbusybox-w32-417622cc2e1ce0f3bc797b3997f5728433c8ccee.tar.gz
busybox-w32-417622cc2e1ce0f3bc797b3997f5728433c8ccee.tar.bz2
busybox-w32-417622cc2e1ce0f3bc797b3997f5728433c8ccee.zip
ash: fix breakage of ${v/pat/str}
The commit ash: move parse-time quote flag detection to run-time breaks pattern substitution in parameter expansion. Fix this and revise the code so that the different handling of the pattern and the replacement string takes place in rmescapes rather than the separate function parse_sub_pattern. function old new delta rmescapes 227 273 +46 static.qchars 3 4 +1 subevalvar 1177 1157 -20 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 47/-20) Total: 27 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c79
1 files changed, 28 insertions, 51 deletions
diff --git a/shell/ash.c b/shell/ash.c
index e7e70817f..282f761fc 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5525,6 +5525,7 @@ ash_arith(const char *s)
5525#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ 5525#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5526#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ 5526#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5527#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ 5527#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5528#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */
5528 5529
5529/* Add CTLESC when necessary. */ 5530/* Add CTLESC when necessary. */
5530#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) 5531#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR)
@@ -5594,14 +5595,16 @@ esclen(const char *start, const char *p)
5594static char * 5595static char *
5595rmescapes(char *str, int flag) 5596rmescapes(char *str, int flag)
5596{ 5597{
5597 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; 5598 static const char qchars[] ALIGN1 = {
5599 IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' };
5598 5600
5599 char *p, *q, *r; 5601 char *p, *q, *r;
5600 unsigned inquotes; 5602 unsigned inquotes;
5601 unsigned protect_against_glob; 5603 unsigned protect_against_glob;
5602 unsigned globbing; 5604 unsigned globbing;
5605 IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;)
5603 5606
5604 p = strpbrk(str, qchars); 5607 p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash));
5605 if (!p) 5608 if (!p)
5606 return str; 5609 return str;
5607 5610
@@ -5650,6 +5653,13 @@ rmescapes(char *str, int flag)
5650 protect_against_glob = 0; 5653 protect_against_glob = 0;
5651 goto copy; 5654 goto copy;
5652 } 5655 }
5656#if ENABLE_ASH_BASH_COMPAT
5657 else if (*p == '/' && slash) {
5658 /* stop handling globbing and mark location of slash */
5659 globbing = slash = 0;
5660 *p = CTLESC;
5661 }
5662#endif
5653 protect_against_glob = globbing; 5663 protect_against_glob = globbing;
5654 copy: 5664 copy:
5655 *q++ = *p++; 5665 *q++ = *p++;
@@ -6274,50 +6284,6 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
6274 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); 6284 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
6275} 6285}
6276 6286
6277#if ENABLE_ASH_BASH_COMPAT
6278static char *
6279parse_sub_pattern(char *arg, int quoted)
6280{
6281 char *idx, *repl = NULL;
6282 unsigned char c;
6283
6284 //char *org_arg = arg;
6285 //bb_error_msg("arg:'%s' quoted:%x", arg, quoted);
6286 idx = arg;
6287 while (1) {
6288 c = *arg;
6289 if (!c)
6290 break;
6291 if (c == '/') {
6292 /* Only the first '/' seen is our separator */
6293 if (!repl) {
6294 repl = idx + 1;
6295 c = '\0';
6296 }
6297 }
6298 *idx++ = c;
6299 arg++;
6300 /*
6301 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
6302 * The result is a_\_z_c (not a\_\_z_c)!
6303 *
6304 * Enable debug prints in this function and you'll see:
6305 * ash: arg:'\\b/_\\_z_' varflags:d
6306 * ash: pattern:'\\b' repl:'_\_z_'
6307 * That is, \\b is interpreted as \\b, but \\_ as \_!
6308 * IOW: search pattern and replace string treat backslashes
6309 * differently! That is the reason why we check repl below:
6310 */
6311 if (c == '\\' && *arg == '\\' && repl && !quoted)
6312 arg++; /* skip both '\', not just first one */
6313 }
6314 *idx = c; /* NUL */
6315 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
6316
6317 return repl;
6318}
6319#endif /* ENABLE_ASH_BASH_COMPAT */
6320
6321static const char * 6287static const char *
6322subevalvar(char *p, char *varname, int strloc, int subtype, 6288subevalvar(char *p, char *varname, int strloc, int subtype,
6323 int startloc, int varflags, int flag, struct strlist *var_str_list) 6289 int startloc, int varflags, int flag, struct strlist *var_str_list)
@@ -6328,7 +6294,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6328 char *loc; 6294 char *loc;
6329 char *rmesc, *rmescend; 6295 char *rmesc, *rmescend;
6330 char *str; 6296 char *str;
6331 IF_ASH_BASH_COMPAT(const char *repl = NULL;) 6297 IF_ASH_BASH_COMPAT(char *repl = NULL;)
6332 IF_ASH_BASH_COMPAT(int pos, len, orig_len;) 6298 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
6333 int saveherefd = herefd; 6299 int saveherefd = herefd;
6334 int amount, resetloc; 6300 int amount, resetloc;
@@ -6453,7 +6419,17 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6453 } 6419 }
6454 rmescend--; 6420 rmescend--;
6455 str = (char *)stackblock() + strloc; 6421 str = (char *)stackblock() + strloc;
6456 preglob(str, 0); 6422 /*
6423 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
6424 * The result is a_\_z_c (not a\_\_z_c)!
6425 *
6426 * The search pattern and replace string treat backslashes differently!
6427 * RMESCAPE_SLASH causes preglob to work differently on the pattern
6428 * and string. It's only used on the first call.
6429 */
6430 preglob(str, IF_ASH_BASH_COMPAT(
6431 (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ?
6432 RMESCAPE_SLASH :) 0);
6457 6433
6458#if ENABLE_ASH_BASH_COMPAT 6434#if ENABLE_ASH_BASH_COMPAT
6459 workloc = expdest - (char *)stackblock(); 6435 workloc = expdest - (char *)stackblock();
@@ -6461,11 +6437,12 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6461 char *idx, *end; 6437 char *idx, *end;
6462 6438
6463 if (!repl) { 6439 if (!repl) {
6464 repl = parse_sub_pattern(str, flag & EXP_QUOTED); 6440 if ((repl=strchr(str, CTLESC)))
6465 //bb_error_msg("repl:'%s'", repl); 6441 *repl++ = '\0';
6466 if (!repl) 6442 else
6467 repl = nullstr; 6443 repl = nullstr;
6468 } 6444 }
6445 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
6469 6446
6470 /* If there's no pattern to match, return the expansion unmolested */ 6447 /* If there's no pattern to match, return the expansion unmolested */
6471 if (str[0] == '\0') 6448 if (str[0] == '\0')