diff options
| author | Ron Yorston <rmy@pobox.com> | 2025-09-06 14:52:34 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2025-09-06 14:52:34 +0100 |
| commit | 01fbdbf4b48cedf0869e010ef5ccb817d6677e42 (patch) | |
| tree | c7a7819b76621b206d3d3aaf0b7ceb08d02ca853 /shell | |
| parent | d8086da8bfbf76b9910d04e3e7f646ebc7f4b593 (diff) | |
| parent | 53b3854e8141f4fc5fad10f180fc4fac2feee954 (diff) | |
| download | busybox-w32-01fbdbf4b48cedf0869e010ef5ccb817d6677e42.tar.gz busybox-w32-01fbdbf4b48cedf0869e010ef5ccb817d6677e42.tar.bz2 busybox-w32-01fbdbf4b48cedf0869e010ef5ccb817d6677e42.zip | |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
18 files changed, 290 insertions, 75 deletions
diff --git a/shell/ash.c b/shell/ash.c index 605215e41..656f3d73b 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -1078,28 +1078,47 @@ out2str(const char *p) | |||
| 1078 | # define CTL_LAST CTLFROMPROC | 1078 | # define CTL_LAST CTLFROMPROC |
| 1079 | #endif | 1079 | #endif |
| 1080 | 1080 | ||
| 1081 | /* variable substitution byte (follows CTLVAR) */ | 1081 | /* ${VAR[ops]} encoding is CTLVAR,<type_byte>,"VARNAME=",<ops_encoded(details?)>,CTLENDVAR */ |
| 1082 | #define VSTYPE 0x0f /* type of variable substitution */ | 1082 | /* variable type byte (follows CTLVAR) */ |
| 1083 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | 1083 | #define VSTYPE 0x0f /* type of variable substitution */ |
| 1084 | 1084 | #define VSNUL 0x10 /* colon: the op is one of :- :+ :? := */ | |
| 1085 | /* values of VSTYPE field */ | 1085 | /* values of VSTYPE field. The first 5 must be in this order, "}-+?=" string is used elsewhere to index into them */ |
| 1086 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | 1086 | #define VSNORMAL 0x1 /* $var or ${var} */ |
| 1087 | #define VSMINUS 0x2 /* ${var-text} */ | 1087 | #define VSMINUS 0x2 /* ${var[:]-text} */ |
| 1088 | #define VSPLUS 0x3 /* ${var+text} */ | 1088 | #define VSPLUS 0x3 /* ${var[:]+text} */ |
| 1089 | #define VSQUESTION 0x4 /* ${var?message} */ | 1089 | #define VSQUESTION 0x4 /* ${var[:]?message} */ |
| 1090 | #define VSASSIGN 0x5 /* ${var=text} */ | 1090 | #define VSASSIGN 0x5 /* ${var[:]=text} */ |
| 1091 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ | 1091 | #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ |
| 1092 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ | 1092 | #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ |
| 1093 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ | 1093 | #define VSTRIMLEFT 0x8 /* ${var#pattern} */ |
| 1094 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ | 1094 | #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ |
| 1095 | #define VSLENGTH 0xa /* ${#var} */ | 1095 | #define VSLENGTH 0xa /* ${#var} */ |
| 1096 | #if BASH_SUBSTR | 1096 | #if BASH_SUBSTR |
| 1097 | #define VSSUBSTR 0xc /* ${var:position:length} */ | 1097 | #define VSSUBSTR 0xb /* ${var:position:length} */ |
| 1098 | #endif | 1098 | #endif |
| 1099 | #if BASH_PATTERN_SUBST | 1099 | #if BASH_PATTERN_SUBST |
| 1100 | #define VSREPLACE 0xd /* ${var/pattern/replacement} */ | 1100 | #define VSREPLACE 0xc /* ${var/pattern/replacement} */ |
| 1101 | #define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ | 1101 | #define VSREPLACEALL 0xd /* ${var//pattern/replacement} */ |
| 1102 | #endif | ||
| 1103 | static const char vstype_suffix[][3] ALIGN1 = { | ||
| 1104 | [VSNORMAL - VSNORMAL] = "}", // $var or ${var} | ||
| 1105 | [VSMINUS - VSNORMAL] = "-", // ${var-text} | ||
| 1106 | [VSPLUS - VSNORMAL] = "+", // ${var+text} | ||
| 1107 | [VSQUESTION - VSNORMAL] = "?", // ${var?message} | ||
| 1108 | [VSASSIGN - VSNORMAL] = "=", // ${var=text} | ||
| 1109 | [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern} | ||
| 1110 | [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern} | ||
| 1111 | [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} | ||
| 1112 | [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} | ||
| 1113 | [VSLENGTH - VSNORMAL] = "", // ${#var} | ||
| 1114 | #if BASH_SUBSTR | ||
| 1115 | [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} | ||
| 1116 | #endif | ||
| 1117 | #if BASH_PATTERN_SUBST | ||
| 1118 | [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} | ||
| 1119 | [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} | ||
| 1102 | #endif | 1120 | #endif |
| 1121 | }; | ||
| 1103 | 1122 | ||
| 1104 | static const char dolatstr[] ALIGN1 = { | 1123 | static const char dolatstr[] ALIGN1 = { |
| 1105 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' | 1124 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' |
| @@ -1473,7 +1492,9 @@ sharg(union node *arg, FILE *fp) | |||
| 1473 | 1492 | ||
| 1474 | if (subtype & VSNUL) | 1493 | if (subtype & VSNUL) |
| 1475 | putc(':', fp); | 1494 | putc(':', fp); |
| 1476 | 1495 | #if 1 | |
| 1496 | fputs(vstype_suffix[(subtype & VSTYPE) - VSNORMAL], fp); | ||
| 1497 | #else | ||
| 1477 | switch (subtype & VSTYPE) { | 1498 | switch (subtype & VSTYPE) { |
| 1478 | case VSNORMAL: | 1499 | case VSNORMAL: |
| 1479 | putc('}', fp); | 1500 | putc('}', fp); |
| @@ -1509,6 +1530,7 @@ sharg(union node *arg, FILE *fp) | |||
| 1509 | default: | 1530 | default: |
| 1510 | out1fmt("<subtype %d>", subtype); | 1531 | out1fmt("<subtype %d>", subtype); |
| 1511 | } | 1532 | } |
| 1533 | #endif | ||
| 1512 | break; | 1534 | break; |
| 1513 | case CTLENDVAR: | 1535 | case CTLENDVAR: |
| 1514 | putc('}', fp); | 1536 | putc('}', fp); |
| @@ -1649,13 +1671,14 @@ showtree(union node *n) | |||
| 1649 | static void | 1671 | static void |
| 1650 | ash_vmsg(const char *msg, va_list ap) | 1672 | ash_vmsg(const char *msg, va_list ap) |
| 1651 | { | 1673 | { |
| 1674 | //In dash, the order/format is different: | ||
| 1675 | // arg0: LINENO: [commandname:] MSG | ||
| 1676 | //If you fix it, change testsuite to match | ||
| 1652 | fprintf(stderr, "%s: ", arg0); | 1677 | fprintf(stderr, "%s: ", arg0); |
| 1653 | if (commandname) { | 1678 | if (commandname && strcmp(arg0, commandname) != 0) |
| 1654 | if (strcmp(arg0, commandname)) | 1679 | fprintf(stderr, "%s: ", commandname); |
| 1655 | fprintf(stderr, "%s: ", commandname); | 1680 | if (!iflag || g_parsefile->pf_fd > 0) |
| 1656 | if (!iflag || g_parsefile->pf_fd > 0) | 1681 | fprintf(stderr, "line %d: ", errlinno); |
| 1657 | fprintf(stderr, "line %d: ", errlinno); | ||
| 1658 | } | ||
| 1659 | vfprintf(stderr, msg, ap); | 1682 | vfprintf(stderr, msg, ap); |
| 1660 | newline_and_flush(stderr); | 1683 | newline_and_flush(stderr); |
| 1661 | } | 1684 | } |
| @@ -4224,7 +4247,7 @@ struct job { | |||
| 4224 | struct job *prev_job; /* previous job */ | 4247 | struct job *prev_job; /* previous job */ |
| 4225 | }; | 4248 | }; |
| 4226 | 4249 | ||
| 4227 | static struct job *makejob(/*union node *,*/ int); | 4250 | static struct job *makejob(int); |
| 4228 | #if !ENABLE_PLATFORM_MINGW32 | 4251 | #if !ENABLE_PLATFORM_MINGW32 |
| 4229 | static int forkshell(struct job *, union node *, int); | 4252 | static int forkshell(struct job *, union node *, int); |
| 4230 | #endif | 4253 | #endif |
| @@ -5475,7 +5498,7 @@ growjobtab(void) | |||
| 5475 | * Called with interrupts off. | 5498 | * Called with interrupts off. |
| 5476 | */ | 5499 | */ |
| 5477 | static struct job * | 5500 | static struct job * |
| 5478 | makejob(/*union node *node,*/ int nprocs) | 5501 | makejob(int nprocs) |
| 5479 | { | 5502 | { |
| 5480 | int i; | 5503 | int i; |
| 5481 | struct job *jp; | 5504 | struct job *jp; |
| @@ -5525,13 +5548,6 @@ static char *cmdnextc; | |||
| 5525 | static void | 5548 | static void |
| 5526 | cmdputs(const char *s) | 5549 | cmdputs(const char *s) |
| 5527 | { | 5550 | { |
| 5528 | static const char vstype[VSTYPE + 1][3] ALIGN1 = { | ||
| 5529 | "", "}", "-", "+", "?", "=", | ||
| 5530 | "%", "%%", "#", "##" | ||
| 5531 | IF_BASH_SUBSTR(, ":") | ||
| 5532 | IF_BASH_PATTERN_SUBST(, "/", "//") | ||
| 5533 | }; | ||
| 5534 | |||
| 5535 | const char *p, *str; | 5551 | const char *p, *str; |
| 5536 | char cc[2]; | 5552 | char cc[2]; |
| 5537 | char *nextc; | 5553 | char *nextc; |
| @@ -5594,32 +5610,34 @@ cmdputs(const char *s) | |||
| 5594 | case '=': | 5610 | case '=': |
| 5595 | if (subtype == 0) | 5611 | if (subtype == 0) |
| 5596 | break; | 5612 | break; |
| 5613 | /* We are in variable name */ | ||
| 5597 | if ((subtype & VSTYPE) != VSNORMAL) | 5614 | if ((subtype & VSTYPE) != VSNORMAL) |
| 5598 | quoted <<= 1; | 5615 | quoted <<= 1; |
| 5599 | str = vstype[subtype & VSTYPE]; | 5616 | str = vstype_suffix[(subtype & VSTYPE) - VSNORMAL]; |
| 5600 | if (subtype & VSNUL) | 5617 | if (!(subtype & VSNUL)) |
| 5601 | c = ':'; | 5618 | goto dostr; |
| 5602 | else | 5619 | c = ':'; |
| 5603 | goto checkstr; | ||
| 5604 | break; | 5620 | break; |
| 5605 | case '\'': | 5621 | case '$': |
| 5622 | /* Can happen inside quotes, or in variable name $$ */ | ||
| 5623 | if (subtype != 0) | ||
| 5624 | // Testcase: | ||
| 5625 | // $ true $$ & | ||
| 5626 | // $ <cr> | ||
| 5627 | // [1]+ Done true ${$} // shows ${\$} without "if (subtype)" check | ||
| 5628 | break; | ||
| 5629 | /* Not in variable name - show as \$ */ | ||
| 5630 | case '\'': /* These can only happen inside quotes */ | ||
| 5606 | case '\\': | 5631 | case '\\': |
| 5607 | case '"': | 5632 | case '"': |
| 5608 | case '$': | ||
| 5609 | /* These can only happen inside quotes */ | ||
| 5610 | cc[0] = c; | 5633 | cc[0] = c; |
| 5611 | str = cc; | 5634 | str = cc; |
| 5612 | //FIXME: | ||
| 5613 | // $ true $$ & | ||
| 5614 | // $ <cr> | ||
| 5615 | // [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok) | ||
| 5616 | c = '\\'; | 5635 | c = '\\'; |
| 5617 | break; | 5636 | break; |
| 5618 | default: | 5637 | default: |
| 5619 | break; | 5638 | break; |
| 5620 | } | 5639 | } |
| 5621 | USTPUTC(c, nextc); | 5640 | USTPUTC(c, nextc); |
| 5622 | checkstr: | ||
| 5623 | if (!str) | 5641 | if (!str) |
| 5624 | continue; | 5642 | continue; |
| 5625 | dostr: | 5643 | dostr: |
| @@ -5631,7 +5649,7 @@ cmdputs(const char *s) | |||
| 5631 | if (quoted & 1) { | 5649 | if (quoted & 1) { |
| 5632 | USTPUTC('"', nextc); | 5650 | USTPUTC('"', nextc); |
| 5633 | } | 5651 | } |
| 5634 | *nextc = 0; | 5652 | *nextc = '\0'; |
| 5635 | cmdnextc = nextc; | 5653 | cmdnextc = nextc; |
| 5636 | } | 5654 | } |
| 5637 | 5655 | ||
| @@ -7392,7 +7410,7 @@ evalbackcmd(union node *n, struct backcmd *result | |||
| 7392 | if (pipe(pip) < 0) | 7410 | if (pipe(pip) < 0) |
| 7393 | ash_msg_and_raise_perror("can't create pipe"); | 7411 | ash_msg_and_raise_perror("can't create pipe"); |
| 7394 | /* process substitution uses NULL job, like openhere() */ | 7412 | /* process substitution uses NULL job, like openhere() */ |
| 7395 | jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; | 7413 | jp = (ctl == CTLBACKQ) ? makejob(1) : NULL; |
| 7396 | #if ENABLE_PLATFORM_MINGW32 | 7414 | #if ENABLE_PLATFORM_MINGW32 |
| 7397 | memset(&fs, 0, sizeof(fs)); | 7415 | memset(&fs, 0, sizeof(fs)); |
| 7398 | fs.fpid = FS_EVALBACKCMD; | 7416 | fs.fpid = FS_EVALBACKCMD; |
| @@ -10462,7 +10480,7 @@ evaltree(union node *n, int flags) | |||
| 10462 | #endif | 10480 | #endif |
| 10463 | case NNOT: | 10481 | case NNOT: |
| 10464 | status = !evaltree(n->nnot.com, EV_TESTED); | 10482 | status = !evaltree(n->nnot.com, EV_TESTED); |
| 10465 | goto setstatus; | 10483 | break; |
| 10466 | case NREDIR: | 10484 | case NREDIR: |
| 10467 | errlinno = lineno = n->nredir.linno; | 10485 | errlinno = lineno = n->nredir.linno; |
| 10468 | expredir(n->nredir.redirect); | 10486 | expredir(n->nredir.redirect); |
| @@ -10473,7 +10491,7 @@ evaltree(union node *n, int flags) | |||
| 10473 | } | 10491 | } |
| 10474 | if (n->nredir.redirect) | 10492 | if (n->nredir.redirect) |
| 10475 | popredir(/*drop:*/ 0); | 10493 | popredir(/*drop:*/ 0); |
| 10476 | goto setstatus; | 10494 | break; |
| 10477 | case NCMD: | 10495 | case NCMD: |
| 10478 | evalfn = evalcommand; | 10496 | evalfn = evalcommand; |
| 10479 | checkexit: | 10497 | checkexit: |
| @@ -10517,7 +10535,7 @@ evaltree(union node *n, int flags) | |||
| 10517 | evalfn = evaltree; | 10535 | evalfn = evaltree; |
| 10518 | calleval: | 10536 | calleval: |
| 10519 | status = evalfn(n, flags); | 10537 | status = evalfn(n, flags); |
| 10520 | goto setstatus; | 10538 | break; |
| 10521 | } | 10539 | } |
| 10522 | case NIF: | 10540 | case NIF: |
| 10523 | status = evaltree(n->nif.test, EV_TESTED); | 10541 | status = evaltree(n->nif.test, EV_TESTED); |
| @@ -10531,17 +10549,18 @@ evaltree(union node *n, int flags) | |||
| 10531 | goto evaln; | 10549 | goto evaln; |
| 10532 | } | 10550 | } |
| 10533 | status = 0; | 10551 | status = 0; |
| 10534 | goto setstatus; | 10552 | break; |
| 10535 | case NDEFUN: | 10553 | case NDEFUN: |
| 10536 | defun(n); | 10554 | defun(n); |
| 10537 | /* Not necessary. To test it: | 10555 | /* Not necessary. To test it: |
| 10538 | * "false; f() { qwerty; }; echo $?" should print 0. | 10556 | * "false; f() { qwerty; }; echo $?" should print 0. |
| 10539 | */ | 10557 | */ |
| 10540 | /* status = 0; */ | 10558 | /* status = 0; */ |
| 10541 | setstatus: | ||
| 10542 | exitstatus = status; | ||
| 10543 | break; | 10559 | break; |
| 10544 | } | 10560 | } |
| 10561 | |||
| 10562 | exitstatus = status; | ||
| 10563 | |||
| 10545 | out: | 10564 | out: |
| 10546 | /* Order of checks below is important: | 10565 | /* Order of checks below is important: |
| 10547 | * signal handlers trigger before exit caused by "set -e". | 10566 | * signal handlers trigger before exit caused by "set -e". |
| @@ -10723,7 +10742,7 @@ evalsubshell(union node *n, int flags) | |||
| 10723 | INT_OFF; | 10742 | INT_OFF; |
| 10724 | if (backgnd == FORK_FG) | 10743 | if (backgnd == FORK_FG) |
| 10725 | get_tty_state(); | 10744 | get_tty_state(); |
| 10726 | jp = makejob(/*n,*/ 1); | 10745 | jp = makejob(1); |
| 10727 | #if ENABLE_PLATFORM_MINGW32 | 10746 | #if ENABLE_PLATFORM_MINGW32 |
| 10728 | memset(&fs, 0, sizeof(fs)); | 10747 | memset(&fs, 0, sizeof(fs)); |
| 10729 | fs.fpid = FS_EVALSUBSHELL; | 10748 | fs.fpid = FS_EVALSUBSHELL; |
| @@ -10839,7 +10858,7 @@ evalpipe(union node *n, int flags) | |||
| 10839 | INT_OFF; | 10858 | INT_OFF; |
| 10840 | if (n->npipe.pipe_backgnd == 0) | 10859 | if (n->npipe.pipe_backgnd == 0) |
| 10841 | get_tty_state(); | 10860 | get_tty_state(); |
| 10842 | jp = makejob(/*n,*/ pipelen); | 10861 | jp = makejob(pipelen); |
| 10843 | prevfd = -1; | 10862 | prevfd = -1; |
| 10844 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) { | 10863 | for (lp = n->npipe.cmdlist; lp; lp = lp->next) { |
| 10845 | prehash(lp->n); | 10864 | prehash(lp->n); |
| @@ -11822,7 +11841,7 @@ evalcommand(union node *cmd, int flags) | |||
| 11822 | /* No, forking off a child is necessary */ | 11841 | /* No, forking off a child is necessary */ |
| 11823 | INT_OFF; | 11842 | INT_OFF; |
| 11824 | get_tty_state(); | 11843 | get_tty_state(); |
| 11825 | jp = makejob(/*cmd,*/ 1); | 11844 | jp = makejob(1); |
| 11826 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 11845 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
| 11827 | /* parent */ | 11846 | /* parent */ |
| 11828 | break; | 11847 | break; |
| @@ -13779,12 +13798,12 @@ decode_dollar_squote(void) | |||
| 13779 | #endif | 13798 | #endif |
| 13780 | 13799 | ||
| 13781 | /* Used by expandstr to get here-doc like behaviour. */ | 13800 | /* Used by expandstr to get here-doc like behaviour. */ |
| 13782 | #define FAKEEOFMARK ((char*)(uintptr_t)1) | 13801 | #define FAKEEOFMARK ((struct heredoc*)(uintptr_t)1) |
| 13783 | 13802 | ||
| 13784 | static ALWAYS_INLINE int | 13803 | static ALWAYS_INLINE int |
| 13785 | realeofmark(const char *eofmark) | 13804 | realeofmark(struct heredoc *here) |
| 13786 | { | 13805 | { |
| 13787 | return eofmark && eofmark != FAKEEOFMARK; | 13806 | return here && here != FAKEEOFMARK; |
| 13788 | } | 13807 | } |
| 13789 | 13808 | ||
| 13790 | /* | 13809 | /* |
| @@ -13806,7 +13825,7 @@ realeofmark(const char *eofmark) | |||
| 13806 | #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} | 13825 | #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} |
| 13807 | #define PARSEARITH() {goto parsearith; parsearith_return:;} | 13826 | #define PARSEARITH() {goto parsearith; parsearith_return:;} |
| 13808 | static int | 13827 | static int |
| 13809 | readtoken1(int c, int syntax, char *eofmark, int striptabs) | 13828 | readtoken1(int c, int syntax, struct heredoc *eofmark) |
| 13810 | { | 13829 | { |
| 13811 | /* NB: syntax parameter fits into smallint */ | 13830 | /* NB: syntax parameter fits into smallint */ |
| 13812 | /* c parameter is an unsigned char or PEOF */ | 13831 | /* c parameter is an unsigned char or PEOF */ |
| @@ -14058,23 +14077,30 @@ checkend: { | |||
| 14058 | int markloc; | 14077 | int markloc; |
| 14059 | char *p; | 14078 | char *p; |
| 14060 | 14079 | ||
| 14061 | if (striptabs) { | 14080 | if (eofmark->striptabs) { |
| 14062 | while (c == '\t') | 14081 | while (c == '\t') |
| 14063 | c = pgetc(); | 14082 | if (eofmark->here->type == NHERE) |
| 14083 | c = pgetc(); /* dash always does pgetc() */ | ||
| 14084 | else /* NXHERE */ | ||
| 14085 | c = pgetc_eatbnl(); | ||
| 14086 | /* (see heredoc_bkslash_newline3a.tests) */ | ||
| 14064 | } | 14087 | } |
| 14065 | 14088 | ||
| 14066 | markloc = out - (char *)stackblock(); | 14089 | markloc = out - (char *)stackblock(); |
| 14067 | for (p = eofmark; STPUTC(c, out), *p; p++) { | 14090 | for (p = eofmark->eofmark; STPUTC(c, out), *p; p++) { |
| 14068 | if (c != *p) | 14091 | if (c != *p) |
| 14069 | goto more_heredoc; | 14092 | goto more_heredoc; |
| 14070 | /* FIXME: fails for backslash-newlined terminator: | 14093 | /* dash still has this not fixed (as of 2025-08) |
| 14071 | * cat <<EOF | 14094 | * cat <<EOF |
| 14072 | * ... | 14095 | * ... |
| 14073 | * EO\ | 14096 | * EO\ |
| 14074 | * F | 14097 | * F |
| 14075 | * (see heredoc_bkslash_newline2.tests) | 14098 | * (see heredoc_bkslash_newline2.tests) |
| 14076 | */ | 14099 | */ |
| 14077 | c = pgetc(); | 14100 | if (eofmark->here->type == NHERE) |
| 14101 | c = pgetc(); /* dash always does pgetc() */ | ||
| 14102 | else /* NXHERE */ | ||
| 14103 | c = pgetc_eatbnl(); | ||
| 14078 | } | 14104 | } |
| 14079 | 14105 | ||
| 14080 | if (c == '\n' || c == PEOF) { | 14106 | if (c == '\n' || c == PEOF) { |
| @@ -14084,7 +14110,6 @@ checkend: { | |||
| 14084 | needprompt = doprompt; | 14110 | needprompt = doprompt; |
| 14085 | } else { | 14111 | } else { |
| 14086 | int len_here; | 14112 | int len_here; |
| 14087 | |||
| 14088 | more_heredoc: | 14113 | more_heredoc: |
| 14089 | p = (char *)stackblock() + markloc + 1; | 14114 | p = (char *)stackblock() + markloc + 1; |
| 14090 | len_here = out - p; | 14115 | len_here = out - p; |
| @@ -14593,7 +14618,7 @@ xxreadtoken(void) | |||
| 14593 | } | 14618 | } |
| 14594 | } /* for (;;) */ | 14619 | } /* for (;;) */ |
| 14595 | 14620 | ||
| 14596 | return readtoken1(c, BASESYNTAX, (char *) NULL, 0); | 14621 | return readtoken1(c, BASESYNTAX, NULL); |
| 14597 | } | 14622 | } |
| 14598 | #else /* old xxreadtoken */ | 14623 | #else /* old xxreadtoken */ |
| 14599 | #define RETURN(token) return lasttoken = token | 14624 | #define RETURN(token) return lasttoken = token |
| @@ -14644,7 +14669,7 @@ xxreadtoken(void) | |||
| 14644 | } | 14669 | } |
| 14645 | break; | 14670 | break; |
| 14646 | } | 14671 | } |
| 14647 | return readtoken1(c, BASESYNTAX, (char *)NULL, 0); | 14672 | return readtoken1(c, BASESYNTAX, NULL); |
| 14648 | #undef RETURN | 14673 | #undef RETURN |
| 14649 | } | 14674 | } |
| 14650 | #endif /* old xxreadtoken */ | 14675 | #endif /* old xxreadtoken */ |
| @@ -14750,9 +14775,9 @@ parseheredoc(void) | |||
| 14750 | tokpushback = 0; | 14775 | tokpushback = 0; |
| 14751 | setprompt_if(needprompt, 2); | 14776 | setprompt_if(needprompt, 2); |
| 14752 | if (here->here->type == NHERE) | 14777 | if (here->here->type == NHERE) |
| 14753 | readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs); | 14778 | readtoken1(pgetc(), SQSYNTAX, here); |
| 14754 | else | 14779 | else |
| 14755 | readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs); | 14780 | readtoken1(pgetc_eatbnl(), DQSYNTAX, here); |
| 14756 | n = stzalloc(sizeof(struct narg)); | 14781 | n = stzalloc(sizeof(struct narg)); |
| 14757 | n->narg.type = NARG; | 14782 | n->narg.type = NARG; |
| 14758 | /*n->narg.next = NULL; - stzalloc did it */ | 14783 | /*n->narg.next = NULL; - stzalloc did it */ |
| @@ -14795,8 +14820,7 @@ expandstr(const char *ps, int syntax_type) | |||
| 14795 | * PS1='$(date "+%H:%M:%S) > ' | 14820 | * PS1='$(date "+%H:%M:%S) > ' |
| 14796 | */ | 14821 | */ |
| 14797 | exception_handler = &jmploc; | 14822 | exception_handler = &jmploc; |
| 14798 | readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0); | 14823 | readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK); |
| 14799 | |||
| 14800 | n.narg.type = NARG; | 14824 | n.narg.type = NARG; |
| 14801 | n.narg.next = NULL; | 14825 | n.narg.next = NULL; |
| 14802 | n.narg.text = wordtext; | 14826 | n.narg.text = wordtext; |
| @@ -16179,7 +16203,7 @@ procargs(char **argv) | |||
| 16179 | #if ENABLE_PLATFORM_MINGW32 | 16203 | #if ENABLE_PLATFORM_MINGW32 |
| 16180 | login_sh = applet_name[0] == 'l'; | 16204 | login_sh = applet_name[0] == 'l'; |
| 16181 | #else | 16205 | #else |
| 16182 | login_sh = xargv[0] && xargv[0][0] == '-'; | 16206 | login_sh = /*xargv[0] &&*/ xargv[0][0] == '-'; |
| 16183 | #endif | 16207 | #endif |
| 16184 | #if NUM_SCRIPTS > 0 | 16208 | #if NUM_SCRIPTS > 0 |
| 16185 | if (minusc) | 16209 | if (minusc) |
| @@ -16231,7 +16255,6 @@ procargs(char **argv) | |||
| 16231 | #endif | 16255 | #endif |
| 16232 | setarg0: | 16256 | setarg0: |
| 16233 | arg0 = *xargv++; | 16257 | arg0 = *xargv++; |
| 16234 | commandname = arg0; | ||
| 16235 | } | 16258 | } |
| 16236 | 16259 | ||
| 16237 | shellparam.p = xargv; | 16260 | shellparam.p = xargv; |
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests new file mode 100755 index 000000000..eb2223031 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | EO\ | ||
| 4 | F | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests new file mode 100755 index 000000000..de21132d1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests new file mode 100755 index 000000000..da3860804 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right new file mode 100644 index 000000000..f384da1f5 --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.right | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | a is '\*bc' | ||
| 2 | b is '\' | ||
| 3 | c is '*' | ||
| 4 | ${a##?*} removes everything: || - matches one char, then all | ||
| 5 | ${a##?"*"} removes \*: |bc| - matches one char, then * | ||
| 6 | ${a##\*} removes nothing: |\*bc| - first char is not * | ||
| 7 | ${a##\\*} removes everything: || - matches \, then all | ||
| 8 | ${a##\\\*} removes \*: |bc| - matches \, then * | ||
| 9 | ${a##?$c} removes everything: || - matches one char, then all | ||
| 10 | ${a##?"$c"} removes \*: |bc| - matches one char, then * | ||
| 11 | ${a##\\$c} removes everything: || - matches \, then all | ||
| 12 | ${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 13 | ${a##\\"$c"} removes \*: |bc| - matches \, then * | ||
| 14 | ${a##$b} removes \: |*bc| - matches \ | ||
| 15 | ${a##"$b"} removes \: |*bc| - matches \ | ||
| 16 | |||
| 17 | ${a##"$b"?} removes \*: |bc| - matches \, then one char | ||
| 18 | ${a##"$b"*} removes everything: || - matches \, then all | ||
| 19 | ${a##"$b""?"} removes nothing: |\*bc| - second char is not ? | ||
| 20 | ${a##"$b""*"} removes \*: |bc| - matches \, then * | ||
| 21 | ${a##"$b"\*} removes \*: |bc| - matches \, then * | ||
| 22 | ${a##"$b"$c} removes everything:|| - matches \, then all | ||
| 23 | ${a##"$b""$c"} removes \*: |bc| - matches \, then * | ||
| 24 | ${a##"$b?"} removes nothing: |\*bc| - second char is not ? | ||
| 25 | ${a##"$b*"} removes \*: |bc| - matches \, then * | ||
| 26 | ${a##"$b$c"} removes \*: |bc| - matches \, then * | ||
diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests new file mode 100755 index 000000000..7eea3cc19 --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.tests | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | a='\*bc' | ||
| 2 | b='\' | ||
| 3 | c='*' | ||
| 4 | echo "a is '$a'" | ||
| 5 | echo "b is '$b'" | ||
| 6 | echo "c is '$c'" | ||
| 7 | echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' | ||
| 8 | echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' | ||
| 9 | echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' | ||
| 10 | echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' | ||
| 11 | echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' | ||
| 12 | echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' | ||
| 13 | echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' | ||
| 14 | echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' | ||
| 15 | echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' | ||
| 16 | echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' | ||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | ||
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | ||
| 19 | echo | ||
| 20 | ## In bash, this isn't working as expected | ||
| 21 | #echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| | ||
| 22 | #echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| | ||
| 23 | #echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| | ||
| 24 | #echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| | ||
| 25 | ## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: | ||
| 26 | ## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) | ||
| 27 | ## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| | ||
| 28 | #echo | ||
| 29 | echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' | ||
| 30 | echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' | ||
| 31 | echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| | ||
| 32 | echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' | ||
| 33 | echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' | ||
| 34 | echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' | ||
| 35 | echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' | ||
| 36 | echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| | ||
| 37 | echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || | ||
| 38 | echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' | ||
diff --git a/shell/hush.c b/shell/hush.c index 09ab6ebc0..2cceef25a 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -115,6 +115,11 @@ | |||
| 115 | //config:# It's only needed to get "nice" menuconfig indenting. | 115 | //config:# It's only needed to get "nice" menuconfig indenting. |
| 116 | //config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH | 116 | //config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH |
| 117 | //config: | 117 | //config: |
| 118 | //config:config HUSH_NEED_FOR_SPEED | ||
| 119 | //config: bool "Faster, but larger code" | ||
| 120 | //config: default y | ||
| 121 | //config: depends on SHELL_HUSH | ||
| 122 | //config: | ||
| 118 | //config:config HUSH_BASH_COMPAT | 123 | //config:config HUSH_BASH_COMPAT |
| 119 | //config: bool "bash-compatible extensions" | 124 | //config: bool "bash-compatible extensions" |
| 120 | //config: default y | 125 | //config: default y |
| @@ -3388,7 +3393,19 @@ static int glob_needed(const char *s) | |||
| 3388 | s += 2; | 3393 | s += 2; |
| 3389 | continue; | 3394 | continue; |
| 3390 | } | 3395 | } |
| 3391 | if (*s == '*' || *s == '[' || *s == '?' || *s == '{') | 3396 | if (*s == '*' || *s == '?') |
| 3397 | return 1; | ||
| 3398 | /* Only force glob if "..[..].." detected. | ||
| 3399 | * Not merely "[", "[[", "][" etc. | ||
| 3400 | * Optimization to avoid glob() | ||
| 3401 | * on "[ COND ]" and "[[ COND ]]": | ||
| 3402 | * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' | ||
| 3403 | * shouldn't be doing 50000 stat("["). | ||
| 3404 | * (Can do it for "{" too, but it's not a common case). | ||
| 3405 | */ | ||
| 3406 | if (*s == '[' && strchr(s+1, ']')) | ||
| 3407 | return 1; | ||
| 3408 | if (*s == '{' /* && strchr(s+1, '}')*/) | ||
| 3392 | return 1; | 3409 | return 1; |
| 3393 | s++; | 3410 | s++; |
| 3394 | } | 3411 | } |
| @@ -3579,7 +3596,16 @@ static int glob_needed(const char *s) | |||
| 3579 | s += 2; | 3596 | s += 2; |
| 3580 | continue; | 3597 | continue; |
| 3581 | } | 3598 | } |
| 3582 | if (*s == '*' || *s == '[' || *s == '?') | 3599 | if (*s == '*' || *s == '?') |
| 3600 | return 1; | ||
| 3601 | /* Only force glob if "..[..].." detected. | ||
| 3602 | * Not merely "[", "[[", "][" etc. | ||
| 3603 | * Optimization to avoid glob() | ||
| 3604 | * on "[ COND ]" and "[[ COND ]]": | ||
| 3605 | * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' | ||
| 3606 | * shouldn't be doing 50000 stat("["). | ||
| 3607 | */ | ||
| 3608 | if (*s == '[' && strchr(s+1, ']')) | ||
| 3583 | return 1; | 3609 | return 1; |
| 3584 | s++; | 3610 | s++; |
| 3585 | } | 3611 | } |
| @@ -4599,7 +4625,15 @@ static char *fetch_till_str(o_string *as_string, | |||
| 4599 | past_EOL = heredoc.length; | 4625 | past_EOL = heredoc.length; |
| 4600 | /* Get 1st char of next line, possibly skipping leading tabs */ | 4626 | /* Get 1st char of next line, possibly skipping leading tabs */ |
| 4601 | do { | 4627 | do { |
| 4602 | ch = i_getch(input); | 4628 | if (heredoc_flags & HEREDOC_QUOTED) |
| 4629 | ch = i_getch(input); | ||
| 4630 | else { /* see heredoc_bkslash_newline3a.tests: | ||
| 4631 | * cat <<-EOF | ||
| 4632 | * <tab>\ | ||
| 4633 | * <tab>EOF | ||
| 4634 | */ | ||
| 4635 | ch = i_getch_and_eat_bkslash_nl(input); | ||
| 4636 | } | ||
| 4603 | if (ch != EOF) | 4637 | if (ch != EOF) |
| 4604 | nommu_addchr(as_string, ch); | 4638 | nommu_addchr(as_string, ch); |
| 4605 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | 4639 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); |
| @@ -4625,7 +4659,7 @@ static char *fetch_till_str(o_string *as_string, | |||
| 4625 | prev = 0; /* not '\' */ | 4659 | prev = 0; /* not '\' */ |
| 4626 | continue; | 4660 | continue; |
| 4627 | } | 4661 | } |
| 4628 | } | 4662 | } /* if (\n or EOF) */ |
| 4629 | if (ch == EOF) { | 4663 | if (ch == EOF) { |
| 4630 | o_free(&heredoc); | 4664 | o_free(&heredoc); |
| 4631 | return NULL; /* error */ | 4665 | return NULL; /* error */ |
diff --git a/shell/hush_test/hush-bugs/var_backslash1.right b/shell/hush_test/hush-bugs/var_backslash1.right new file mode 100644 index 000000000..f384da1f5 --- /dev/null +++ b/shell/hush_test/hush-bugs/var_backslash1.right | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | a is '\*bc' | ||
| 2 | b is '\' | ||
| 3 | c is '*' | ||
| 4 | ${a##?*} removes everything: || - matches one char, then all | ||
| 5 | ${a##?"*"} removes \*: |bc| - matches one char, then * | ||
| 6 | ${a##\*} removes nothing: |\*bc| - first char is not * | ||
| 7 | ${a##\\*} removes everything: || - matches \, then all | ||
| 8 | ${a##\\\*} removes \*: |bc| - matches \, then * | ||
| 9 | ${a##?$c} removes everything: || - matches one char, then all | ||
| 10 | ${a##?"$c"} removes \*: |bc| - matches one char, then * | ||
| 11 | ${a##\\$c} removes everything: || - matches \, then all | ||
| 12 | ${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ | ||
| 13 | ${a##\\"$c"} removes \*: |bc| - matches \, then * | ||
| 14 | ${a##$b} removes \: |*bc| - matches \ | ||
| 15 | ${a##"$b"} removes \: |*bc| - matches \ | ||
| 16 | |||
| 17 | ${a##"$b"?} removes \*: |bc| - matches \, then one char | ||
| 18 | ${a##"$b"*} removes everything: || - matches \, then all | ||
| 19 | ${a##"$b""?"} removes nothing: |\*bc| - second char is not ? | ||
| 20 | ${a##"$b""*"} removes \*: |bc| - matches \, then * | ||
| 21 | ${a##"$b"\*} removes \*: |bc| - matches \, then * | ||
| 22 | ${a##"$b"$c} removes everything:|| - matches \, then all | ||
| 23 | ${a##"$b""$c"} removes \*: |bc| - matches \, then * | ||
| 24 | ${a##"$b?"} removes nothing: |\*bc| - second char is not ? | ||
| 25 | ${a##"$b*"} removes \*: |bc| - matches \, then * | ||
| 26 | ${a##"$b$c"} removes \*: |bc| - matches \, then * | ||
diff --git a/shell/hush_test/hush-bugs/var_backslash1.tests b/shell/hush_test/hush-bugs/var_backslash1.tests new file mode 100755 index 000000000..7eea3cc19 --- /dev/null +++ b/shell/hush_test/hush-bugs/var_backslash1.tests | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | a='\*bc' | ||
| 2 | b='\' | ||
| 3 | c='*' | ||
| 4 | echo "a is '$a'" | ||
| 5 | echo "b is '$b'" | ||
| 6 | echo "c is '$c'" | ||
| 7 | echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' | ||
| 8 | echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' | ||
| 9 | echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' | ||
| 10 | echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' | ||
| 11 | echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' | ||
| 12 | echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' | ||
| 13 | echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' | ||
| 14 | echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' | ||
| 15 | echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' | ||
| 16 | echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' | ||
| 17 | echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' | ||
| 18 | echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' | ||
| 19 | echo | ||
| 20 | ## In bash, this isn't working as expected | ||
| 21 | #echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| | ||
| 22 | #echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| | ||
| 23 | #echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| | ||
| 24 | #echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| | ||
| 25 | ## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: | ||
| 26 | ## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) | ||
| 27 | ## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| | ||
| 28 | #echo | ||
| 29 | echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' | ||
| 30 | echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' | ||
| 31 | echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| | ||
| 32 | echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' | ||
| 33 | echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' | ||
| 34 | echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' | ||
| 35 | echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' | ||
| 36 | echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| | ||
| 37 | echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || | ||
| 38 | echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests new file mode 100755 index 000000000..eb2223031 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | EO\ | ||
| 4 | F | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests new file mode 100755 index 000000000..de21132d1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right | |||
| @@ -0,0 +1 @@ | |||
| Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests new file mode 100755 index 000000000..da3860804 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | cat <<-EOF | ||
| 2 | Ok1 | ||
| 3 | \ | ||
| 4 | EOF | ||
