From 740058b42bbfdab225706f8284e8d72b74a5df3b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 9 Jan 2018 17:01:00 +0100 Subject: ash: fix var_bash5.tests - ${VAR/pattern/repl} construct function old new delta subevalvar 1198 1279 +81 rmescapes 308 330 +22 preglob 8 10 +2 parsefname 152 154 +2 expandarg 973 975 +2 argstr 1144 1146 +2 mklocal 290 288 -2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 6/1 up/down: 111/-2) Total: 109 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 97 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 25 deletions(-) (limited to 'shell/ash.c') diff --git a/shell/ash.c b/shell/ash.c index b73a79975..5381f34eb 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5811,7 +5811,6 @@ ash_arith(const char *s) #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ -#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ /* Add CTLESC when necessary. */ #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) @@ -5992,8 +5991,12 @@ esclen(const char *start, const char *p) /* * Remove any CTLESC characters from a string. */ +#if !BASH_PATTERN_SUBST +#define rmescapes(str, flag, slash_position) \ + rmescapes(str, flag) +#endif static char * -rmescapes(char *str, int flag) +rmescapes(char *str, int flag, int *slash_position) { static const char qchars[] ALIGN1 = { IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; @@ -6002,9 +6005,8 @@ rmescapes(char *str, int flag) unsigned inquotes; unsigned protect_against_glob; unsigned globbing; - IF_BASH_PATTERN_SUBST(unsigned slash = flag & RMESCAPE_SLASH;) - p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash)); + p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position)); if (!p) return str; @@ -6084,10 +6086,11 @@ rmescapes(char *str, int flag) goto copy; } #if BASH_PATTERN_SUBST - else if (*p == '/' && slash) { - /* stop handling globbing and mark location of slash */ - globbing = slash = 0; - *p = CTLESC; + else if (slash_position && p == str + *slash_position) { + /* stop handling globbing */ + globbing = 0; + *slash_position = q - r; + slash_position = NULL; } #endif protect_against_glob = globbing; @@ -6111,7 +6114,7 @@ rmescapes(char *str, int flag) static char * preglob(const char *pattern, int flag) { - return rmescapes((char *)pattern, flag | RMESCAPE_GLOB); + return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL); } /* @@ -6454,7 +6457,7 @@ expari(int flag) expdest = p; if (flag & QUOTES_ESC) - rmescapes(p + 1, 0); + rmescapes(p + 1, 0, NULL); len = cvtnum(ash_arith(p + 1)); @@ -6742,20 +6745,57 @@ subevalvar(char *p, char *varname, int strloc, int subtype, char *rmesc, *rmescend; char *str; int amount, resetloc; + int argstr_flags; IF_BASH_PATTERN_SUBST(int workloc;) - IF_BASH_PATTERN_SUBST(char *repl = NULL;) + IF_BASH_PATTERN_SUBST(int slash_pos;) + IF_BASH_PATTERN_SUBST(char *repl;) int zero; char *(*scan)(char*, char*, char*, char*, int, int); //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)", // p, varname, strloc, subtype, startloc, varflags, quotes); - argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? - (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0) - ); +#if BASH_PATTERN_SUBST + repl = NULL; + if (subtype == VSREPLACE || subtype == VSREPLACEALL) { + /* Find '/' and replace with NUL */ + repl = p; + for (;;) { + /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ + if (*repl == '\0') { + repl = NULL; + break; + } + if (*repl == '/') { + *repl = '\0'; + break; + } + if ((unsigned char)*repl == CTLESC + && repl[1] + ) { + repl++; + } + repl++; + } + } +#endif + argstr_flags = EXP_TILDE; + if (subtype != VSASSIGN && subtype != VSQUESTION) + argstr_flags |= (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE); + argstr(p, argstr_flags); +#if BASH_PATTERN_SUBST + slash_pos = -1; + if (repl) { + slash_pos = expdest - ((char *)stackblock() + strloc); + STPUTC('/', expdest); + argstr(repl + 1, argstr_flags); + *repl = '/'; + } +#endif STPUTC('\0', expdest); argbackq = saveargbackq; startp = (char *)stackblock() + startloc; + //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc); switch (subtype) { case VSASSIGN: @@ -6853,6 +6893,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, resetloc = expdest - (char *)stackblock(); #if BASH_PATTERN_SUBST + repl = NULL; + /* We'll comeback here if we grow the stack while handling * a VSREPLACE or VSREPLACEALL, since our pointers into the * stack will need rebasing, and we'll need to remove our work @@ -6867,8 +6909,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype, rmesc = startp; rmescend = (char *)stackblock() + strloc; + //bb_error_msg("str7:'%s'", rmescend); if (quotes) { - rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); +//TODO: how to handle slash_pos here if string changes (shortens?) + rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL); if (rmesc != startp) { rmescend = expdest; startp = (char *)stackblock() + startloc; @@ -6881,12 +6925,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, * The result is a_\_z_c (not a\_\_z_c)! * * The search pattern and replace string treat backslashes differently! - * RMESCAPE_SLASH causes preglob to work differently on the pattern + * "&slash_pos" causes rmescapes() to work differently on the pattern * and string. It's only used on the first call. */ - preglob(str, IF_BASH_PATTERN_SUBST( - (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? - RMESCAPE_SLASH : ) 0); + //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos); + rmescapes(str, RMESCAPE_GLOB, + repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos) + ); #if BASH_PATTERN_SUBST workloc = expdest - (char *)stackblock(); @@ -6895,11 +6940,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, char *idx, *end; if (!repl) { - repl = strchr(str, CTLESC); - if (repl) + //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos); + if (slash_pos >= 0) { + repl = str + slash_pos; *repl++ = '\0'; - else + } else { repl = nullstr; + } } //bb_error_msg("str:'%s' repl:'%s'", str, repl); @@ -7419,7 +7466,7 @@ expandmeta(struct strlist *str /*, int flag*/) INT_ON; nometa: *exparg.lastp = str; - rmescapes(str->text, 0); + rmescapes(str->text, 0, NULL); exparg.lastp = &str->next; break; default: /* GLOB_NOSPACE */ @@ -7648,7 +7695,7 @@ expandmeta(struct strlist *str /*, int flag*/) */ nometa: *exparg.lastp = str; - rmescapes(str->text, 0); + rmescapes(str->text, 0, NULL); exparg.lastp = &str->next; } else { *exparg.lastp = NULL; @@ -11328,7 +11375,7 @@ parsefname(void) if (quoteflag == 0) n->type = NXHERE; TRACE(("Here document %d\n", n->type)); - rmescapes(wordtext, 0); + rmescapes(wordtext, 0, NULL); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) -- cgit v1.2.3