diff options
Diffstat (limited to 'shell/ash.c')
| -rw-r--r-- | shell/ash.c | 388 |
1 files changed, 167 insertions, 221 deletions
diff --git a/shell/ash.c b/shell/ash.c index 8cc3f0872..578904478 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -666,8 +666,6 @@ out2str(const char *p) | |||
| 666 | #define CTLVAR ((unsigned char)'\202') /* variable defn */ | 666 | #define CTLVAR ((unsigned char)'\202') /* variable defn */ |
| 667 | #define CTLENDVAR ((unsigned char)'\203') | 667 | #define CTLENDVAR ((unsigned char)'\203') |
| 668 | #define CTLBACKQ ((unsigned char)'\204') | 668 | #define CTLBACKQ ((unsigned char)'\204') |
| 669 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ | ||
| 670 | /* CTLBACKQ | CTLQUOTE == '\205' */ | ||
| 671 | #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ | 669 | #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ |
| 672 | #define CTLENDARI ((unsigned char)'\207') | 670 | #define CTLENDARI ((unsigned char)'\207') |
| 673 | #define CTLQUOTEMARK ((unsigned char)'\210') | 671 | #define CTLQUOTEMARK ((unsigned char)'\210') |
| @@ -676,7 +674,6 @@ out2str(const char *p) | |||
| 676 | /* variable substitution byte (follows CTLVAR) */ | 674 | /* variable substitution byte (follows CTLVAR) */ |
| 677 | #define VSTYPE 0x0f /* type of variable substitution */ | 675 | #define VSTYPE 0x0f /* type of variable substitution */ |
| 678 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | 676 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ |
| 679 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ | ||
| 680 | 677 | ||
| 681 | /* values of VSTYPE field */ | 678 | /* values of VSTYPE field */ |
| 682 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | 679 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ |
| @@ -696,8 +693,9 @@ out2str(const char *p) | |||
| 696 | #endif | 693 | #endif |
| 697 | 694 | ||
| 698 | static const char dolatstr[] ALIGN1 = { | 695 | static const char dolatstr[] ALIGN1 = { |
| 699 | CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' | 696 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' |
| 700 | }; | 697 | }; |
| 698 | #define DOLATSTRLEN 6 | ||
| 701 | 699 | ||
| 702 | #define NCMD 0 | 700 | #define NCMD 0 |
| 703 | #define NPIPE 1 | 701 | #define NPIPE 1 |
| @@ -933,9 +931,7 @@ trace_puts_quoted(char *s) | |||
| 933 | case '\\': c = '\\'; goto backslash; | 931 | case '\\': c = '\\'; goto backslash; |
| 934 | case CTLESC: c = 'e'; goto backslash; | 932 | case CTLESC: c = 'e'; goto backslash; |
| 935 | case CTLVAR: c = 'v'; goto backslash; | 933 | case CTLVAR: c = 'v'; goto backslash; |
| 936 | case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; | ||
| 937 | case CTLBACKQ: c = 'q'; goto backslash; | 934 | case CTLBACKQ: c = 'q'; goto backslash; |
| 938 | case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; | ||
| 939 | backslash: | 935 | backslash: |
| 940 | putc('\\', tracefile); | 936 | putc('\\', tracefile); |
| 941 | putc(c, tracefile); | 937 | putc(c, tracefile); |
| @@ -1098,7 +1094,6 @@ sharg(union node *arg, FILE *fp) | |||
| 1098 | putc('}', fp); | 1094 | putc('}', fp); |
| 1099 | break; | 1095 | break; |
| 1100 | case CTLBACKQ: | 1096 | case CTLBACKQ: |
| 1101 | case CTLBACKQ|CTLQUOTE: | ||
| 1102 | putc('$', fp); | 1097 | putc('$', fp); |
| 1103 | putc('(', fp); | 1098 | putc('(', fp); |
| 1104 | shtree(bqlist->n, -1, NULL, fp); | 1099 | shtree(bqlist->n, -1, NULL, fp); |
| @@ -2098,7 +2093,7 @@ varcmp(const char *p, const char *q) | |||
| 2098 | int c, d; | 2093 | int c, d; |
| 2099 | 2094 | ||
| 2100 | while ((c = *p) == (d = *q)) { | 2095 | while ((c = *p) == (d = *q)) { |
| 2101 | if (!c || c == '=') | 2096 | if (c == '\0' || c == '=') |
| 2102 | goto out; | 2097 | goto out; |
| 2103 | p++; | 2098 | p++; |
| 2104 | q++; | 2099 | q++; |
| @@ -2315,7 +2310,7 @@ setvar(const char *name, const char *val, int flags) | |||
| 2315 | } | 2310 | } |
| 2316 | 2311 | ||
| 2317 | static void FAST_FUNC | 2312 | static void FAST_FUNC |
| 2318 | setvar2(const char *name, const char *val) | 2313 | setvar0(const char *name, const char *val) |
| 2319 | { | 2314 | { |
| 2320 | setvar(name, val, 0); | 2315 | setvar(name, val, 0); |
| 2321 | } | 2316 | } |
| @@ -2378,7 +2373,7 @@ unsetvar(const char *s) | |||
| 2378 | free(vp); | 2373 | free(vp); |
| 2379 | INT_ON; | 2374 | INT_ON; |
| 2380 | } else { | 2375 | } else { |
| 2381 | setvar2(s, 0); | 2376 | setvar0(s, NULL); |
| 2382 | vp->flags &= ~VEXPORT; | 2377 | vp->flags &= ~VEXPORT; |
| 2383 | } | 2378 | } |
| 2384 | ok: | 2379 | ok: |
| @@ -4689,11 +4684,7 @@ cmdputs(const char *s) | |||
| 4689 | str = "${#"; | 4684 | str = "${#"; |
| 4690 | else | 4685 | else |
| 4691 | str = "${"; | 4686 | str = "${"; |
| 4692 | if (!(subtype & VSQUOTE) == !(quoted & 1)) | 4687 | goto dostr; |
| 4693 | goto dostr; | ||
| 4694 | quoted ^= 1; | ||
| 4695 | c = '"'; | ||
| 4696 | break; | ||
| 4697 | case CTLENDVAR: | 4688 | case CTLENDVAR: |
| 4698 | str = "\"}" + !(quoted & 1); | 4689 | str = "\"}" + !(quoted & 1); |
| 4699 | quoted >>= 1; | 4690 | quoted >>= 1; |
| @@ -4702,9 +4693,6 @@ cmdputs(const char *s) | |||
| 4702 | case CTLBACKQ: | 4693 | case CTLBACKQ: |
| 4703 | str = "$(...)"; | 4694 | str = "$(...)"; |
| 4704 | goto dostr; | 4695 | goto dostr; |
| 4705 | case CTLBACKQ+CTLQUOTE: | ||
| 4706 | str = "\"$(...)\""; | ||
| 4707 | goto dostr; | ||
| 4708 | #if ENABLE_SH_MATH_SUPPORT | 4696 | #if ENABLE_SH_MATH_SUPPORT |
| 4709 | case CTLARI: | 4697 | case CTLARI: |
| 4710 | str = "$(("; | 4698 | str = "$(("; |
| @@ -5822,7 +5810,7 @@ ash_arith(const char *s) | |||
| 5822 | arith_t result; | 5810 | arith_t result; |
| 5823 | 5811 | ||
| 5824 | math_state.lookupvar = lookupvar; | 5812 | math_state.lookupvar = lookupvar; |
| 5825 | math_state.setvar = setvar2; | 5813 | math_state.setvar = setvar0; |
| 5826 | //math_state.endofname = endofname; | 5814 | //math_state.endofname = endofname; |
| 5827 | 5815 | ||
| 5828 | INT_OFF; | 5816 | INT_OFF; |
| @@ -5843,18 +5831,23 @@ ash_arith(const char *s) | |||
| 5843 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ | 5831 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ |
| 5844 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ | 5832 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ |
| 5845 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | 5833 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
| 5846 | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ | 5834 | #define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ |
| 5847 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ | 5835 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ |
| 5848 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | 5836 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
| 5849 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ | 5837 | #define EXP_QUOTED 0x100 /* expand word in double quotes */ |
| 5850 | /* | 5838 | /* |
| 5851 | * rmescape() flags | 5839 | * rmescape() flags |
| 5852 | */ | 5840 | */ |
| 5853 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ | 5841 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
| 5854 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | 5842 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
| 5855 | #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ | ||
| 5856 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ | 5843 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ |
| 5857 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | 5844 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ |
| 5845 | #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ | ||
| 5846 | |||
| 5847 | /* Add CTLESC when necessary. */ | ||
| 5848 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) | ||
| 5849 | /* Do not skip NUL characters. */ | ||
| 5850 | #define QUOTES_KEEPNUL EXP_TILDE | ||
| 5858 | 5851 | ||
| 5859 | /* | 5852 | /* |
| 5860 | * Structure specifying which parts of the string should be searched | 5853 | * Structure specifying which parts of the string should be searched |
| @@ -5919,14 +5912,16 @@ esclen(const char *start, const char *p) | |||
| 5919 | static char * | 5912 | static char * |
| 5920 | rmescapes(char *str, int flag) | 5913 | rmescapes(char *str, int flag) |
| 5921 | { | 5914 | { |
| 5922 | static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; | 5915 | static const char qchars[] ALIGN1 = { |
| 5916 | IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' }; | ||
| 5923 | 5917 | ||
| 5924 | char *p, *q, *r; | 5918 | char *p, *q, *r; |
| 5925 | unsigned inquotes; | 5919 | unsigned inquotes; |
| 5926 | unsigned protect_against_glob; | 5920 | unsigned protect_against_glob; |
| 5927 | unsigned globbing; | 5921 | unsigned globbing; |
| 5922 | IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;) | ||
| 5928 | 5923 | ||
| 5929 | p = strpbrk(str, qchars); | 5924 | p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash)); |
| 5930 | if (!p) | 5925 | if (!p) |
| 5931 | return str; | 5926 | return str; |
| 5932 | 5927 | ||
| @@ -5953,13 +5948,11 @@ rmescapes(char *str, int flag) | |||
| 5953 | } | 5948 | } |
| 5954 | } | 5949 | } |
| 5955 | 5950 | ||
| 5956 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; | 5951 | inquotes = 0; |
| 5957 | globbing = flag & RMESCAPE_GLOB; | 5952 | globbing = flag & RMESCAPE_GLOB; |
| 5958 | protect_against_glob = globbing; | 5953 | protect_against_glob = globbing; |
| 5959 | while (*p) { | 5954 | while (*p) { |
| 5960 | if ((unsigned char)*p == CTLQUOTEMARK) { | 5955 | if ((unsigned char)*p == CTLQUOTEMARK) { |
| 5961 | // TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 | ||
| 5962 | // (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? | ||
| 5963 | // Note: both inquotes and protect_against_glob only affect whether | 5956 | // Note: both inquotes and protect_against_glob only affect whether |
| 5964 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | 5957 | // CTLESC,<ch> gets converted to <ch> or to \<ch> |
| 5965 | inquotes = ~inquotes; | 5958 | inquotes = ~inquotes; |
| @@ -5967,17 +5960,23 @@ rmescapes(char *str, int flag) | |||
| 5967 | protect_against_glob = globbing; | 5960 | protect_against_glob = globbing; |
| 5968 | continue; | 5961 | continue; |
| 5969 | } | 5962 | } |
| 5970 | if (*p == '\\') { | ||
| 5971 | /* naked back slash */ | ||
| 5972 | protect_against_glob = 0; | ||
| 5973 | goto copy; | ||
| 5974 | } | ||
| 5975 | if ((unsigned char)*p == CTLESC) { | 5963 | if ((unsigned char)*p == CTLESC) { |
| 5976 | p++; | 5964 | p++; |
| 5977 | if (protect_against_glob && inquotes && *p != '/') { | 5965 | if (protect_against_glob) { |
| 5978 | *q++ = '\\'; | 5966 | *q++ = '\\'; |
| 5979 | } | 5967 | } |
| 5968 | } else if (*p == '\\' && !inquotes) { | ||
| 5969 | /* naked back slash */ | ||
| 5970 | protect_against_glob = 0; | ||
| 5971 | goto copy; | ||
| 5972 | } | ||
| 5973 | #if ENABLE_ASH_BASH_COMPAT | ||
| 5974 | else if (*p == '/' && slash) { | ||
| 5975 | /* stop handling globbing and mark location of slash */ | ||
| 5976 | globbing = slash = 0; | ||
| 5977 | *p = CTLESC; | ||
| 5980 | } | 5978 | } |
| 5979 | #endif | ||
| 5981 | protect_against_glob = globbing; | 5980 | protect_against_glob = globbing; |
| 5982 | copy: | 5981 | copy: |
| 5983 | *q++ = *p++; | 5982 | *q++ = *p++; |
| @@ -5997,13 +5996,9 @@ rmescapes(char *str, int flag) | |||
| 5997 | * Returns an stalloced string. | 5996 | * Returns an stalloced string. |
| 5998 | */ | 5997 | */ |
| 5999 | static char * | 5998 | static char * |
| 6000 | preglob(const char *pattern, int quoted, int flag) | 5999 | preglob(const char *pattern, int flag) |
| 6001 | { | 6000 | { |
| 6002 | flag |= RMESCAPE_GLOB; | 6001 | return rmescapes((char *)pattern, flag | RMESCAPE_GLOB); |
| 6003 | if (quoted) { | ||
| 6004 | flag |= RMESCAPE_QUOTED; | ||
| 6005 | } | ||
| 6006 | return rmescapes((char *)pattern, flag); | ||
| 6007 | } | 6002 | } |
| 6008 | 6003 | ||
| 6009 | /* | 6004 | /* |
| @@ -6012,29 +6007,36 @@ preglob(const char *pattern, int quoted, int flag) | |||
| 6012 | static void | 6007 | static void |
| 6013 | memtodest(const char *p, size_t len, int syntax, int quotes) | 6008 | memtodest(const char *p, size_t len, int syntax, int quotes) |
| 6014 | { | 6009 | { |
| 6015 | char *q = expdest; | 6010 | char *q; |
| 6011 | |||
| 6012 | if (!len) | ||
| 6013 | return; | ||
| 6016 | 6014 | ||
| 6017 | q = makestrspace(quotes ? len * 2 : len, q); | 6015 | q = makestrspace((quotes & QUOTES_ESC) ? len * 2 : len, expdest); |
| 6018 | 6016 | ||
| 6019 | while (len--) { | 6017 | do { |
| 6020 | unsigned char c = *p++; | 6018 | unsigned char c = *p++; |
| 6021 | if (c == '\0') | 6019 | if (c) { |
| 6022 | continue; | ||
| 6023 | if (quotes) { | ||
| 6024 | int n = SIT(c, syntax); | 6020 | int n = SIT(c, syntax); |
| 6025 | if (n == CCTL || n == CBACK) | 6021 | if ((quotes & QUOTES_ESC) && |
| 6022 | ((n == CCTL) || | ||
| 6023 | (((quotes & EXP_FULL) || syntax != BASESYNTAX) && | ||
| 6024 | n == CBACK))) | ||
| 6026 | USTPUTC(CTLESC, q); | 6025 | USTPUTC(CTLESC, q); |
| 6027 | } | 6026 | } else if (!(quotes & QUOTES_KEEPNUL)) |
| 6027 | continue; | ||
| 6028 | USTPUTC(c, q); | 6028 | USTPUTC(c, q); |
| 6029 | } | 6029 | } while (--len); |
| 6030 | 6030 | ||
| 6031 | expdest = q; | 6031 | expdest = q; |
| 6032 | } | 6032 | } |
| 6033 | 6033 | ||
| 6034 | static void | 6034 | static size_t |
| 6035 | strtodest(const char *p, int syntax, int quotes) | 6035 | strtodest(const char *p, int syntax, int quotes) |
| 6036 | { | 6036 | { |
| 6037 | memtodest(p, strlen(p), syntax, quotes); | 6037 | size_t len = strlen(p); |
| 6038 | memtodest(p, len, syntax, quotes); | ||
| 6039 | return len; | ||
| 6038 | } | 6040 | } |
| 6039 | 6041 | ||
| 6040 | /* | 6042 | /* |
| @@ -6107,7 +6109,7 @@ exptilde(char *startp, char *p, int flags) | |||
| 6107 | char *name; | 6109 | char *name; |
| 6108 | struct passwd *pw; | 6110 | struct passwd *pw; |
| 6109 | const char *home; | 6111 | const char *home; |
| 6110 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); | 6112 | int quotes = flags & QUOTES_ESC; |
| 6111 | 6113 | ||
| 6112 | name = p + 1; | 6114 | name = p + 1; |
| 6113 | 6115 | ||
| @@ -6223,7 +6225,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
| 6223 | * Expand stuff in backwards quotes. | 6225 | * Expand stuff in backwards quotes. |
| 6224 | */ | 6226 | */ |
| 6225 | static void | 6227 | static void |
| 6226 | expbackq(union node *cmd, int quoted, int quotes) | 6228 | expbackq(union node *cmd, int flag) |
| 6227 | { | 6229 | { |
| 6228 | struct backcmd in; | 6230 | struct backcmd in; |
| 6229 | int i; | 6231 | int i; |
| @@ -6231,7 +6233,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
| 6231 | char *p; | 6233 | char *p; |
| 6232 | char *dest; | 6234 | char *dest; |
| 6233 | int startloc; | 6235 | int startloc; |
| 6234 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6236 | int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; |
| 6235 | struct stackmark smark; | 6237 | struct stackmark smark; |
| 6236 | 6238 | ||
| 6237 | INT_OFF; | 6239 | INT_OFF; |
| @@ -6247,11 +6249,11 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
| 6247 | if (i == 0) | 6249 | if (i == 0) |
| 6248 | goto read; | 6250 | goto read; |
| 6249 | for (;;) { | 6251 | for (;;) { |
| 6250 | memtodest(p, i, syntax, quotes); | 6252 | memtodest(p, i, syntax, flag & QUOTES_ESC); |
| 6251 | read: | 6253 | read: |
| 6252 | if (in.fd < 0) | 6254 | if (in.fd < 0) |
| 6253 | break; | 6255 | break; |
| 6254 | i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); | 6256 | i = nonblock_immune_read(in.fd, buf, sizeof(buf)); |
| 6255 | TRACE(("expbackq: read returns %d\n", i)); | 6257 | TRACE(("expbackq: read returns %d\n", i)); |
| 6256 | if (i <= 0) | 6258 | if (i <= 0) |
| 6257 | break; | 6259 | break; |
| @@ -6272,7 +6274,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
| 6272 | STUNPUTC(dest); | 6274 | STUNPUTC(dest); |
| 6273 | expdest = dest; | 6275 | expdest = dest; |
| 6274 | 6276 | ||
| 6275 | if (quoted == 0) | 6277 | if (!(flag & EXP_QUOTED)) |
| 6276 | recordregion(startloc, dest - (char *)stackblock(), 0); | 6278 | recordregion(startloc, dest - (char *)stackblock(), 0); |
| 6277 | TRACE(("evalbackq: size:%d:'%.*s'\n", | 6279 | TRACE(("evalbackq: size:%d:'%.*s'\n", |
| 6278 | (int)((dest - (char *)stackblock()) - startloc), | 6280 | (int)((dest - (char *)stackblock()) - startloc), |
| @@ -6286,11 +6288,10 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
| 6286 | * evaluate, place result in (backed up) result, adjust string position. | 6288 | * evaluate, place result in (backed up) result, adjust string position. |
| 6287 | */ | 6289 | */ |
| 6288 | static void | 6290 | static void |
| 6289 | expari(int quotes) | 6291 | expari(int flag) |
| 6290 | { | 6292 | { |
| 6291 | char *p, *start; | 6293 | char *p, *start; |
| 6292 | int begoff; | 6294 | int begoff; |
| 6293 | int flag; | ||
| 6294 | int len; | 6295 | int len; |
| 6295 | 6296 | ||
| 6296 | /* ifsfree(); */ | 6297 | /* ifsfree(); */ |
| @@ -6328,16 +6329,14 @@ expari(int quotes) | |||
| 6328 | 6329 | ||
| 6329 | removerecordregions(begoff); | 6330 | removerecordregions(begoff); |
| 6330 | 6331 | ||
| 6331 | flag = p[1]; | ||
| 6332 | |||
| 6333 | expdest = p; | 6332 | expdest = p; |
| 6334 | 6333 | ||
| 6335 | if (quotes) | 6334 | if (flag & QUOTES_ESC) |
| 6336 | rmescapes(p + 2, 0); | 6335 | rmescapes(p + 1, 0); |
| 6337 | 6336 | ||
| 6338 | len = cvtnum(ash_arith(p + 2)); | 6337 | len = cvtnum(ash_arith(p + 1)); |
| 6339 | 6338 | ||
| 6340 | if (flag != '"') | 6339 | if (!(flag & EXP_QUOTED)) |
| 6341 | recordregion(begoff, begoff + len, 0); | 6340 | recordregion(begoff, begoff + len, 0); |
| 6342 | } | 6341 | } |
| 6343 | #endif | 6342 | #endif |
| @@ -6365,15 +6364,13 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
| 6365 | CTLESC, | 6364 | CTLESC, |
| 6366 | CTLVAR, | 6365 | CTLVAR, |
| 6367 | CTLBACKQ, | 6366 | CTLBACKQ, |
| 6368 | CTLBACKQ | CTLQUOTE, | ||
| 6369 | #if ENABLE_SH_MATH_SUPPORT | 6367 | #if ENABLE_SH_MATH_SUPPORT |
| 6370 | CTLENDARI, | 6368 | CTLENDARI, |
| 6371 | #endif | 6369 | #endif |
| 6372 | '\0' | 6370 | '\0' |
| 6373 | }; | 6371 | }; |
| 6374 | const char *reject = spclchars; | 6372 | const char *reject = spclchars; |
| 6375 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ | 6373 | int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD; |
| 6376 | int breakall = flags & EXP_WORD; | ||
| 6377 | int inquotes; | 6374 | int inquotes; |
| 6378 | size_t length; | 6375 | size_t length; |
| 6379 | int startloc; | 6376 | int startloc; |
| @@ -6391,8 +6388,6 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
| 6391 | flags &= ~EXP_TILDE; | 6388 | flags &= ~EXP_TILDE; |
| 6392 | tilde: | 6389 | tilde: |
| 6393 | q = p; | 6390 | q = p; |
| 6394 | if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD)) | ||
| 6395 | q++; | ||
| 6396 | if (*q == '~') | 6391 | if (*q == '~') |
| 6397 | p = exptilde(p, q, flags); | 6392 | p = exptilde(p, q, flags); |
| 6398 | } | 6393 | } |
| @@ -6449,19 +6444,14 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
| 6449 | case CTLENDVAR: /* ??? */ | 6444 | case CTLENDVAR: /* ??? */ |
| 6450 | goto breakloop; | 6445 | goto breakloop; |
| 6451 | case CTLQUOTEMARK: | 6446 | case CTLQUOTEMARK: |
| 6447 | inquotes ^= EXP_QUOTED; | ||
| 6452 | /* "$@" syntax adherence hack */ | 6448 | /* "$@" syntax adherence hack */ |
| 6453 | if (!inquotes | 6449 | if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { |
| 6454 | && memcmp(p, dolatstr, 4) == 0 | 6450 | p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1; |
| 6455 | && ( p[4] == (char)CTLQUOTEMARK | ||
| 6456 | || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK) | ||
| 6457 | ) | ||
| 6458 | ) { | ||
| 6459 | p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; | ||
| 6460 | goto start; | 6451 | goto start; |
| 6461 | } | 6452 | } |
| 6462 | inquotes = !inquotes; | ||
| 6463 | addquote: | 6453 | addquote: |
| 6464 | if (quotes) { | 6454 | if (flags & QUOTES_ESC) { |
| 6465 | p--; | 6455 | p--; |
| 6466 | length++; | 6456 | length++; |
| 6467 | startloc++; | 6457 | startloc++; |
| @@ -6470,22 +6460,30 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
| 6470 | case CTLESC: | 6460 | case CTLESC: |
| 6471 | startloc++; | 6461 | startloc++; |
| 6472 | length++; | 6462 | length++; |
| 6463 | |||
| 6464 | /* | ||
| 6465 | * Quoted parameter expansion pattern: remove quote | ||
| 6466 | * unless inside inner quotes or we have a literal | ||
| 6467 | * backslash. | ||
| 6468 | */ | ||
| 6469 | if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) == | ||
| 6470 | EXP_QPAT && *p != '\\') | ||
| 6471 | break; | ||
| 6472 | |||
| 6473 | goto addquote; | 6473 | goto addquote; |
| 6474 | case CTLVAR: | 6474 | case CTLVAR: |
| 6475 | TRACE(("argstr: evalvar('%s')\n", p)); | 6475 | TRACE(("argstr: evalvar('%s')\n", p)); |
| 6476 | p = evalvar(p, flags, var_str_list); | 6476 | p = evalvar(p, flags | inquotes, var_str_list); |
| 6477 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); | 6477 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); |
| 6478 | goto start; | 6478 | goto start; |
| 6479 | case CTLBACKQ: | 6479 | case CTLBACKQ: |
| 6480 | c = '\0'; | 6480 | expbackq(argbackq->n, flags | inquotes); |
| 6481 | case CTLBACKQ|CTLQUOTE: | ||
| 6482 | expbackq(argbackq->n, c, quotes); | ||
| 6483 | argbackq = argbackq->next; | 6481 | argbackq = argbackq->next; |
| 6484 | goto start; | 6482 | goto start; |
| 6485 | #if ENABLE_SH_MATH_SUPPORT | 6483 | #if ENABLE_SH_MATH_SUPPORT |
| 6486 | case CTLENDARI: | 6484 | case CTLENDARI: |
| 6487 | p--; | 6485 | p--; |
| 6488 | expari(quotes); | 6486 | expari(flags | inquotes); |
| 6489 | goto start; | 6487 | goto start; |
| 6490 | #endif | 6488 | #endif |
| 6491 | } | 6489 | } |
| @@ -6615,60 +6613,17 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) | |||
| 6615 | ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); | 6613 | ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); |
| 6616 | } | 6614 | } |
| 6617 | 6615 | ||
| 6618 | #if ENABLE_ASH_BASH_COMPAT | ||
| 6619 | static char * | ||
| 6620 | parse_sub_pattern(char *arg, int varflags) | ||
| 6621 | { | ||
| 6622 | char *idx, *repl = NULL; | ||
| 6623 | unsigned char c; | ||
| 6624 | |||
| 6625 | //char *org_arg = arg; | ||
| 6626 | //bb_error_msg("arg:'%s' varflags:%x", arg, varflags); | ||
| 6627 | idx = arg; | ||
| 6628 | while (1) { | ||
| 6629 | c = *arg; | ||
| 6630 | if (!c) | ||
| 6631 | break; | ||
| 6632 | if (c == '/') { | ||
| 6633 | /* Only the first '/' seen is our separator */ | ||
| 6634 | if (!repl) { | ||
| 6635 | repl = idx + 1; | ||
| 6636 | c = '\0'; | ||
| 6637 | } | ||
| 6638 | } | ||
| 6639 | *idx++ = c; | ||
| 6640 | arg++; | ||
| 6641 | /* | ||
| 6642 | * Example: v='ab\c'; echo ${v/\\b/_\\_\z_} | ||
| 6643 | * The result is a_\_z_c (not a\_\_z_c)! | ||
| 6644 | * | ||
| 6645 | * Enable debug prints in this function and you'll see: | ||
| 6646 | * ash: arg:'\\b/_\\_z_' varflags:d | ||
| 6647 | * ash: pattern:'\\b' repl:'_\_z_' | ||
| 6648 | * That is, \\b is interpreted as \\b, but \\_ as \_! | ||
| 6649 | * IOW: search pattern and replace string treat backslashes | ||
| 6650 | * differently! That is the reason why we check repl below: | ||
| 6651 | */ | ||
| 6652 | if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE)) | ||
| 6653 | arg++; /* skip both '\', not just first one */ | ||
| 6654 | } | ||
| 6655 | *idx = c; /* NUL */ | ||
| 6656 | //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl); | ||
| 6657 | |||
| 6658 | return repl; | ||
| 6659 | } | ||
| 6660 | #endif /* ENABLE_ASH_BASH_COMPAT */ | ||
| 6661 | |||
| 6662 | static const char * | 6616 | static const char * |
| 6663 | subevalvar(char *p, char *varname, int strloc, int subtype, | 6617 | subevalvar(char *p, char *varname, int strloc, int subtype, |
| 6664 | int startloc, int varflags, int quotes, struct strlist *var_str_list) | 6618 | int startloc, int varflags, int flag, struct strlist *var_str_list) |
| 6665 | { | 6619 | { |
| 6666 | struct nodelist *saveargbackq = argbackq; | 6620 | struct nodelist *saveargbackq = argbackq; |
| 6621 | int quotes = flag & QUOTES_ESC; | ||
| 6667 | char *startp; | 6622 | char *startp; |
| 6668 | char *loc; | 6623 | char *loc; |
| 6669 | char *rmesc, *rmescend; | 6624 | char *rmesc, *rmescend; |
| 6670 | char *str; | 6625 | char *str; |
| 6671 | IF_ASH_BASH_COMPAT(const char *repl = NULL;) | 6626 | IF_ASH_BASH_COMPAT(char *repl = NULL;) |
| 6672 | IF_ASH_BASH_COMPAT(int pos, len, orig_len;) | 6627 | IF_ASH_BASH_COMPAT(int pos, len, orig_len;) |
| 6673 | int saveherefd = herefd; | 6628 | int saveherefd = herefd; |
| 6674 | int amount, resetloc; | 6629 | int amount, resetloc; |
| @@ -6680,7 +6635,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
| 6680 | // p, varname, strloc, subtype, startloc, varflags, quotes); | 6635 | // p, varname, strloc, subtype, startloc, varflags, quotes); |
| 6681 | 6636 | ||
| 6682 | herefd = -1; | 6637 | herefd = -1; |
| 6683 | argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, | 6638 | argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? |
| 6639 | (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0), | ||
| 6684 | var_str_list); | 6640 | var_str_list); |
| 6685 | STPUTC('\0', expdest); | 6641 | STPUTC('\0', expdest); |
| 6686 | herefd = saveherefd; | 6642 | herefd = saveherefd; |
| @@ -6689,7 +6645,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
| 6689 | 6645 | ||
| 6690 | switch (subtype) { | 6646 | switch (subtype) { |
| 6691 | case VSASSIGN: | 6647 | case VSASSIGN: |
| 6692 | setvar2(varname, startp); | 6648 | setvar0(varname, startp); |
| 6693 | amount = startp - expdest; | 6649 | amount = startp - expdest; |
| 6694 | STADJUST(amount, expdest); | 6650 | STADJUST(amount, expdest); |
| 6695 | return startp; | 6651 | return startp; |
| @@ -6792,7 +6748,17 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
| 6792 | } | 6748 | } |
| 6793 | rmescend--; | 6749 | rmescend--; |
| 6794 | str = (char *)stackblock() + strloc; | 6750 | str = (char *)stackblock() + strloc; |
| 6795 | preglob(str, varflags & VSQUOTE, 0); | 6751 | /* |
| 6752 | * Example: v='a\bc'; echo ${v/\\b/_\\_\z_} | ||
| 6753 | * The result is a_\_z_c (not a\_\_z_c)! | ||
| 6754 | * | ||
| 6755 | * The search pattern and replace string treat backslashes differently! | ||
| 6756 | * RMESCAPE_SLASH causes preglob to work differently on the pattern | ||
| 6757 | * and string. It's only used on the first call. | ||
| 6758 | */ | ||
| 6759 | preglob(str, IF_ASH_BASH_COMPAT( | ||
| 6760 | (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? | ||
| 6761 | RMESCAPE_SLASH :) 0); | ||
| 6796 | 6762 | ||
| 6797 | #if ENABLE_ASH_BASH_COMPAT | 6763 | #if ENABLE_ASH_BASH_COMPAT |
| 6798 | workloc = expdest - (char *)stackblock(); | 6764 | workloc = expdest - (char *)stackblock(); |
| @@ -6800,11 +6766,12 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
| 6800 | char *idx, *end; | 6766 | char *idx, *end; |
| 6801 | 6767 | ||
| 6802 | if (!repl) { | 6768 | if (!repl) { |
| 6803 | repl = parse_sub_pattern(str, varflags); | 6769 | if ((repl=strchr(str, CTLESC))) |
| 6804 | //bb_error_msg("repl:'%s'", repl); | 6770 | *repl++ = '\0'; |
| 6805 | if (!repl) | 6771 | else |
| 6806 | repl = nullstr; | 6772 | repl = nullstr; |
| 6807 | } | 6773 | } |
| 6774 | //bb_error_msg("str:'%s' repl:'%s'", str, repl); | ||
| 6808 | 6775 | ||
| 6809 | /* If there's no pattern to match, return the expansion unmolested */ | 6776 | /* If there's no pattern to match, return the expansion unmolested */ |
| 6810 | if (str[0] == '\0') | 6777 | if (str[0] == '\0') |
| @@ -6937,13 +6904,16 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
| 6937 | const char *p; | 6904 | const char *p; |
| 6938 | int num; | 6905 | int num; |
| 6939 | int i; | 6906 | int i; |
| 6940 | int sepq = 0; | ||
| 6941 | ssize_t len = 0; | 6907 | ssize_t len = 0; |
| 6908 | int sep; | ||
| 6909 | int quoted = flags & EXP_QUOTED; | ||
| 6942 | int subtype = varflags & VSTYPE; | 6910 | int subtype = varflags & VSTYPE; |
| 6943 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); | 6911 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
| 6944 | int quoted = varflags & VSQUOTE; | 6912 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
| 6945 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6913 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; |
| 6946 | 6914 | ||
| 6915 | sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; | ||
| 6916 | |||
| 6947 | switch (*name) { | 6917 | switch (*name) { |
| 6948 | case '$': | 6918 | case '$': |
| 6949 | num = rootpid; | 6919 | num = rootpid; |
| @@ -6978,7 +6948,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
| 6978 | break; | 6948 | break; |
| 6979 | case '@': { | 6949 | case '@': { |
| 6980 | char **ap; | 6950 | char **ap; |
| 6981 | int sep; | 6951 | char sepc; |
| 6982 | 6952 | ||
| 6983 | if (quoted && (flags & EXP_FULL)) { | 6953 | if (quoted && (flags & EXP_FULL)) { |
| 6984 | /* note: this is not meant as PEOF value */ | 6954 | /* note: this is not meant as PEOF value */ |
| @@ -6988,39 +6958,20 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
| 6988 | /* fall through */ | 6958 | /* fall through */ |
| 6989 | case '*': | 6959 | case '*': |
| 6990 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; | 6960 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; |
| 6991 | i = SIT(sep, syntax); | ||
| 6992 | if (quotes && (i == CCTL || i == CBACK)) | ||
| 6993 | sepq = 1; | ||
| 6994 | param: | 6961 | param: |
| 6995 | ap = shellparam.p; | 6962 | ap = shellparam.p; |
| 6963 | sepc = sep; | ||
| 6996 | if (!ap) | 6964 | if (!ap) |
| 6997 | return -1; | 6965 | return -1; |
| 6998 | while ((p = *ap++) != NULL) { | 6966 | while ((p = *ap++) != NULL) { |
| 6999 | size_t partlen; | 6967 | len += strtodest(p, syntax, quotes); |
| 7000 | |||
| 7001 | partlen = strlen(p); | ||
| 7002 | len += partlen; | ||
| 7003 | |||
| 7004 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 7005 | memtodest(p, partlen, syntax, quotes); | ||
| 7006 | 6968 | ||
| 7007 | if (*ap && sep) { | 6969 | if (*ap && sep) { |
| 7008 | char *q; | ||
| 7009 | |||
| 7010 | len++; | 6970 | len++; |
| 7011 | if (subtype == VSPLUS || subtype == VSLENGTH) { | 6971 | memtodest(&sepc, 1, syntax, quotes); |
| 7012 | continue; | ||
| 7013 | } | ||
| 7014 | q = expdest; | ||
| 7015 | if (sepq) | ||
| 7016 | STPUTC(CTLESC, q); | ||
| 7017 | /* note: may put NUL despite sep != 0 | ||
| 7018 | * (see sep = 1 << CHAR_BIT above) */ | ||
| 7019 | STPUTC(sep, q); | ||
| 7020 | expdest = q; | ||
| 7021 | } | 6972 | } |
| 7022 | } | 6973 | } |
| 7023 | return len; | 6974 | break; |
| 7024 | } /* case '@' and '*' */ | 6975 | } /* case '@' and '*' */ |
| 7025 | case '0': | 6976 | case '0': |
| 7026 | case '1': | 6977 | case '1': |
| @@ -7069,9 +7020,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
| 7069 | if (!p) | 7020 | if (!p) |
| 7070 | return -1; | 7021 | return -1; |
| 7071 | 7022 | ||
| 7072 | len = strlen(p); | 7023 | len = strtodest(p, syntax, quotes); |
| 7073 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 7074 | memtodest(p, len, syntax, quotes); | ||
| 7075 | #if ENABLE_UNICODE_SUPPORT | 7024 | #if ENABLE_UNICODE_SUPPORT |
| 7076 | if (subtype == VSLENGTH && len > 0) { | 7025 | if (subtype == VSLENGTH && len > 0) { |
| 7077 | reinit_unicode_for_ash(); | 7026 | reinit_unicode_for_ash(); |
| @@ -7080,10 +7029,10 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
| 7080 | } | 7029 | } |
| 7081 | } | 7030 | } |
| 7082 | #endif | 7031 | #endif |
| 7083 | return len; | 7032 | break; |
| 7084 | } | 7033 | } |
| 7085 | 7034 | ||
| 7086 | if (subtype == VSPLUS || subtype == VSLENGTH) | 7035 | if (discard) |
| 7087 | STADJUST(-len, expdest); | 7036 | STADJUST(-len, expdest); |
| 7088 | return len; | 7037 | return len; |
| 7089 | } | 7038 | } |
| @@ -7097,7 +7046,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7097 | { | 7046 | { |
| 7098 | char varflags; | 7047 | char varflags; |
| 7099 | char subtype; | 7048 | char subtype; |
| 7100 | char quoted; | 7049 | int quoted; |
| 7101 | char easy; | 7050 | char easy; |
| 7102 | char *var; | 7051 | char *var; |
| 7103 | int patloc; | 7052 | int patloc; |
| @@ -7106,7 +7055,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7106 | 7055 | ||
| 7107 | varflags = (unsigned char) *p++; | 7056 | varflags = (unsigned char) *p++; |
| 7108 | subtype = varflags & VSTYPE; | 7057 | subtype = varflags & VSTYPE; |
| 7109 | quoted = varflags & VSQUOTE; | 7058 | quoted = flags & EXP_QUOTED; |
| 7110 | var = p; | 7059 | var = p; |
| 7111 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | 7060 | easy = (!quoted || (*var == '@' && shellparam.nparam)); |
| 7112 | startloc = expdest - (char *)stackblock(); | 7061 | startloc = expdest - (char *)stackblock(); |
| @@ -7127,7 +7076,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7127 | if (varlen < 0) { | 7076 | if (varlen < 0) { |
| 7128 | argstr( | 7077 | argstr( |
| 7129 | p, | 7078 | p, |
| 7130 | flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD), | 7079 | flags | EXP_TILDE | EXP_WORD, |
| 7131 | var_str_list | 7080 | var_str_list |
| 7132 | ); | 7081 | ); |
| 7133 | goto end; | 7082 | goto end; |
| @@ -7141,7 +7090,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7141 | if (varlen < 0) { | 7090 | if (varlen < 0) { |
| 7142 | if (subevalvar(p, var, /* strloc: */ 0, | 7091 | if (subevalvar(p, var, /* strloc: */ 0, |
| 7143 | subtype, startloc, varflags, | 7092 | subtype, startloc, varflags, |
| 7144 | /* quotes: */ 0, | 7093 | /* quotes: */ flags & ~QUOTES_ESC, |
| 7145 | var_str_list) | 7094 | var_str_list) |
| 7146 | ) { | 7095 | ) { |
| 7147 | varflags &= ~VSNUL; | 7096 | varflags &= ~VSNUL; |
| @@ -7198,10 +7147,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7198 | STPUTC('\0', expdest); | 7147 | STPUTC('\0', expdest); |
| 7199 | patloc = expdest - (char *)stackblock(); | 7148 | patloc = expdest - (char *)stackblock(); |
| 7200 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, | 7149 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, |
| 7201 | startloc, varflags, | 7150 | startloc, varflags, flags, var_str_list)) { |
| 7202 | /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR), | ||
| 7203 | var_str_list) | ||
| 7204 | ) { | ||
| 7205 | int amount = expdest - ( | 7151 | int amount = expdest - ( |
| 7206 | (char *)stackblock() + patloc - 1 | 7152 | (char *)stackblock() + patloc - 1 |
| 7207 | ); | 7153 | ); |
| @@ -7220,7 +7166,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
| 7220 | unsigned char c = *p++; | 7166 | unsigned char c = *p++; |
| 7221 | if (c == CTLESC) | 7167 | if (c == CTLESC) |
| 7222 | p++; | 7168 | p++; |
| 7223 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { | 7169 | else if (c == CTLBACKQ) { |
| 7224 | if (varlen >= 0) | 7170 | if (varlen >= 0) |
| 7225 | argbackq = argbackq->next; | 7171 | argbackq = argbackq->next; |
| 7226 | } else if (c == CTLVAR) { | 7172 | } else if (c == CTLVAR) { |
| @@ -7556,7 +7502,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
| 7556 | savelastp = exparg.lastp; | 7502 | savelastp = exparg.lastp; |
| 7557 | 7503 | ||
| 7558 | INT_OFF; | 7504 | INT_OFF; |
| 7559 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 7505 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
| 7560 | { | 7506 | { |
| 7561 | int i = strlen(str->text); | 7507 | int i = strlen(str->text); |
| 7562 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | 7508 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ |
| @@ -7646,7 +7592,7 @@ static void | |||
| 7646 | expandhere(union node *arg, int fd) | 7592 | expandhere(union node *arg, int fd) |
| 7647 | { | 7593 | { |
| 7648 | herefd = fd; | 7594 | herefd = fd; |
| 7649 | expandarg(arg, (struct arglist *)NULL, 0); | 7595 | expandarg(arg, (struct arglist *)NULL, EXP_QUOTED); |
| 7650 | full_write(fd, stackblock(), expdest - (char *)stackblock()); | 7596 | full_write(fd, stackblock(), expdest - (char *)stackblock()); |
| 7651 | } | 7597 | } |
| 7652 | 7598 | ||
| @@ -7656,7 +7602,7 @@ expandhere(union node *arg, int fd) | |||
| 7656 | static int | 7602 | static int |
| 7657 | patmatch(char *pattern, const char *string) | 7603 | patmatch(char *pattern, const char *string) |
| 7658 | { | 7604 | { |
| 7659 | return pmatch(preglob(pattern, 0, 0), string); | 7605 | return pmatch(preglob(pattern, 0), string); |
| 7660 | } | 7606 | } |
| 7661 | 7607 | ||
| 7662 | /* | 7608 | /* |
| @@ -8956,7 +8902,7 @@ evalfor(union node *n, int flags) | |||
| 8956 | arglist.list = NULL; | 8902 | arglist.list = NULL; |
| 8957 | arglist.lastp = &arglist.list; | 8903 | arglist.lastp = &arglist.list; |
| 8958 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { | 8904 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { |
| 8959 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); | 8905 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); |
| 8960 | /* XXX */ | 8906 | /* XXX */ |
| 8961 | if (evalskip) | 8907 | if (evalskip) |
| 8962 | goto out; | 8908 | goto out; |
| @@ -8967,7 +8913,7 @@ evalfor(union node *n, int flags) | |||
| 8967 | loopnest++; | 8913 | loopnest++; |
| 8968 | flags &= EV_TESTED; | 8914 | flags &= EV_TESTED; |
| 8969 | for (sp = arglist.list; sp; sp = sp->next) { | 8915 | for (sp = arglist.list; sp; sp = sp->next) { |
| 8970 | setvar2(n->nfor.var, sp->text); | 8916 | setvar0(n->nfor.var, sp->text); |
| 8971 | evaltree(n->nfor.body, flags); | 8917 | evaltree(n->nfor.body, flags); |
| 8972 | if (evalskip) { | 8918 | if (evalskip) { |
| 8973 | if (evalskip == SKIPCONT && --skipcount <= 0) { | 8919 | if (evalskip == SKIPCONT && --skipcount <= 0) { |
| @@ -9362,7 +9308,8 @@ parse_command_args(char **argv, const char **path) | |||
| 9362 | * Make a variable a local variable. When a variable is made local, it's | 9308 | * Make a variable a local variable. When a variable is made local, it's |
| 9363 | * value and flags are saved in a localvar structure. The saved values | 9309 | * value and flags are saved in a localvar structure. The saved values |
| 9364 | * will be restored when the shell function returns. We handle the name | 9310 | * will be restored when the shell function returns. We handle the name |
| 9365 | * "-" as a special case. | 9311 | * "-" as a special case: it makes changes to "set +-options" local |
| 9312 | * (options will be restored on return from the function). | ||
| 9366 | */ | 9313 | */ |
| 9367 | static void | 9314 | static void |
| 9368 | mklocal(char *name) | 9315 | mklocal(char *name) |
| @@ -9370,21 +9317,37 @@ mklocal(char *name) | |||
| 9370 | struct localvar *lvp; | 9317 | struct localvar *lvp; |
| 9371 | struct var **vpp; | 9318 | struct var **vpp; |
| 9372 | struct var *vp; | 9319 | struct var *vp; |
| 9320 | char *eq = strchr(name, '='); | ||
| 9373 | 9321 | ||
| 9374 | INT_OFF; | 9322 | INT_OFF; |
| 9375 | lvp = ckzalloc(sizeof(struct localvar)); | 9323 | /* Cater for duplicate "local". Examples: |
| 9324 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x | ||
| 9325 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x | ||
| 9326 | */ | ||
| 9327 | lvp = localvars; | ||
| 9328 | while (lvp) { | ||
| 9329 | if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { | ||
| 9330 | if (eq) | ||
| 9331 | setvareq(name, 0); | ||
| 9332 | /* else: | ||
| 9333 | * it's a duplicate "local VAR" declaration, do nothing | ||
| 9334 | */ | ||
| 9335 | return; | ||
| 9336 | } | ||
| 9337 | lvp = lvp->next; | ||
| 9338 | } | ||
| 9339 | |||
| 9340 | lvp = ckzalloc(sizeof(*lvp)); | ||
| 9376 | if (LONE_DASH(name)) { | 9341 | if (LONE_DASH(name)) { |
| 9377 | char *p; | 9342 | char *p; |
| 9378 | p = ckmalloc(sizeof(optlist)); | 9343 | p = ckmalloc(sizeof(optlist)); |
| 9379 | lvp->text = memcpy(p, optlist, sizeof(optlist)); | 9344 | lvp->text = memcpy(p, optlist, sizeof(optlist)); |
| 9380 | vp = NULL; | 9345 | vp = NULL; |
| 9381 | } else { | 9346 | } else { |
| 9382 | char *eq; | ||
| 9383 | |||
| 9384 | vpp = hashvar(name); | 9347 | vpp = hashvar(name); |
| 9385 | vp = *findvar(vpp, name); | 9348 | vp = *findvar(vpp, name); |
| 9386 | eq = strchr(name, '='); | ||
| 9387 | if (vp == NULL) { | 9349 | if (vp == NULL) { |
| 9350 | /* variable did not exist yet */ | ||
| 9388 | if (eq) | 9351 | if (eq) |
| 9389 | setvareq(name, VSTRFIXED); | 9352 | setvareq(name, VSTRFIXED); |
| 9390 | else | 9353 | else |
| @@ -9394,12 +9357,15 @@ mklocal(char *name) | |||
| 9394 | } else { | 9357 | } else { |
| 9395 | lvp->text = vp->var_text; | 9358 | lvp->text = vp->var_text; |
| 9396 | lvp->flags = vp->flags; | 9359 | lvp->flags = vp->flags; |
| 9360 | /* make sure neither "struct var" nor string gets freed | ||
| 9361 | * during (un)setting: | ||
| 9362 | */ | ||
| 9397 | vp->flags |= VSTRFIXED|VTEXTFIXED; | 9363 | vp->flags |= VSTRFIXED|VTEXTFIXED; |
| 9398 | if (eq) | 9364 | if (eq) |
| 9399 | setvareq(name, 0); | 9365 | setvareq(name, 0); |
| 9400 | else | 9366 | else |
| 9401 | /* "local VAR" unsets VAR: */ | 9367 | /* "local VAR" unsets VAR: */ |
| 9402 | setvar(name, NULL, 0); | 9368 | setvar0(name, NULL); |
| 9403 | } | 9369 | } |
| 9404 | } | 9370 | } |
| 9405 | lvp->vp = vp; | 9371 | lvp->vp = vp; |
| @@ -9914,7 +9880,7 @@ evalcommand(union node *cmd, int flags) | |||
| 9914 | * '_' in 'vi' command mode during line editing... | 9880 | * '_' in 'vi' command mode during line editing... |
| 9915 | * However I implemented that within libedit itself. | 9881 | * However I implemented that within libedit itself. |
| 9916 | */ | 9882 | */ |
| 9917 | setvar2("_", lastarg); | 9883 | setvar0("_", lastarg); |
| 9918 | } | 9884 | } |
| 9919 | popstackmark(&smark); | 9885 | popstackmark(&smark); |
| 9920 | } | 9886 | } |
| @@ -10100,7 +10066,7 @@ preadfd(void) | |||
| 10100 | #if ENABLE_FEATURE_EDITING | 10066 | #if ENABLE_FEATURE_EDITING |
| 10101 | retry: | 10067 | retry: |
| 10102 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10068 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
| 10103 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); | 10069 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
| 10104 | else { | 10070 | else { |
| 10105 | int timeout = -1; | 10071 | int timeout = -1; |
| 10106 | # if ENABLE_ASH_IDLE_TIMEOUT | 10072 | # if ENABLE_ASH_IDLE_TIMEOUT |
| @@ -10142,7 +10108,7 @@ preadfd(void) | |||
| 10142 | } | 10108 | } |
| 10143 | } | 10109 | } |
| 10144 | #else | 10110 | #else |
| 10145 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); | 10111 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
| 10146 | #endif | 10112 | #endif |
| 10147 | 10113 | ||
| 10148 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ | 10114 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ |
| @@ -11675,11 +11641,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
| 11675 | && c != '$' | 11641 | && c != '$' |
| 11676 | && (c != '"' || eofmark != NULL) | 11642 | && (c != '"' || eofmark != NULL) |
| 11677 | ) { | 11643 | ) { |
| 11678 | USTPUTC(CTLESC, out); | ||
| 11679 | USTPUTC('\\', out); | 11644 | USTPUTC('\\', out); |
| 11680 | } | 11645 | } |
| 11681 | if (SIT(c, SQSYNTAX) == CCTL) | 11646 | USTPUTC(CTLESC, out); |
| 11682 | USTPUTC(CTLESC, out); | ||
| 11683 | USTPUTC(c, out); | 11647 | USTPUTC(c, out); |
| 11684 | quotef = 1; | 11648 | quotef = 1; |
| 11685 | } | 11649 | } |
| @@ -11697,9 +11661,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
| 11697 | goto quotemark; | 11661 | goto quotemark; |
| 11698 | case CENDQUOTE: | 11662 | case CENDQUOTE: |
| 11699 | IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) | 11663 | IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) |
| 11700 | if (eofmark != NULL && arinest == 0 | 11664 | if (eofmark != NULL && varnest == 0) { |
| 11701 | && varnest == 0 | ||
| 11702 | ) { | ||
| 11703 | USTPUTC(c, out); | 11665 | USTPUTC(c, out); |
| 11704 | } else { | 11666 | } else { |
| 11705 | if (dqvarnest == 0) { | 11667 | if (dqvarnest == 0) { |
| @@ -11733,10 +11695,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
| 11733 | parenlevel--; | 11695 | parenlevel--; |
| 11734 | } else { | 11696 | } else { |
| 11735 | if (pgetc() == ')') { | 11697 | if (pgetc() == ')') { |
| 11698 | c = CTLENDARI; | ||
| 11736 | if (--arinest == 0) { | 11699 | if (--arinest == 0) { |
| 11737 | syntax = prevsyntax; | 11700 | syntax = prevsyntax; |
| 11738 | dblquote = (syntax == DQSYNTAX); | ||
| 11739 | c = CTLENDARI; | ||
| 11740 | } | 11701 | } |
| 11741 | } else { | 11702 | } else { |
| 11742 | /* | 11703 | /* |
| @@ -12055,12 +12016,10 @@ parsesub: { | |||
| 12055 | do_pungetc: | 12016 | do_pungetc: |
| 12056 | pungetc(); | 12017 | pungetc(); |
| 12057 | } | 12018 | } |
| 12058 | if (dblquote || arinest) | ||
| 12059 | flags |= VSQUOTE; | ||
| 12060 | ((unsigned char *)stackblock())[typeloc] = subtype | flags; | 12019 | ((unsigned char *)stackblock())[typeloc] = subtype | flags; |
| 12061 | if (subtype != VSNORMAL) { | 12020 | if (subtype != VSNORMAL) { |
| 12062 | varnest++; | 12021 | varnest++; |
| 12063 | if (dblquote || arinest) { | 12022 | if (dblquote) { |
| 12064 | dqvarnest++; | 12023 | dqvarnest++; |
| 12065 | } | 12024 | } |
| 12066 | } | 12025 | } |
| @@ -12210,10 +12169,7 @@ parsebackq: { | |||
| 12210 | } | 12169 | } |
| 12211 | parsebackquote = savepbq; | 12170 | parsebackquote = savepbq; |
| 12212 | exception_handler = savehandler; | 12171 | exception_handler = savehandler; |
| 12213 | if (arinest || dblquote) | 12172 | USTPUTC(CTLBACKQ, out); |
| 12214 | USTPUTC(CTLBACKQ | CTLQUOTE, out); | ||
| 12215 | else | ||
| 12216 | USTPUTC(CTLBACKQ, out); | ||
| 12217 | if (oldstyle) | 12173 | if (oldstyle) |
| 12218 | goto parsebackq_oldreturn; | 12174 | goto parsebackq_oldreturn; |
| 12219 | goto parsebackq_newreturn; | 12175 | goto parsebackq_newreturn; |
| @@ -12227,18 +12183,8 @@ parsearith: { | |||
| 12227 | if (++arinest == 1) { | 12183 | if (++arinest == 1) { |
| 12228 | prevsyntax = syntax; | 12184 | prevsyntax = syntax; |
| 12229 | syntax = ARISYNTAX; | 12185 | syntax = ARISYNTAX; |
| 12230 | USTPUTC(CTLARI, out); | ||
| 12231 | if (dblquote) | ||
| 12232 | USTPUTC('"', out); | ||
| 12233 | else | ||
| 12234 | USTPUTC(' ', out); | ||
| 12235 | } else { | ||
| 12236 | /* | ||
| 12237 | * we collapse embedded arithmetic expansion to | ||
| 12238 | * parenthesis, which should be equivalent | ||
| 12239 | */ | ||
| 12240 | USTPUTC('(', out); | ||
| 12241 | } | 12186 | } |
| 12187 | USTPUTC(CTLARI, out); | ||
| 12242 | goto parsearith_return; | 12188 | goto parsearith_return; |
| 12243 | } | 12189 | } |
| 12244 | #endif | 12190 | #endif |
| @@ -12553,7 +12499,7 @@ expandstr(const char *ps) | |||
| 12553 | n.narg.text = wordtext; | 12499 | n.narg.text = wordtext; |
| 12554 | n.narg.backquote = backquotelist; | 12500 | n.narg.backquote = backquotelist; |
| 12555 | 12501 | ||
| 12556 | expandarg(&n, NULL, 0); | 12502 | expandarg(&n, NULL, EXP_QUOTED); |
| 12557 | return stackblock(); | 12503 | return stackblock(); |
| 12558 | } | 12504 | } |
| 12559 | #endif | 12505 | #endif |
| @@ -13348,7 +13294,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 13348 | * to jump out of it. | 13294 | * to jump out of it. |
| 13349 | */ | 13295 | */ |
| 13350 | INT_OFF; | 13296 | INT_OFF; |
| 13351 | r = shell_builtin_read(setvar2, | 13297 | r = shell_builtin_read(setvar0, |
| 13352 | argptr, | 13298 | argptr, |
| 13353 | bltinlookup("IFS"), /* can be NULL */ | 13299 | bltinlookup("IFS"), /* can be NULL */ |
| 13354 | read_flags, | 13300 | read_flags, |
| @@ -13560,14 +13506,14 @@ init(void) | |||
| 13560 | } | 13506 | } |
| 13561 | } | 13507 | } |
| 13562 | 13508 | ||
| 13563 | setvar2("PPID", utoa(getppid())); | 13509 | setvar0("PPID", utoa(getppid())); |
| 13564 | #if ENABLE_ASH_BASH_COMPAT | 13510 | #if ENABLE_ASH_BASH_COMPAT |
| 13565 | p = lookupvar("SHLVL"); | 13511 | p = lookupvar("SHLVL"); |
| 13566 | setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); | 13512 | setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); |
| 13567 | if (!lookupvar("HOSTNAME")) { | 13513 | if (!lookupvar("HOSTNAME")) { |
| 13568 | struct utsname uts; | 13514 | struct utsname uts; |
| 13569 | uname(&uts); | 13515 | uname(&uts); |
| 13570 | setvar2("HOSTNAME", uts.nodename); | 13516 | setvar0("HOSTNAME", uts.nodename); |
| 13571 | } | 13517 | } |
| 13572 | #endif | 13518 | #endif |
| 13573 | p = lookupvar("PWD"); | 13519 | p = lookupvar("PWD"); |
| @@ -13848,7 +13794,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 13848 | hp = lookupvar("HOME"); | 13794 | hp = lookupvar("HOME"); |
| 13849 | if (hp) { | 13795 | if (hp) { |
| 13850 | hp = concat_path_file(hp, ".ash_history"); | 13796 | hp = concat_path_file(hp, ".ash_history"); |
| 13851 | setvar2("HISTFILE", hp); | 13797 | setvar0("HISTFILE", hp); |
| 13852 | free((char*)hp); | 13798 | free((char*)hp); |
| 13853 | hp = lookupvar("HISTFILE"); | 13799 | hp = lookupvar("HISTFILE"); |
| 13854 | } | 13800 | } |
