diff options
author | Ron Yorston <rmy@pobox.com> | 2018-02-13 09:44:44 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-02-13 09:44:44 +0000 |
commit | dc19a361bd6c6df30338371532691bbc7f7126bb (patch) | |
tree | 1fb2cd646d54b5f8e425c4f11f3e09fc21d1966b /shell | |
parent | 096aee2bb468d1ab044de36e176ed1f6c7e3674d (diff) | |
parent | 3459024bf404af814cacfe90a0deb719e282ae62 (diff) | |
download | busybox-w32-dc19a361bd6c6df30338371532691bbc7f7126bb.tar.gz busybox-w32-dc19a361bd6c6df30338371532691bbc7f7126bb.tar.bz2 busybox-w32-dc19a361bd6c6df30338371532691bbc7f7126bb.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
42 files changed, 696 insertions, 684 deletions
diff --git a/shell/ash.c b/shell/ash.c index 81845dc60..36ddda1bc 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -66,6 +66,22 @@ | |||
66 | //config: default y | 66 | //config: default y |
67 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH | 67 | //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH |
68 | //config: | 68 | //config: |
69 | //config:config ASH_BASH_SOURCE_CURDIR | ||
70 | //config: bool "'source' and '.' builtins search current directory after $PATH" | ||
71 | //config: default n # do not encourage non-standard behavior | ||
72 | //config: depends on ASH_BASH_COMPAT | ||
73 | //config: help | ||
74 | //config: This is not compliant with standards. Avoid if possible. | ||
75 | //config: | ||
76 | //config:config ASH_BASH_NOT_FOUND_HOOK | ||
77 | //config: bool "command_not_found_handle hook support" | ||
78 | //config: default y | ||
79 | //config: depends on ASH_BASH_COMPAT | ||
80 | //config: help | ||
81 | //config: Enable support for the 'command_not_found_handle' hook function, | ||
82 | //config: from GNU bash, which allows for alternative command not found | ||
83 | //config: handling. | ||
84 | //config: | ||
69 | //config:config ASH_JOB_CONTROL | 85 | //config:config ASH_JOB_CONTROL |
70 | //config: bool "Job control" | 86 | //config: bool "Job control" |
71 | //config: default y | 87 | //config: default y |
@@ -278,6 +294,19 @@ typedef long arith_t; | |||
278 | # error "Do not even bother, ash will not run on NOMMU machine" | 294 | # error "Do not even bother, ash will not run on NOMMU machine" |
279 | #endif | 295 | #endif |
280 | 296 | ||
297 | /* We use a trick to have more optimized code (fewer pointer reloads): | ||
298 | * ash.c: extern struct globals *const ash_ptr_to_globals; | ||
299 | * ash_ptr_hack.c: struct globals *ash_ptr_to_globals; | ||
300 | * This way, compiler in ash.c knows the pointer can not change. | ||
301 | * | ||
302 | * However, this may break on weird arches or toolchains. In this case, | ||
303 | * set "-DBB_GLOBAL_CONST=''" in CONFIG_EXTRA_CFLAGS to disable | ||
304 | * this optimization. | ||
305 | */ | ||
306 | #ifndef BB_GLOBAL_CONST | ||
307 | # define BB_GLOBAL_CONST const | ||
308 | #endif | ||
309 | |||
281 | #if ENABLE_PLATFORM_MINGW32 | 310 | #if ENABLE_PLATFORM_MINGW32 |
282 | union node; | 311 | union node; |
283 | struct strlist; | 312 | struct strlist; |
@@ -393,6 +422,8 @@ struct globals_misc { | |||
393 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | 422 | /* shell level: 0 for the main shell, 1 for its children, and so on */ |
394 | int shlvl; | 423 | int shlvl; |
395 | #define rootshell (!shlvl) | 424 | #define rootshell (!shlvl) |
425 | int errlinno; | ||
426 | |||
396 | char *minusc; /* argument to -c option */ | 427 | char *minusc; /* argument to -c option */ |
397 | 428 | ||
398 | char *curdir; // = nullstr; /* current working directory */ | 429 | char *curdir; // = nullstr; /* current working directory */ |
@@ -469,13 +500,14 @@ struct globals_misc { | |||
469 | #endif | 500 | #endif |
470 | pid_t backgndpid; /* pid of last background process */ | 501 | pid_t backgndpid; /* pid of last background process */ |
471 | }; | 502 | }; |
472 | extern struct globals_misc *const ash_ptr_to_globals_misc; | 503 | extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; |
473 | #define G_misc (*ash_ptr_to_globals_misc) | 504 | #define G_misc (*ash_ptr_to_globals_misc) |
474 | #define exitstatus (G_misc.exitstatus ) | 505 | #define exitstatus (G_misc.exitstatus ) |
475 | #define back_exitstatus (G_misc.back_exitstatus ) | 506 | #define back_exitstatus (G_misc.back_exitstatus ) |
476 | #define job_warning (G_misc.job_warning) | 507 | #define job_warning (G_misc.job_warning) |
477 | #define rootpid (G_misc.rootpid ) | 508 | #define rootpid (G_misc.rootpid ) |
478 | #define shlvl (G_misc.shlvl ) | 509 | #define shlvl (G_misc.shlvl ) |
510 | #define errlinno (G_misc.errlinno ) | ||
479 | #define minusc (G_misc.minusc ) | 511 | #define minusc (G_misc.minusc ) |
480 | #define curdir (G_misc.curdir ) | 512 | #define curdir (G_misc.curdir ) |
481 | #define physdir (G_misc.physdir ) | 513 | #define physdir (G_misc.physdir ) |
@@ -810,6 +842,7 @@ union node; | |||
810 | 842 | ||
811 | struct ncmd { | 843 | struct ncmd { |
812 | smallint type; /* Nxxxx */ | 844 | smallint type; /* Nxxxx */ |
845 | int linno; | ||
813 | union node *assign; | 846 | union node *assign; |
814 | union node *args; | 847 | union node *args; |
815 | union node *redirect; | 848 | union node *redirect; |
@@ -823,6 +856,7 @@ struct npipe { | |||
823 | 856 | ||
824 | struct nredir { | 857 | struct nredir { |
825 | smallint type; | 858 | smallint type; |
859 | int linno; | ||
826 | union node *n; | 860 | union node *n; |
827 | union node *redirect; | 861 | union node *redirect; |
828 | }; | 862 | }; |
@@ -842,6 +876,7 @@ struct nif { | |||
842 | 876 | ||
843 | struct nfor { | 877 | struct nfor { |
844 | smallint type; | 878 | smallint type; |
879 | int linno; | ||
845 | union node *args; | 880 | union node *args; |
846 | union node *body; | 881 | union node *body; |
847 | char *var; | 882 | char *var; |
@@ -849,6 +884,7 @@ struct nfor { | |||
849 | 884 | ||
850 | struct ncase { | 885 | struct ncase { |
851 | smallint type; | 886 | smallint type; |
887 | int linno; | ||
852 | union node *expr; | 888 | union node *expr; |
853 | union node *cases; | 889 | union node *cases; |
854 | }; | 890 | }; |
@@ -860,6 +896,13 @@ struct nclist { | |||
860 | union node *body; | 896 | union node *body; |
861 | }; | 897 | }; |
862 | 898 | ||
899 | struct ndefun { | ||
900 | smallint type; | ||
901 | int linno; | ||
902 | char *text; | ||
903 | union node *body; | ||
904 | }; | ||
905 | |||
863 | struct narg { | 906 | struct narg { |
864 | smallint type; | 907 | smallint type; |
865 | union node *next; | 908 | union node *next; |
@@ -911,6 +954,7 @@ union node { | |||
911 | struct nfor nfor; | 954 | struct nfor nfor; |
912 | struct ncase ncase; | 955 | struct ncase ncase; |
913 | struct nclist nclist; | 956 | struct nclist nclist; |
957 | struct ndefun ndefun; | ||
914 | struct narg narg; | 958 | struct narg narg; |
915 | struct nfile nfile; | 959 | struct nfile nfile; |
916 | struct ndup ndup; | 960 | struct ndup ndup; |
@@ -1340,7 +1384,6 @@ struct parsefile { | |||
1340 | 1384 | ||
1341 | static struct parsefile basepf; /* top level input file */ | 1385 | static struct parsefile basepf; /* top level input file */ |
1342 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | 1386 | static struct parsefile *g_parsefile = &basepf; /* current input file */ |
1343 | static int startlinno; /* line # where last token started */ | ||
1344 | static char *commandname; /* currently executing command */ | 1387 | static char *commandname; /* currently executing command */ |
1345 | 1388 | ||
1346 | 1389 | ||
@@ -1354,7 +1397,7 @@ ash_vmsg(const char *msg, va_list ap) | |||
1354 | if (strcmp(arg0, commandname)) | 1397 | if (strcmp(arg0, commandname)) |
1355 | fprintf(stderr, "%s: ", commandname); | 1398 | fprintf(stderr, "%s: ", commandname); |
1356 | if (!iflag || g_parsefile->pf_fd > 0) | 1399 | if (!iflag || g_parsefile->pf_fd > 0) |
1357 | fprintf(stderr, "line %d: ", startlinno); | 1400 | fprintf(stderr, "line %d: ", errlinno); |
1358 | } | 1401 | } |
1359 | vfprintf(stderr, msg, ap); | 1402 | vfprintf(stderr, msg, ap); |
1360 | newline_and_flush(stderr); | 1403 | newline_and_flush(stderr); |
@@ -1407,6 +1450,7 @@ static void raise_error_syntax(const char *) NORETURN; | |||
1407 | static void | 1450 | static void |
1408 | raise_error_syntax(const char *msg) | 1451 | raise_error_syntax(const char *msg) |
1409 | { | 1452 | { |
1453 | errlinno = g_parsefile->linno; | ||
1410 | ash_msg_and_raise_error("syntax error: %s", msg); | 1454 | ash_msg_and_raise_error("syntax error: %s", msg); |
1411 | /* NOTREACHED */ | 1455 | /* NOTREACHED */ |
1412 | } | 1456 | } |
@@ -1529,7 +1573,7 @@ struct globals_memstack { | |||
1529 | size_t g_stacknleft; // = MINSIZE; | 1573 | size_t g_stacknleft; // = MINSIZE; |
1530 | struct stack_block stackbase; | 1574 | struct stack_block stackbase; |
1531 | }; | 1575 | }; |
1532 | extern struct globals_memstack *const ash_ptr_to_globals_memstack; | 1576 | extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack; |
1533 | #define G_memstack (*ash_ptr_to_globals_memstack) | 1577 | #define G_memstack (*ash_ptr_to_globals_memstack) |
1534 | #define g_stackp (G_memstack.g_stackp ) | 1578 | #define g_stackp (G_memstack.g_stackp ) |
1535 | #define g_stacknxt (G_memstack.g_stacknxt ) | 1579 | #define g_stacknxt (G_memstack.g_stacknxt ) |
@@ -1619,7 +1663,7 @@ sstrdup(const char *p) | |||
1619 | return memcpy(stalloc(len), p, len); | 1663 | return memcpy(stalloc(len), p, len); |
1620 | } | 1664 | } |
1621 | 1665 | ||
1622 | static inline void | 1666 | static ALWAYS_INLINE void |
1623 | grabstackblock(size_t len) | 1667 | grabstackblock(size_t len) |
1624 | { | 1668 | { |
1625 | stalloc(len); | 1669 | stalloc(len); |
@@ -2094,6 +2138,7 @@ static const struct { | |||
2094 | #if ENABLE_ASH_GETOPTS | 2138 | #if ENABLE_ASH_GETOPTS |
2095 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, | 2139 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, |
2096 | #endif | 2140 | #endif |
2141 | { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL }, | ||
2097 | #if ENABLE_ASH_RANDOM_SUPPORT | 2142 | #if ENABLE_ASH_RANDOM_SUPPORT |
2098 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, | 2143 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, |
2099 | #endif | 2144 | #endif |
@@ -2114,25 +2159,18 @@ struct globals_var { | |||
2114 | int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */ | 2159 | int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */ |
2115 | struct var *vartab[VTABSIZE]; | 2160 | struct var *vartab[VTABSIZE]; |
2116 | struct var varinit[ARRAY_SIZE(varinit_data)]; | 2161 | struct var varinit[ARRAY_SIZE(varinit_data)]; |
2162 | int lineno; | ||
2163 | char linenovar[sizeof("LINENO=") + sizeof(int)*3]; | ||
2117 | }; | 2164 | }; |
2118 | extern struct globals_var *const ash_ptr_to_globals_var; | 2165 | extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; |
2119 | #define G_var (*ash_ptr_to_globals_var) | 2166 | #define G_var (*ash_ptr_to_globals_var) |
2120 | #define shellparam (G_var.shellparam ) | 2167 | #define shellparam (G_var.shellparam ) |
2121 | //#define redirlist (G_var.redirlist ) | 2168 | //#define redirlist (G_var.redirlist ) |
2122 | #define preverrout_fd (G_var.preverrout_fd) | 2169 | #define preverrout_fd (G_var.preverrout_fd) |
2123 | #define vartab (G_var.vartab ) | 2170 | #define vartab (G_var.vartab ) |
2124 | #define varinit (G_var.varinit ) | 2171 | #define varinit (G_var.varinit ) |
2125 | #define INIT_G_var() do { \ | 2172 | #define lineno (G_var.lineno ) |
2126 | unsigned i; \ | 2173 | #define linenovar (G_var.linenovar ) |
2127 | (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ | ||
2128 | barrier(); \ | ||
2129 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ | ||
2130 | varinit[i].flags = varinit_data[i].flags; \ | ||
2131 | varinit[i].var_text = varinit_data[i].var_text; \ | ||
2132 | varinit[i].var_func = varinit_data[i].var_func; \ | ||
2133 | } \ | ||
2134 | } while (0) | ||
2135 | |||
2136 | #define vifs varinit[0] | 2174 | #define vifs varinit[0] |
2137 | #if ENABLE_ASH_MAIL | 2175 | #if ENABLE_ASH_MAIL |
2138 | # define vmail (&vifs)[1] | 2176 | # define vmail (&vifs)[1] |
@@ -2146,14 +2184,28 @@ extern struct globals_var *const ash_ptr_to_globals_var; | |||
2146 | #define vps4 (&vps2)[1] | 2184 | #define vps4 (&vps2)[1] |
2147 | #if ENABLE_ASH_GETOPTS | 2185 | #if ENABLE_ASH_GETOPTS |
2148 | # define voptind (&vps4)[1] | 2186 | # define voptind (&vps4)[1] |
2187 | # define vlineno (&voptind)[1] | ||
2149 | # if ENABLE_ASH_RANDOM_SUPPORT | 2188 | # if ENABLE_ASH_RANDOM_SUPPORT |
2150 | # define vrandom (&voptind)[1] | 2189 | # define vrandom (&vlineno)[1] |
2151 | # endif | 2190 | # endif |
2152 | #else | 2191 | #else |
2192 | # define vlineno (&vps4)[1] | ||
2153 | # if ENABLE_ASH_RANDOM_SUPPORT | 2193 | # if ENABLE_ASH_RANDOM_SUPPORT |
2154 | # define vrandom (&vps4)[1] | 2194 | # define vrandom (&vlineno)[1] |
2155 | # endif | 2195 | # endif |
2156 | #endif | 2196 | #endif |
2197 | #define INIT_G_var() do { \ | ||
2198 | unsigned i; \ | ||
2199 | (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ | ||
2200 | barrier(); \ | ||
2201 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ | ||
2202 | varinit[i].flags = varinit_data[i].flags; \ | ||
2203 | varinit[i].var_text = varinit_data[i].var_text; \ | ||
2204 | varinit[i].var_func = varinit_data[i].var_func; \ | ||
2205 | } \ | ||
2206 | strcpy(linenovar, "LINENO="); \ | ||
2207 | vlineno.var_text = linenovar; \ | ||
2208 | } while (0) | ||
2157 | 2209 | ||
2158 | /* | 2210 | /* |
2159 | * The following macros access the values of the above variables. | 2211 | * The following macros access the values of the above variables. |
@@ -2289,8 +2341,12 @@ lookupvar(const char *name) | |||
2289 | if (v->flags & VDYNAMIC) | 2341 | if (v->flags & VDYNAMIC) |
2290 | v->var_func(NULL); | 2342 | v->var_func(NULL); |
2291 | #endif | 2343 | #endif |
2292 | if (!(v->flags & VUNSET)) | 2344 | if (!(v->flags & VUNSET)) { |
2345 | if (v == &vlineno && v->var_text == linenovar) { | ||
2346 | fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); | ||
2347 | } | ||
2293 | return var_end(v->var_text); | 2348 | return var_end(v->var_text); |
2349 | } | ||
2294 | } | 2350 | } |
2295 | return NULL; | 2351 | return NULL; |
2296 | } | 2352 | } |
@@ -5125,7 +5181,7 @@ cmdtxt(union node *n) | |||
5125 | p = "; done"; | 5181 | p = "; done"; |
5126 | goto dodo; | 5182 | goto dodo; |
5127 | case NDEFUN: | 5183 | case NDEFUN: |
5128 | cmdputs(n->narg.text); | 5184 | cmdputs(n->ndefun.text); |
5129 | p = "() { ... }"; | 5185 | p = "() { ... }"; |
5130 | goto dotail2; | 5186 | goto dotail2; |
5131 | case NCMD: | 5187 | case NCMD: |
@@ -6151,6 +6207,26 @@ ash_arith(const char *s) | |||
6151 | return result; | 6207 | return result; |
6152 | } | 6208 | } |
6153 | #endif | 6209 | #endif |
6210 | #if BASH_SUBSTR | ||
6211 | # if ENABLE_FEATURE_SH_MATH | ||
6212 | static int substr_atoi(const char *s) | ||
6213 | { | ||
6214 | arith_t t = ash_arith(s); | ||
6215 | if (sizeof(t) > sizeof(int)) { | ||
6216 | /* clamp very large or very large negative nums for ${v:N:M}: | ||
6217 | * else "${v:0:0x100000001}" would work as "${v:0:1}" | ||
6218 | */ | ||
6219 | if (t > INT_MAX) | ||
6220 | t = INT_MAX; | ||
6221 | if (t < INT_MIN) | ||
6222 | t = INT_MIN; | ||
6223 | } | ||
6224 | return t; | ||
6225 | } | ||
6226 | # else | ||
6227 | # define substr_atoi(s) number(s) | ||
6228 | # endif | ||
6229 | #endif | ||
6154 | 6230 | ||
6155 | /* | 6231 | /* |
6156 | * expandarg flags | 6232 | * expandarg flags |
@@ -6182,7 +6258,6 @@ ash_arith(const char *s) | |||
6182 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | 6258 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
6183 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ | 6259 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ |
6184 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | 6260 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ |
6185 | #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ | ||
6186 | 6261 | ||
6187 | /* Add CTLESC when necessary. */ | 6262 | /* Add CTLESC when necessary. */ |
6188 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) | 6263 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) |
@@ -6363,8 +6438,12 @@ esclen(const char *start, const char *p) | |||
6363 | /* | 6438 | /* |
6364 | * Remove any CTLESC characters from a string. | 6439 | * Remove any CTLESC characters from a string. |
6365 | */ | 6440 | */ |
6441 | #if !BASH_PATTERN_SUBST | ||
6442 | #define rmescapes(str, flag, slash_position) \ | ||
6443 | rmescapes(str, flag) | ||
6444 | #endif | ||
6366 | static char * | 6445 | static char * |
6367 | rmescapes(char *str, int flag) | 6446 | rmescapes(char *str, int flag, int *slash_position) |
6368 | { | 6447 | { |
6369 | static const char qchars[] ALIGN1 = { | 6448 | static const char qchars[] ALIGN1 = { |
6370 | IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; | 6449 | IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; |
@@ -6373,9 +6452,8 @@ rmescapes(char *str, int flag) | |||
6373 | unsigned inquotes; | 6452 | unsigned inquotes; |
6374 | unsigned protect_against_glob; | 6453 | unsigned protect_against_glob; |
6375 | unsigned globbing; | 6454 | unsigned globbing; |
6376 | IF_BASH_PATTERN_SUBST(unsigned slash = flag & RMESCAPE_SLASH;) | ||
6377 | 6455 | ||
6378 | p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash)); | 6456 | p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position)); |
6379 | if (!p) | 6457 | if (!p) |
6380 | return str; | 6458 | return str; |
6381 | 6459 | ||
@@ -6455,10 +6533,11 @@ rmescapes(char *str, int flag) | |||
6455 | goto copy; | 6533 | goto copy; |
6456 | } | 6534 | } |
6457 | #if BASH_PATTERN_SUBST | 6535 | #if BASH_PATTERN_SUBST |
6458 | else if (*p == '/' && slash) { | 6536 | else if (slash_position && p == str + *slash_position) { |
6459 | /* stop handling globbing and mark location of slash */ | 6537 | /* stop handling globbing */ |
6460 | globbing = slash = 0; | 6538 | globbing = 0; |
6461 | *p = CTLESC; | 6539 | *slash_position = q - r; |
6540 | slash_position = NULL; | ||
6462 | } | 6541 | } |
6463 | #endif | 6542 | #endif |
6464 | protect_against_glob = globbing; | 6543 | protect_against_glob = globbing; |
@@ -6482,7 +6561,7 @@ rmescapes(char *str, int flag) | |||
6482 | static char * | 6561 | static char * |
6483 | preglob(const char *pattern, int flag) | 6562 | preglob(const char *pattern, int flag) |
6484 | { | 6563 | { |
6485 | return rmescapes((char *)pattern, flag | RMESCAPE_GLOB); | 6564 | return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL); |
6486 | } | 6565 | } |
6487 | 6566 | ||
6488 | /* | 6567 | /* |
@@ -6837,7 +6916,7 @@ expari(int flag) | |||
6837 | expdest = p; | 6916 | expdest = p; |
6838 | 6917 | ||
6839 | if (flag & QUOTES_ESC) | 6918 | if (flag & QUOTES_ESC) |
6840 | rmescapes(p + 1, 0); | 6919 | rmescapes(p + 1, 0, NULL); |
6841 | 6920 | ||
6842 | len = cvtnum(ash_arith(p + 1)); | 6921 | len = cvtnum(ash_arith(p + 1)); |
6843 | 6922 | ||
@@ -7125,20 +7204,58 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7125 | char *rmesc, *rmescend; | 7204 | char *rmesc, *rmescend; |
7126 | char *str; | 7205 | char *str; |
7127 | int amount, resetloc; | 7206 | int amount, resetloc; |
7207 | int argstr_flags; | ||
7128 | IF_BASH_PATTERN_SUBST(int workloc;) | 7208 | IF_BASH_PATTERN_SUBST(int workloc;) |
7129 | IF_BASH_PATTERN_SUBST(char *repl = NULL;) | 7209 | IF_BASH_PATTERN_SUBST(int slash_pos;) |
7210 | IF_BASH_PATTERN_SUBST(char *repl;) | ||
7130 | int zero; | 7211 | int zero; |
7131 | char *(*scan)(char*, char*, char*, char*, int, int); | 7212 | char *(*scan)(char*, char*, char*, char*, int, int); |
7132 | 7213 | ||
7133 | //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)", | 7214 | //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)", |
7134 | // p, varname, strloc, subtype, startloc, varflags, quotes); | 7215 | // p, varname, strloc, subtype, startloc, varflags, quotes); |
7135 | 7216 | ||
7136 | argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? | 7217 | #if BASH_PATTERN_SUBST |
7137 | (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0) | 7218 | /* For "${v/pattern/repl}", we must find the delimiter _before_ |
7138 | ); | 7219 | * argstr() call expands possible variable references in pattern: |
7220 | * think about "v=a; a=a/; echo ${v/$a/r}" case. | ||
7221 | */ | ||
7222 | repl = NULL; | ||
7223 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { | ||
7224 | /* Find '/' and replace with NUL */ | ||
7225 | repl = p; | ||
7226 | for (;;) { | ||
7227 | /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ | ||
7228 | if (*repl == '\0') { | ||
7229 | repl = NULL; | ||
7230 | break; | ||
7231 | } | ||
7232 | if (*repl == '/') { | ||
7233 | *repl = '\0'; | ||
7234 | break; | ||
7235 | } | ||
7236 | if ((unsigned char)*repl == CTLESC && repl[1]) | ||
7237 | repl++; | ||
7238 | repl++; | ||
7239 | } | ||
7240 | } | ||
7241 | #endif | ||
7242 | argstr_flags = EXP_TILDE; | ||
7243 | if (subtype != VSASSIGN && subtype != VSQUESTION) | ||
7244 | argstr_flags |= (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE); | ||
7245 | argstr(p, argstr_flags); | ||
7246 | #if BASH_PATTERN_SUBST | ||
7247 | slash_pos = -1; | ||
7248 | if (repl) { | ||
7249 | slash_pos = expdest - ((char *)stackblock() + strloc); | ||
7250 | STPUTC('/', expdest); | ||
7251 | argstr(repl + 1, argstr_flags); | ||
7252 | *repl = '/'; | ||
7253 | } | ||
7254 | #endif | ||
7139 | STPUTC('\0', expdest); | 7255 | STPUTC('\0', expdest); |
7140 | argbackq = saveargbackq; | 7256 | argbackq = saveargbackq; |
7141 | startp = (char *)stackblock() + startloc; | 7257 | startp = (char *)stackblock() + startloc; |
7258 | //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc); | ||
7142 | 7259 | ||
7143 | switch (subtype) { | 7260 | switch (subtype) { |
7144 | case VSASSIGN: | 7261 | case VSASSIGN: |
@@ -7158,13 +7275,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7158 | 7275 | ||
7159 | loc = str = stackblock() + strloc; | 7276 | loc = str = stackblock() + strloc; |
7160 | 7277 | ||
7161 | # if !ENABLE_FEATURE_SH_MATH | ||
7162 | # define ash_arith number | ||
7163 | # endif | ||
7164 | /* Read POS in ${var:POS:LEN} */ | 7278 | /* Read POS in ${var:POS:LEN} */ |
7165 | colon = strchr(loc, ':'); | 7279 | colon = strchr(loc, ':'); |
7166 | if (colon) *colon = '\0'; | 7280 | if (colon) *colon = '\0'; |
7167 | pos = ash_arith(loc); | 7281 | pos = substr_atoi(loc); |
7168 | if (colon) *colon = ':'; | 7282 | if (colon) *colon = ':'; |
7169 | 7283 | ||
7170 | /* Read LEN in ${var:POS:LEN} */ | 7284 | /* Read LEN in ${var:POS:LEN} */ |
@@ -7172,7 +7286,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7172 | /* *loc != '\0', guaranteed by parser */ | 7286 | /* *loc != '\0', guaranteed by parser */ |
7173 | if (quotes) { | 7287 | if (quotes) { |
7174 | char *ptr; | 7288 | char *ptr; |
7175 | |||
7176 | /* Adjust the length by the number of escapes */ | 7289 | /* Adjust the length by the number of escapes */ |
7177 | for (ptr = startp; ptr < (str - 1); ptr++) { | 7290 | for (ptr = startp; ptr < (str - 1); ptr++) { |
7178 | if ((unsigned char)*ptr == CTLESC) { | 7291 | if ((unsigned char)*ptr == CTLESC) { |
@@ -7184,19 +7297,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7184 | orig_len = len; | 7297 | orig_len = len; |
7185 | if (*loc++ == ':') { | 7298 | if (*loc++ == ':') { |
7186 | /* ${var::LEN} */ | 7299 | /* ${var::LEN} */ |
7187 | len = ash_arith(loc); | 7300 | len = substr_atoi(loc); |
7188 | } else { | 7301 | } else { |
7189 | /* Skip POS in ${var:POS:LEN} */ | 7302 | /* Skip POS in ${var:POS:LEN} */ |
7190 | len = orig_len; | 7303 | len = orig_len; |
7191 | while (*loc && *loc != ':') { | 7304 | while (*loc && *loc != ':') |
7192 | loc++; | 7305 | loc++; |
7193 | } | 7306 | if (*loc++ == ':') |
7194 | if (*loc++ == ':') { | 7307 | len = substr_atoi(loc); |
7195 | len = ash_arith(loc); | ||
7196 | } | ||
7197 | } | 7308 | } |
7198 | # undef ash_arith | ||
7199 | |||
7200 | if (pos < 0) { | 7309 | if (pos < 0) { |
7201 | /* ${VAR:$((-n)):l} starts n chars from the end */ | 7310 | /* ${VAR:$((-n)):l} starts n chars from the end */ |
7202 | pos = orig_len + pos; | 7311 | pos = orig_len + pos; |
@@ -7236,6 +7345,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7236 | resetloc = expdest - (char *)stackblock(); | 7345 | resetloc = expdest - (char *)stackblock(); |
7237 | 7346 | ||
7238 | #if BASH_PATTERN_SUBST | 7347 | #if BASH_PATTERN_SUBST |
7348 | repl = NULL; | ||
7349 | |||
7239 | /* We'll comeback here if we grow the stack while handling | 7350 | /* We'll comeback here if we grow the stack while handling |
7240 | * a VSREPLACE or VSREPLACEALL, since our pointers into the | 7351 | * a VSREPLACE or VSREPLACEALL, since our pointers into the |
7241 | * stack will need rebasing, and we'll need to remove our work | 7352 | * stack will need rebasing, and we'll need to remove our work |
@@ -7250,8 +7361,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7250 | 7361 | ||
7251 | rmesc = startp; | 7362 | rmesc = startp; |
7252 | rmescend = (char *)stackblock() + strloc; | 7363 | rmescend = (char *)stackblock() + strloc; |
7364 | //bb_error_msg("str7:'%s'", rmescend); | ||
7253 | if (quotes) { | 7365 | if (quotes) { |
7254 | rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); | 7366 | //TODO: how to handle slash_pos here if string changes (shortens?) |
7367 | rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL); | ||
7255 | if (rmesc != startp) { | 7368 | if (rmesc != startp) { |
7256 | rmescend = expdest; | 7369 | rmescend = expdest; |
7257 | startp = (char *)stackblock() + startloc; | 7370 | startp = (char *)stackblock() + startloc; |
@@ -7264,12 +7377,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7264 | * The result is a_\_z_c (not a\_\_z_c)! | 7377 | * The result is a_\_z_c (not a\_\_z_c)! |
7265 | * | 7378 | * |
7266 | * The search pattern and replace string treat backslashes differently! | 7379 | * The search pattern and replace string treat backslashes differently! |
7267 | * RMESCAPE_SLASH causes preglob to work differently on the pattern | 7380 | * "&slash_pos" causes rmescapes() to work differently on the pattern |
7268 | * and string. It's only used on the first call. | 7381 | * and string. It's only used on the first call. |
7269 | */ | 7382 | */ |
7270 | preglob(str, IF_BASH_PATTERN_SUBST( | 7383 | //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos); |
7271 | (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? | 7384 | rmescapes(str, RMESCAPE_GLOB, |
7272 | RMESCAPE_SLASH : ) 0); | 7385 | repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos) |
7386 | ); | ||
7273 | 7387 | ||
7274 | #if BASH_PATTERN_SUBST | 7388 | #if BASH_PATTERN_SUBST |
7275 | workloc = expdest - (char *)stackblock(); | 7389 | workloc = expdest - (char *)stackblock(); |
@@ -7278,11 +7392,12 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7278 | char *idx, *end; | 7392 | char *idx, *end; |
7279 | 7393 | ||
7280 | if (!repl) { | 7394 | if (!repl) { |
7281 | repl = strchr(str, CTLESC); | 7395 | //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos); |
7282 | if (repl) | 7396 | repl = nullstr; |
7397 | if (slash_pos >= 0) { | ||
7398 | repl = str + slash_pos; | ||
7283 | *repl++ = '\0'; | 7399 | *repl++ = '\0'; |
7284 | else | 7400 | } |
7285 | repl = nullstr; | ||
7286 | } | 7401 | } |
7287 | //bb_error_msg("str:'%s' repl:'%s'", str, repl); | 7402 | //bb_error_msg("str:'%s' repl:'%s'", str, repl); |
7288 | 7403 | ||
@@ -7802,7 +7917,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7802 | INT_ON; | 7917 | INT_ON; |
7803 | nometa: | 7918 | nometa: |
7804 | *exparg.lastp = str; | 7919 | *exparg.lastp = str; |
7805 | rmescapes(str->text, 0); | 7920 | rmescapes(str->text, 0, NULL); |
7806 | exparg.lastp = &str->next; | 7921 | exparg.lastp = &str->next; |
7807 | break; | 7922 | break; |
7808 | default: /* GLOB_NOSPACE */ | 7923 | default: /* GLOB_NOSPACE */ |
@@ -8031,7 +8146,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
8031 | */ | 8146 | */ |
8032 | nometa: | 8147 | nometa: |
8033 | *exparg.lastp = str; | 8148 | *exparg.lastp = str; |
8034 | rmescapes(str->text, 0); | 8149 | rmescapes(str->text, 0, NULL); |
8035 | exparg.lastp = &str->next; | 8150 | exparg.lastp = &str->next; |
8036 | } else { | 8151 | } else { |
8037 | *exparg.lastp = NULL; | 8152 | *exparg.lastp = NULL; |
@@ -8970,6 +9085,10 @@ calcsize(union node *n) | |||
8970 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | 9085 | IF_PLATFORM_MINGW32(nodeptrsize += 3); |
8971 | break; | 9086 | break; |
8972 | case NDEFUN: | 9087 | case NDEFUN: |
9088 | calcsize(n->ndefun.body); | ||
9089 | funcstringsize += strlen(n->ndefun.text) + 1; | ||
9090 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
9091 | break; | ||
8973 | case NARG: | 9092 | case NARG: |
8974 | sizenodelist(n->narg.backquote); | 9093 | sizenodelist(n->narg.backquote); |
8975 | funcstringsize += strlen(n->narg.text) + 1; | 9094 | funcstringsize += strlen(n->narg.text) + 1; |
@@ -9067,6 +9186,7 @@ copynode(union node *n) | |||
9067 | new->ncmd.redirect = copynode(n->ncmd.redirect); | 9186 | new->ncmd.redirect = copynode(n->ncmd.redirect); |
9068 | new->ncmd.args = copynode(n->ncmd.args); | 9187 | new->ncmd.args = copynode(n->ncmd.args); |
9069 | new->ncmd.assign = copynode(n->ncmd.assign); | 9188 | new->ncmd.assign = copynode(n->ncmd.assign); |
9189 | new->ncmd.linno = n->ncmd.linno; | ||
9070 | SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign); | 9190 | SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign); |
9071 | break; | 9191 | break; |
9072 | case NPIPE: | 9192 | case NPIPE: |
@@ -9079,6 +9199,7 @@ copynode(union node *n) | |||
9079 | case NSUBSHELL: | 9199 | case NSUBSHELL: |
9080 | new->nredir.redirect = copynode(n->nredir.redirect); | 9200 | new->nredir.redirect = copynode(n->nredir.redirect); |
9081 | new->nredir.n = copynode(n->nredir.n); | 9201 | new->nredir.n = copynode(n->nredir.n); |
9202 | new->nredir.linno = n->nredir.linno; | ||
9082 | SAVE_PTR2(new->nredir.redirect,new->nredir.n); | 9203 | SAVE_PTR2(new->nredir.redirect,new->nredir.n); |
9083 | break; | 9204 | break; |
9084 | case NAND: | 9205 | case NAND: |
@@ -9100,11 +9221,13 @@ copynode(union node *n) | |||
9100 | new->nfor.var = nodeckstrdup(n->nfor.var); | 9221 | new->nfor.var = nodeckstrdup(n->nfor.var); |
9101 | new->nfor.body = copynode(n->nfor.body); | 9222 | new->nfor.body = copynode(n->nfor.body); |
9102 | new->nfor.args = copynode(n->nfor.args); | 9223 | new->nfor.args = copynode(n->nfor.args); |
9224 | new->nfor.linno = n->nfor.linno; | ||
9103 | SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args); | 9225 | SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args); |
9104 | break; | 9226 | break; |
9105 | case NCASE: | 9227 | case NCASE: |
9106 | new->ncase.cases = copynode(n->ncase.cases); | 9228 | new->ncase.cases = copynode(n->ncase.cases); |
9107 | new->ncase.expr = copynode(n->ncase.expr); | 9229 | new->ncase.expr = copynode(n->ncase.expr); |
9230 | new->ncase.linno = n->ncase.linno; | ||
9108 | SAVE_PTR2(new->ncase.cases,new->ncase.expr); | 9231 | SAVE_PTR2(new->ncase.cases,new->ncase.expr); |
9109 | break; | 9232 | break; |
9110 | case NCLIST: | 9233 | case NCLIST: |
@@ -9114,6 +9237,11 @@ copynode(union node *n) | |||
9114 | SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next); | 9237 | SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next); |
9115 | break; | 9238 | break; |
9116 | case NDEFUN: | 9239 | case NDEFUN: |
9240 | new->ndefun.body = copynode(n->ndefun.body); | ||
9241 | new->ndefun.text = nodeckstrdup(n->ndefun.text); | ||
9242 | new->ndefun.linno = n->ndefun.linno; | ||
9243 | SAVE_PTR2(new->ndefun.body,new->ndefun.text); | ||
9244 | break; | ||
9117 | case NARG: | 9245 | case NARG: |
9118 | new->narg.backquote = copynodelist(n->narg.backquote); | 9246 | new->narg.backquote = copynodelist(n->narg.backquote); |
9119 | new->narg.text = nodeckstrdup(n->narg.text); | 9247 | new->narg.text = nodeckstrdup(n->narg.text); |
@@ -9190,7 +9318,7 @@ defun(union node *func) | |||
9190 | INT_OFF; | 9318 | INT_OFF; |
9191 | entry.cmdtype = CMDFUNCTION; | 9319 | entry.cmdtype = CMDFUNCTION; |
9192 | entry.u.func = copyfunc(func); | 9320 | entry.u.func = copyfunc(func); |
9193 | addcmdentry(func->narg.text, &entry); | 9321 | addcmdentry(func->ndefun.text, &entry); |
9194 | INT_ON; | 9322 | INT_ON; |
9195 | } | 9323 | } |
9196 | 9324 | ||
@@ -9200,8 +9328,8 @@ defun(union node *func) | |||
9200 | #define SKIPFUNC (1 << 2) | 9328 | #define SKIPFUNC (1 << 2) |
9201 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | 9329 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ |
9202 | static int skipcount; /* number of levels to skip */ | 9330 | static int skipcount; /* number of levels to skip */ |
9203 | static int funcnest; /* depth of function calls */ | ||
9204 | static int loopnest; /* current loop nesting level */ | 9331 | static int loopnest; /* current loop nesting level */ |
9332 | static int funcline; /* starting line number of current function, or 0 if not in a function */ | ||
9205 | 9333 | ||
9206 | /* Forward decl way out to parsing code - dotrap needs it */ | 9334 | /* Forward decl way out to parsing code - dotrap needs it */ |
9207 | static int evalstring(char *s, int flags); | 9335 | static int evalstring(char *s, int flags); |
@@ -9296,6 +9424,9 @@ evaltree(union node *n, int flags) | |||
9296 | status = !evaltree(n->nnot.com, EV_TESTED); | 9424 | status = !evaltree(n->nnot.com, EV_TESTED); |
9297 | goto setstatus; | 9425 | goto setstatus; |
9298 | case NREDIR: | 9426 | case NREDIR: |
9427 | errlinno = lineno = n->nredir.linno; | ||
9428 | if (funcline) | ||
9429 | lineno -= funcline - 1; | ||
9299 | expredir(n->nredir.redirect); | 9430 | expredir(n->nredir.redirect); |
9300 | pushredir(n->nredir.redirect); | 9431 | pushredir(n->nredir.redirect); |
9301 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | 9432 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); |
@@ -9450,6 +9581,10 @@ evalfor(union node *n, int flags) | |||
9450 | struct stackmark smark; | 9581 | struct stackmark smark; |
9451 | int status = 0; | 9582 | int status = 0; |
9452 | 9583 | ||
9584 | errlinno = lineno = n->ncase.linno; | ||
9585 | if (funcline) | ||
9586 | lineno -= funcline - 1; | ||
9587 | |||
9453 | setstackmark(&smark); | 9588 | setstackmark(&smark); |
9454 | arglist.list = NULL; | 9589 | arglist.list = NULL; |
9455 | arglist.lastp = &arglist.list; | 9590 | arglist.lastp = &arglist.list; |
@@ -9481,6 +9616,10 @@ evalcase(union node *n, int flags) | |||
9481 | struct stackmark smark; | 9616 | struct stackmark smark; |
9482 | int status = 0; | 9617 | int status = 0; |
9483 | 9618 | ||
9619 | errlinno = lineno = n->ncase.linno; | ||
9620 | if (funcline) | ||
9621 | lineno -= funcline - 1; | ||
9622 | |||
9484 | setstackmark(&smark); | 9623 | setstackmark(&smark); |
9485 | arglist.list = NULL; | 9624 | arglist.list = NULL; |
9486 | arglist.lastp = &arglist.list; | 9625 | arglist.lastp = &arglist.list; |
@@ -9516,6 +9655,10 @@ evalsubshell(union node *n, int flags) | |||
9516 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ | 9655 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ |
9517 | int status; | 9656 | int status; |
9518 | 9657 | ||
9658 | errlinno = lineno = n->nredir.linno; | ||
9659 | if (funcline) | ||
9660 | lineno -= funcline - 1; | ||
9661 | |||
9519 | expredir(n->nredir.redirect); | 9662 | expredir(n->nredir.redirect); |
9520 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) | 9663 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) |
9521 | goto nofork; | 9664 | goto nofork; |
@@ -9846,8 +9989,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9846 | struct jmploc *volatile savehandler; | 9989 | struct jmploc *volatile savehandler; |
9847 | struct jmploc jmploc; | 9990 | struct jmploc jmploc; |
9848 | int e; | 9991 | int e; |
9992 | int savefuncline; | ||
9849 | 9993 | ||
9850 | saveparam = shellparam; | 9994 | saveparam = shellparam; |
9995 | savefuncline = funcline; | ||
9851 | savehandler = exception_handler; | 9996 | savehandler = exception_handler; |
9852 | e = setjmp(jmploc.loc); | 9997 | e = setjmp(jmploc.loc); |
9853 | if (e) { | 9998 | if (e) { |
@@ -9857,7 +10002,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9857 | exception_handler = &jmploc; | 10002 | exception_handler = &jmploc; |
9858 | shellparam.malloced = 0; | 10003 | shellparam.malloced = 0; |
9859 | func->count++; | 10004 | func->count++; |
9860 | funcnest++; | 10005 | funcline = func->n.ndefun.linno; |
9861 | INT_ON; | 10006 | INT_ON; |
9862 | shellparam.nparam = argc - 1; | 10007 | shellparam.nparam = argc - 1; |
9863 | shellparam.p = argv + 1; | 10008 | shellparam.p = argv + 1; |
@@ -9866,11 +10011,11 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9866 | shellparam.optoff = -1; | 10011 | shellparam.optoff = -1; |
9867 | #endif | 10012 | #endif |
9868 | pushlocalvars(); | 10013 | pushlocalvars(); |
9869 | evaltree(func->n.narg.next, flags & EV_TESTED); | 10014 | evaltree(func->n.ndefun.body, flags & EV_TESTED); |
9870 | poplocalvars(0); | 10015 | poplocalvars(0); |
9871 | funcdone: | 10016 | funcdone: |
9872 | INT_OFF; | 10017 | INT_OFF; |
9873 | funcnest--; | 10018 | funcline = savefuncline; |
9874 | freefunc(func); | 10019 | freefunc(func); |
9875 | freeparam(&shellparam); | 10020 | freeparam(&shellparam); |
9876 | shellparam = saveparam; | 10021 | shellparam = saveparam; |
@@ -10235,6 +10380,10 @@ evalcommand(union node *cmd, int flags) | |||
10235 | char **nargv; | 10380 | char **nargv; |
10236 | smallint cmd_is_exec; | 10381 | smallint cmd_is_exec; |
10237 | 10382 | ||
10383 | errlinno = lineno = cmd->ncmd.linno; | ||
10384 | if (funcline) | ||
10385 | lineno -= funcline - 1; | ||
10386 | |||
10238 | /* First expand the arguments. */ | 10387 | /* First expand the arguments. */ |
10239 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | 10388 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); |
10240 | setstackmark(&smark); | 10389 | setstackmark(&smark); |
@@ -10280,7 +10429,7 @@ evalcommand(union node *cmd, int flags) | |||
10280 | *nargv = NULL; | 10429 | *nargv = NULL; |
10281 | 10430 | ||
10282 | lastarg = NULL; | 10431 | lastarg = NULL; |
10283 | if (iflag && funcnest == 0 && argc > 0) | 10432 | if (iflag && funcline == 0 && argc > 0) |
10284 | lastarg = nargv[-1]; | 10433 | lastarg = nargv[-1]; |
10285 | 10434 | ||
10286 | expredir(cmd->ncmd.redirect); | 10435 | expredir(cmd->ncmd.redirect); |
@@ -10410,7 +10559,9 @@ evalcommand(union node *cmd, int flags) | |||
10410 | switch (cmdentry.cmdtype) { | 10559 | switch (cmdentry.cmdtype) { |
10411 | default: { | 10560 | default: { |
10412 | 10561 | ||
10413 | #if ENABLE_FEATURE_SH_NOFORK | 10562 | #if ENABLE_FEATURE_SH_STANDALONE \ |
10563 | && ENABLE_FEATURE_SH_NOFORK \ | ||
10564 | && NUM_APPLETS > 1 | ||
10414 | /* (1) BUG: if variables are set, we need to fork, or save/restore them | 10565 | /* (1) BUG: if variables are set, we need to fork, or save/restore them |
10415 | * around run_nofork_applet() call. | 10566 | * around run_nofork_applet() call. |
10416 | * (2) Should this check also be done in forkshell()? | 10567 | * (2) Should this check also be done in forkshell()? |
@@ -11341,7 +11492,7 @@ shiftcmd(int argc UNUSED_PARAM, char **argv) | |||
11341 | if (argv[1]) | 11492 | if (argv[1]) |
11342 | n = number(argv[1]); | 11493 | n = number(argv[1]); |
11343 | if (n > shellparam.nparam) | 11494 | if (n > shellparam.nparam) |
11344 | n = 0; /* bash compat, was = shellparam.nparam; */ | 11495 | return 1; |
11345 | INT_OFF; | 11496 | INT_OFF; |
11346 | shellparam.nparam -= n; | 11497 | shellparam.nparam -= n; |
11347 | for (ap1 = shellparam.p; --n >= 0; ap1++) { | 11498 | for (ap1 = shellparam.p; --n >= 0; ap1++) { |
@@ -11811,7 +11962,7 @@ parsefname(void) | |||
11811 | if (quoteflag == 0) | 11962 | if (quoteflag == 0) |
11812 | n->type = NXHERE; | 11963 | n->type = NXHERE; |
11813 | TRACE(("Here document %d\n", n->type)); | 11964 | TRACE(("Here document %d\n", n->type)); |
11814 | rmescapes(wordtext, 0); | 11965 | rmescapes(wordtext, 0, NULL); |
11815 | here->eofmark = wordtext; | 11966 | here->eofmark = wordtext; |
11816 | here->next = NULL; | 11967 | here->next = NULL; |
11817 | if (heredoclist == NULL) | 11968 | if (heredoclist == NULL) |
@@ -11836,6 +11987,7 @@ simplecmd(void) | |||
11836 | union node *vars, **vpp; | 11987 | union node *vars, **vpp; |
11837 | union node **rpp, *redir; | 11988 | union node **rpp, *redir; |
11838 | int savecheckkwd; | 11989 | int savecheckkwd; |
11990 | int savelinno; | ||
11839 | #if BASH_TEST2 | 11991 | #if BASH_TEST2 |
11840 | smallint double_brackets_flag = 0; | 11992 | smallint double_brackets_flag = 0; |
11841 | #endif | 11993 | #endif |
@@ -11849,6 +12001,7 @@ simplecmd(void) | |||
11849 | rpp = &redir; | 12001 | rpp = &redir; |
11850 | 12002 | ||
11851 | savecheckkwd = CHKALIAS; | 12003 | savecheckkwd = CHKALIAS; |
12004 | savelinno = g_parsefile->linno; | ||
11852 | for (;;) { | 12005 | for (;;) { |
11853 | int t; | 12006 | int t; |
11854 | checkkwd = savecheckkwd; | 12007 | checkkwd = savecheckkwd; |
@@ -11938,7 +12091,9 @@ simplecmd(void) | |||
11938 | } | 12091 | } |
11939 | n->type = NDEFUN; | 12092 | n->type = NDEFUN; |
11940 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | 12093 | checkkwd = CHKNL | CHKKWD | CHKALIAS; |
11941 | n->narg.next = parse_command(); | 12094 | n->ndefun.text = n->narg.text; |
12095 | n->ndefun.linno = g_parsefile->linno; | ||
12096 | n->ndefun.body = parse_command(); | ||
11942 | return n; | 12097 | return n; |
11943 | } | 12098 | } |
11944 | IF_BASH_FUNCTION(function_flag = 0;) | 12099 | IF_BASH_FUNCTION(function_flag = 0;) |
@@ -11954,6 +12109,7 @@ simplecmd(void) | |||
11954 | *rpp = NULL; | 12109 | *rpp = NULL; |
11955 | n = stzalloc(sizeof(struct ncmd)); | 12110 | n = stzalloc(sizeof(struct ncmd)); |
11956 | n->type = NCMD; | 12111 | n->type = NCMD; |
12112 | n->ncmd.linno = savelinno; | ||
11957 | n->ncmd.args = args; | 12113 | n->ncmd.args = args; |
11958 | n->ncmd.assign = vars; | 12114 | n->ncmd.assign = vars; |
11959 | n->ncmd.redirect = redir; | 12115 | n->ncmd.redirect = redir; |
@@ -11969,10 +12125,13 @@ parse_command(void) | |||
11969 | union node *redir, **rpp; | 12125 | union node *redir, **rpp; |
11970 | union node **rpp2; | 12126 | union node **rpp2; |
11971 | int t; | 12127 | int t; |
12128 | int savelinno; | ||
11972 | 12129 | ||
11973 | redir = NULL; | 12130 | redir = NULL; |
11974 | rpp2 = &redir; | 12131 | rpp2 = &redir; |
11975 | 12132 | ||
12133 | savelinno = g_parsefile->linno; | ||
12134 | |||
11976 | switch (readtoken()) { | 12135 | switch (readtoken()) { |
11977 | default: | 12136 | default: |
11978 | raise_error_unexpected_syntax(-1); | 12137 | raise_error_unexpected_syntax(-1); |
@@ -12023,6 +12182,7 @@ parse_command(void) | |||
12023 | raise_error_syntax("bad for loop variable"); | 12182 | raise_error_syntax("bad for loop variable"); |
12024 | n1 = stzalloc(sizeof(struct nfor)); | 12183 | n1 = stzalloc(sizeof(struct nfor)); |
12025 | n1->type = NFOR; | 12184 | n1->type = NFOR; |
12185 | n1->nfor.linno = savelinno; | ||
12026 | n1->nfor.var = wordtext; | 12186 | n1->nfor.var = wordtext; |
12027 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | 12187 | checkkwd = CHKNL | CHKKWD | CHKALIAS; |
12028 | if (readtoken() == TIN) { | 12188 | if (readtoken() == TIN) { |
@@ -12063,6 +12223,7 @@ parse_command(void) | |||
12063 | case TCASE: | 12223 | case TCASE: |
12064 | n1 = stzalloc(sizeof(struct ncase)); | 12224 | n1 = stzalloc(sizeof(struct ncase)); |
12065 | n1->type = NCASE; | 12225 | n1->type = NCASE; |
12226 | n1->ncase.linno = savelinno; | ||
12066 | if (readtoken() != TWORD) | 12227 | if (readtoken() != TWORD) |
12067 | raise_error_unexpected_syntax(TWORD); | 12228 | raise_error_unexpected_syntax(TWORD); |
12068 | n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); | 12229 | n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); |
@@ -12114,6 +12275,7 @@ parse_command(void) | |||
12114 | case TLP: | 12275 | case TLP: |
12115 | n1 = stzalloc(sizeof(struct nredir)); | 12276 | n1 = stzalloc(sizeof(struct nredir)); |
12116 | n1->type = NSUBSHELL; | 12277 | n1->type = NSUBSHELL; |
12278 | n1->nredir.linno = savelinno; | ||
12117 | n1->nredir.n = list(0); | 12279 | n1->nredir.n = list(0); |
12118 | /*n1->nredir.redirect = NULL; - stzalloc did it */ | 12280 | /*n1->nredir.redirect = NULL; - stzalloc did it */ |
12119 | t = TRP; | 12281 | t = TRP; |
@@ -12147,6 +12309,7 @@ parse_command(void) | |||
12147 | if (n1->type != NSUBSHELL) { | 12309 | if (n1->type != NSUBSHELL) { |
12148 | n2 = stzalloc(sizeof(struct nredir)); | 12310 | n2 = stzalloc(sizeof(struct nredir)); |
12149 | n2->type = NREDIR; | 12311 | n2->type = NREDIR; |
12312 | n2->nredir.linno = savelinno; | ||
12150 | n2->nredir.n = n1; | 12313 | n2->nredir.n = n1; |
12151 | n1 = n2; | 12314 | n1 = n2; |
12152 | } | 12315 | } |
@@ -12245,10 +12408,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12245 | IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ | 12408 | IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ |
12246 | IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ | 12409 | IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ |
12247 | int dqvarnest; /* levels of variables expansion within double quotes */ | 12410 | int dqvarnest; /* levels of variables expansion within double quotes */ |
12248 | |||
12249 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) | 12411 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) |
12250 | 12412 | ||
12251 | startlinno = g_parsefile->linno; | ||
12252 | bqlist = NULL; | 12413 | bqlist = NULL; |
12253 | quotef = 0; | 12414 | quotef = 0; |
12254 | IF_FEATURE_SH_MATH(prevsyntax = 0;) | 12415 | IF_FEATURE_SH_MATH(prevsyntax = 0;) |
@@ -12425,7 +12586,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12425 | if (syntax != BASESYNTAX && eofmark == NULL) | 12586 | if (syntax != BASESYNTAX && eofmark == NULL) |
12426 | raise_error_syntax("unterminated quoted string"); | 12587 | raise_error_syntax("unterminated quoted string"); |
12427 | if (varnest != 0) { | 12588 | if (varnest != 0) { |
12428 | startlinno = g_parsefile->linno; | ||
12429 | /* { */ | 12589 | /* { */ |
12430 | raise_error_syntax("missing '}'"); | 12590 | raise_error_syntax("missing '}'"); |
12431 | } | 12591 | } |
@@ -12817,7 +12977,6 @@ parsebackq: { | |||
12817 | 12977 | ||
12818 | case PEOF: | 12978 | case PEOF: |
12819 | IF_ASH_ALIAS(case PEOA:) | 12979 | IF_ASH_ALIAS(case PEOA:) |
12820 | startlinno = g_parsefile->linno; | ||
12821 | raise_error_syntax("EOF in backquote substitution"); | 12980 | raise_error_syntax("EOF in backquote substitution"); |
12822 | 12981 | ||
12823 | case '\n': | 12982 | case '\n': |
@@ -12899,8 +13058,6 @@ parsearith: { | |||
12899 | * quoted. | 13058 | * quoted. |
12900 | * If the token is TREDIR, then we set redirnode to a structure containing | 13059 | * If the token is TREDIR, then we set redirnode to a structure containing |
12901 | * the redirection. | 13060 | * the redirection. |
12902 | * In all cases, the variable startlinno is set to the number of the line | ||
12903 | * on which the token starts. | ||
12904 | * | 13061 | * |
12905 | * [Change comment: here documents and internal procedures] | 13062 | * [Change comment: here documents and internal procedures] |
12906 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | 13063 | * [Readtoken shouldn't have any arguments. Perhaps we should make the |
@@ -12938,7 +13095,6 @@ xxreadtoken(void) | |||
12938 | return lasttoken; | 13095 | return lasttoken; |
12939 | } | 13096 | } |
12940 | setprompt_if(needprompt, 2); | 13097 | setprompt_if(needprompt, 2); |
12941 | startlinno = g_parsefile->linno; | ||
12942 | for (;;) { /* until token or start of word found */ | 13098 | for (;;) { /* until token or start of word found */ |
12943 | c = pgetc(); | 13099 | c = pgetc(); |
12944 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) | 13100 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) |
@@ -12999,7 +13155,6 @@ xxreadtoken(void) | |||
12999 | return lasttoken; | 13155 | return lasttoken; |
13000 | } | 13156 | } |
13001 | setprompt_if(needprompt, 2); | 13157 | setprompt_if(needprompt, 2); |
13002 | startlinno = g_parsefile->linno; | ||
13003 | for (;;) { /* until token or start of word found */ | 13158 | for (;;) { /* until token or start of word found */ |
13004 | c = pgetc(); | 13159 | c = pgetc(); |
13005 | switch (c) { | 13160 | switch (c) { |
@@ -13391,10 +13546,14 @@ find_dot_file(char *name) | |||
13391 | if (fullname != name) | 13546 | if (fullname != name) |
13392 | stunalloc(fullname); | 13547 | stunalloc(fullname); |
13393 | } | 13548 | } |
13549 | /* not found in PATH */ | ||
13394 | 13550 | ||
13395 | /* not found in the PATH */ | 13551 | #if ENABLE_ASH_BASH_SOURCE_CURDIR |
13552 | return name; | ||
13553 | #else | ||
13396 | ash_msg_and_raise_error("%s: not found", name); | 13554 | ash_msg_and_raise_error("%s: not found", name); |
13397 | /* NOTREACHED */ | 13555 | /* NOTREACHED */ |
13556 | #endif | ||
13398 | } | 13557 | } |
13399 | 13558 | ||
13400 | static int FAST_FUNC | 13559 | static int FAST_FUNC |
@@ -13689,8 +13848,21 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13689 | /* We failed. If there was an entry for this command, delete it */ | 13848 | /* We failed. If there was an entry for this command, delete it */ |
13690 | if (cmdp && updatetbl) | 13849 | if (cmdp && updatetbl) |
13691 | delete_cmd_entry(); | 13850 | delete_cmd_entry(); |
13692 | if (act & DO_ERR) | 13851 | if (act & DO_ERR) { |
13852 | #if ENABLE_ASH_BASH_NOT_FOUND_HOOK | ||
13853 | struct tblentry *hookp = cmdlookup("command_not_found_handle", 0); | ||
13854 | if (hookp && hookp->cmdtype == CMDFUNCTION) { | ||
13855 | char *argv[3]; | ||
13856 | argv[0] = (char*) "command_not_found_handle"; | ||
13857 | argv[1] = name; | ||
13858 | argv[2] = NULL; | ||
13859 | evalfun(hookp->param.func, 2, argv, 0); | ||
13860 | entry->cmdtype = CMDUNKNOWN; | ||
13861 | return; | ||
13862 | } | ||
13863 | #endif | ||
13693 | ash_msg("%s: %s", name, errmsg(e, "not found")); | 13864 | ash_msg("%s: %s", name, errmsg(e, "not found")); |
13865 | } | ||
13694 | entry->cmdtype = CMDUNKNOWN; | 13866 | entry->cmdtype = CMDUNKNOWN; |
13695 | return; | 13867 | return; |
13696 | 13868 | ||
diff --git a/shell/ash_LINENO.patch b/shell/ash_LINENO.patch deleted file mode 100644 index a71549d6a..000000000 --- a/shell/ash_LINENO.patch +++ /dev/null | |||
@@ -1,498 +0,0 @@ | |||
1 | This patch is a backport from dash of the combination of: | ||
2 | [SHELL] Add preliminary LINENO support | ||
3 | [VAR] Fix varinit ordering that broke fc | ||
4 | [SHELL] Improve LINENO support | ||
5 | |||
6 | Applies cleanly on top of: | ||
7 | commit 9832bbaba966f0e52e183f10cd93fad7f8f643fe | ||
8 | Date: Tue Aug 15 15:44:41 2017 +0200 | ||
9 | |||
10 | Testsuite needs some tweaks (line numbers in some messages change). | ||
11 | |||
12 | Unfortunately, it is somewhat big: | ||
13 | |||
14 | function old new delta | ||
15 | parse_command 1581 1658 +77 | ||
16 | calcsize 203 272 +69 | ||
17 | copynode 195 257 +62 | ||
18 | lookupvar 59 108 +49 | ||
19 | evaltree 494 534 +40 | ||
20 | evalfor 152 187 +35 | ||
21 | evalcase 278 313 +35 | ||
22 | evalcommand 1547 1581 +34 | ||
23 | evalsubshell 169 199 +30 | ||
24 | linenovar - 22 +22 | ||
25 | raise_error_syntax 11 29 +18 | ||
26 | evalfun 266 280 +14 | ||
27 | varinit_data 96 108 +12 | ||
28 | cmdtxt 626 631 +5 | ||
29 | lineno - 4 +4 | ||
30 | funcline - 4 +4 | ||
31 | ash_vmsg 144 141 -3 | ||
32 | startlinno 4 - -4 | ||
33 | funcnest 4 - -4 | ||
34 | xxreadtoken 272 259 -13 | ||
35 | readtoken1 2635 2594 -41 | ||
36 | ------------------------------------------------------------------------------ | ||
37 | (add/remove: 3/2 grow/shrink: 13/3 up/down: 510/-65) Total: 445 bytes | ||
38 | text data bss dec hex filename | ||
39 | 912030 563 5844 918437 e03a5 busybox_old | ||
40 | 912473 587 5844 918904 e0578 busybox_unstripped | ||
41 | |||
42 | diff --git a/shell/ash.c b/shell/ash.c | ||
43 | index 703802f..93a3814 100644 | ||
44 | --- a/shell/ash.c | ||
45 | +++ b/shell/ash.c | ||
46 | @@ -312,6 +312,8 @@ struct globals_misc { | ||
47 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | ||
48 | int shlvl; | ||
49 | #define rootshell (!shlvl) | ||
50 | + int errlinno; | ||
51 | + | ||
52 | char *minusc; /* argument to -c option */ | ||
53 | |||
54 | char *curdir; // = nullstr; /* current working directory */ | ||
55 | @@ -389,6 +391,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | ||
56 | #define job_warning (G_misc.job_warning) | ||
57 | #define rootpid (G_misc.rootpid ) | ||
58 | #define shlvl (G_misc.shlvl ) | ||
59 | +#define errlinno (G_misc.errlinno ) | ||
60 | #define minusc (G_misc.minusc ) | ||
61 | #define curdir (G_misc.curdir ) | ||
62 | #define physdir (G_misc.physdir ) | ||
63 | @@ -723,6 +726,7 @@ union node; | ||
64 | |||
65 | struct ncmd { | ||
66 | smallint type; /* Nxxxx */ | ||
67 | + int linno; | ||
68 | union node *assign; | ||
69 | union node *args; | ||
70 | union node *redirect; | ||
71 | @@ -736,6 +740,7 @@ struct npipe { | ||
72 | |||
73 | struct nredir { | ||
74 | smallint type; | ||
75 | + int linno; | ||
76 | union node *n; | ||
77 | union node *redirect; | ||
78 | }; | ||
79 | @@ -755,6 +760,7 @@ struct nif { | ||
80 | |||
81 | struct nfor { | ||
82 | smallint type; | ||
83 | + int linno; | ||
84 | union node *args; | ||
85 | union node *body; | ||
86 | char *var; | ||
87 | @@ -762,6 +768,7 @@ struct nfor { | ||
88 | |||
89 | struct ncase { | ||
90 | smallint type; | ||
91 | + int linno; | ||
92 | union node *expr; | ||
93 | union node *cases; | ||
94 | }; | ||
95 | @@ -773,6 +780,13 @@ struct nclist { | ||
96 | union node *body; | ||
97 | }; | ||
98 | |||
99 | +struct ndefun { | ||
100 | + smallint type; | ||
101 | + int linno; | ||
102 | + char *text; | ||
103 | + union node *body; | ||
104 | +}; | ||
105 | + | ||
106 | struct narg { | ||
107 | smallint type; | ||
108 | union node *next; | ||
109 | @@ -824,6 +838,7 @@ union node { | ||
110 | struct nfor nfor; | ||
111 | struct ncase ncase; | ||
112 | struct nclist nclist; | ||
113 | + struct ndefun ndefun; | ||
114 | struct narg narg; | ||
115 | struct nfile nfile; | ||
116 | struct ndup ndup; | ||
117 | @@ -1253,7 +1268,6 @@ struct parsefile { | ||
118 | |||
119 | static struct parsefile basepf; /* top level input file */ | ||
120 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | ||
121 | -static int startlinno; /* line # where last token started */ | ||
122 | static char *commandname; /* currently executing command */ | ||
123 | |||
124 | |||
125 | @@ -1267,7 +1281,7 @@ ash_vmsg(const char *msg, va_list ap) | ||
126 | if (strcmp(arg0, commandname)) | ||
127 | fprintf(stderr, "%s: ", commandname); | ||
128 | if (!iflag || g_parsefile->pf_fd > 0) | ||
129 | - fprintf(stderr, "line %d: ", startlinno); | ||
130 | + fprintf(stderr, "line %d: ", errlinno); | ||
131 | } | ||
132 | vfprintf(stderr, msg, ap); | ||
133 | newline_and_flush(stderr); | ||
134 | @@ -1327,6 +1341,7 @@ static void raise_error_syntax(const char *) NORETURN; | ||
135 | static void | ||
136 | raise_error_syntax(const char *msg) | ||
137 | { | ||
138 | + errlinno = g_parsefile->linno; | ||
139 | ash_msg_and_raise_error("syntax error: %s", msg); | ||
140 | /* NOTREACHED */ | ||
141 | } | ||
142 | @@ -1993,6 +2008,9 @@ static void changepath(const char *) FAST_FUNC; | ||
143 | static void change_random(const char *) FAST_FUNC; | ||
144 | #endif | ||
145 | |||
146 | +static int lineno; | ||
147 | +static char linenovar[sizeof("LINENO=%d") + sizeof(int)*3] = "LINENO="; | ||
148 | + | ||
149 | static const struct { | ||
150 | int flags; | ||
151 | const char *var_text; | ||
152 | @@ -2014,6 +2032,7 @@ static const struct { | ||
153 | #if ENABLE_ASH_GETOPTS | ||
154 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, | ||
155 | #endif | ||
156 | + { VSTRFIXED|VTEXTFIXED , linenovar , NULL }, | ||
157 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
158 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, | ||
159 | #endif | ||
160 | @@ -2066,12 +2085,14 @@ extern struct globals_var *const ash_ptr_to_globals_var; | ||
161 | #define vps4 (&vps2)[1] | ||
162 | #if ENABLE_ASH_GETOPTS | ||
163 | # define voptind (&vps4)[1] | ||
164 | +# define vlineno (&voptind)[1] | ||
165 | # if ENABLE_ASH_RANDOM_SUPPORT | ||
166 | -# define vrandom (&voptind)[1] | ||
167 | +# define vrandom (&vlineno)[1] | ||
168 | # endif | ||
169 | #else | ||
170 | +# define vlineno (&vps4)[1] | ||
171 | # if ENABLE_ASH_RANDOM_SUPPORT | ||
172 | -# define vrandom (&vps4)[1] | ||
173 | +# define vrandom (&vlineno)[1] | ||
174 | # endif | ||
175 | #endif | ||
176 | |||
177 | @@ -2209,8 +2230,12 @@ lookupvar(const char *name) | ||
178 | if (v->flags & VDYNAMIC) | ||
179 | v->var_func(NULL); | ||
180 | #endif | ||
181 | - if (!(v->flags & VUNSET)) | ||
182 | + if (!(v->flags & VUNSET)) { | ||
183 | + if (v == &vlineno && v->var_text == linenovar) { | ||
184 | + fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); | ||
185 | + } | ||
186 | return var_end(v->var_text); | ||
187 | + } | ||
188 | } | ||
189 | return NULL; | ||
190 | } | ||
191 | @@ -4783,7 +4808,7 @@ cmdtxt(union node *n) | ||
192 | p = "; done"; | ||
193 | goto dodo; | ||
194 | case NDEFUN: | ||
195 | - cmdputs(n->narg.text); | ||
196 | + cmdputs(n->ndefun.text); | ||
197 | p = "() { ... }"; | ||
198 | goto dotail2; | ||
199 | case NCMD: | ||
200 | @@ -8551,6 +8576,9 @@ calcsize(int funcblocksize, union node *n) | ||
201 | funcblocksize = calcsize(funcblocksize, n->nclist.next); | ||
202 | break; | ||
203 | case NDEFUN: | ||
204 | + funcblocksize = calcsize(funcblocksize, n->ndefun.body); | ||
205 | + funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1); | ||
206 | + break; | ||
207 | case NARG: | ||
208 | funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); | ||
209 | funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ | ||
210 | @@ -8626,6 +8654,7 @@ copynode(union node *n) | ||
211 | new->ncmd.redirect = copynode(n->ncmd.redirect); | ||
212 | new->ncmd.args = copynode(n->ncmd.args); | ||
213 | new->ncmd.assign = copynode(n->ncmd.assign); | ||
214 | + new->ncmd.linno = n->ncmd.linno; | ||
215 | break; | ||
216 | case NPIPE: | ||
217 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); | ||
218 | @@ -8636,6 +8665,7 @@ copynode(union node *n) | ||
219 | case NSUBSHELL: | ||
220 | new->nredir.redirect = copynode(n->nredir.redirect); | ||
221 | new->nredir.n = copynode(n->nredir.n); | ||
222 | + new->nredir.linno = n->nredir.linno; | ||
223 | break; | ||
224 | case NAND: | ||
225 | case NOR: | ||
226 | @@ -8654,10 +8684,12 @@ copynode(union node *n) | ||
227 | new->nfor.var = nodeckstrdup(n->nfor.var); | ||
228 | new->nfor.body = copynode(n->nfor.body); | ||
229 | new->nfor.args = copynode(n->nfor.args); | ||
230 | + new->nfor.linno = n->nfor.linno; | ||
231 | break; | ||
232 | case NCASE: | ||
233 | new->ncase.cases = copynode(n->ncase.cases); | ||
234 | new->ncase.expr = copynode(n->ncase.expr); | ||
235 | + new->ncase.linno = n->ncase.linno; | ||
236 | break; | ||
237 | case NCLIST: | ||
238 | new->nclist.body = copynode(n->nclist.body); | ||
239 | @@ -8665,6 +8697,10 @@ copynode(union node *n) | ||
240 | new->nclist.next = copynode(n->nclist.next); | ||
241 | break; | ||
242 | case NDEFUN: | ||
243 | + new->ndefun.body = copynode(n->ndefun.body); | ||
244 | + new->ndefun.text = nodeckstrdup(n->ndefun.text); | ||
245 | + new->ndefun.linno = n->ndefun.linno; | ||
246 | + break; | ||
247 | case NARG: | ||
248 | new->narg.backquote = copynodelist(n->narg.backquote); | ||
249 | new->narg.text = nodeckstrdup(n->narg.text); | ||
250 | @@ -8733,7 +8769,7 @@ defun(union node *func) | ||
251 | INT_OFF; | ||
252 | entry.cmdtype = CMDFUNCTION; | ||
253 | entry.u.func = copyfunc(func); | ||
254 | - addcmdentry(func->narg.text, &entry); | ||
255 | + addcmdentry(func->ndefun.text, &entry); | ||
256 | INT_ON; | ||
257 | } | ||
258 | |||
259 | @@ -8743,8 +8779,8 @@ defun(union node *func) | ||
260 | #define SKIPFUNC (1 << 2) | ||
261 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | ||
262 | static int skipcount; /* number of levels to skip */ | ||
263 | -static int funcnest; /* depth of function calls */ | ||
264 | static int loopnest; /* current loop nesting level */ | ||
265 | +static int funcline; /* starting line number of current function, or 0 if not in a function */ | ||
266 | |||
267 | /* Forward decl way out to parsing code - dotrap needs it */ | ||
268 | static int evalstring(char *s, int flags); | ||
269 | @@ -8839,6 +8875,9 @@ evaltree(union node *n, int flags) | ||
270 | status = !evaltree(n->nnot.com, EV_TESTED); | ||
271 | goto setstatus; | ||
272 | case NREDIR: | ||
273 | + errlinno = lineno = n->nredir.linno; | ||
274 | + if (funcline) | ||
275 | + lineno -= funcline - 1; | ||
276 | expredir(n->nredir.redirect); | ||
277 | pushredir(n->nredir.redirect); | ||
278 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | ||
279 | @@ -8993,6 +9032,10 @@ evalfor(union node *n, int flags) | ||
280 | struct stackmark smark; | ||
281 | int status = 0; | ||
282 | |||
283 | + errlinno = lineno = n->ncase.linno; | ||
284 | + if (funcline) | ||
285 | + lineno -= funcline - 1; | ||
286 | + | ||
287 | setstackmark(&smark); | ||
288 | arglist.list = NULL; | ||
289 | arglist.lastp = &arglist.list; | ||
290 | @@ -9024,6 +9067,10 @@ evalcase(union node *n, int flags) | ||
291 | struct stackmark smark; | ||
292 | int status = 0; | ||
293 | |||
294 | + errlinno = lineno = n->ncase.linno; | ||
295 | + if (funcline) | ||
296 | + lineno -= funcline - 1; | ||
297 | + | ||
298 | setstackmark(&smark); | ||
299 | arglist.list = NULL; | ||
300 | arglist.lastp = &arglist.list; | ||
301 | @@ -9058,6 +9105,10 @@ evalsubshell(union node *n, int flags) | ||
302 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ | ||
303 | int status; | ||
304 | |||
305 | + errlinno = lineno = n->nredir.linno; | ||
306 | + if (funcline) | ||
307 | + lineno -= funcline - 1; | ||
308 | + | ||
309 | expredir(n->nredir.redirect); | ||
310 | if (!backgnd && (flags & EV_EXIT) && !may_have_traps) | ||
311 | goto nofork; | ||
312 | @@ -9365,8 +9416,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
313 | struct jmploc *volatile savehandler; | ||
314 | struct jmploc jmploc; | ||
315 | int e; | ||
316 | + int savefuncline; | ||
317 | |||
318 | saveparam = shellparam; | ||
319 | + savefuncline = funcline; | ||
320 | savehandler = exception_handler; | ||
321 | e = setjmp(jmploc.loc); | ||
322 | if (e) { | ||
323 | @@ -9376,7 +9429,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
324 | exception_handler = &jmploc; | ||
325 | shellparam.malloced = 0; | ||
326 | func->count++; | ||
327 | - funcnest++; | ||
328 | + funcline = func->n.ndefun.linno; | ||
329 | INT_ON; | ||
330 | shellparam.nparam = argc - 1; | ||
331 | shellparam.p = argv + 1; | ||
332 | @@ -9385,11 +9438,11 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | ||
333 | shellparam.optoff = -1; | ||
334 | #endif | ||
335 | pushlocalvars(); | ||
336 | - evaltree(func->n.narg.next, flags & EV_TESTED); | ||
337 | + evaltree(func->n.ndefun.body, flags & EV_TESTED); | ||
338 | poplocalvars(0); | ||
339 | funcdone: | ||
340 | INT_OFF; | ||
341 | - funcnest--; | ||
342 | + funcline = savefuncline; | ||
343 | freefunc(func); | ||
344 | freeparam(&shellparam); | ||
345 | shellparam = saveparam; | ||
346 | @@ -9753,6 +9806,10 @@ evalcommand(union node *cmd, int flags) | ||
347 | char **nargv; | ||
348 | smallint cmd_is_exec; | ||
349 | |||
350 | + errlinno = lineno = cmd->ncmd.linno; | ||
351 | + if (funcline) | ||
352 | + lineno -= funcline - 1; | ||
353 | + | ||
354 | /* First expand the arguments. */ | ||
355 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | ||
356 | setstackmark(&smark); | ||
357 | @@ -9798,7 +9855,7 @@ evalcommand(union node *cmd, int flags) | ||
358 | *nargv = NULL; | ||
359 | |||
360 | lastarg = NULL; | ||
361 | - if (iflag && funcnest == 0 && argc > 0) | ||
362 | + if (iflag && funcline == 0 && argc > 0) | ||
363 | lastarg = nargv[-1]; | ||
364 | |||
365 | expredir(cmd->ncmd.redirect); | ||
366 | @@ -11317,6 +11374,7 @@ simplecmd(void) | ||
367 | union node *vars, **vpp; | ||
368 | union node **rpp, *redir; | ||
369 | int savecheckkwd; | ||
370 | + int savelinno; | ||
371 | #if BASH_TEST2 | ||
372 | smallint double_brackets_flag = 0; | ||
373 | #endif | ||
374 | @@ -11330,6 +11388,7 @@ simplecmd(void) | ||
375 | rpp = &redir; | ||
376 | |||
377 | savecheckkwd = CHKALIAS; | ||
378 | + savelinno = g_parsefile->linno; | ||
379 | for (;;) { | ||
380 | int t; | ||
381 | checkkwd = savecheckkwd; | ||
382 | @@ -11419,7 +11478,9 @@ simplecmd(void) | ||
383 | } | ||
384 | n->type = NDEFUN; | ||
385 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
386 | - n->narg.next = parse_command(); | ||
387 | + n->ndefun.text = n->narg.text; | ||
388 | + n->ndefun.linno = g_parsefile->linno; | ||
389 | + n->ndefun.body = parse_command(); | ||
390 | return n; | ||
391 | } | ||
392 | IF_BASH_FUNCTION(function_flag = 0;) | ||
393 | @@ -11435,6 +11496,7 @@ simplecmd(void) | ||
394 | *rpp = NULL; | ||
395 | n = stzalloc(sizeof(struct ncmd)); | ||
396 | n->type = NCMD; | ||
397 | + n->ncmd.linno = savelinno; | ||
398 | n->ncmd.args = args; | ||
399 | n->ncmd.assign = vars; | ||
400 | n->ncmd.redirect = redir; | ||
401 | @@ -11450,10 +11512,13 @@ parse_command(void) | ||
402 | union node *redir, **rpp; | ||
403 | union node **rpp2; | ||
404 | int t; | ||
405 | + int savelinno; | ||
406 | |||
407 | redir = NULL; | ||
408 | rpp2 = &redir; | ||
409 | |||
410 | + savelinno = g_parsefile->linno; | ||
411 | + | ||
412 | switch (readtoken()) { | ||
413 | default: | ||
414 | raise_error_unexpected_syntax(-1); | ||
415 | @@ -11504,6 +11569,7 @@ parse_command(void) | ||
416 | raise_error_syntax("bad for loop variable"); | ||
417 | n1 = stzalloc(sizeof(struct nfor)); | ||
418 | n1->type = NFOR; | ||
419 | + n1->nfor.linno = savelinno; | ||
420 | n1->nfor.var = wordtext; | ||
421 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
422 | if (readtoken() == TIN) { | ||
423 | @@ -11544,6 +11610,7 @@ parse_command(void) | ||
424 | case TCASE: | ||
425 | n1 = stzalloc(sizeof(struct ncase)); | ||
426 | n1->type = NCASE; | ||
427 | + n1->ncase.linno = savelinno; | ||
428 | if (readtoken() != TWORD) | ||
429 | raise_error_unexpected_syntax(TWORD); | ||
430 | n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); | ||
431 | @@ -11595,6 +11662,7 @@ parse_command(void) | ||
432 | case TLP: | ||
433 | n1 = stzalloc(sizeof(struct nredir)); | ||
434 | n1->type = NSUBSHELL; | ||
435 | + n1->nredir.linno = savelinno; | ||
436 | n1->nredir.n = list(0); | ||
437 | /*n1->nredir.redirect = NULL; - stzalloc did it */ | ||
438 | t = TRP; | ||
439 | @@ -11628,6 +11696,7 @@ parse_command(void) | ||
440 | if (n1->type != NSUBSHELL) { | ||
441 | n2 = stzalloc(sizeof(struct nredir)); | ||
442 | n2->type = NREDIR; | ||
443 | + n2->nredir.linno = savelinno; | ||
444 | n2->nredir.n = n1; | ||
445 | n1 = n2; | ||
446 | } | ||
447 | @@ -11726,10 +11795,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | ||
448 | IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ | ||
449 | IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ | ||
450 | int dqvarnest; /* levels of variables expansion within double quotes */ | ||
451 | - | ||
452 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) | ||
453 | |||
454 | - startlinno = g_parsefile->linno; | ||
455 | bqlist = NULL; | ||
456 | quotef = 0; | ||
457 | IF_FEATURE_SH_MATH(prevsyntax = 0;) | ||
458 | @@ -11906,7 +11973,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | ||
459 | if (syntax != BASESYNTAX && eofmark == NULL) | ||
460 | raise_error_syntax("unterminated quoted string"); | ||
461 | if (varnest != 0) { | ||
462 | - startlinno = g_parsefile->linno; | ||
463 | /* { */ | ||
464 | raise_error_syntax("missing '}'"); | ||
465 | } | ||
466 | @@ -12298,7 +12364,6 @@ parsebackq: { | ||
467 | |||
468 | case PEOF: | ||
469 | IF_ASH_ALIAS(case PEOA:) | ||
470 | - startlinno = g_parsefile->linno; | ||
471 | raise_error_syntax("EOF in backquote substitution"); | ||
472 | |||
473 | case '\n': | ||
474 | @@ -12380,8 +12445,6 @@ parsearith: { | ||
475 | * quoted. | ||
476 | * If the token is TREDIR, then we set redirnode to a structure containing | ||
477 | * the redirection. | ||
478 | - * In all cases, the variable startlinno is set to the number of the line | ||
479 | - * on which the token starts. | ||
480 | * | ||
481 | * [Change comment: here documents and internal procedures] | ||
482 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | ||
483 | @@ -12419,7 +12482,6 @@ xxreadtoken(void) | ||
484 | return lasttoken; | ||
485 | } | ||
486 | setprompt_if(needprompt, 2); | ||
487 | - startlinno = g_parsefile->linno; | ||
488 | for (;;) { /* until token or start of word found */ | ||
489 | c = pgetc(); | ||
490 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) | ||
491 | @@ -12480,7 +12542,6 @@ xxreadtoken(void) | ||
492 | return lasttoken; | ||
493 | } | ||
494 | setprompt_if(needprompt, 2); | ||
495 | - startlinno = g_parsefile->linno; | ||
496 | for (;;) { /* until token or start of word found */ | ||
497 | c = pgetc(); | ||
498 | switch (c) { | ||
diff --git a/shell/ash_test/ash-arith/arith-postinc.right b/shell/ash_test/ash-arith/arith-postinc.right new file mode 100644 index 000000000..c95ce02bf --- /dev/null +++ b/shell/ash_test/ash-arith/arith-postinc.right | |||
@@ -0,0 +1,5 @@ | |||
1 | 1 1 | ||
2 | 1 1 | ||
3 | 1 1 | ||
4 | 1 1 | ||
5 | Ok:0 | ||
diff --git a/shell/ash_test/ash-arith/arith-postinc.tests b/shell/ash_test/ash-arith/arith-postinc.tests new file mode 100755 index 000000000..3fd9bfed5 --- /dev/null +++ b/shell/ash_test/ash-arith/arith-postinc.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | echo 1 $((0++1)) | ||
2 | echo 1 $((0--1)) | ||
3 | x=-1; echo 1 $((0-$x)) | ||
4 | x=+1; echo 1 $((0+$x)) | ||
5 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-arith/arith.right b/shell/ash_test/ash-arith/arith.right index 9b9ca8e2f..6936f1269 100644 --- a/shell/ash_test/ash-arith/arith.right +++ b/shell/ash_test/ash-arith/arith.right | |||
@@ -126,6 +126,10 @@ ghi | |||
126 | ./arith2.sub: line 5: arithmetic syntax error | 126 | ./arith2.sub: line 5: arithmetic syntax error |
127 | 5 5 | 127 | 5 5 |
128 | 1 1 | 128 | 1 1 |
129 | 6 6 | ||
130 | 2 2 | ||
131 | 3 3 | ||
132 | 1 1 | ||
129 | 4 4 | 133 | 4 4 |
130 | 0 0 | 134 | 0 0 |
131 | ./arith2.sub: line 42: arithmetic syntax error | 135 | ./arith2.sub: line 42: arithmetic syntax error |
diff --git a/shell/ash_test/ash-arith/arith2.sub b/shell/ash_test/ash-arith/arith2.sub index f7e3c9235..9105059db 100755 --- a/shell/ash_test/ash-arith/arith2.sub +++ b/shell/ash_test/ash-arith/arith2.sub | |||
@@ -23,14 +23,14 @@ | |||
23 | echo 5 $(( 4 + ++a )) | 23 | echo 5 $(( 4 + ++a )) |
24 | echo 1 $a | 24 | echo 1 $a |
25 | 25 | ||
26 | # ash doesn't handle it right... | 26 | # this is treated as 4 + ++a |
27 | #ash# echo 6 $(( 4+++a )) | 27 | echo 6 $(( 4+++a )) |
28 | #ash# echo 2 $a | 28 | echo 2 $a |
29 | a=2 | 29 | a=2 |
30 | 30 | ||
31 | # ash doesn't handle it right... | 31 | # this is treated as 4 - --a |
32 | #ash# echo 3 $(( 4---a )) | 32 | echo 3 $(( 4---a )) |
33 | #ash# echo 1 $a | 33 | echo 1 $a |
34 | a=1 | 34 | a=1 |
35 | 35 | ||
36 | echo 4 $(( 4 - -- a )) | 36 | echo 4 $(( 4 - -- a )) |
diff --git a/shell/ash_test/ash-misc/control_char1.right b/shell/ash_test/ash-misc/control_char1.right new file mode 100644 index 000000000..6f8c2533c --- /dev/null +++ b/shell/ash_test/ash-misc/control_char1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | |||
2 | b#c | ||
3 | Done:0 | ||
diff --git a/shell/ash_test/ash-misc/control_char1.tests b/shell/ash_test/ash-misc/control_char1.tests new file mode 100755 index 000000000..0cfe60141 --- /dev/null +++ b/shell/ash_test/ash-misc/control_char1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | echo | ||
2 | echo 'b#c' | ||
3 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-misc/control_char2.right b/shell/ash_test/ash-misc/control_char2.right new file mode 100644 index 000000000..9498b420d --- /dev/null +++ b/shell/ash_test/ash-misc/control_char2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Done:0 | ||
diff --git a/shell/ash_test/ash-misc/control_char2.tests b/shell/ash_test/ash-misc/control_char2.tests new file mode 100755 index 000000000..e77d7a1a6 --- /dev/null +++ b/shell/ash_test/ash-misc/control_char2.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | c=`printf '\3'` | ||
2 | eval "echo $c" | ||
3 | echo Done:$? | ||
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.right b/shell/ash_test/ash-misc/for_with_bslashes.right index 02d96692c..cd8501050 100644 --- a/shell/ash_test/ash-misc/for_with_bslashes.right +++ b/shell/ash_test/ash-misc/for_with_bslashes.right | |||
@@ -5,4 +5,5 @@ b"c | |||
5 | b'c | 5 | b'c |
6 | b$c | 6 | b$c |
7 | b`true`c | 7 | b`true`c |
8 | b#c | ||
8 | Zero:0 | 9 | Zero:0 |
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.tests b/shell/ash_test/ash-misc/for_with_bslashes.tests index 363f3d85b..8acd9808a 100755 --- a/shell/ash_test/ash-misc/for_with_bslashes.tests +++ b/shell/ash_test/ash-misc/for_with_bslashes.tests | |||
@@ -1,9 +1,5 @@ | |||
1 | # UNFIXED BUG. | 1 | # last word contains ^C character. |
2 | # commented-out words contain ^C character. | 2 | for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' 'b#c' |
3 | # It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it. | ||
4 | # When it is fixed, update this test. | ||
5 | |||
6 | for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c' | ||
7 | do | 3 | do |
8 | echo $a | 4 | echo $a |
9 | done | 5 | done |
diff --git a/shell/ash_test/ash-psubst/emptytick.right b/shell/ash_test/ash-psubst/emptytick.right index 7629deba6..459c4f735 100644 --- a/shell/ash_test/ash-psubst/emptytick.right +++ b/shell/ash_test/ash-psubst/emptytick.right | |||
@@ -1,8 +1,8 @@ | |||
1 | 0 | 1 | 0 |
2 | 0 | 2 | 0 |
3 | ./emptytick.tests: line 3: : Permission denied | 3 | ./emptytick.tests: line 1: : Permission denied |
4 | 127 | 4 | 127 |
5 | ./emptytick.tests: line 4: : Permission denied | 5 | ./emptytick.tests: line 1: : Permission denied |
6 | 127 | 6 | 127 |
7 | 0 | 7 | 0 |
8 | 0 | 8 | 0 |
diff --git a/shell/ash_test/ash-quoting/mode_x.right b/shell/ash_test/ash-quoting/mode_x.right index c2dd3550c..d1f670af6 100644 --- a/shell/ash_test/ash-quoting/mode_x.right +++ b/shell/ash_test/ash-quoting/mode_x.right | |||
@@ -3,8 +3,8 @@ | |||
3 | + true '%s\n' one 'two '"'"'three' four | 3 | + true '%s\n' one 'two '"'"'three' four |
4 | + this=command | 4 | + this=command |
5 | + 'this=command' | 5 | + 'this=command' |
6 | ./mode_x.tests: line 1: this=command: not found | 6 | ./mode_x.tests: line 10: this=command: not found |
7 | + true | 7 | + true |
8 | + true | 8 | + true |
9 | + 'if' true | 9 | + 'if' true |
10 | ./mode_x.tests: line 1: if: not found | 10 | ./mode_x.tests: line 14: if: not found |
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.right b/shell/ash_test/ash-vars/param_expand_bash_substring.right index 9ad6dbcad..687dd9002 100644 --- a/shell/ash_test/ash-vars/param_expand_bash_substring.right +++ b/shell/ash_test/ash-vars/param_expand_bash_substring.right | |||
@@ -3,6 +3,7 @@ SHELL: line 1: syntax error: bad substitution | |||
3 | SHELL: line 1: syntax error: bad substitution | 3 | SHELL: line 1: syntax error: bad substitution |
4 | SHELL: line 1: syntax error: bad substitution | 4 | SHELL: line 1: syntax error: bad substitution |
5 | SHELL: line 1: syntax error: missing '}' | 5 | SHELL: line 1: syntax error: missing '}' |
6 | 0 | ||
6 | 1 =|| | 7 | 1 =|| |
7 | 1:1 =|| | 8 | 1:1 =|| |
8 | 1:1:2=|| | 9 | 1:1:2=|| |
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.tests b/shell/ash_test/ash-vars/param_expand_bash_substring.tests index cce9f123e..512da351b 100755 --- a/shell/ash_test/ash-vars/param_expand_bash_substring.tests +++ b/shell/ash_test/ash-vars/param_expand_bash_substring.tests | |||
@@ -11,7 +11,7 @@ export var=0123456789 | |||
11 | "$THIS_SH" -c 'echo ${var:}' SHELL | 11 | "$THIS_SH" -c 'echo ${var:}' SHELL |
12 | 12 | ||
13 | # then some funky ones | 13 | # then some funky ones |
14 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | 14 | "$THIS_SH" -c 'echo ${?:0}' SHELL |
15 | 15 | ||
16 | # now some valid ones | 16 | # now some valid ones |
17 | set --; echo "1 =|${1}|" | 17 | set --; echo "1 =|${1}|" |
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.right b/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.right new file mode 100644 index 000000000..d400a7e31 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.right | |||
@@ -0,0 +1,2 @@ | |||
1 | v | ||
2 | Ok:0 | ||
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.tests b/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.tests new file mode 100755 index 000000000..6e8aa2afa --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_repl_empty_pattern.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | v=v | ||
2 | echo ${v//} | ||
3 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_var.right b/shell/ash_test/ash-vars/var_bash_repl_empty_var.right new file mode 100644 index 000000000..892916783 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_repl_empty_var.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Ok:0 | ||
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests b/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests new file mode 100755 index 000000000..73a43d38e --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | v='' | ||
2 | echo ${v/*/w} | ||
3 | echo Ok:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index 708555ac4..8f1017e3c 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -47,17 +47,13 @@ | |||
47 | * follow IFS rules more precisely, including update semantics | 47 | * follow IFS rules more precisely, including update semantics |
48 | * tilde expansion | 48 | * tilde expansion |
49 | * aliases | 49 | * aliases |
50 | * builtins mandated by standards we don't support: | 50 | * "command" missing features: |
51 | * [un]alias, command, fc: | 51 | * command -p CMD: run CMD using default $PATH |
52 | * command -v CMD: print "/path/to/CMD" | 52 | * (can use this to override standalone shell as well?) |
53 | * prints "CMD" for builtins | ||
54 | * prints "alias ALIAS='EXPANSION'" for aliases | ||
55 | * prints nothing and sets $? to 1 if not found | ||
56 | * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc" | ||
57 | * command [-p] CMD: run CMD, even if a function CMD also exists | ||
58 | * (can use this to override standalone shell as well) | ||
59 | * -p: use default $PATH | ||
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 53 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
54 | * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) | ||
55 | * builtins mandated by standards we don't support: | ||
56 | * [un]alias, fc: | ||
61 | * fc -l[nr] [BEG] [END]: list range of commands in history | 57 | * fc -l[nr] [BEG] [END]: list range of commands in history |
62 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 58 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
63 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 59 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
@@ -124,6 +120,18 @@ | |||
124 | //config: help | 120 | //config: help |
125 | //config: Enable {abc,def} extension. | 121 | //config: Enable {abc,def} extension. |
126 | //config: | 122 | //config: |
123 | //config:config HUSH_LINENO_VAR | ||
124 | //config: bool "$LINENO variable" | ||
125 | //config: default y | ||
126 | //config: depends on HUSH_BASH_COMPAT | ||
127 | //config: | ||
128 | //config:config HUSH_BASH_SOURCE_CURDIR | ||
129 | //config: bool "'source' and '.' builtins search current directory after $PATH" | ||
130 | //config: default n # do not encourage non-standard behavior | ||
131 | //config: depends on HUSH_BASH_COMPAT | ||
132 | //config: help | ||
133 | //config: This is not compliant with standards. Avoid if possible. | ||
134 | //config: | ||
127 | //config:config HUSH_INTERACTIVE | 135 | //config:config HUSH_INTERACTIVE |
128 | //config: bool "Interactive mode" | 136 | //config: bool "Interactive mode" |
129 | //config: default y | 137 | //config: default y |
@@ -253,6 +261,11 @@ | |||
253 | //config: default y | 261 | //config: default y |
254 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 262 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
255 | //config: | 263 | //config: |
264 | //config:config HUSH_COMMAND | ||
265 | //config: bool "command builtin" | ||
266 | //config: default y | ||
267 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
268 | //config: | ||
256 | //config:config HUSH_TRAP | 269 | //config:config HUSH_TRAP |
257 | //config: bool "trap builtin" | 270 | //config: bool "trap builtin" |
258 | //config: default y | 271 | //config: default y |
@@ -462,7 +475,10 @@ | |||
462 | # define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3) | 475 | # define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3) |
463 | #endif | 476 | #endif |
464 | 477 | ||
465 | #define SPECIAL_VAR_SYMBOL 3 | 478 | #define SPECIAL_VAR_SYMBOL_STR "\3" |
479 | #define SPECIAL_VAR_SYMBOL 3 | ||
480 | /* The "variable" with name "\1" emits string "\3". Testcase: "echo ^C" */ | ||
481 | #define SPECIAL_VAR_QUOTED_SVS 1 | ||
466 | 482 | ||
467 | struct variable; | 483 | struct variable; |
468 | 484 | ||
@@ -608,6 +624,9 @@ typedef enum redir_type { | |||
608 | struct command { | 624 | struct command { |
609 | pid_t pid; /* 0 if exited */ | 625 | pid_t pid; /* 0 if exited */ |
610 | int assignment_cnt; /* how many argv[i] are assignments? */ | 626 | int assignment_cnt; /* how many argv[i] are assignments? */ |
627 | #if ENABLE_HUSH_LINENO_VAR | ||
628 | unsigned lineno; | ||
629 | #endif | ||
611 | smallint cmd_type; /* CMD_xxx */ | 630 | smallint cmd_type; /* CMD_xxx */ |
612 | #define CMD_NORMAL 0 | 631 | #define CMD_NORMAL 0 |
613 | #define CMD_SUBSHELL 1 | 632 | #define CMD_SUBSHELL 1 |
@@ -927,6 +946,10 @@ struct globals { | |||
927 | unsigned handled_SIGCHLD; | 946 | unsigned handled_SIGCHLD; |
928 | smallint we_have_children; | 947 | smallint we_have_children; |
929 | #endif | 948 | #endif |
949 | #if ENABLE_HUSH_LINENO_VAR | ||
950 | unsigned lineno; | ||
951 | char *lineno_var; | ||
952 | #endif | ||
930 | struct FILE_list *FILE_list; | 953 | struct FILE_list *FILE_list; |
931 | /* Which signals have non-DFL handler (even with no traps set)? | 954 | /* Which signals have non-DFL handler (even with no traps set)? |
932 | * Set at the start to: | 955 | * Set at the start to: |
@@ -1924,7 +1947,7 @@ static void hush_exit(int exitcode) | |||
1924 | if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) { | 1947 | if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) { |
1925 | char *argv[3]; | 1948 | char *argv[3]; |
1926 | /* argv[0] is unused */ | 1949 | /* argv[0] is unused */ |
1927 | argv[1] = G_traps[0]; | 1950 | argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */ |
1928 | argv[2] = NULL; | 1951 | argv[2] = NULL; |
1929 | G.exiting = 1; /* prevent EXIT trap recursion */ | 1952 | G.exiting = 1; /* prevent EXIT trap recursion */ |
1930 | /* Note: G_traps[0] is not cleared! | 1953 | /* Note: G_traps[0] is not cleared! |
@@ -1985,10 +2008,12 @@ static int check_and_run_traps(void) | |||
1985 | smalluint save_rcode; | 2008 | smalluint save_rcode; |
1986 | char *argv[3]; | 2009 | char *argv[3]; |
1987 | /* argv[0] is unused */ | 2010 | /* argv[0] is unused */ |
1988 | argv[1] = G_traps[sig]; | 2011 | argv[1] = xstrdup(G_traps[sig]); |
2012 | /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */ | ||
1989 | argv[2] = NULL; | 2013 | argv[2] = NULL; |
1990 | save_rcode = G.last_exitcode; | 2014 | save_rcode = G.last_exitcode; |
1991 | builtin_eval(argv); | 2015 | builtin_eval(argv); |
2016 | free(argv[1]); | ||
1992 | //FIXME: shouldn't it be set to 128 + sig instead? | 2017 | //FIXME: shouldn't it be set to 128 + sig instead? |
1993 | G.last_exitcode = save_rcode; | 2018 | G.last_exitcode = save_rcode; |
1994 | last_sig = sig; | 2019 | last_sig = sig; |
@@ -2131,6 +2156,13 @@ static int set_local_var(char *str, unsigned flags) | |||
2131 | } | 2156 | } |
2132 | 2157 | ||
2133 | name_len = eq_sign - str + 1; /* including '=' */ | 2158 | name_len = eq_sign - str + 1; /* including '=' */ |
2159 | #if ENABLE_HUSH_LINENO_VAR | ||
2160 | if (G.lineno_var) { | ||
2161 | if (name_len == 7 && strncmp("LINENO", str, 6) == 0) | ||
2162 | G.lineno_var = NULL; | ||
2163 | } | ||
2164 | #endif | ||
2165 | |||
2134 | var_pp = &G.top_var; | 2166 | var_pp = &G.top_var; |
2135 | while ((cur = *var_pp) != NULL) { | 2167 | while ((cur = *var_pp) != NULL) { |
2136 | if (strncmp(cur->varstr, str, name_len) != 0) { | 2168 | if (strncmp(cur->varstr, str, name_len) != 0) { |
@@ -2252,10 +2284,16 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2252 | 2284 | ||
2253 | if (!name) | 2285 | if (!name) |
2254 | return EXIT_SUCCESS; | 2286 | return EXIT_SUCCESS; |
2287 | |||
2255 | #if ENABLE_HUSH_GETOPTS | 2288 | #if ENABLE_HUSH_GETOPTS |
2256 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) | 2289 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) |
2257 | G.getopt_count = 0; | 2290 | G.getopt_count = 0; |
2258 | #endif | 2291 | #endif |
2292 | #if ENABLE_HUSH_LINENO_VAR | ||
2293 | if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0) | ||
2294 | G.lineno_var = NULL; | ||
2295 | #endif | ||
2296 | |||
2259 | var_pp = &G.top_var; | 2297 | var_pp = &G.top_var; |
2260 | while ((cur = *var_pp) != NULL) { | 2298 | while ((cur = *var_pp) != NULL) { |
2261 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { | 2299 | if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { |
@@ -2278,7 +2316,7 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2278 | return EXIT_SUCCESS; | 2316 | return EXIT_SUCCESS; |
2279 | } | 2317 | } |
2280 | 2318 | ||
2281 | #if ENABLE_HUSH_UNSET | 2319 | #if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS |
2282 | static int unset_local_var(const char *name) | 2320 | static int unset_local_var(const char *name) |
2283 | { | 2321 | { |
2284 | return unset_local_var_len(name, strlen(name)); | 2322 | return unset_local_var_len(name, strlen(name)); |
@@ -2300,7 +2338,7 @@ static void unset_vars(char **strings) | |||
2300 | free(strings); | 2338 | free(strings); |
2301 | } | 2339 | } |
2302 | 2340 | ||
2303 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ | 2341 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS |
2304 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2342 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) |
2305 | { | 2343 | { |
2306 | char *var = xasprintf("%s=%s", name, val); | 2344 | char *var = xasprintf("%s=%s", name, val); |
@@ -2574,6 +2612,12 @@ static int i_getch(struct in_str *i) | |||
2574 | out: | 2612 | out: |
2575 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2613 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2576 | i->last_char = ch; | 2614 | i->last_char = ch; |
2615 | #if ENABLE_HUSH_LINENO_VAR | ||
2616 | if (ch == '\n') { | ||
2617 | G.lineno++; | ||
2618 | debug_printf_parse("G.lineno++ = %u\n", G.lineno); | ||
2619 | } | ||
2620 | #endif | ||
2577 | return ch; | 2621 | return ch; |
2578 | } | 2622 | } |
2579 | 2623 | ||
@@ -3374,8 +3418,13 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3374 | 3418 | ||
3375 | pin = 0; | 3419 | pin = 0; |
3376 | while (pi) { | 3420 | while (pi) { |
3377 | fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", | 3421 | fdprintf(2, "%*spipe %d %sres_word=%s followup=%d %s\n", |
3378 | pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); | 3422 | lvl*2, "", |
3423 | pin, | ||
3424 | (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""), | ||
3425 | RES[pi->res_word], | ||
3426 | pi->followup, PIPE[pi->followup] | ||
3427 | ); | ||
3379 | prn = 0; | 3428 | prn = 0; |
3380 | while (prn < pi->num_cmds) { | 3429 | while (prn < pi->num_cmds) { |
3381 | struct command *command = &pi->cmds[prn]; | 3430 | struct command *command = &pi->cmds[prn]; |
@@ -3384,6 +3433,9 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3384 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", | 3433 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", |
3385 | lvl*2, "", prn, | 3434 | lvl*2, "", prn, |
3386 | command->assignment_cnt); | 3435 | command->assignment_cnt); |
3436 | #if ENABLE_HUSH_LINENO_VAR | ||
3437 | fdprintf(2, " LINENO:%u", command->lineno); | ||
3438 | #endif | ||
3387 | if (command->group) { | 3439 | if (command->group) { |
3388 | fdprintf(2, " group %s: (argv=%p)%s%s\n", | 3440 | fdprintf(2, " group %s: (argv=%p)%s%s\n", |
3389 | CMDTYPE[command->cmd_type], | 3441 | CMDTYPE[command->cmd_type], |
@@ -3456,6 +3508,10 @@ static int done_command(struct parse_context *ctx) | |||
3456 | ctx->command = command = &pi->cmds[pi->num_cmds]; | 3508 | ctx->command = command = &pi->cmds[pi->num_cmds]; |
3457 | clear_and_ret: | 3509 | clear_and_ret: |
3458 | memset(command, 0, sizeof(*command)); | 3510 | memset(command, 0, sizeof(*command)); |
3511 | #if ENABLE_HUSH_LINENO_VAR | ||
3512 | command->lineno = G.lineno; | ||
3513 | debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); | ||
3514 | #endif | ||
3459 | return pi->num_cmds; /* used only for 0/nonzero check */ | 3515 | return pi->num_cmds; /* used only for 0/nonzero check */ |
3460 | } | 3516 | } |
3461 | 3517 | ||
@@ -3643,9 +3699,9 @@ static const struct reserved_combo* match_reserved_word(o_string *word) | |||
3643 | } | 3699 | } |
3644 | return NULL; | 3700 | return NULL; |
3645 | } | 3701 | } |
3646 | /* Return 0: not a keyword, 1: keyword | 3702 | /* Return NULL: not a keyword, else: keyword |
3647 | */ | 3703 | */ |
3648 | static int reserved_word(o_string *word, struct parse_context *ctx) | 3704 | static const struct reserved_combo* reserved_word(o_string *word, struct parse_context *ctx) |
3649 | { | 3705 | { |
3650 | # if ENABLE_HUSH_CASE | 3706 | # if ENABLE_HUSH_CASE |
3651 | static const struct reserved_combo reserved_match = { | 3707 | static const struct reserved_combo reserved_match = { |
@@ -3658,7 +3714,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3658 | return 0; | 3714 | return 0; |
3659 | r = match_reserved_word(word); | 3715 | r = match_reserved_word(word); |
3660 | if (!r) | 3716 | if (!r) |
3661 | return 0; | 3717 | return r; /* NULL */ |
3662 | 3718 | ||
3663 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 3719 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
3664 | # if ENABLE_HUSH_CASE | 3720 | # if ENABLE_HUSH_CASE |
@@ -3673,7 +3729,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3673 | ctx->ctx_res_w = RES_SNTX; | 3729 | ctx->ctx_res_w = RES_SNTX; |
3674 | } | 3730 | } |
3675 | ctx->ctx_inverted = 1; | 3731 | ctx->ctx_inverted = 1; |
3676 | return 1; | 3732 | return r; |
3677 | } | 3733 | } |
3678 | if (r->flag & FLAG_START) { | 3734 | if (r->flag & FLAG_START) { |
3679 | struct parse_context *old; | 3735 | struct parse_context *old; |
@@ -3685,7 +3741,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3685 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | 3741 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
3686 | syntax_error_at(word->data); | 3742 | syntax_error_at(word->data); |
3687 | ctx->ctx_res_w = RES_SNTX; | 3743 | ctx->ctx_res_w = RES_SNTX; |
3688 | return 1; | 3744 | return r; |
3689 | } else { | 3745 | } else { |
3690 | /* "{...} fi" is ok. "{...} if" is not | 3746 | /* "{...} fi" is ok. "{...} if" is not |
3691 | * Example: | 3747 | * Example: |
@@ -3735,7 +3791,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3735 | *ctx = *old; /* physical copy */ | 3791 | *ctx = *old; /* physical copy */ |
3736 | free(old); | 3792 | free(old); |
3737 | } | 3793 | } |
3738 | return 1; | 3794 | return r; |
3739 | } | 3795 | } |
3740 | #endif /* HAS_KEYWORDS */ | 3796 | #endif /* HAS_KEYWORDS */ |
3741 | 3797 | ||
@@ -3801,9 +3857,26 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3801 | && ctx->ctx_res_w != RES_CASE | 3857 | && ctx->ctx_res_w != RES_CASE |
3802 | # endif | 3858 | # endif |
3803 | ) { | 3859 | ) { |
3804 | int reserved = reserved_word(word, ctx); | 3860 | const struct reserved_combo *reserved; |
3805 | debug_printf_parse("checking for reserved-ness: %d\n", reserved); | 3861 | reserved = reserved_word(word, ctx); |
3862 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); | ||
3806 | if (reserved) { | 3863 | if (reserved) { |
3864 | # if ENABLE_HUSH_LINENO_VAR | ||
3865 | /* Case: | ||
3866 | * "while ...; do | ||
3867 | * cmd ..." | ||
3868 | * If we don't close the pipe _now_, immediately after "do", lineno logic | ||
3869 | * sees "cmd" as starting at "do" - i.e., at the previous line. | ||
3870 | */ | ||
3871 | if (0 | ||
3872 | IF_HUSH_IF(|| reserved->res == RES_THEN) | ||
3873 | IF_HUSH_IF(|| reserved->res == RES_ELIF) | ||
3874 | IF_HUSH_IF(|| reserved->res == RES_ELSE) | ||
3875 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) | ||
3876 | ) { | ||
3877 | done_pipe(ctx, PIPE_SEQ); | ||
3878 | } | ||
3879 | # endif | ||
3807 | o_reset_to_empty_unquoted(word); | 3880 | o_reset_to_empty_unquoted(word); |
3808 | debug_printf_parse("done_word return %d\n", | 3881 | debug_printf_parse("done_word return %d\n", |
3809 | (ctx->ctx_res_w == RES_SNTX)); | 3882 | (ctx->ctx_res_w == RES_SNTX)); |
@@ -3840,21 +3913,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3840 | word->o_assignment = MAYBE_ASSIGNMENT; | 3913 | word->o_assignment = MAYBE_ASSIGNMENT; |
3841 | } | 3914 | } |
3842 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | 3915 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); |
3843 | |||
3844 | if (word->has_quoted_part | ||
3845 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | ||
3846 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | ||
3847 | /* (otherwise it's known to be not empty and is already safe) */ | ||
3848 | ) { | ||
3849 | /* exclude "$@" - it can expand to no word despite "" */ | ||
3850 | char *p = word->data; | ||
3851 | while (p[0] == SPECIAL_VAR_SYMBOL | ||
3852 | && (p[1] & 0x7f) == '@' | ||
3853 | && p[2] == SPECIAL_VAR_SYMBOL | ||
3854 | ) { | ||
3855 | p += 3; | ||
3856 | } | ||
3857 | } | ||
3858 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | 3916 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); |
3859 | debug_print_strings("word appended to argv", command->argv); | 3917 | debug_print_strings("word appended to argv", command->argv); |
3860 | } | 3918 | } |
@@ -4503,9 +4561,10 @@ static int parse_dollar(o_string *as_string, | |||
4503 | 4561 | ||
4504 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | 4562 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); |
4505 | if (isalpha(ch)) { | 4563 | if (isalpha(ch)) { |
4564 | make_var: | ||
4506 | ch = i_getch(input); | 4565 | ch = i_getch(input); |
4507 | nommu_addchr(as_string, ch); | 4566 | nommu_addchr(as_string, ch); |
4508 | make_var: | 4567 | /*make_var1:*/ |
4509 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4568 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4510 | while (1) { | 4569 | while (1) { |
4511 | debug_printf_parse(": '%c'\n", ch); | 4570 | debug_printf_parse(": '%c'\n", ch); |
@@ -4698,19 +4757,22 @@ static int parse_dollar(o_string *as_string, | |||
4698 | } | 4757 | } |
4699 | #endif | 4758 | #endif |
4700 | case '_': | 4759 | case '_': |
4760 | goto make_var; | ||
4761 | #if 0 | ||
4762 | /* TODO: $_ and $-: */ | ||
4763 | /* $_ Shell or shell script name; or last argument of last command | ||
4764 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
4765 | * but in command's env, set to full pathname used to invoke it */ | ||
4766 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
4701 | ch = i_getch(input); | 4767 | ch = i_getch(input); |
4702 | nommu_addchr(as_string, ch); | 4768 | nommu_addchr(as_string, ch); |
4703 | ch = i_peek_and_eat_bkslash_nl(input); | 4769 | ch = i_peek_and_eat_bkslash_nl(input); |
4704 | if (isalnum(ch)) { /* it's $_name or $_123 */ | 4770 | if (isalnum(ch)) { /* it's $_name or $_123 */ |
4705 | ch = '_'; | 4771 | ch = '_'; |
4706 | goto make_var; | 4772 | goto make_var1; |
4707 | } | 4773 | } |
4708 | /* else: it's $_ */ | 4774 | /* else: it's $_ */ |
4709 | /* TODO: $_ and $-: */ | 4775 | #endif |
4710 | /* $_ Shell or shell script name; or last argument of last command | ||
4711 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
4712 | * but in command's env, set to full pathname used to invoke it */ | ||
4713 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
4714 | default: | 4776 | default: |
4715 | o_addQchr(dest, '$'); | 4777 | o_addQchr(dest, '$'); |
4716 | } | 4778 | } |
@@ -4914,7 +4976,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4914 | next = i_peek(input); | 4976 | next = i_peek(input); |
4915 | 4977 | ||
4916 | is_special = "{}<>;&|()#'" /* special outside of "str" */ | 4978 | is_special = "{}<>;&|()#'" /* special outside of "str" */ |
4917 | "\\$\"" IF_HUSH_TICK("`"); /* always special */ | 4979 | "\\$\"" IF_HUSH_TICK("`") /* always special */ |
4980 | SPECIAL_VAR_SYMBOL_STR; | ||
4918 | /* Are { and } special here? */ | 4981 | /* Are { and } special here? */ |
4919 | if (ctx.command->argv /* word [word]{... - non-special */ | 4982 | if (ctx.command->argv /* word [word]{... - non-special */ |
4920 | || dest.length /* word{... - non-special */ | 4983 | || dest.length /* word{... - non-special */ |
@@ -4948,6 +5011,22 @@ static struct pipe *parse_stream(char **pstring, | |||
4948 | } | 5011 | } |
4949 | 5012 | ||
4950 | if (is_blank) { | 5013 | if (is_blank) { |
5014 | #if ENABLE_HUSH_LINENO_VAR | ||
5015 | /* Case: | ||
5016 | * "while ...; do<whitespace><newline> | ||
5017 | * cmd ..." | ||
5018 | * would think that "cmd" starts in <whitespace> - | ||
5019 | * i.e., at the previous line. | ||
5020 | * We need to skip all whitespace before newlines. | ||
5021 | */ | ||
5022 | while (ch != '\n') { | ||
5023 | next = i_peek(input); | ||
5024 | if (next != ' ' && next != '\t' && next != '\n') | ||
5025 | break; /* next char is not ws */ | ||
5026 | ch = i_getch(input); | ||
5027 | } | ||
5028 | /* ch == last eaten whitespace char */ | ||
5029 | #endif | ||
4951 | if (done_word(&dest, &ctx)) { | 5030 | if (done_word(&dest, &ctx)) { |
4952 | goto parse_error; | 5031 | goto parse_error; |
4953 | } | 5032 | } |
@@ -5186,8 +5265,14 @@ static struct pipe *parse_stream(char **pstring, | |||
5186 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | 5265 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
5187 | 5266 | ||
5188 | switch (ch) { | 5267 | switch (ch) { |
5189 | case '#': /* non-comment #: "echo a#b" etc */ | 5268 | case SPECIAL_VAR_SYMBOL: |
5190 | o_addQchr(&dest, ch); | 5269 | /* Convert raw ^C to corresponding special variable reference */ |
5270 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5271 | o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS); | ||
5272 | /* fall through */ | ||
5273 | case '#': | ||
5274 | /* non-comment #: "echo a#b" etc */ | ||
5275 | o_addchr(&dest, ch); | ||
5191 | break; | 5276 | break; |
5192 | case '\\': | 5277 | case '\\': |
5193 | if (next == EOF) { | 5278 | if (next == EOF) { |
@@ -5229,6 +5314,11 @@ static struct pipe *parse_stream(char **pstring, | |||
5229 | nommu_addchr(&ctx.as_string, ch); | 5314 | nommu_addchr(&ctx.as_string, ch); |
5230 | if (ch == '\'') | 5315 | if (ch == '\'') |
5231 | break; | 5316 | break; |
5317 | if (ch == SPECIAL_VAR_SYMBOL) { | ||
5318 | /* Convert raw ^C to corresponding special variable reference */ | ||
5319 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5320 | o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS); | ||
5321 | } | ||
5232 | o_addqchr(&dest, ch); | 5322 | o_addqchr(&dest, ch); |
5233 | } | 5323 | } |
5234 | } | 5324 | } |
@@ -5534,7 +5624,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha | |||
5534 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) | 5624 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) |
5535 | { | 5625 | { |
5536 | #if !BASH_PATTERN_SUBST | 5626 | #if !BASH_PATTERN_SUBST |
5537 | const int do_unbackslash = 1; | 5627 | enum { do_unbackslash = 1 }; |
5538 | #endif | 5628 | #endif |
5539 | char *exp_str; | 5629 | char *exp_str; |
5540 | struct in_str input; | 5630 | struct in_str input; |
@@ -5610,6 +5700,10 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5610 | unsigned res_len = 0; | 5700 | unsigned res_len = 0; |
5611 | unsigned repl_len = strlen(repl); | 5701 | unsigned repl_len = strlen(repl); |
5612 | 5702 | ||
5703 | /* Null pattern never matches, including if "var" is empty */ | ||
5704 | if (!pattern[0]) | ||
5705 | return result; /* NULL, no replaces happened */ | ||
5706 | |||
5613 | while (1) { | 5707 | while (1) { |
5614 | int size; | 5708 | int size; |
5615 | char *s = strstr_pattern(val, pattern, &size); | 5709 | char *s = strstr_pattern(val, pattern, &size); |
@@ -5640,9 +5734,9 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5640 | */ | 5734 | */ |
5641 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) | 5735 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) |
5642 | { | 5736 | { |
5643 | const char *val = NULL; | 5737 | const char *val; |
5644 | char *to_be_freed = NULL; | 5738 | char *to_be_freed; |
5645 | char *p = *pp; | 5739 | char *p; |
5646 | char *var; | 5740 | char *var; |
5647 | char first_char; | 5741 | char first_char; |
5648 | char exp_op; | 5742 | char exp_op; |
@@ -5651,6 +5745,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5651 | char *exp_word = exp_word; /* for compiler */ | 5745 | char *exp_word = exp_word; /* for compiler */ |
5652 | char arg0; | 5746 | char arg0; |
5653 | 5747 | ||
5748 | val = NULL; | ||
5749 | to_be_freed = NULL; | ||
5750 | p = *pp; | ||
5654 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ | 5751 | *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */ |
5655 | var = arg; | 5752 | var = arg; |
5656 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | 5753 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; |
@@ -5773,8 +5870,6 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5773 | * and if // is used, it is encoded as \: | 5870 | * and if // is used, it is encoded as \: |
5774 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> | 5871 | * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> |
5775 | */ | 5872 | */ |
5776 | /* Empty variable always gives nothing: */ | ||
5777 | // "v=''; echo ${v/*/w}" prints "", not "w" | ||
5778 | if (val && val[0]) { | 5873 | if (val && val[0]) { |
5779 | /* pattern uses non-standard expansion. | 5874 | /* pattern uses non-standard expansion. |
5780 | * repl should be unbackslashed and globbed | 5875 | * repl should be unbackslashed and globbed |
@@ -5810,6 +5905,13 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5810 | val = to_be_freed; | 5905 | val = to_be_freed; |
5811 | free(pattern); | 5906 | free(pattern); |
5812 | free(repl); | 5907 | free(repl); |
5908 | } else { | ||
5909 | /* Empty variable always gives nothing */ | ||
5910 | // "v=''; echo ${v/*/w}" prints "", not "w" | ||
5911 | /* Just skip "replace" part */ | ||
5912 | *p++ = SPECIAL_VAR_SYMBOL; | ||
5913 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
5914 | *p = '\0'; | ||
5813 | } | 5915 | } |
5814 | } | 5916 | } |
5815 | #endif /* BASH_PATTERN_SUBST */ | 5917 | #endif /* BASH_PATTERN_SUBST */ |
@@ -6041,6 +6143,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6041 | arg++; | 6143 | arg++; |
6042 | cant_be_null = 0x80; | 6144 | cant_be_null = 0x80; |
6043 | break; | 6145 | break; |
6146 | case SPECIAL_VAR_QUOTED_SVS: | ||
6147 | /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */ | ||
6148 | arg++; | ||
6149 | val = SPECIAL_VAR_SYMBOL_STR; | ||
6150 | break; | ||
6044 | #if ENABLE_HUSH_TICK | 6151 | #if ENABLE_HUSH_TICK |
6045 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 6152 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
6046 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 6153 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
@@ -6199,7 +6306,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash) | |||
6199 | return (char*)list; | 6306 | return (char*)list; |
6200 | } | 6307 | } |
6201 | 6308 | ||
6202 | /* Used for "eval" builtin and case string */ | 6309 | #if ENABLE_HUSH_CASE |
6203 | static char* expand_strvec_to_string(char **argv) | 6310 | static char* expand_strvec_to_string(char **argv) |
6204 | { | 6311 | { |
6205 | char **list; | 6312 | char **list; |
@@ -6221,6 +6328,7 @@ static char* expand_strvec_to_string(char **argv) | |||
6221 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | 6328 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); |
6222 | return (char*)list; | 6329 | return (char*)list; |
6223 | } | 6330 | } |
6331 | #endif | ||
6224 | 6332 | ||
6225 | static char **expand_assignments(char **argv, int count) | 6333 | static char **expand_assignments(char **argv, int count) |
6226 | { | 6334 | { |
@@ -6513,8 +6621,17 @@ static void parse_and_run_string(const char *s) | |||
6513 | static void parse_and_run_file(FILE *f) | 6621 | static void parse_and_run_file(FILE *f) |
6514 | { | 6622 | { |
6515 | struct in_str input; | 6623 | struct in_str input; |
6624 | #if ENABLE_HUSH_LINENO_VAR | ||
6625 | unsigned sv; | ||
6626 | |||
6627 | sv = G.lineno; | ||
6628 | G.lineno = 1; | ||
6629 | #endif | ||
6516 | setup_file_in_str(&input, f); | 6630 | setup_file_in_str(&input, f); |
6517 | parse_and_run_stream(&input, ';'); | 6631 | parse_and_run_stream(&input, ';'); |
6632 | #if ENABLE_HUSH_LINENO_VAR | ||
6633 | G.lineno = sv; | ||
6634 | #endif | ||
6518 | } | 6635 | } |
6519 | 6636 | ||
6520 | #if ENABLE_HUSH_TICK | 6637 | #if ENABLE_HUSH_TICK |
@@ -7330,6 +7447,32 @@ static void dump_cmd_in_x_mode(char **argv) | |||
7330 | # define dump_cmd_in_x_mode(argv) ((void)0) | 7447 | # define dump_cmd_in_x_mode(argv) ((void)0) |
7331 | #endif | 7448 | #endif |
7332 | 7449 | ||
7450 | #if ENABLE_HUSH_COMMAND | ||
7451 | static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation) | ||
7452 | { | ||
7453 | char *to_free; | ||
7454 | |||
7455 | if (!opt_vV) | ||
7456 | return; | ||
7457 | |||
7458 | to_free = NULL; | ||
7459 | if (!explanation) { | ||
7460 | char *path = getenv("PATH"); | ||
7461 | explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */ | ||
7462 | if (!explanation) | ||
7463 | _exit(1); /* PROG was not found */ | ||
7464 | if (opt_vV != 'V') | ||
7465 | cmd = to_free; /* -v PROG prints "/path/to/PROG" */ | ||
7466 | } | ||
7467 | printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation); | ||
7468 | free(to_free); | ||
7469 | fflush_all(); | ||
7470 | _exit(0); | ||
7471 | } | ||
7472 | #else | ||
7473 | # define if_command_vV_print_and_exit(a,b,c) ((void)0) | ||
7474 | #endif | ||
7475 | |||
7333 | #if BB_MMU | 7476 | #if BB_MMU |
7334 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | 7477 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ |
7335 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | 7478 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
@@ -7350,7 +7493,11 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7350 | char **argv, int assignment_cnt, | 7493 | char **argv, int assignment_cnt, |
7351 | char **argv_expanded) | 7494 | char **argv_expanded) |
7352 | { | 7495 | { |
7496 | const struct built_in_command *x; | ||
7353 | char **new_env; | 7497 | char **new_env; |
7498 | #if ENABLE_HUSH_COMMAND | ||
7499 | char opt_vV = 0; | ||
7500 | #endif | ||
7354 | 7501 | ||
7355 | new_env = expand_assignments(argv, assignment_cnt); | 7502 | new_env = expand_assignments(argv, assignment_cnt); |
7356 | dump_cmd_in_x_mode(new_env); | 7503 | dump_cmd_in_x_mode(new_env); |
@@ -7399,21 +7546,58 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7399 | } | 7546 | } |
7400 | #endif | 7547 | #endif |
7401 | 7548 | ||
7549 | #if ENABLE_HUSH_COMMAND | ||
7550 | /* "command BAR": run BAR without looking it up among functions | ||
7551 | * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1 | ||
7552 | * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}" | ||
7553 | */ | ||
7554 | while (strcmp(argv[0], "command") == 0 && argv[1]) { | ||
7555 | char *p; | ||
7556 | |||
7557 | argv++; | ||
7558 | p = *argv; | ||
7559 | if (p[0] != '-' || !p[1]) | ||
7560 | continue; /* bash allows "command command command [-OPT] BAR" */ | ||
7561 | |||
7562 | for (;;) { | ||
7563 | p++; | ||
7564 | switch (*p) { | ||
7565 | case '\0': | ||
7566 | argv++; | ||
7567 | p = *argv; | ||
7568 | if (p[0] != '-' || !p[1]) | ||
7569 | goto after_opts; | ||
7570 | continue; /* next arg is also -opts, process it too */ | ||
7571 | case 'v': | ||
7572 | case 'V': | ||
7573 | opt_vV = *p; | ||
7574 | continue; | ||
7575 | default: | ||
7576 | bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]); | ||
7577 | } | ||
7578 | } | ||
7579 | } | ||
7580 | after_opts: | ||
7581 | # if ENABLE_HUSH_FUNCTIONS | ||
7582 | if (opt_vV && find_function(argv[0])) | ||
7583 | if_command_vV_print_and_exit(opt_vV, argv[0], "a function"); | ||
7584 | # endif | ||
7585 | #endif | ||
7586 | |||
7402 | /* Check if the command matches any of the builtins. | 7587 | /* Check if the command matches any of the builtins. |
7403 | * Depending on context, this might be redundant. But it's | 7588 | * Depending on context, this might be redundant. But it's |
7404 | * easier to waste a few CPU cycles than it is to figure out | 7589 | * easier to waste a few CPU cycles than it is to figure out |
7405 | * if this is one of those cases. | 7590 | * if this is one of those cases. |
7406 | */ | 7591 | */ |
7407 | { | 7592 | /* Why "BB_MMU ? :" difference in logic? - |
7408 | /* On NOMMU, it is more expensive to re-execute shell | 7593 | * On NOMMU, it is more expensive to re-execute shell |
7409 | * just in order to run echo or test builtin. | 7594 | * just in order to run echo or test builtin. |
7410 | * It's better to skip it here and run corresponding | 7595 | * It's better to skip it here and run corresponding |
7411 | * non-builtin later. */ | 7596 | * non-builtin later. */ |
7412 | const struct built_in_command *x; | 7597 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); |
7413 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); | 7598 | if (x) { |
7414 | if (x) { | 7599 | if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin"); |
7415 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); | 7600 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); |
7416 | } | ||
7417 | } | 7601 | } |
7418 | 7602 | ||
7419 | #if ENABLE_FEATURE_SH_STANDALONE | 7603 | #if ENABLE_FEATURE_SH_STANDALONE |
@@ -7421,6 +7605,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7421 | { | 7605 | { |
7422 | int a = find_applet_by_name(argv[0]); | 7606 | int a = find_applet_by_name(argv[0]); |
7423 | if (a >= 0) { | 7607 | if (a >= 0) { |
7608 | if_command_vV_print_and_exit(opt_vV, argv[0], "an applet"); | ||
7424 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 7609 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
7425 | if (APPLET_IS_NOEXEC(a)) { | 7610 | if (APPLET_IS_NOEXEC(a)) { |
7426 | /* Do not leak open fds from opened script files etc. | 7611 | /* Do not leak open fds from opened script files etc. |
@@ -7450,6 +7635,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7450 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU | 7635 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU |
7451 | skip: | 7636 | skip: |
7452 | #endif | 7637 | #endif |
7638 | if_command_vV_print_and_exit(opt_vV, argv[0], NULL); | ||
7453 | execvp_or_die(argv); | 7639 | execvp_or_die(argv); |
7454 | } | 7640 | } |
7455 | 7641 | ||
@@ -7992,6 +8178,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7992 | char **new_env = NULL; | 8178 | char **new_env = NULL; |
7993 | struct variable *old_vars = NULL; | 8179 | struct variable *old_vars = NULL; |
7994 | 8180 | ||
8181 | #if ENABLE_HUSH_LINENO_VAR | ||
8182 | if (G.lineno_var) | ||
8183 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
8184 | #endif | ||
8185 | |||
7995 | if (argv[command->assignment_cnt] == NULL) { | 8186 | if (argv[command->assignment_cnt] == NULL) { |
7996 | /* Assignments, but no command */ | 8187 | /* Assignments, but no command */ |
7997 | /* Ensure redirects take effect (that is, create files). | 8188 | /* Ensure redirects take effect (that is, create files). |
@@ -8139,7 +8330,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8139 | return rcode; | 8330 | return rcode; |
8140 | } | 8331 | } |
8141 | 8332 | ||
8142 | if (ENABLE_FEATURE_SH_NOFORK) { | 8333 | if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) { |
8143 | int n = find_applet_by_name(argv_expanded[0]); | 8334 | int n = find_applet_by_name(argv_expanded[0]); |
8144 | if (n >= 0 && APPLET_IS_NOFORK(n)) { | 8335 | if (n >= 0 && APPLET_IS_NOFORK(n)) { |
8145 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); | 8336 | rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded); |
@@ -8196,6 +8387,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8196 | if (cmd_no < pi->num_cmds) | 8387 | if (cmd_no < pi->num_cmds) |
8197 | xpiped_pair(pipefds); | 8388 | xpiped_pair(pipefds); |
8198 | 8389 | ||
8390 | #if ENABLE_HUSH_LINENO_VAR | ||
8391 | if (G.lineno_var) | ||
8392 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | ||
8393 | #endif | ||
8394 | |||
8199 | command->pid = BB_MMU ? fork() : vfork(); | 8395 | command->pid = BB_MMU ? fork() : vfork(); |
8200 | if (!command->pid) { /* child */ | 8396 | if (!command->pid) { /* child */ |
8201 | #if ENABLE_HUSH_JOB | 8397 | #if ENABLE_HUSH_JOB |
@@ -8387,7 +8583,10 @@ static int run_list(struct pipe *pi) | |||
8387 | rword, cond_code, last_rword); | 8583 | rword, cond_code, last_rword); |
8388 | 8584 | ||
8389 | sv_errexit_depth = G.errexit_depth; | 8585 | sv_errexit_depth = G.errexit_depth; |
8390 | if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||) | 8586 | if ( |
8587 | #if ENABLE_HUSH_IF | ||
8588 | rword == RES_IF || rword == RES_ELIF || | ||
8589 | #endif | ||
8391 | pi->followup != PIPE_SEQ | 8590 | pi->followup != PIPE_SEQ |
8392 | ) { | 8591 | ) { |
8393 | G.errexit_depth++; | 8592 | G.errexit_depth++; |
@@ -8828,17 +9027,19 @@ int hush_main(int argc, char **argv) | |||
8828 | #if !BB_MMU | 9027 | #if !BB_MMU |
8829 | G.argv0_for_re_execing = argv[0]; | 9028 | G.argv0_for_re_execing = argv[0]; |
8830 | #endif | 9029 | #endif |
9030 | |||
8831 | /* Deal with HUSH_VERSION */ | 9031 | /* Deal with HUSH_VERSION */ |
9032 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | ||
9033 | unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ | ||
8832 | shell_ver = xzalloc(sizeof(*shell_ver)); | 9034 | shell_ver = xzalloc(sizeof(*shell_ver)); |
8833 | shell_ver->flg_export = 1; | 9035 | shell_ver->flg_export = 1; |
8834 | shell_ver->flg_read_only = 1; | 9036 | shell_ver->flg_read_only = 1; |
8835 | /* Code which handles ${var<op>...} needs writable values for all variables, | 9037 | /* Code which handles ${var<op>...} needs writable values for all variables, |
8836 | * therefore we xstrdup: */ | 9038 | * therefore we xstrdup: */ |
8837 | shell_ver->varstr = xstrdup(hush_version_str); | 9039 | shell_ver->varstr = xstrdup(hush_version_str); |
9040 | |||
8838 | /* Create shell local variables from the values | 9041 | /* Create shell local variables from the values |
8839 | * currently living in the environment */ | 9042 | * currently living in the environment */ |
8840 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | ||
8841 | unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ | ||
8842 | G.top_var = shell_ver; | 9043 | G.top_var = shell_ver; |
8843 | cur_var = G.top_var; | 9044 | cur_var = G.top_var; |
8844 | e = environ; | 9045 | e = environ; |
@@ -8904,6 +9105,14 @@ int hush_main(int argc, char **argv) | |||
8904 | */ | 9105 | */ |
8905 | #endif | 9106 | #endif |
8906 | 9107 | ||
9108 | #if ENABLE_HUSH_LINENO_VAR | ||
9109 | if (ENABLE_HUSH_LINENO_VAR) { | ||
9110 | char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); | ||
9111 | set_local_var(p, /*flags*/ 0); | ||
9112 | G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ | ||
9113 | } | ||
9114 | #endif | ||
9115 | |||
8907 | #if ENABLE_FEATURE_EDITING | 9116 | #if ENABLE_FEATURE_EDITING |
8908 | G.line_input_state = new_line_input_t(FOR_SHELL); | 9117 | G.line_input_state = new_line_input_t(FOR_SHELL); |
8909 | #endif | 9118 | #endif |
@@ -9346,13 +9555,34 @@ static int FAST_FUNC builtin_eval(char **argv) | |||
9346 | int rcode = EXIT_SUCCESS; | 9555 | int rcode = EXIT_SUCCESS; |
9347 | 9556 | ||
9348 | argv = skip_dash_dash(argv); | 9557 | argv = skip_dash_dash(argv); |
9349 | if (*argv) { | 9558 | if (argv[0]) { |
9350 | char *str = expand_strvec_to_string(argv); | 9559 | char *str = NULL; |
9560 | |||
9561 | if (argv[1]) { | ||
9562 | /* "The eval utility shall construct a command by | ||
9563 | * concatenating arguments together, separating | ||
9564 | * each with a <space> character." | ||
9565 | */ | ||
9566 | char *p; | ||
9567 | unsigned len = 0; | ||
9568 | char **pp = argv; | ||
9569 | do | ||
9570 | len += strlen(*pp) + 1; | ||
9571 | while (*++pp); | ||
9572 | str = p = xmalloc(len); | ||
9573 | pp = argv; | ||
9574 | do { | ||
9575 | p = stpcpy(p, *pp); | ||
9576 | *p++ = ' '; | ||
9577 | } while (*++pp); | ||
9578 | p[-1] = '\0'; | ||
9579 | } | ||
9580 | |||
9351 | /* bash: | 9581 | /* bash: |
9352 | * eval "echo Hi; done" ("done" is syntax error): | 9582 | * eval "echo Hi; done" ("done" is syntax error): |
9353 | * "echo Hi" will not execute too. | 9583 | * "echo Hi" will not execute too. |
9354 | */ | 9584 | */ |
9355 | parse_and_run_string(str); | 9585 | parse_and_run_string(str ? str : argv[0]); |
9356 | free(str); | 9586 | free(str); |
9357 | rcode = G.last_exitcode; | 9587 | rcode = G.last_exitcode; |
9358 | } | 9588 | } |
@@ -9855,7 +10085,7 @@ static int FAST_FUNC builtin_set(char **argv) | |||
9855 | 10085 | ||
9856 | /* Nothing known, so abort */ | 10086 | /* Nothing known, so abort */ |
9857 | error: | 10087 | error: |
9858 | bb_error_msg("set: %s: invalid option", arg); | 10088 | bb_error_msg("%s: %s: invalid option", "set", arg); |
9859 | return EXIT_FAILURE; | 10089 | return EXIT_FAILURE; |
9860 | } | 10090 | } |
9861 | #endif | 10091 | #endif |
@@ -10038,6 +10268,11 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10038 | arg_path = find_in_path(filename); | 10268 | arg_path = find_in_path(filename); |
10039 | if (arg_path) | 10269 | if (arg_path) |
10040 | filename = arg_path; | 10270 | filename = arg_path; |
10271 | else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) { | ||
10272 | errno = ENOENT; | ||
10273 | bb_simple_perror_msg(filename); | ||
10274 | return EXIT_FAILURE; | ||
10275 | } | ||
10041 | } | 10276 | } |
10042 | input = remember_FILE(fopen_or_warn(filename, "r")); | 10277 | input = remember_FILE(fopen_or_warn(filename, "r")); |
10043 | free(arg_path); | 10278 | free(arg_path); |
diff --git a/shell/hush_test/hush-arith/arith-postinc.right b/shell/hush_test/hush-arith/arith-postinc.right new file mode 100644 index 000000000..c95ce02bf --- /dev/null +++ b/shell/hush_test/hush-arith/arith-postinc.right | |||
@@ -0,0 +1,5 @@ | |||
1 | 1 1 | ||
2 | 1 1 | ||
3 | 1 1 | ||
4 | 1 1 | ||
5 | Ok:0 | ||
diff --git a/shell/hush_test/hush-arith/arith-postinc.tests b/shell/hush_test/hush-arith/arith-postinc.tests new file mode 100755 index 000000000..3fd9bfed5 --- /dev/null +++ b/shell/hush_test/hush-arith/arith-postinc.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | echo 1 $((0++1)) | ||
2 | echo 1 $((0--1)) | ||
3 | x=-1; echo 1 $((0-$x)) | ||
4 | x=+1; echo 1 $((0+$x)) | ||
5 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right index 8a201fb3b..c48e468a5 100644 --- a/shell/hush_test/hush-arith/arith.right +++ b/shell/hush_test/hush-arith/arith.right | |||
@@ -135,6 +135,10 @@ hush: arithmetic syntax error | |||
135 | hush: arithmetic syntax error | 135 | hush: arithmetic syntax error |
136 | 5 5 | 136 | 5 5 |
137 | 1 1 | 137 | 1 1 |
138 | 6 6 | ||
139 | 2 2 | ||
140 | 3 3 | ||
141 | 1 1 | ||
138 | 4 4 | 142 | 4 4 |
139 | 0 0 | 143 | 0 0 |
140 | hush: arithmetic syntax error | 144 | hush: arithmetic syntax error |
diff --git a/shell/hush_test/hush-arith/arith2.sub b/shell/hush_test/hush-arith/arith2.sub index f7e3c9235..9105059db 100755 --- a/shell/hush_test/hush-arith/arith2.sub +++ b/shell/hush_test/hush-arith/arith2.sub | |||
@@ -23,14 +23,14 @@ | |||
23 | echo 5 $(( 4 + ++a )) | 23 | echo 5 $(( 4 + ++a )) |
24 | echo 1 $a | 24 | echo 1 $a |
25 | 25 | ||
26 | # ash doesn't handle it right... | 26 | # this is treated as 4 + ++a |
27 | #ash# echo 6 $(( 4+++a )) | 27 | echo 6 $(( 4+++a )) |
28 | #ash# echo 2 $a | 28 | echo 2 $a |
29 | a=2 | 29 | a=2 |
30 | 30 | ||
31 | # ash doesn't handle it right... | 31 | # this is treated as 4 - --a |
32 | #ash# echo 3 $(( 4---a )) | 32 | echo 3 $(( 4---a )) |
33 | #ash# echo 1 $a | 33 | echo 1 $a |
34 | a=1 | 34 | a=1 |
35 | 35 | ||
36 | echo 4 $(( 4 - -- a )) | 36 | echo 4 $(( 4 - -- a )) |
diff --git a/shell/hush_test/hush-misc/command2.right b/shell/hush_test/hush-misc/command2.right new file mode 100644 index 000000000..e3214f0a9 --- /dev/null +++ b/shell/hush_test/hush-misc/command2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | test1 | ||
2 | hush: can't execute './test2.sh': Permission denied | ||
diff --git a/shell/hush_test/hush-misc/command2.tests b/shell/hush_test/hush-misc/command2.tests new file mode 100755 index 000000000..9d9de9a89 --- /dev/null +++ b/shell/hush_test/hush-misc/command2.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | echo "echo test1; ./test2.sh" >test1.sh | ||
2 | echo "echo test2" >test2.sh | ||
3 | |||
4 | command . ./test1.sh | ||
5 | |||
6 | rm -f test1.sh test2.sh | ||
diff --git a/shell/hush_test/hush-misc/control_char1.right b/shell/hush_test/hush-misc/control_char1.right new file mode 100644 index 000000000..6f8c2533c --- /dev/null +++ b/shell/hush_test/hush-misc/control_char1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | |||
2 | b#c | ||
3 | Done:0 | ||
diff --git a/shell/hush_test/hush-misc/control_char1.tests b/shell/hush_test/hush-misc/control_char1.tests new file mode 100755 index 000000000..0cfe60141 --- /dev/null +++ b/shell/hush_test/hush-misc/control_char1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | echo | ||
2 | echo 'b#c' | ||
3 | echo Done:$? | ||
diff --git a/shell/hush_test/hush-misc/control_char2.right b/shell/hush_test/hush-misc/control_char2.right new file mode 100644 index 000000000..9498b420d --- /dev/null +++ b/shell/hush_test/hush-misc/control_char2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Done:0 | ||
diff --git a/shell/hush_test/hush-misc/control_char2.tests b/shell/hush_test/hush-misc/control_char2.tests new file mode 100755 index 000000000..e77d7a1a6 --- /dev/null +++ b/shell/hush_test/hush-misc/control_char2.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | c=`printf '\3'` | ||
2 | eval "echo $c" | ||
3 | echo Done:$? | ||
diff --git a/shell/hush_test/hush-misc/for_with_bslashes.right b/shell/hush_test/hush-misc/for_with_bslashes.right index 02d96692c..cd8501050 100644 --- a/shell/hush_test/hush-misc/for_with_bslashes.right +++ b/shell/hush_test/hush-misc/for_with_bslashes.right | |||
@@ -5,4 +5,5 @@ b"c | |||
5 | b'c | 5 | b'c |
6 | b$c | 6 | b$c |
7 | b`true`c | 7 | b`true`c |
8 | b#c | ||
8 | Zero:0 | 9 | Zero:0 |
diff --git a/shell/hush_test/hush-misc/for_with_bslashes.tests b/shell/hush_test/hush-misc/for_with_bslashes.tests index 363f3d85b..8acd9808a 100755 --- a/shell/hush_test/hush-misc/for_with_bslashes.tests +++ b/shell/hush_test/hush-misc/for_with_bslashes.tests | |||
@@ -1,9 +1,5 @@ | |||
1 | # UNFIXED BUG. | 1 | # last word contains ^C character. |
2 | # commented-out words contain ^C character. | 2 | for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' 'b#c' |
3 | # It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it. | ||
4 | # When it is fixed, update this test. | ||
5 | |||
6 | for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c' | ||
7 | do | 3 | do |
8 | echo $a | 4 | echo $a |
9 | done | 5 | done |
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right index 2f4c51d06..a3cb549f7 100644 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.right +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right | |||
@@ -3,6 +3,7 @@ hush: syntax error: unterminated ${name} | |||
3 | hush: syntax error: unterminated ${name} | 3 | hush: syntax error: unterminated ${name} |
4 | hush: syntax error: unterminated ${name} | 4 | hush: syntax error: unterminated ${name} |
5 | 0123456789 | 5 | 0123456789 |
6 | 0 | ||
6 | 1 =|| | 7 | 1 =|| |
7 | 1:1 =|| | 8 | 1:1 =|| |
8 | 1:1:2=|| | 9 | 1:1:2=|| |
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests index cce9f123e..512da351b 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests | |||
@@ -11,7 +11,7 @@ export var=0123456789 | |||
11 | "$THIS_SH" -c 'echo ${var:}' SHELL | 11 | "$THIS_SH" -c 'echo ${var:}' SHELL |
12 | 12 | ||
13 | # then some funky ones | 13 | # then some funky ones |
14 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | 14 | "$THIS_SH" -c 'echo ${?:0}' SHELL |
15 | 15 | ||
16 | # now some valid ones | 16 | # now some valid ones |
17 | set --; echo "1 =|${1}|" | 17 | set --; echo "1 =|${1}|" |
diff --git a/shell/hush_test/hush-vars/var_LINENO1.right b/shell/hush_test/hush-vars/var_LINENO1.right new file mode 100644 index 000000000..31e1a4478 --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO1.right | |||
@@ -0,0 +1,8 @@ | |||
1 | 2:2 | ||
2 | 3:3 | ||
3 | 4:4 | ||
4 | 5:5 | ||
5 | 2:2 | ||
6 | 3:3 | ||
7 | 4:4 | ||
8 | 5:5 | ||
diff --git a/shell/hush_test/hush-vars/var_LINENO1.tests b/shell/hush_test/hush-vars/var_LINENO1.tests new file mode 100755 index 000000000..851b52cf5 --- /dev/null +++ b/shell/hush_test/hush-vars/var_LINENO1.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | env | grep LINENO | ||
2 | echo 2:$LINENO | ||
3 | echo 3:$LINENO >&2 \ | ||
4 | | { sleep 0.1; echo 4:$LINENO; } | ||
5 | echo 5:$LINENO | ||
6 | test "$1" || . ./var_LINENO1.tests norepeat | ||
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.right b/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.right new file mode 100644 index 000000000..d400a7e31 --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.right | |||
@@ -0,0 +1,2 @@ | |||
1 | v | ||
2 | Ok:0 | ||
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.tests b/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.tests new file mode 100755 index 000000000..6e8aa2afa --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_repl_empty_pattern.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | v=v | ||
2 | echo ${v//} | ||
3 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_var.right b/shell/hush_test/hush-vars/var_bash_repl_empty_var.right new file mode 100644 index 000000000..892916783 --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_repl_empty_var.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Ok:0 | ||
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests b/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests new file mode 100755 index 000000000..73a43d38e --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | v='' | ||
2 | echo ${v/*/w} | ||
3 | echo Ok:$? | ||
diff --git a/shell/math.c b/shell/math.c index f01f24362..611b3beab 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -598,10 +598,24 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
598 | } | 598 | } |
599 | 599 | ||
600 | /* Should be an operator */ | 600 | /* Should be an operator */ |
601 | |||
602 | /* Special case: NUM-- and NUM++ are not recognized if NUM | ||
603 | * is a literal number, not a variable. IOW: | ||
604 | * "a+++v" is a++ + v. | ||
605 | * "7+++v" is 7 + ++v, not 7++ + v. | ||
606 | */ | ||
607 | if (lasttok == TOK_NUM && !numstackptr[-1].var /* number literal */ | ||
608 | && (expr[0] == '+' || expr[0] == '-') | ||
609 | && (expr[1] == expr[0]) | ||
610 | ) { | ||
611 | //bb_error_msg("special %c%c", expr[0], expr[0]); | ||
612 | op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); | ||
613 | expr += 1; | ||
614 | goto tok_found1; | ||
615 | } | ||
616 | |||
601 | p = op_tokens; | 617 | p = op_tokens; |
602 | while (1) { | 618 | while (1) { |
603 | // TODO: bash allows 7+++v, treats it as 7 + ++v | ||
604 | // we treat it as 7++ + v and reject | ||
605 | /* Compare expr to current op_tokens[] element */ | 619 | /* Compare expr to current op_tokens[] element */ |
606 | const char *e = expr; | 620 | const char *e = expr; |
607 | while (1) { | 621 | while (1) { |
@@ -627,6 +641,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
627 | } | 641 | } |
628 | tok_found: | 642 | tok_found: |
629 | op = p[1]; /* fetch TOK_foo value */ | 643 | op = p[1]; /* fetch TOK_foo value */ |
644 | tok_found1: | ||
630 | /* NB: expr now points past the operator */ | 645 | /* NB: expr now points past the operator */ |
631 | 646 | ||
632 | /* post grammar: a++ reduce to num */ | 647 | /* post grammar: a++ reduce to num */ |