diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-25 01:17:40 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-25 01:17:40 +0000 |
commit | 92e13c2a11381eec06a382de6917369ded240804 (patch) | |
tree | ac3d3ce111212fb83a731dc64e653fae4ec9dc03 /shell/ash.c | |
parent | 59f351ccda4c66785b864086bf23868768ca6f61 (diff) | |
download | busybox-w32-92e13c2a11381eec06a382de6917369ded240804.tar.gz busybox-w32-92e13c2a11381eec06a382de6917369ded240804.tar.bz2 busybox-w32-92e13c2a11381eec06a382de6917369ded240804.zip |
ash: optional bash-like pattern subst and substring opts
(by James Simmons <jsimmons AT infradead.org>)
TODO: write testsuite!
BASH_COMPAT off:
scanleft 101 262 +161
subevalvar 346 335 -11
BASH_COMPAT on:
subevalvar 346 1397 +1051
scanleft 101 262 +161
readtoken1 2739 2807 +68
cmdputs 397 399 +2
static.vstype 64 48 -16
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/1 up/down: 1282/-16) Total: 1266 bytes
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 353 |
1 files changed, 311 insertions, 42 deletions
diff --git a/shell/ash.c b/shell/ash.c index 2b6133d20..62380b3ab 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -466,16 +466,21 @@ out2str(const char *p) | |||
466 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ | 466 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ |
467 | 467 | ||
468 | /* values of VSTYPE field */ | 468 | /* values of VSTYPE field */ |
469 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | 469 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ |
470 | #define VSMINUS 0x2 /* ${var-text} */ | 470 | #define VSMINUS 0x2 /* ${var-text} */ |
471 | #define VSPLUS 0x3 /* ${var+text} */ | 471 | #define VSPLUS 0x3 /* ${var+text} */ |
472 | #define VSQUESTION 0x4 /* ${var?message} */ | 472 | #define VSQUESTION 0x4 /* ${var?message} */ |
473 | #define VSASSIGN 0x5 /* ${var=text} */ | 473 | #define VSASSIGN 0x5 /* ${var=text} */ |
474 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ | 474 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ |
475 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ | 475 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ |
476 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ | 476 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ |
477 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ | 477 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ |
478 | #define VSLENGTH 0xa /* ${#var} */ | 478 | #define VSLENGTH 0xa /* ${#var} */ |
479 | #if ENABLE_ASH_BASH_COMPAT | ||
480 | #define VSSUBSTR 0xc /* ${var:position:length} */ | ||
481 | #define VSREPLACE 0xd /* ${var/pattern/replacement} */ | ||
482 | #define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ | ||
483 | #endif | ||
479 | 484 | ||
480 | static const char dolatstr[] ALIGN1 = { | 485 | static const char dolatstr[] ALIGN1 = { |
481 | CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' | 486 | CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' |
@@ -3471,6 +3476,7 @@ getjob(const char *name, int getctl) | |||
3471 | } | 3476 | } |
3472 | 3477 | ||
3473 | if (is_number(p)) { | 3478 | if (is_number(p)) { |
3479 | // TODO: number() instead? It does error checking... | ||
3474 | num = atoi(p); | 3480 | num = atoi(p); |
3475 | if (num < njobs) { | 3481 | if (num < njobs) { |
3476 | jp = jobtab + num - 1; | 3482 | jp = jobtab + num - 1; |
@@ -4178,15 +4184,17 @@ static char *cmdnextc; | |||
4178 | static void | 4184 | static void |
4179 | cmdputs(const char *s) | 4185 | cmdputs(const char *s) |
4180 | { | 4186 | { |
4187 | static const char vstype[VSTYPE + 1][3] = { | ||
4188 | "", "}", "-", "+", "?", "=", | ||
4189 | "%", "%%", "#", "##" | ||
4190 | USE_ASH_BASH_COMPAT(, ":", "/", "//") | ||
4191 | }; | ||
4192 | |||
4181 | const char *p, *str; | 4193 | const char *p, *str; |
4182 | char c, cc[2] = " "; | 4194 | char c, cc[2] = " "; |
4183 | char *nextc; | 4195 | char *nextc; |
4184 | int subtype = 0; | 4196 | int subtype = 0; |
4185 | int quoted = 0; | 4197 | int quoted = 0; |
4186 | static const char vstype[VSTYPE + 1][4] = { | ||
4187 | "", "}", "-", "+", "?", "=", | ||
4188 | "%", "%%", "#", "##" | ||
4189 | }; | ||
4190 | 4198 | ||
4191 | nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); | 4199 | nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); |
4192 | p = s; | 4200 | p = s; |
@@ -5681,23 +5689,37 @@ static char * | |||
5681 | scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes, | 5689 | scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes, |
5682 | int zero) | 5690 | int zero) |
5683 | { | 5691 | { |
5684 | char *loc; | 5692 | char *loc, *loc2, *full; |
5685 | char *loc2; | ||
5686 | char c; | 5693 | char c; |
5687 | 5694 | ||
5688 | loc = startp; | 5695 | loc = startp; |
5689 | loc2 = rmesc; | 5696 | loc2 = rmesc; |
5690 | do { | 5697 | do { |
5691 | int match; | 5698 | int match = strlen(str); |
5692 | const char *s = loc2; | 5699 | const char *s = loc2; |
5700 | |||
5693 | c = *loc2; | 5701 | c = *loc2; |
5694 | if (zero) { | 5702 | if (zero) { |
5695 | *loc2 = '\0'; | 5703 | *loc2 = '\0'; |
5696 | s = rmesc; | 5704 | s = rmesc; |
5697 | } | 5705 | } |
5698 | match = pmatch(str, s); | 5706 | |
5707 | // chop off end if its '*' | ||
5708 | full = strrchr(str, '*'); | ||
5709 | if (full && full != str) | ||
5710 | match--; | ||
5711 | |||
5712 | // If str starts with '*' replace with s. | ||
5713 | if ((*str == '*') && strlen(s) >= match) { | ||
5714 | full = xstrdup(s); | ||
5715 | strncpy(full+strlen(s)-match+1, str+1, match-1); | ||
5716 | } else | ||
5717 | full = xstrndup(str, match); | ||
5718 | match = strncmp(s, full, strlen(full)); | ||
5719 | free(full); | ||
5720 | |||
5699 | *loc2 = c; | 5721 | *loc2 = c; |
5700 | if (match) | 5722 | if (!match) |
5701 | return loc; | 5723 | return loc; |
5702 | if (quotes && *loc == CTLESC) | 5724 | if (quotes && *loc == CTLESC) |
5703 | loc++; | 5725 | loc++; |
@@ -5760,18 +5782,98 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) | |||
5760 | ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); | 5782 | ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); |
5761 | } | 5783 | } |
5762 | 5784 | ||
5785 | #if ENABLE_ASH_BASH_COMPAT | ||
5786 | static char * | ||
5787 | parse_sub_pattern(char *arg, int inquotes) | ||
5788 | { | ||
5789 | char *idx, *repl = NULL; | ||
5790 | unsigned char c; | ||
5791 | |||
5792 | for (idx = arg; *arg; arg++) { | ||
5793 | if (*arg == '/') { | ||
5794 | /* Only the first '/' seen is our seperator */ | ||
5795 | if (!repl) { | ||
5796 | *idx++ = '\0'; | ||
5797 | repl = idx; | ||
5798 | } else | ||
5799 | *idx++ = *arg; | ||
5800 | } else if (*arg != '\\') { | ||
5801 | *idx++ = *arg; | ||
5802 | } else { | ||
5803 | if (inquotes) | ||
5804 | arg++; | ||
5805 | else { | ||
5806 | if (*(arg + 1) != '\\') | ||
5807 | goto single_backslash; | ||
5808 | arg += 2; | ||
5809 | } | ||
5810 | |||
5811 | switch (*arg) { | ||
5812 | case 'n': c = '\n'; break; | ||
5813 | case 'r': c = '\r'; break; | ||
5814 | case 't': c = '\t'; break; | ||
5815 | case 'v': c = '\v'; break; | ||
5816 | case 'f': c = '\f'; break; | ||
5817 | case 'b': c = '\b'; break; | ||
5818 | case 'a': c = '\a'; break; | ||
5819 | case '\\': | ||
5820 | if (*(arg + 1) != '\\' && !inquotes) | ||
5821 | goto single_backslash; | ||
5822 | arg++; | ||
5823 | /* FALLTHROUGH */ | ||
5824 | case '\0': | ||
5825 | /* Trailing backslash, just stuff one in the buffer | ||
5826 | * and backup arg so the loop will exit. | ||
5827 | */ | ||
5828 | c = '\\'; | ||
5829 | if (!*arg) | ||
5830 | arg--; | ||
5831 | break; | ||
5832 | default: | ||
5833 | c = *arg; | ||
5834 | if (isdigit(c)) { | ||
5835 | /* It's an octal number, parse it. */ | ||
5836 | int i; | ||
5837 | c = 0; | ||
5838 | |||
5839 | for (i = 0; *arg && i < 3; arg++, i++) { | ||
5840 | if (*arg >= '8' || *arg < '0') | ||
5841 | ash_msg_and_raise_error("Invalid octal char in pattern"); | ||
5842 | // TODO: number() instead? It does error checking... | ||
5843 | c = (c << 3) + atoi(arg); | ||
5844 | } | ||
5845 | /* back off one (so outer loop can do it) */ | ||
5846 | arg--; | ||
5847 | } | ||
5848 | } | ||
5849 | *idx++ = c; | ||
5850 | } | ||
5851 | } | ||
5852 | *idx = *arg; | ||
5853 | |||
5854 | return repl; | ||
5855 | |||
5856 | single_backslash: | ||
5857 | ash_msg_and_raise_error("single backslash unexpected"); | ||
5858 | /* NOTREACHED */ | ||
5859 | } | ||
5860 | #endif /* ENABLE_ASH_BASH_COMPAT */ | ||
5861 | |||
5763 | static const char * | 5862 | static const char * |
5764 | subevalvar(char *p, char *str, int strloc, int subtype, | 5863 | subevalvar(char *p, char *str, int strloc, int subtype, |
5765 | int startloc, int varflags, int quotes, struct strlist *var_str_list) | 5864 | int startloc, int varflags, int quotes, struct strlist *var_str_list) |
5766 | { | 5865 | { |
5866 | struct nodelist *saveargbackq = argbackq; | ||
5767 | char *startp; | 5867 | char *startp; |
5768 | char *loc; | 5868 | char *loc; |
5769 | int saveherefd = herefd; | ||
5770 | struct nodelist *saveargbackq = argbackq; | ||
5771 | int amount; | ||
5772 | char *rmesc, *rmescend; | 5869 | char *rmesc, *rmescend; |
5870 | USE_ASH_BASH_COMPAT(char *repl = NULL;) | ||
5871 | USE_ASH_BASH_COMPAT(char null = '\0';) | ||
5872 | USE_ASH_BASH_COMPAT(int pos, len, orig_len;) | ||
5873 | int saveherefd = herefd; | ||
5874 | int amount, workloc, resetloc; | ||
5773 | int zero; | 5875 | int zero; |
5774 | char *(*scan)(char *, char *, char *, char *, int , int); | 5876 | char *(*scan)(char*, char*, char*, char*, int, int); |
5775 | 5877 | ||
5776 | herefd = -1; | 5878 | herefd = -1; |
5777 | argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, | 5879 | argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, |
@@ -5788,16 +5890,76 @@ subevalvar(char *p, char *str, int strloc, int subtype, | |||
5788 | STADJUST(amount, expdest); | 5890 | STADJUST(amount, expdest); |
5789 | return startp; | 5891 | return startp; |
5790 | 5892 | ||
5893 | #if ENABLE_ASH_BASH_COMPAT | ||
5894 | case VSSUBSTR: | ||
5895 | loc = str = stackblock() + strloc; | ||
5896 | // TODO: number() instead? It does error checking... | ||
5897 | pos = atoi(loc); | ||
5898 | len = str - startp - 1; | ||
5899 | |||
5900 | /* *loc != '\0', guaranteed by parser */ | ||
5901 | if (quotes) { | ||
5902 | char *ptr; | ||
5903 | |||
5904 | /* We must adjust the length by the number of escapes we find. */ | ||
5905 | for (ptr = startp; ptr < (str - 1); ptr++) { | ||
5906 | if(*ptr == CTLESC) { | ||
5907 | len--; | ||
5908 | ptr++; | ||
5909 | } | ||
5910 | } | ||
5911 | } | ||
5912 | orig_len = len; | ||
5913 | |||
5914 | if (*loc++ == ':') { | ||
5915 | // TODO: number() instead? It does error checking... | ||
5916 | len = atoi(loc); | ||
5917 | } else { | ||
5918 | len = orig_len; | ||
5919 | while (*loc && *loc != ':') | ||
5920 | loc++; | ||
5921 | if (*loc++ == ':') | ||
5922 | // TODO: number() instead? It does error checking... | ||
5923 | len = atoi(loc); | ||
5924 | } | ||
5925 | if (pos >= orig_len) { | ||
5926 | pos = 0; | ||
5927 | len = 0; | ||
5928 | } | ||
5929 | if (len > (orig_len - pos)) | ||
5930 | len = orig_len - pos; | ||
5931 | |||
5932 | for (str = startp; pos; str++, pos--) { | ||
5933 | if (quotes && *str == CTLESC) | ||
5934 | str++; | ||
5935 | } | ||
5936 | for (loc = startp; len; len--) { | ||
5937 | if (quotes && *str == CTLESC) | ||
5938 | *loc++ = *str++; | ||
5939 | *loc++ = *str++; | ||
5940 | } | ||
5941 | *loc = '\0'; | ||
5942 | amount = loc - expdest; | ||
5943 | STADJUST(amount, expdest); | ||
5944 | return loc; | ||
5945 | #endif | ||
5946 | |||
5791 | case VSQUESTION: | 5947 | case VSQUESTION: |
5792 | varunset(p, str, startp, varflags); | 5948 | varunset(p, str, startp, varflags); |
5793 | /* NOTREACHED */ | 5949 | /* NOTREACHED */ |
5794 | } | 5950 | } |
5951 | resetloc = expdest - (char *)stackblock(); | ||
5795 | 5952 | ||
5796 | subtype -= VSTRIMRIGHT; | 5953 | /* We'll comeback here if we grow the stack while handling |
5797 | #if DEBUG | 5954 | * a VSREPLACE or VSREPLACEALL, since our pointers into the |
5798 | if (subtype < 0 || subtype > 3) | 5955 | * stack will need rebasing, and we'll need to remove our work |
5799 | abort(); | 5956 | * areas each time |
5800 | #endif | 5957 | */ |
5958 | USE_ASH_BASH_COMPAT(restart:) | ||
5959 | |||
5960 | amount = expdest - ((char *)stackblock() + resetloc); | ||
5961 | STADJUST(-amount, expdest); | ||
5962 | startp = stackblock() + startloc; | ||
5801 | 5963 | ||
5802 | rmesc = startp; | 5964 | rmesc = startp; |
5803 | rmescend = stackblock() + strloc; | 5965 | rmescend = stackblock() + strloc; |
@@ -5811,7 +5973,93 @@ subevalvar(char *p, char *str, int strloc, int subtype, | |||
5811 | rmescend--; | 5973 | rmescend--; |
5812 | str = stackblock() + strloc; | 5974 | str = stackblock() + strloc; |
5813 | preglob(str, varflags & VSQUOTE, 0); | 5975 | preglob(str, varflags & VSQUOTE, 0); |
5976 | workloc = expdest - (char *)stackblock(); | ||
5977 | |||
5978 | #if ENABLE_ASH_BASH_COMPAT | ||
5979 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { | ||
5980 | char *idx, *end, *restart_detect; | ||
5981 | |||
5982 | if(!repl) { | ||
5983 | repl = parse_sub_pattern(str, varflags & VSQUOTE); | ||
5984 | if (!repl) | ||
5985 | repl = &null; | ||
5986 | } | ||
5987 | |||
5988 | /* If there's no pattern to match, return the expansion unmolested */ | ||
5989 | if (*str == '\0') | ||
5990 | return 0; | ||
5991 | |||
5992 | len = 0; | ||
5993 | idx = startp; | ||
5994 | end = str - 1; | ||
5995 | while (idx < end) { | ||
5996 | loc = scanright(idx, rmesc, rmescend, str, quotes, 1); | ||
5997 | if (!loc) { | ||
5998 | /* No match, advance */ | ||
5999 | restart_detect = stackblock(); | ||
6000 | STPUTC(*idx, expdest); | ||
6001 | if (quotes && *idx == CTLESC) { | ||
6002 | idx++; | ||
6003 | len++; | ||
6004 | STPUTC(*idx, expdest); | ||
6005 | } | ||
6006 | if (stackblock() != restart_detect) | ||
6007 | goto restart; | ||
6008 | idx++; | ||
6009 | len++; | ||
6010 | rmesc++; | ||
6011 | continue; | ||
6012 | } | ||
6013 | |||
6014 | if (subtype == VSREPLACEALL) { | ||
6015 | while (idx < loc) { | ||
6016 | if (quotes && *idx == CTLESC) | ||
6017 | idx++; | ||
6018 | idx++; | ||
6019 | rmesc++; | ||
6020 | } | ||
6021 | } else | ||
6022 | idx = loc; | ||
6023 | |||
6024 | for (loc = repl; *loc; loc++) { | ||
6025 | restart_detect = stackblock(); | ||
6026 | STPUTC(*loc, expdest); | ||
6027 | if (stackblock() != restart_detect) | ||
6028 | goto restart; | ||
6029 | len++; | ||
6030 | } | ||
6031 | |||
6032 | if (subtype == VSREPLACE) { | ||
6033 | while (*idx) { | ||
6034 | restart_detect = stackblock(); | ||
6035 | STPUTC(*idx, expdest); | ||
6036 | if (stackblock() != restart_detect) | ||
6037 | goto restart; | ||
6038 | len++; | ||
6039 | idx++; | ||
6040 | } | ||
6041 | break; | ||
6042 | } | ||
6043 | } | ||
5814 | 6044 | ||
6045 | /* We've put the replaced text into a buffer at workloc, now | ||
6046 | * move it to the right place and adjust the stack. | ||
6047 | */ | ||
6048 | startp = stackblock() + startloc; | ||
6049 | STPUTC('\0', expdest); | ||
6050 | memmove(startp, stackblock() + workloc, len); | ||
6051 | startp[len++] = '\0'; | ||
6052 | amount = expdest - ((char *)stackblock() + startloc + len - 1); | ||
6053 | STADJUST(-amount, expdest); | ||
6054 | return startp; | ||
6055 | } | ||
6056 | #endif /* ENABLE_ASH_BASH_COMPAT */ | ||
6057 | |||
6058 | subtype -= VSTRIMRIGHT; | ||
6059 | #if DEBUG | ||
6060 | if (subtype < 0 || subtype > 7) | ||
6061 | abort(); | ||
6062 | #endif | ||
5815 | /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ | 6063 | /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ |
5816 | zero = subtype >> 1; | 6064 | zero = subtype >> 1; |
5817 | /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ | 6065 | /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ |
@@ -5925,6 +6173,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
5925 | case '7': | 6173 | case '7': |
5926 | case '8': | 6174 | case '8': |
5927 | case '9': | 6175 | case '9': |
6176 | // TODO: number() instead? It does error checking... | ||
5928 | num = atoi(name); | 6177 | num = atoi(name); |
5929 | if (num < 0 || num > shellparam.nparam) | 6178 | if (num < 0 || num > shellparam.nparam) |
5930 | return -1; | 6179 | return -1; |
@@ -6063,6 +6312,11 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
6063 | case VSTRIMLEFTMAX: | 6312 | case VSTRIMLEFTMAX: |
6064 | case VSTRIMRIGHT: | 6313 | case VSTRIMRIGHT: |
6065 | case VSTRIMRIGHTMAX: | 6314 | case VSTRIMRIGHTMAX: |
6315 | #if ENABLE_ASH_BASH_COMPAT | ||
6316 | case VSSUBSTR: | ||
6317 | case VSREPLACE: | ||
6318 | case VSREPLACEALL: | ||
6319 | #endif | ||
6066 | break; | 6320 | break; |
6067 | default: | 6321 | default: |
6068 | abort(); | 6322 | abort(); |
@@ -10459,8 +10713,15 @@ parsesub: { | |||
10459 | if (subtype == 0) { | 10713 | if (subtype == 0) { |
10460 | switch (c) { | 10714 | switch (c) { |
10461 | case ':': | 10715 | case ':': |
10462 | flags = VSNUL; | ||
10463 | c = pgetc(); | 10716 | c = pgetc(); |
10717 | #if ENABLE_ASH_BASH_COMPAT | ||
10718 | if (c == ':' || c == '$' || isdigit(c)) { | ||
10719 | pungetc(); | ||
10720 | subtype = VSSUBSTR; | ||
10721 | break; | ||
10722 | } | ||
10723 | #endif | ||
10724 | flags = VSNUL; | ||
10464 | /*FALLTHROUGH*/ | 10725 | /*FALLTHROUGH*/ |
10465 | default: | 10726 | default: |
10466 | p = strchr(types, c); | 10727 | p = strchr(types, c); |
@@ -10469,18 +10730,26 @@ parsesub: { | |||
10469 | subtype = p - types + VSNORMAL; | 10730 | subtype = p - types + VSNORMAL; |
10470 | break; | 10731 | break; |
10471 | case '%': | 10732 | case '%': |
10472 | case '#': | 10733 | case '#': { |
10473 | { | 10734 | int cc = c; |
10474 | int cc = c; | 10735 | subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT; |
10475 | subtype = c == '#' ? VSTRIMLEFT : | 10736 | c = pgetc(); |
10476 | VSTRIMRIGHT; | 10737 | if (c == cc) |
10477 | c = pgetc(); | 10738 | subtype++; |
10478 | if (c == cc) | 10739 | else |
10479 | subtype++; | 10740 | pungetc(); |
10480 | else | 10741 | break; |
10481 | pungetc(); | 10742 | } |
10482 | break; | 10743 | #if ENABLE_ASH_BASH_COMPAT |
10483 | } | 10744 | case '/': |
10745 | subtype = VSREPLACE; | ||
10746 | c = pgetc(); | ||
10747 | if (c == '/') | ||
10748 | subtype++; /* VSREPLACEALL */ | ||
10749 | else | ||
10750 | pungetc(); | ||
10751 | break; | ||
10752 | #endif | ||
10484 | } | 10753 | } |
10485 | } else { | 10754 | } else { |
10486 | pungetc(); | 10755 | pungetc(); |
@@ -12621,7 +12890,7 @@ static const char op_tokens[] ALIGN1 = { | |||
12621 | 0 | 12890 | 0 |
12622 | }; | 12891 | }; |
12623 | /* ptr to ")" */ | 12892 | /* ptr to ")" */ |
12624 | #define endexpression &op_tokens[sizeof(op_tokens)-7] | 12893 | #define endexpression (&op_tokens[sizeof(op_tokens)-7]) |
12625 | 12894 | ||
12626 | static arith_t | 12895 | static arith_t |
12627 | arith(const char *expr, int *perrcode) | 12896 | arith(const char *expr, int *perrcode) |