From 23bdfc7fd2a012d5205ed22d18eb39e41c8fbc95 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 18 Nov 2010 10:07:55 +0000 Subject: 28418: add ${NAME:OFFSET:LENGTH} substitution --- Src/lex.c | 26 ++++++++---- Src/params.c | 4 +- Src/subst.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/Src/lex.c b/Src/lex.c index 28899fef0..fdb4b98ac 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1398,7 +1398,12 @@ gettokstr(int c, int sub) } -/* Return non-zero for error (character to unget), else zero */ +/* + * Parse input as if in double quotes. + * endchar is the end character to expect. + * sub has got something to do with whether we are doing quoted substitution. + * Return non-zero for error (character to unget), else zero + */ /**/ static int @@ -1591,14 +1596,20 @@ parsestrnoerr(char *s) return err; } +/* + * Parse a subscript in string s. + * sub is passed down to dquote_parse(). + * endchar is the final character. + * Return the next character, or NULL. + */ /**/ mod_export char * -parse_subscript(char *s, int sub) +parse_subscript(char *s, int sub, int endchar) { int l = strlen(s), err; char *t; - if (!*s || *s == ']') + if (!*s || *s == endchar) return 0; lexsave(); untokenize(t = dupstring(s)); @@ -1607,15 +1618,16 @@ parse_subscript(char *s, int sub) len = 0; bptr = tokstr = s; bsiz = l + 1; - err = dquote_parse(']', sub); + err = dquote_parse(endchar, sub); if (err) { err = *bptr; - *bptr = 0; + *bptr = '\0'; untokenize(s); *bptr = err; - s = 0; - } else + s = NULL; + } else { s = bptr; + } strinend(); inpop(); DPUTS(cmdsp, "BUG: parse_subscript: cmdstack not empty."); diff --git a/Src/params.c b/Src/params.c index 7ac33b912..92e0e5368 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1013,7 +1013,7 @@ isident(char *s) return 0; /* Require balanced [ ] pairs with something between */ - if (!(ss = parse_subscript(++ss, 1))) + if (!(ss = parse_subscript(++ss, 1, ']'))) return 0; untokenize(s); return !ss[1]; @@ -1628,7 +1628,7 @@ getindex(char **pptr, Value v, int flags) *s++ = '['; /* Error handled after untokenizing */ - s = parse_subscript(s, flags & SCANPM_DQUOTED); + s = parse_subscript(s, flags & SCANPM_DQUOTED, ']'); /* Now we untokenize everything except inull() markers so we can check * * for the '*' and '@' special subscripts. The inull()s are removed * * in getarg() after we know whether we're doing reverse indexing. */ diff --git a/Src/subst.c b/Src/subst.c index 5f65945a5..c0fb38a48 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1371,6 +1371,43 @@ untok_and_escape(char *s, int escapes, int tok_arg) return dst; } +/* + * See if an argument str looks like a subscript or length following + * a colon and parse it. It must be followed by a ':' or nothing. + * If this succeeds, expand and return the evaulated expression if + * found, else return NULL. + * + * We assume this is what is meant if the first character is not + * an alphabetic character or '&', which signify modifiers. + * + * Set *endp to point to the next character following. + */ +static char * +check_colon_subscript(char *str, char **endp) +{ + int sav; + + /* Could this be a modifier (or empty)? */ + if (!*str || ialpha(*str) || *str == '&') + return NULL; + + *endp = parse_subscript(str, 0, ':'); + if (!*endp) { + /* No trailing colon? */ + *endp = parse_subscript(str, 0, '\0'); + if (!*endp) + return NULL; + } + sav = **endp; + **endp = '\0'; + if (parsestr(str = dupstring(str))) + return NULL; + singsub(&str); + + **endp = sav; + return str; +} + /* parameter substitution */ #define isstring(c) ((c) == '$' || (char)(c) == String || (char)(c) == Qstring) @@ -2683,6 +2720,97 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) } val = dupstring(""); } + if (colf && inbrace) { + /* + * Look for ${PARAM:OFFSET} or ${PARAM:OFFSET:LENGTH}. + * This must appear before modifiers. For compatibility + * with bash we perform both standard string substitutions + * and math eval. + */ + char *check_offset2; + char *check_offset = check_colon_subscript(s, &check_offset2); + if (check_offset) { + zlong offset = mathevali(check_offset); + zlong length = (zlong)-1; + if (errflag) + return NULL; + if ((*check_offset2 && *check_offset2 != ':')) { + zerr("invalid subscript: %s", check_offset); + return NULL; + } + if (*check_offset2) { + check_offset = check_colon_subscript(check_offset2 + 1, + &check_offset2); + if (*check_offset2 && *check_offset2 != ':') { + zerr("invalid length: %s", check_offset); + return NULL; + } + length = mathevali(check_offset); + if (errflag) + return NULL; + if (length < (zlong)0) { + zerr("invalid length: %s", check_offset); + return NULL; + } + } + if (!isset(KSHARRAYS) && offset > 0) + offset--; + if (isarr) { + int alen = arrlen(aval), count; + char **srcptr, **dstptr, **newarr; + + if (offset < 0) { + offset += alen; + if (offset < 0) + offset = 0; + } + if (length < 0) + length = alen; + if (offset > alen) + offset = alen; + if (offset + length > alen) + length = alen - offset; + count = length; + srcptr = aval + offset; + newarr = dstptr = (char **) + zhalloc((length+1)*sizeof(char *)); + while (count--) + *dstptr++ = dupstring(*srcptr++); + *dstptr = (char *)NULL; + aval = newarr; + } else { + char *sptr, *eptr; + if (offset < 0) { + MB_METACHARINIT(); + for (sptr = val; *sptr; ) { + sptr += MB_METACHARLEN(sptr); + offset++; + } + if (offset < 0) + offset = 0; + } + MB_METACHARINIT(); + for (sptr = val; *sptr && offset; ) { + sptr += MB_METACHARLEN(sptr); + offset--; + } + if (length >= 0) { + for (eptr = sptr; *eptr && length; ) { + eptr += MB_METACHARLEN(eptr); + length--; + } + val = dupstrpfx(sptr, eptr - sptr); + } else { + val = dupstring(sptr); + } + } + if (!*check_offset2) { + colf = 0; + } else { + s = check_offset2 + 1; + } + } + } if (colf) { /* * History style colon modifiers. May need to apply -- cgit 1.4.1