diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 01:05:52 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 01:05:52 +0000 |
| commit | bc54cffc8345fc91c07700ffa911e95735d7d337 (patch) | |
| tree | e5dfedc7e1ff7cc4c72d26ba4f3ec3f2ab0b1d1d /shell | |
| parent | 4d2183bee1f28c655fd7020ff13adc6b4b45110f (diff) | |
| download | busybox-w32-bc54cffc8345fc91c07700ffa911e95735d7d337.tar.gz busybox-w32-bc54cffc8345fc91c07700ffa911e95735d7d337.tar.bz2 busybox-w32-bc54cffc8345fc91c07700ffa911e95735d7d337.zip | |
ash: cleanup part 9
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 3237 |
1 files changed, 1579 insertions, 1658 deletions
diff --git a/shell/ash.c b/shell/ash.c index 94333360b..0e039322b 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -326,7 +326,7 @@ onsig(int signo) | |||
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | 328 | ||
| 329 | /* ============ stdout/stderr output */ | 329 | /* ============ Stdout/stderr output */ |
| 330 | 330 | ||
| 331 | static void | 331 | static void |
| 332 | outstr(const char *p, FILE *file) | 332 | outstr(const char *p, FILE *file) |
| @@ -2351,37 +2351,6 @@ pwdcmd(int argc, char **argv) | |||
| 2351 | /* ============ Unsorted yet */ | 2351 | /* ============ Unsorted yet */ |
| 2352 | 2352 | ||
| 2353 | 2353 | ||
| 2354 | /* expand.h */ | ||
| 2355 | |||
| 2356 | struct arglist { | ||
| 2357 | struct strlist *list; | ||
| 2358 | struct strlist **lastp; | ||
| 2359 | }; | ||
| 2360 | |||
| 2361 | /* | ||
| 2362 | * expandarg() flags | ||
| 2363 | */ | ||
| 2364 | #define EXP_FULL 0x1 /* perform word splitting & file globbing */ | ||
| 2365 | #define EXP_TILDE 0x2 /* do normal tilde expansion */ | ||
| 2366 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ | ||
| 2367 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ | ||
| 2368 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | ||
| 2369 | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ | ||
| 2370 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ | ||
| 2371 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | ||
| 2372 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ | ||
| 2373 | |||
| 2374 | |||
| 2375 | static void expandarg(union node *, struct arglist *, int); | ||
| 2376 | #define rmescapes(p) _rmescapes((p), 0) | ||
| 2377 | static char *_rmescapes(char *, int); | ||
| 2378 | static int casematch(union node *, char *); | ||
| 2379 | |||
| 2380 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 2381 | static void expari(int); | ||
| 2382 | #endif | ||
| 2383 | |||
| 2384 | |||
| 2385 | /* parser.h */ | 2354 | /* parser.h */ |
| 2386 | 2355 | ||
| 2387 | /* control characters in argument strings */ | 2356 | /* control characters in argument strings */ |
| @@ -3210,8 +3179,6 @@ static int redirectsafe(union node *, int); | |||
| 3210 | static void clear_traps(void); | 3179 | static void clear_traps(void); |
| 3211 | static void setsignal(int); | 3180 | static void setsignal(int); |
| 3212 | static int dotrap(void); | 3181 | static int dotrap(void); |
| 3213 | static void setinteractive(int); | ||
| 3214 | static void exitshell(void) ATTRIBUTE_NORETURN; | ||
| 3215 | 3182 | ||
| 3216 | 3183 | ||
| 3217 | static int is_safe_applet(char *name) | 3184 | static int is_safe_applet(char *name) |
| @@ -3445,14 +3412,1501 @@ unaliascmd(int argc, char **argv) | |||
| 3445 | #endif /* ASH_ALIAS */ | 3412 | #endif /* ASH_ALIAS */ |
| 3446 | 3413 | ||
| 3447 | 3414 | ||
| 3448 | /* ============ eval.c */ | 3415 | /* ============ Routines to expand arguments to commands |
| 3416 | * | ||
| 3417 | * We have to deal with backquotes, shell variables, and file metacharacters. | ||
| 3418 | */ | ||
| 3419 | |||
| 3420 | /* | ||
| 3421 | * expandarg flags | ||
| 3422 | */ | ||
| 3423 | #define EXP_FULL 0x1 /* perform word splitting & file globbing */ | ||
| 3424 | #define EXP_TILDE 0x2 /* do normal tilde expansion */ | ||
| 3425 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ | ||
| 3426 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ | ||
| 3427 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | ||
| 3428 | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ | ||
| 3429 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ | ||
| 3430 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | ||
| 3431 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ | ||
| 3432 | /* | ||
| 3433 | * _rmescape() flags | ||
| 3434 | */ | ||
| 3435 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ | ||
| 3436 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | ||
| 3437 | #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ | ||
| 3438 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ | ||
| 3439 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | ||
| 3440 | |||
| 3441 | /* | ||
| 3442 | * Structure specifying which parts of the string should be searched | ||
| 3443 | * for IFS characters. | ||
| 3444 | */ | ||
| 3445 | struct ifsregion { | ||
| 3446 | struct ifsregion *next; /* next region in list */ | ||
| 3447 | int begoff; /* offset of start of region */ | ||
| 3448 | int endoff; /* offset of end of region */ | ||
| 3449 | int nulonly; /* search for nul bytes only */ | ||
| 3450 | }; | ||
| 3451 | |||
| 3452 | struct arglist { | ||
| 3453 | struct strlist *list; | ||
| 3454 | struct strlist **lastp; | ||
| 3455 | }; | ||
| 3456 | |||
| 3457 | /* output of current string */ | ||
| 3458 | static char *expdest; | ||
| 3459 | /* list of back quote expressions */ | ||
| 3460 | static struct nodelist *argbackq; | ||
| 3461 | /* first struct in list of ifs regions */ | ||
| 3462 | static struct ifsregion ifsfirst; | ||
| 3463 | /* last struct in list */ | ||
| 3464 | static struct ifsregion *ifslastp; | ||
| 3465 | /* holds expanded arg list */ | ||
| 3466 | static struct arglist exparg; | ||
| 3467 | |||
| 3468 | /* | ||
| 3469 | * Our own itoa(). | ||
| 3470 | */ | ||
| 3471 | static int | ||
| 3472 | cvtnum(arith_t num) | ||
| 3473 | { | ||
| 3474 | int len; | ||
| 3475 | |||
| 3476 | expdest = makestrspace(32, expdest); | ||
| 3477 | #if ENABLE_ASH_MATH_SUPPORT_64 | ||
| 3478 | len = fmtstr(expdest, 32, "%lld", (long long) num); | ||
| 3479 | #else | ||
| 3480 | len = fmtstr(expdest, 32, "%ld", num); | ||
| 3481 | #endif | ||
| 3482 | STADJUST(len, expdest); | ||
| 3483 | return len; | ||
| 3484 | } | ||
| 3485 | |||
| 3486 | static size_t | ||
| 3487 | esclen(const char *start, const char *p) | ||
| 3488 | { | ||
| 3489 | size_t esc = 0; | ||
| 3490 | |||
| 3491 | while (p > start && *--p == CTLESC) { | ||
| 3492 | esc++; | ||
| 3493 | } | ||
| 3494 | return esc; | ||
| 3495 | } | ||
| 3496 | |||
| 3497 | /* | ||
| 3498 | * Remove any CTLESC characters from a string. | ||
| 3499 | */ | ||
| 3500 | static char * | ||
| 3501 | _rmescapes(char *str, int flag) | ||
| 3502 | { | ||
| 3503 | char *p, *q, *r; | ||
| 3504 | static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; | ||
| 3505 | unsigned inquotes; | ||
| 3506 | int notescaped; | ||
| 3507 | int globbing; | ||
| 3508 | |||
| 3509 | p = strpbrk(str, qchars); | ||
| 3510 | if (!p) { | ||
| 3511 | return str; | ||
| 3512 | } | ||
| 3513 | q = p; | ||
| 3514 | r = str; | ||
| 3515 | if (flag & RMESCAPE_ALLOC) { | ||
| 3516 | size_t len = p - str; | ||
| 3517 | size_t fulllen = len + strlen(p) + 1; | ||
| 3518 | |||
| 3519 | if (flag & RMESCAPE_GROW) { | ||
| 3520 | r = makestrspace(fulllen, expdest); | ||
| 3521 | } else if (flag & RMESCAPE_HEAP) { | ||
| 3522 | r = ckmalloc(fulllen); | ||
| 3523 | } else { | ||
| 3524 | r = stalloc(fulllen); | ||
| 3525 | } | ||
| 3526 | q = r; | ||
| 3527 | if (len > 0) { | ||
| 3528 | q = memcpy(q, str, len) + len; | ||
| 3529 | } | ||
| 3530 | } | ||
| 3531 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; | ||
| 3532 | globbing = flag & RMESCAPE_GLOB; | ||
| 3533 | notescaped = globbing; | ||
| 3534 | while (*p) { | ||
| 3535 | if (*p == CTLQUOTEMARK) { | ||
| 3536 | inquotes = ~inquotes; | ||
| 3537 | p++; | ||
| 3538 | notescaped = globbing; | ||
| 3539 | continue; | ||
| 3540 | } | ||
| 3541 | if (*p == '\\') { | ||
| 3542 | /* naked back slash */ | ||
| 3543 | notescaped = 0; | ||
| 3544 | goto copy; | ||
| 3545 | } | ||
| 3546 | if (*p == CTLESC) { | ||
| 3547 | p++; | ||
| 3548 | if (notescaped && inquotes && *p != '/') { | ||
| 3549 | *q++ = '\\'; | ||
| 3550 | } | ||
| 3551 | } | ||
| 3552 | notescaped = globbing; | ||
| 3553 | copy: | ||
| 3554 | *q++ = *p++; | ||
| 3555 | } | ||
| 3556 | *q = '\0'; | ||
| 3557 | if (flag & RMESCAPE_GROW) { | ||
| 3558 | expdest = r; | ||
| 3559 | STADJUST(q - r + 1, expdest); | ||
| 3560 | } | ||
| 3561 | return r; | ||
| 3562 | } | ||
| 3563 | #define rmescapes(p) _rmescapes((p), 0) | ||
| 3564 | |||
| 3565 | #define pmatch(a, b) !fnmatch((a), (b), 0) | ||
| 3566 | |||
| 3567 | /* | ||
| 3568 | * Prepare a pattern for a expmeta (internal glob(3)) call. | ||
| 3569 | * | ||
| 3570 | * Returns an stalloced string. | ||
| 3571 | */ | ||
| 3572 | static char * | ||
| 3573 | preglob(const char *pattern, int quoted, int flag) | ||
| 3574 | { | ||
| 3575 | flag |= RMESCAPE_GLOB; | ||
| 3576 | if (quoted) { | ||
| 3577 | flag |= RMESCAPE_QUOTED; | ||
| 3578 | } | ||
| 3579 | return _rmescapes((char *)pattern, flag); | ||
| 3580 | } | ||
| 3581 | |||
| 3582 | /* | ||
| 3583 | * Put a string on the stack. | ||
| 3584 | */ | ||
| 3585 | static void | ||
| 3586 | memtodest(const char *p, size_t len, int syntax, int quotes) | ||
| 3587 | { | ||
| 3588 | char *q = expdest; | ||
| 3589 | |||
| 3590 | q = makestrspace(len * 2, q); | ||
| 3591 | |||
| 3592 | while (len--) { | ||
| 3593 | int c = SC2INT(*p++); | ||
| 3594 | if (!c) | ||
| 3595 | continue; | ||
| 3596 | if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) | ||
| 3597 | USTPUTC(CTLESC, q); | ||
| 3598 | USTPUTC(c, q); | ||
| 3599 | } | ||
| 3600 | |||
| 3601 | expdest = q; | ||
| 3602 | } | ||
| 3603 | |||
| 3604 | static void | ||
| 3605 | strtodest(const char *p, int syntax, int quotes) | ||
| 3606 | { | ||
| 3607 | memtodest(p, strlen(p), syntax, quotes); | ||
| 3608 | } | ||
| 3609 | |||
| 3610 | /* | ||
| 3611 | * Record the fact that we have to scan this region of the | ||
| 3612 | * string for IFS characters. | ||
| 3613 | */ | ||
| 3614 | static void | ||
| 3615 | recordregion(int start, int end, int nulonly) | ||
| 3616 | { | ||
| 3617 | struct ifsregion *ifsp; | ||
| 3618 | |||
| 3619 | if (ifslastp == NULL) { | ||
| 3620 | ifsp = &ifsfirst; | ||
| 3621 | } else { | ||
| 3622 | INT_OFF; | ||
| 3623 | ifsp = ckmalloc(sizeof(*ifsp)); | ||
| 3624 | ifsp->next = NULL; | ||
| 3625 | ifslastp->next = ifsp; | ||
| 3626 | INT_ON; | ||
| 3627 | } | ||
| 3628 | ifslastp = ifsp; | ||
| 3629 | ifslastp->begoff = start; | ||
| 3630 | ifslastp->endoff = end; | ||
| 3631 | ifslastp->nulonly = nulonly; | ||
| 3632 | } | ||
| 3633 | |||
| 3634 | static void | ||
| 3635 | removerecordregions(int endoff) | ||
| 3636 | { | ||
| 3637 | if (ifslastp == NULL) | ||
| 3638 | return; | ||
| 3639 | |||
| 3640 | if (ifsfirst.endoff > endoff) { | ||
| 3641 | while (ifsfirst.next != NULL) { | ||
| 3642 | struct ifsregion *ifsp; | ||
| 3643 | INT_OFF; | ||
| 3644 | ifsp = ifsfirst.next->next; | ||
| 3645 | free(ifsfirst.next); | ||
| 3646 | ifsfirst.next = ifsp; | ||
| 3647 | INT_ON; | ||
| 3648 | } | ||
| 3649 | if (ifsfirst.begoff > endoff) | ||
| 3650 | ifslastp = NULL; | ||
| 3651 | else { | ||
| 3652 | ifslastp = &ifsfirst; | ||
| 3653 | ifsfirst.endoff = endoff; | ||
| 3654 | } | ||
| 3655 | return; | ||
| 3656 | } | ||
| 3657 | |||
| 3658 | ifslastp = &ifsfirst; | ||
| 3659 | while (ifslastp->next && ifslastp->next->begoff < endoff) | ||
| 3660 | ifslastp=ifslastp->next; | ||
| 3661 | while (ifslastp->next != NULL) { | ||
| 3662 | struct ifsregion *ifsp; | ||
| 3663 | INT_OFF; | ||
| 3664 | ifsp = ifslastp->next->next; | ||
| 3665 | free(ifslastp->next); | ||
| 3666 | ifslastp->next = ifsp; | ||
| 3667 | INT_ON; | ||
| 3668 | } | ||
| 3669 | if (ifslastp->endoff > endoff) | ||
| 3670 | ifslastp->endoff = endoff; | ||
| 3671 | } | ||
| 3672 | |||
| 3673 | static char * | ||
| 3674 | exptilde(char *startp, char *p, int flag) | ||
| 3675 | { | ||
| 3676 | char c; | ||
| 3677 | char *name; | ||
| 3678 | struct passwd *pw; | ||
| 3679 | const char *home; | ||
| 3680 | int quotes = flag & (EXP_FULL | EXP_CASE); | ||
| 3681 | int startloc; | ||
| 3682 | |||
| 3683 | name = p + 1; | ||
| 3684 | |||
| 3685 | while ((c = *++p) != '\0') { | ||
| 3686 | switch (c) { | ||
| 3687 | case CTLESC: | ||
| 3688 | return startp; | ||
| 3689 | case CTLQUOTEMARK: | ||
| 3690 | return startp; | ||
| 3691 | case ':': | ||
| 3692 | if (flag & EXP_VARTILDE) | ||
| 3693 | goto done; | ||
| 3694 | break; | ||
| 3695 | case '/': | ||
| 3696 | case CTLENDVAR: | ||
| 3697 | goto done; | ||
| 3698 | } | ||
| 3699 | } | ||
| 3700 | done: | ||
| 3701 | *p = '\0'; | ||
| 3702 | if (*name == '\0') { | ||
| 3703 | home = lookupvar(homestr); | ||
| 3704 | } else { | ||
| 3705 | pw = getpwnam(name); | ||
| 3706 | if (pw == NULL) | ||
| 3707 | goto lose; | ||
| 3708 | home = pw->pw_dir; | ||
| 3709 | } | ||
| 3710 | if (!home || !*home) | ||
| 3711 | goto lose; | ||
| 3712 | *p = c; | ||
| 3713 | startloc = expdest - (char *)stackblock(); | ||
| 3714 | strtodest(home, SQSYNTAX, quotes); | ||
| 3715 | recordregion(startloc, expdest - (char *)stackblock(), 0); | ||
| 3716 | return p; | ||
| 3717 | lose: | ||
| 3718 | *p = c; | ||
| 3719 | return startp; | ||
| 3720 | } | ||
| 3721 | |||
| 3722 | /* | ||
| 3723 | * Execute a command inside back quotes. If it's a builtin command, we | ||
| 3724 | * want to save its output in a block obtained from malloc. Otherwise | ||
| 3725 | * we fork off a subprocess and get the output of the command via a pipe. | ||
| 3726 | * Should be called with interrupts off. | ||
| 3727 | */ | ||
| 3728 | struct backcmd { /* result of evalbackcmd */ | ||
| 3729 | int fd; /* file descriptor to read from */ | ||
| 3730 | char *buf; /* buffer */ | ||
| 3731 | int nleft; /* number of chars in buffer */ | ||
| 3732 | struct job *jp; /* job structure for command */ | ||
| 3733 | }; | ||
| 3734 | |||
| 3735 | /* These forward decls are needed to use "eval" code for backticks handling: */ | ||
| 3736 | static int back_exitstatus; /* exit status of backquoted command */ | ||
| 3737 | #define EV_EXIT 01 /* exit after evaluating tree */ | ||
| 3738 | static void evaltree(union node *, int); | ||
| 3739 | |||
| 3740 | static void | ||
| 3741 | evalbackcmd(union node *n, struct backcmd *result) | ||
| 3742 | { | ||
| 3743 | int saveherefd; | ||
| 3744 | |||
| 3745 | result->fd = -1; | ||
| 3746 | result->buf = NULL; | ||
| 3747 | result->nleft = 0; | ||
| 3748 | result->jp = NULL; | ||
| 3749 | if (n == NULL) { | ||
| 3750 | goto out; | ||
| 3751 | } | ||
| 3752 | |||
| 3753 | saveherefd = herefd; | ||
| 3754 | herefd = -1; | ||
| 3755 | |||
| 3756 | { | ||
| 3757 | int pip[2]; | ||
| 3758 | struct job *jp; | ||
| 3759 | |||
| 3760 | if (pipe(pip) < 0) | ||
| 3761 | ash_msg_and_raise_error("Pipe call failed"); | ||
| 3762 | jp = makejob(n, 1); | ||
| 3763 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | ||
| 3764 | FORCE_INT_ON; | ||
| 3765 | close(pip[0]); | ||
| 3766 | if (pip[1] != 1) { | ||
| 3767 | close(1); | ||
| 3768 | copyfd(pip[1], 1); | ||
| 3769 | close(pip[1]); | ||
| 3770 | } | ||
| 3771 | eflag = 0; | ||
| 3772 | evaltree(n, EV_EXIT); /* actually evaltreenr... */ | ||
| 3773 | /* NOTREACHED */ | ||
| 3774 | } | ||
| 3775 | close(pip[1]); | ||
| 3776 | result->fd = pip[0]; | ||
| 3777 | result->jp = jp; | ||
| 3778 | } | ||
| 3779 | herefd = saveherefd; | ||
| 3780 | out: | ||
| 3781 | TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", | ||
| 3782 | result->fd, result->buf, result->nleft, result->jp)); | ||
| 3783 | } | ||
| 3784 | |||
| 3785 | /* | ||
| 3786 | * Expand stuff in backwards quotes. | ||
| 3787 | */ | ||
| 3788 | static void | ||
| 3789 | expbackq(union node *cmd, int quoted, int quotes) | ||
| 3790 | { | ||
| 3791 | struct backcmd in; | ||
| 3792 | int i; | ||
| 3793 | char buf[128]; | ||
| 3794 | char *p; | ||
| 3795 | char *dest; | ||
| 3796 | int startloc; | ||
| 3797 | int syntax = quoted? DQSYNTAX : BASESYNTAX; | ||
| 3798 | struct stackmark smark; | ||
| 3799 | |||
| 3800 | INT_OFF; | ||
| 3801 | setstackmark(&smark); | ||
| 3802 | dest = expdest; | ||
| 3803 | startloc = dest - (char *)stackblock(); | ||
| 3804 | grabstackstr(dest); | ||
| 3805 | evalbackcmd(cmd, &in); | ||
| 3806 | popstackmark(&smark); | ||
| 3807 | |||
| 3808 | p = in.buf; | ||
| 3809 | i = in.nleft; | ||
| 3810 | if (i == 0) | ||
| 3811 | goto read; | ||
| 3812 | for (;;) { | ||
| 3813 | memtodest(p, i, syntax, quotes); | ||
| 3814 | read: | ||
| 3815 | if (in.fd < 0) | ||
| 3816 | break; | ||
| 3817 | i = safe_read(in.fd, buf, sizeof(buf)); | ||
| 3818 | TRACE(("expbackq: read returns %d\n", i)); | ||
| 3819 | if (i <= 0) | ||
| 3820 | break; | ||
| 3821 | p = buf; | ||
| 3822 | } | ||
| 3823 | |||
| 3824 | if (in.buf) | ||
| 3825 | free(in.buf); | ||
| 3826 | if (in.fd >= 0) { | ||
| 3827 | close(in.fd); | ||
| 3828 | back_exitstatus = waitforjob(in.jp); | ||
| 3829 | } | ||
| 3830 | INT_ON; | ||
| 3831 | |||
| 3832 | /* Eat all trailing newlines */ | ||
| 3833 | dest = expdest; | ||
| 3834 | for (; dest > (char *)stackblock() && dest[-1] == '\n';) | ||
| 3835 | STUNPUTC(dest); | ||
| 3836 | expdest = dest; | ||
| 3837 | |||
| 3838 | if (quoted == 0) | ||
| 3839 | recordregion(startloc, dest - (char *)stackblock(), 0); | ||
| 3840 | TRACE(("evalbackq: size=%d: \"%.*s\"\n", | ||
| 3841 | (dest - (char *)stackblock()) - startloc, | ||
| 3842 | (dest - (char *)stackblock()) - startloc, | ||
| 3843 | stackblock() + startloc)); | ||
| 3844 | } | ||
| 3845 | |||
| 3846 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 3847 | /* | ||
| 3848 | * Expand arithmetic expression. Backup to start of expression, | ||
| 3849 | * evaluate, place result in (backed up) result, adjust string position. | ||
| 3850 | */ | ||
| 3851 | static void | ||
| 3852 | expari(int quotes) | ||
| 3853 | { | ||
| 3854 | char *p, *start; | ||
| 3855 | int begoff; | ||
| 3856 | int flag; | ||
| 3857 | int len; | ||
| 3858 | |||
| 3859 | /* ifsfree(); */ | ||
| 3860 | |||
| 3861 | /* | ||
| 3862 | * This routine is slightly over-complicated for | ||
| 3863 | * efficiency. Next we scan backwards looking for the | ||
| 3864 | * start of arithmetic. | ||
| 3865 | */ | ||
| 3866 | start = stackblock(); | ||
| 3867 | p = expdest - 1; | ||
| 3868 | *p = '\0'; | ||
| 3869 | p--; | ||
| 3870 | do { | ||
| 3871 | int esc; | ||
| 3872 | |||
| 3873 | while (*p != CTLARI) { | ||
| 3874 | p--; | ||
| 3875 | #if DEBUG | ||
| 3876 | if (p < start) { | ||
| 3877 | ash_msg_and_raise_error("missing CTLARI (shouldn't happen)"); | ||
| 3878 | } | ||
| 3879 | #endif | ||
| 3880 | } | ||
| 3881 | |||
| 3882 | esc = esclen(start, p); | ||
| 3883 | if (!(esc % 2)) { | ||
| 3884 | break; | ||
| 3885 | } | ||
| 3886 | |||
| 3887 | p -= esc + 1; | ||
| 3888 | } while (1); | ||
| 3889 | |||
| 3890 | begoff = p - start; | ||
| 3891 | |||
| 3892 | removerecordregions(begoff); | ||
| 3893 | |||
| 3894 | flag = p[1]; | ||
| 3895 | |||
| 3896 | expdest = p; | ||
| 3897 | |||
| 3898 | if (quotes) | ||
| 3899 | rmescapes(p + 2); | ||
| 3900 | |||
| 3901 | len = cvtnum(dash_arith(p + 2)); | ||
| 3902 | |||
| 3903 | if (flag != '"') | ||
| 3904 | recordregion(begoff, begoff + len, 0); | ||
| 3905 | } | ||
| 3906 | #endif | ||
| 3907 | |||
| 3908 | /* argstr needs it */ | ||
| 3909 | static char *evalvar(char *p, int flag); | ||
| 3910 | |||
| 3911 | /* | ||
| 3912 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC | ||
| 3913 | * characters to allow for further processing. Otherwise treat | ||
| 3914 | * $@ like $* since no splitting will be performed. | ||
| 3915 | */ | ||
| 3916 | static void | ||
| 3917 | argstr(char *p, int flag) | ||
| 3918 | { | ||
| 3919 | static const char spclchars[] = { | ||
| 3920 | '=', | ||
| 3921 | ':', | ||
| 3922 | CTLQUOTEMARK, | ||
| 3923 | CTLENDVAR, | ||
| 3924 | CTLESC, | ||
| 3925 | CTLVAR, | ||
| 3926 | CTLBACKQ, | ||
| 3927 | CTLBACKQ | CTLQUOTE, | ||
| 3928 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 3929 | CTLENDARI, | ||
| 3930 | #endif | ||
| 3931 | 0 | ||
| 3932 | }; | ||
| 3933 | const char *reject = spclchars; | ||
| 3934 | int c; | ||
| 3935 | int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ | ||
| 3936 | int breakall = flag & EXP_WORD; | ||
| 3937 | int inquotes; | ||
| 3938 | size_t length; | ||
| 3939 | int startloc; | ||
| 3940 | |||
| 3941 | if (!(flag & EXP_VARTILDE)) { | ||
| 3942 | reject += 2; | ||
| 3943 | } else if (flag & EXP_VARTILDE2) { | ||
| 3944 | reject++; | ||
| 3945 | } | ||
| 3946 | inquotes = 0; | ||
| 3947 | length = 0; | ||
| 3948 | if (flag & EXP_TILDE) { | ||
| 3949 | char *q; | ||
| 3950 | |||
| 3951 | flag &= ~EXP_TILDE; | ||
| 3952 | tilde: | ||
| 3953 | q = p; | ||
| 3954 | if (*q == CTLESC && (flag & EXP_QWORD)) | ||
| 3955 | q++; | ||
| 3956 | if (*q == '~') | ||
| 3957 | p = exptilde(p, q, flag); | ||
| 3958 | } | ||
| 3959 | start: | ||
| 3960 | startloc = expdest - (char *)stackblock(); | ||
| 3961 | for (;;) { | ||
| 3962 | length += strcspn(p + length, reject); | ||
| 3963 | c = p[length]; | ||
| 3964 | if (c && (!(c & 0x80) | ||
| 3965 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 3966 | || c == CTLENDARI | ||
| 3967 | #endif | ||
| 3968 | )) { | ||
| 3969 | /* c == '=' || c == ':' || c == CTLENDARI */ | ||
| 3970 | length++; | ||
| 3971 | } | ||
| 3972 | if (length > 0) { | ||
| 3973 | int newloc; | ||
| 3974 | expdest = stack_nputstr(p, length, expdest); | ||
| 3975 | newloc = expdest - (char *)stackblock(); | ||
| 3976 | if (breakall && !inquotes && newloc > startloc) { | ||
| 3977 | recordregion(startloc, newloc, 0); | ||
| 3978 | } | ||
| 3979 | startloc = newloc; | ||
| 3980 | } | ||
| 3981 | p += length + 1; | ||
| 3982 | length = 0; | ||
| 3983 | |||
| 3984 | switch (c) { | ||
| 3985 | case '\0': | ||
| 3986 | goto breakloop; | ||
| 3987 | case '=': | ||
| 3988 | if (flag & EXP_VARTILDE2) { | ||
| 3989 | p--; | ||
| 3990 | continue; | ||
| 3991 | } | ||
| 3992 | flag |= EXP_VARTILDE2; | ||
| 3993 | reject++; | ||
| 3994 | /* fall through */ | ||
| 3995 | case ':': | ||
| 3996 | /* | ||
| 3997 | * sort of a hack - expand tildes in variable | ||
| 3998 | * assignments (after the first '=' and after ':'s). | ||
| 3999 | */ | ||
| 4000 | if (*--p == '~') { | ||
| 4001 | goto tilde; | ||
| 4002 | } | ||
| 4003 | continue; | ||
| 4004 | } | ||
| 4005 | |||
| 4006 | switch (c) { | ||
| 4007 | case CTLENDVAR: /* ??? */ | ||
| 4008 | goto breakloop; | ||
| 4009 | case CTLQUOTEMARK: | ||
| 4010 | /* "$@" syntax adherence hack */ | ||
| 4011 | if ( | ||
| 4012 | !inquotes && | ||
| 4013 | !memcmp(p, dolatstr, 4) && | ||
| 4014 | (p[4] == CTLQUOTEMARK || ( | ||
| 4015 | p[4] == CTLENDVAR && | ||
| 4016 | p[5] == CTLQUOTEMARK | ||
| 4017 | )) | ||
| 4018 | ) { | ||
| 4019 | p = evalvar(p + 1, flag) + 1; | ||
| 4020 | goto start; | ||
| 4021 | } | ||
| 4022 | inquotes = !inquotes; | ||
| 4023 | addquote: | ||
| 4024 | if (quotes) { | ||
| 4025 | p--; | ||
| 4026 | length++; | ||
| 4027 | startloc++; | ||
| 4028 | } | ||
| 4029 | break; | ||
| 4030 | case CTLESC: | ||
| 4031 | startloc++; | ||
| 4032 | length++; | ||
| 4033 | goto addquote; | ||
| 4034 | case CTLVAR: | ||
| 4035 | p = evalvar(p, flag); | ||
| 4036 | goto start; | ||
| 4037 | case CTLBACKQ: | ||
| 4038 | c = 0; | ||
| 4039 | case CTLBACKQ|CTLQUOTE: | ||
| 4040 | expbackq(argbackq->n, c, quotes); | ||
| 4041 | argbackq = argbackq->next; | ||
| 4042 | goto start; | ||
| 4043 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 4044 | case CTLENDARI: | ||
| 4045 | p--; | ||
| 4046 | expari(quotes); | ||
| 4047 | goto start; | ||
| 4048 | #endif | ||
| 4049 | } | ||
| 4050 | } | ||
| 4051 | breakloop: | ||
| 4052 | ; | ||
| 4053 | } | ||
| 4054 | |||
| 4055 | static char * | ||
| 4056 | scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes, | ||
| 4057 | int zero) | ||
| 4058 | { | ||
| 4059 | char *loc; | ||
| 4060 | char *loc2; | ||
| 4061 | char c; | ||
| 4062 | |||
| 4063 | loc = startp; | ||
| 4064 | loc2 = rmesc; | ||
| 4065 | do { | ||
| 4066 | int match; | ||
| 4067 | const char *s = loc2; | ||
| 4068 | c = *loc2; | ||
| 4069 | if (zero) { | ||
| 4070 | *loc2 = '\0'; | ||
| 4071 | s = rmesc; | ||
| 4072 | } | ||
| 4073 | match = pmatch(str, s); | ||
| 4074 | *loc2 = c; | ||
| 4075 | if (match) | ||
| 4076 | return loc; | ||
| 4077 | if (quotes && *loc == CTLESC) | ||
| 4078 | loc++; | ||
| 4079 | loc++; | ||
| 4080 | loc2++; | ||
| 4081 | } while (c); | ||
| 4082 | return 0; | ||
| 4083 | } | ||
| 4084 | |||
| 4085 | static char * | ||
| 4086 | scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes, | ||
| 4087 | int zero) | ||
| 4088 | { | ||
| 4089 | int esc = 0; | ||
| 4090 | char *loc; | ||
| 4091 | char *loc2; | ||
| 4092 | |||
| 4093 | for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { | ||
| 4094 | int match; | ||
| 4095 | char c = *loc2; | ||
| 4096 | const char *s = loc2; | ||
| 4097 | if (zero) { | ||
| 4098 | *loc2 = '\0'; | ||
| 4099 | s = rmesc; | ||
| 4100 | } | ||
| 4101 | match = pmatch(str, s); | ||
| 4102 | *loc2 = c; | ||
| 4103 | if (match) | ||
| 4104 | return loc; | ||
| 4105 | loc--; | ||
| 4106 | if (quotes) { | ||
| 4107 | if (--esc < 0) { | ||
| 4108 | esc = esclen(startp, loc); | ||
| 4109 | } | ||
| 4110 | if (esc % 2) { | ||
| 4111 | esc--; | ||
| 4112 | loc--; | ||
| 4113 | } | ||
| 4114 | } | ||
| 4115 | } | ||
| 4116 | return 0; | ||
| 4117 | } | ||
| 4118 | |||
| 4119 | static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN; | ||
| 4120 | static void | ||
| 4121 | varunset(const char *end, const char *var, const char *umsg, int varflags) | ||
| 4122 | { | ||
| 4123 | const char *msg; | ||
| 4124 | const char *tail; | ||
| 4125 | |||
| 4126 | tail = nullstr; | ||
| 4127 | msg = "parameter not set"; | ||
| 4128 | if (umsg) { | ||
| 4129 | if (*end == CTLENDVAR) { | ||
| 4130 | if (varflags & VSNUL) | ||
| 4131 | tail = " or null"; | ||
| 4132 | } else | ||
| 4133 | msg = umsg; | ||
| 4134 | } | ||
| 4135 | ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); | ||
| 4136 | } | ||
| 4137 | |||
| 4138 | static const char * | ||
| 4139 | subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) | ||
| 4140 | { | ||
| 4141 | char *startp; | ||
| 4142 | char *loc; | ||
| 4143 | int saveherefd = herefd; | ||
| 4144 | struct nodelist *saveargbackq = argbackq; | ||
| 4145 | int amount; | ||
| 4146 | char *rmesc, *rmescend; | ||
| 4147 | int zero; | ||
| 4148 | char *(*scan)(char *, char *, char *, char *, int , int); | ||
| 4149 | |||
| 4150 | herefd = -1; | ||
| 4151 | argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); | ||
| 4152 | STPUTC('\0', expdest); | ||
| 4153 | herefd = saveherefd; | ||
| 4154 | argbackq = saveargbackq; | ||
| 4155 | startp = stackblock() + startloc; | ||
| 4156 | |||
| 4157 | switch (subtype) { | ||
| 4158 | case VSASSIGN: | ||
| 4159 | setvar(str, startp, 0); | ||
| 4160 | amount = startp - expdest; | ||
| 4161 | STADJUST(amount, expdest); | ||
| 4162 | return startp; | ||
| 4163 | |||
| 4164 | case VSQUESTION: | ||
| 4165 | varunset(p, str, startp, varflags); | ||
| 4166 | /* NOTREACHED */ | ||
| 4167 | } | ||
| 4168 | |||
| 4169 | subtype -= VSTRIMRIGHT; | ||
| 4170 | #if DEBUG | ||
| 4171 | if (subtype < 0 || subtype > 3) | ||
| 4172 | abort(); | ||
| 4173 | #endif | ||
| 4174 | |||
| 4175 | rmesc = startp; | ||
| 4176 | rmescend = stackblock() + strloc; | ||
| 4177 | if (quotes) { | ||
| 4178 | rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); | ||
| 4179 | if (rmesc != startp) { | ||
| 4180 | rmescend = expdest; | ||
| 4181 | startp = stackblock() + startloc; | ||
| 4182 | } | ||
| 4183 | } | ||
| 4184 | rmescend--; | ||
| 4185 | str = stackblock() + strloc; | ||
| 4186 | preglob(str, varflags & VSQUOTE, 0); | ||
| 4187 | |||
| 4188 | /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ | ||
| 4189 | zero = subtype >> 1; | ||
| 4190 | /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ | ||
| 4191 | scan = (subtype & 1) ^ zero ? scanleft : scanright; | ||
| 4192 | |||
| 4193 | loc = scan(startp, rmesc, rmescend, str, quotes, zero); | ||
| 4194 | if (loc) { | ||
| 4195 | if (zero) { | ||
| 4196 | memmove(startp, loc, str - loc); | ||
| 4197 | loc = startp + (str - loc) - 1; | ||
| 4198 | } | ||
| 4199 | *loc = '\0'; | ||
| 4200 | amount = loc - expdest; | ||
| 4201 | STADJUST(amount, expdest); | ||
| 4202 | } | ||
| 4203 | return loc; | ||
| 4204 | } | ||
| 4205 | |||
| 4206 | /* | ||
| 4207 | * Add the value of a specialized variable to the stack string. | ||
| 4208 | */ | ||
| 4209 | static ssize_t | ||
| 4210 | varvalue(char *name, int varflags, int flags) | ||
| 4211 | { | ||
| 4212 | int num; | ||
| 4213 | char *p; | ||
| 4214 | int i; | ||
| 4215 | int sep = 0; | ||
| 4216 | int sepq = 0; | ||
| 4217 | ssize_t len = 0; | ||
| 4218 | char **ap; | ||
| 4219 | int syntax; | ||
| 4220 | int quoted = varflags & VSQUOTE; | ||
| 4221 | int subtype = varflags & VSTYPE; | ||
| 4222 | int quotes = flags & (EXP_FULL | EXP_CASE); | ||
| 4223 | |||
| 4224 | if (quoted && (flags & EXP_FULL)) | ||
| 4225 | sep = 1 << CHAR_BIT; | ||
| 4226 | |||
| 4227 | syntax = quoted ? DQSYNTAX : BASESYNTAX; | ||
| 4228 | switch (*name) { | ||
| 4229 | case '$': | ||
| 4230 | num = rootpid; | ||
| 4231 | goto numvar; | ||
| 4232 | case '?': | ||
| 4233 | num = exitstatus; | ||
| 4234 | goto numvar; | ||
| 4235 | case '#': | ||
| 4236 | num = shellparam.nparam; | ||
| 4237 | goto numvar; | ||
| 4238 | case '!': | ||
| 4239 | num = backgndpid; | ||
| 4240 | if (num == 0) | ||
| 4241 | return -1; | ||
| 4242 | numvar: | ||
| 4243 | len = cvtnum(num); | ||
| 4244 | break; | ||
| 4245 | case '-': | ||
| 4246 | p = makestrspace(NOPTS, expdest); | ||
| 4247 | for (i = NOPTS - 1; i >= 0; i--) { | ||
| 4248 | if (optlist[i]) { | ||
| 4249 | USTPUTC(optletters(i), p); | ||
| 4250 | len++; | ||
| 4251 | } | ||
| 4252 | } | ||
| 4253 | expdest = p; | ||
| 4254 | break; | ||
| 4255 | case '@': | ||
| 4256 | if (sep) | ||
| 4257 | goto param; | ||
| 4258 | /* fall through */ | ||
| 4259 | case '*': | ||
| 4260 | sep = ifsset() ? SC2INT(ifsval()[0]) : ' '; | ||
| 4261 | if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK)) | ||
| 4262 | sepq = 1; | ||
| 4263 | param: | ||
| 4264 | ap = shellparam.p; | ||
| 4265 | if (!ap) | ||
| 4266 | return -1; | ||
| 4267 | while ((p = *ap++)) { | ||
| 4268 | size_t partlen; | ||
| 4269 | |||
| 4270 | partlen = strlen(p); | ||
| 4271 | len += partlen; | ||
| 4272 | |||
| 4273 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 4274 | memtodest(p, partlen, syntax, quotes); | ||
| 4275 | |||
| 4276 | if (*ap && sep) { | ||
| 4277 | char *q; | ||
| 4278 | |||
| 4279 | len++; | ||
| 4280 | if (subtype == VSPLUS || subtype == VSLENGTH) { | ||
| 4281 | continue; | ||
| 4282 | } | ||
| 4283 | q = expdest; | ||
| 4284 | if (sepq) | ||
| 4285 | STPUTC(CTLESC, q); | ||
| 4286 | STPUTC(sep, q); | ||
| 4287 | expdest = q; | ||
| 4288 | } | ||
| 4289 | } | ||
| 4290 | return len; | ||
| 4291 | case '0': | ||
| 4292 | case '1': | ||
| 4293 | case '2': | ||
| 4294 | case '3': | ||
| 4295 | case '4': | ||
| 4296 | case '5': | ||
| 4297 | case '6': | ||
| 4298 | case '7': | ||
| 4299 | case '8': | ||
| 4300 | case '9': | ||
| 4301 | num = atoi(name); | ||
| 4302 | if (num < 0 || num > shellparam.nparam) | ||
| 4303 | return -1; | ||
| 4304 | p = num ? shellparam.p[num - 1] : arg0; | ||
| 4305 | goto value; | ||
| 4306 | default: | ||
| 4307 | p = lookupvar(name); | ||
| 4308 | value: | ||
| 4309 | if (!p) | ||
| 4310 | return -1; | ||
| 4311 | |||
| 4312 | len = strlen(p); | ||
| 4313 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 4314 | memtodest(p, len, syntax, quotes); | ||
| 4315 | return len; | ||
| 4316 | } | ||
| 4317 | |||
| 4318 | if (subtype == VSPLUS || subtype == VSLENGTH) | ||
| 4319 | STADJUST(-len, expdest); | ||
| 4320 | return len; | ||
| 4321 | } | ||
| 4322 | |||
| 4323 | /* | ||
| 4324 | * Expand a variable, and return a pointer to the next character in the | ||
| 4325 | * input string. | ||
| 4326 | */ | ||
| 4327 | static char * | ||
| 4328 | evalvar(char *p, int flag) | ||
| 4329 | { | ||
| 4330 | int subtype; | ||
| 4331 | int varflags; | ||
| 4332 | char *var; | ||
| 4333 | int patloc; | ||
| 4334 | int c; | ||
| 4335 | int startloc; | ||
| 4336 | ssize_t varlen; | ||
| 4337 | int easy; | ||
| 4338 | int quotes; | ||
| 4339 | int quoted; | ||
| 4340 | |||
| 4341 | quotes = flag & (EXP_FULL | EXP_CASE); | ||
| 4342 | varflags = *p++; | ||
| 4343 | subtype = varflags & VSTYPE; | ||
| 4344 | quoted = varflags & VSQUOTE; | ||
| 4345 | var = p; | ||
| 4346 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | ||
| 4347 | startloc = expdest - (char *)stackblock(); | ||
| 4348 | p = strchr(p, '=') + 1; | ||
| 4349 | |||
| 4350 | again: | ||
| 4351 | varlen = varvalue(var, varflags, flag); | ||
| 4352 | if (varflags & VSNUL) | ||
| 4353 | varlen--; | ||
| 4354 | |||
| 4355 | if (subtype == VSPLUS) { | ||
| 4356 | varlen = -1 - varlen; | ||
| 4357 | goto vsplus; | ||
| 4358 | } | ||
| 4359 | |||
| 4360 | if (subtype == VSMINUS) { | ||
| 4361 | vsplus: | ||
| 4362 | if (varlen < 0) { | ||
| 4363 | argstr( | ||
| 4364 | p, flag | EXP_TILDE | | ||
| 4365 | (quoted ? EXP_QWORD : EXP_WORD) | ||
| 4366 | ); | ||
| 4367 | goto end; | ||
| 4368 | } | ||
| 4369 | if (easy) | ||
| 4370 | goto record; | ||
| 4371 | goto end; | ||
| 4372 | } | ||
| 4373 | |||
| 4374 | if (subtype == VSASSIGN || subtype == VSQUESTION) { | ||
| 4375 | if (varlen < 0) { | ||
| 4376 | if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) { | ||
| 4377 | varflags &= ~VSNUL; | ||
| 4378 | /* | ||
| 4379 | * Remove any recorded regions beyond | ||
| 4380 | * start of variable | ||
| 4381 | */ | ||
| 4382 | removerecordregions(startloc); | ||
| 4383 | goto again; | ||
| 4384 | } | ||
| 4385 | goto end; | ||
| 4386 | } | ||
| 4387 | if (easy) | ||
| 4388 | goto record; | ||
| 4389 | goto end; | ||
| 4390 | } | ||
| 4391 | |||
| 4392 | if (varlen < 0 && uflag) | ||
| 4393 | varunset(p, var, 0, 0); | ||
| 4394 | |||
| 4395 | if (subtype == VSLENGTH) { | ||
| 4396 | cvtnum(varlen > 0 ? varlen : 0); | ||
| 4397 | goto record; | ||
| 4398 | } | ||
| 4399 | |||
| 4400 | if (subtype == VSNORMAL) { | ||
| 4401 | if (!easy) | ||
| 4402 | goto end; | ||
| 4403 | record: | ||
| 4404 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
| 4405 | goto end; | ||
| 4406 | } | ||
| 4407 | |||
| 4408 | #if DEBUG | ||
| 4409 | switch (subtype) { | ||
| 4410 | case VSTRIMLEFT: | ||
| 4411 | case VSTRIMLEFTMAX: | ||
| 4412 | case VSTRIMRIGHT: | ||
| 4413 | case VSTRIMRIGHTMAX: | ||
| 4414 | break; | ||
| 4415 | default: | ||
| 4416 | abort(); | ||
| 4417 | } | ||
| 4418 | #endif | ||
| 4419 | |||
| 4420 | if (varlen >= 0) { | ||
| 4421 | /* | ||
| 4422 | * Terminate the string and start recording the pattern | ||
| 4423 | * right after it | ||
| 4424 | */ | ||
| 4425 | STPUTC('\0', expdest); | ||
| 4426 | patloc = expdest - (char *)stackblock(); | ||
| 4427 | if (subevalvar(p, NULL, patloc, subtype, | ||
| 4428 | startloc, varflags, quotes) == 0) { | ||
| 4429 | int amount = expdest - ( | ||
| 4430 | (char *)stackblock() + patloc - 1 | ||
| 4431 | ); | ||
| 4432 | STADJUST(-amount, expdest); | ||
| 4433 | } | ||
| 4434 | /* Remove any recorded regions beyond start of variable */ | ||
| 4435 | removerecordregions(startloc); | ||
| 4436 | goto record; | ||
| 4437 | } | ||
| 4438 | |||
| 4439 | end: | ||
| 4440 | if (subtype != VSNORMAL) { /* skip to end of alternative */ | ||
| 4441 | int nesting = 1; | ||
| 4442 | for (;;) { | ||
| 4443 | c = *p++; | ||
| 4444 | if (c == CTLESC) | ||
| 4445 | p++; | ||
| 4446 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { | ||
| 4447 | if (varlen >= 0) | ||
| 4448 | argbackq = argbackq->next; | ||
| 4449 | } else if (c == CTLVAR) { | ||
| 4450 | if ((*p++ & VSTYPE) != VSNORMAL) | ||
| 4451 | nesting++; | ||
| 4452 | } else if (c == CTLENDVAR) { | ||
| 4453 | if (--nesting == 0) | ||
| 4454 | break; | ||
| 4455 | } | ||
| 4456 | } | ||
| 4457 | } | ||
| 4458 | return p; | ||
| 4459 | } | ||
| 4460 | |||
| 4461 | /* | ||
| 4462 | * Break the argument string into pieces based upon IFS and add the | ||
| 4463 | * strings to the argument list. The regions of the string to be | ||
| 4464 | * searched for IFS characters have been stored by recordregion. | ||
| 4465 | */ | ||
| 4466 | static void | ||
| 4467 | ifsbreakup(char *string, struct arglist *arglist) | ||
| 4468 | { | ||
| 4469 | struct ifsregion *ifsp; | ||
| 4470 | struct strlist *sp; | ||
| 4471 | char *start; | ||
| 4472 | char *p; | ||
| 4473 | char *q; | ||
| 4474 | const char *ifs, *realifs; | ||
| 4475 | int ifsspc; | ||
| 4476 | int nulonly; | ||
| 4477 | |||
| 4478 | start = string; | ||
| 4479 | if (ifslastp != NULL) { | ||
| 4480 | ifsspc = 0; | ||
| 4481 | nulonly = 0; | ||
| 4482 | realifs = ifsset() ? ifsval() : defifs; | ||
| 4483 | ifsp = &ifsfirst; | ||
| 4484 | do { | ||
| 4485 | p = string + ifsp->begoff; | ||
| 4486 | nulonly = ifsp->nulonly; | ||
| 4487 | ifs = nulonly ? nullstr : realifs; | ||
| 4488 | ifsspc = 0; | ||
| 4489 | while (p < string + ifsp->endoff) { | ||
| 4490 | q = p; | ||
| 4491 | if (*p == CTLESC) | ||
| 4492 | p++; | ||
| 4493 | if (!strchr(ifs, *p)) { | ||
| 4494 | p++; | ||
| 4495 | continue; | ||
| 4496 | } | ||
| 4497 | if (!nulonly) | ||
| 4498 | ifsspc = (strchr(defifs, *p) != NULL); | ||
| 4499 | /* Ignore IFS whitespace at start */ | ||
| 4500 | if (q == start && ifsspc) { | ||
| 4501 | p++; | ||
| 4502 | start = p; | ||
| 4503 | continue; | ||
| 4504 | } | ||
| 4505 | *q = '\0'; | ||
| 4506 | sp = stalloc(sizeof(*sp)); | ||
| 4507 | sp->text = start; | ||
| 4508 | *arglist->lastp = sp; | ||
| 4509 | arglist->lastp = &sp->next; | ||
| 4510 | p++; | ||
| 4511 | if (!nulonly) { | ||
| 4512 | for (;;) { | ||
| 4513 | if (p >= string + ifsp->endoff) { | ||
| 4514 | break; | ||
| 4515 | } | ||
| 4516 | q = p; | ||
| 4517 | if (*p == CTLESC) | ||
| 4518 | p++; | ||
| 4519 | if (strchr(ifs, *p) == NULL ) { | ||
| 4520 | p = q; | ||
| 4521 | break; | ||
| 4522 | } else if (strchr(defifs, *p) == NULL) { | ||
| 4523 | if (ifsspc) { | ||
| 4524 | p++; | ||
| 4525 | ifsspc = 0; | ||
| 4526 | } else { | ||
| 4527 | p = q; | ||
| 4528 | break; | ||
| 4529 | } | ||
| 4530 | } else | ||
| 4531 | p++; | ||
| 4532 | } | ||
| 4533 | } | ||
| 4534 | start = p; | ||
| 4535 | } /* while */ | ||
| 4536 | ifsp = ifsp->next; | ||
| 4537 | } while (ifsp != NULL); | ||
| 4538 | if (nulonly) | ||
| 4539 | goto add; | ||
| 4540 | } | ||
| 4541 | |||
| 4542 | if (!*start) | ||
| 4543 | return; | ||
| 4544 | |||
| 4545 | add: | ||
| 4546 | sp = stalloc(sizeof(*sp)); | ||
| 4547 | sp->text = start; | ||
| 4548 | *arglist->lastp = sp; | ||
| 4549 | arglist->lastp = &sp->next; | ||
| 4550 | } | ||
| 4551 | |||
| 4552 | static void | ||
| 4553 | ifsfree(void) | ||
| 4554 | { | ||
| 4555 | struct ifsregion *p; | ||
| 4556 | |||
| 4557 | INT_OFF; | ||
| 4558 | p = ifsfirst.next; | ||
| 4559 | do { | ||
| 4560 | struct ifsregion *ifsp; | ||
| 4561 | ifsp = p->next; | ||
| 4562 | free(p); | ||
| 4563 | p = ifsp; | ||
| 4564 | } while (p); | ||
| 4565 | ifslastp = NULL; | ||
| 4566 | ifsfirst.next = NULL; | ||
| 4567 | INT_ON; | ||
| 4568 | } | ||
| 4569 | |||
| 4570 | /* | ||
| 4571 | * Add a file name to the list. | ||
| 4572 | */ | ||
| 4573 | static void | ||
| 4574 | addfname(const char *name) | ||
| 4575 | { | ||
| 4576 | struct strlist *sp; | ||
| 4577 | |||
| 4578 | sp = stalloc(sizeof(*sp)); | ||
| 4579 | sp->text = ststrdup(name); | ||
| 4580 | *exparg.lastp = sp; | ||
| 4581 | exparg.lastp = &sp->next; | ||
| 4582 | } | ||
| 4583 | |||
| 4584 | static char *expdir; | ||
| 4585 | |||
| 4586 | /* | ||
| 4587 | * Do metacharacter (i.e. *, ?, [...]) expansion. | ||
| 4588 | */ | ||
| 4589 | static void | ||
| 4590 | expmeta(char *enddir, char *name) | ||
| 4591 | { | ||
| 4592 | char *p; | ||
| 4593 | const char *cp; | ||
| 4594 | char *start; | ||
| 4595 | char *endname; | ||
| 4596 | int metaflag; | ||
| 4597 | struct stat statb; | ||
| 4598 | DIR *dirp; | ||
| 4599 | struct dirent *dp; | ||
| 4600 | int atend; | ||
| 4601 | int matchdot; | ||
| 4602 | |||
| 4603 | metaflag = 0; | ||
| 4604 | start = name; | ||
| 4605 | for (p = name; *p; p++) { | ||
| 4606 | if (*p == '*' || *p == '?') | ||
| 4607 | metaflag = 1; | ||
| 4608 | else if (*p == '[') { | ||
| 4609 | char *q = p + 1; | ||
| 4610 | if (*q == '!') | ||
| 4611 | q++; | ||
| 4612 | for (;;) { | ||
| 4613 | if (*q == '\\') | ||
| 4614 | q++; | ||
| 4615 | if (*q == '/' || *q == '\0') | ||
| 4616 | break; | ||
| 4617 | if (*++q == ']') { | ||
| 4618 | metaflag = 1; | ||
| 4619 | break; | ||
| 4620 | } | ||
| 4621 | } | ||
| 4622 | } else if (*p == '\\') | ||
| 4623 | p++; | ||
| 4624 | else if (*p == '/') { | ||
| 4625 | if (metaflag) | ||
| 4626 | goto out; | ||
| 4627 | start = p + 1; | ||
| 4628 | } | ||
| 4629 | } | ||
| 4630 | out: | ||
| 4631 | if (metaflag == 0) { /* we've reached the end of the file name */ | ||
| 4632 | if (enddir != expdir) | ||
| 4633 | metaflag++; | ||
| 4634 | p = name; | ||
| 4635 | do { | ||
| 4636 | if (*p == '\\') | ||
| 4637 | p++; | ||
| 4638 | *enddir++ = *p; | ||
| 4639 | } while (*p++); | ||
| 4640 | if (metaflag == 0 || lstat(expdir, &statb) >= 0) | ||
| 4641 | addfname(expdir); | ||
| 4642 | return; | ||
| 4643 | } | ||
| 4644 | endname = p; | ||
| 4645 | if (name < start) { | ||
| 4646 | p = name; | ||
| 4647 | do { | ||
| 4648 | if (*p == '\\') | ||
| 4649 | p++; | ||
| 4650 | *enddir++ = *p++; | ||
| 4651 | } while (p < start); | ||
| 4652 | } | ||
| 4653 | if (enddir == expdir) { | ||
| 4654 | cp = "."; | ||
| 4655 | } else if (enddir == expdir + 1 && *expdir == '/') { | ||
| 4656 | cp = "/"; | ||
| 4657 | } else { | ||
| 4658 | cp = expdir; | ||
| 4659 | enddir[-1] = '\0'; | ||
| 4660 | } | ||
| 4661 | dirp = opendir(cp); | ||
| 4662 | if (dirp == NULL) | ||
| 4663 | return; | ||
| 4664 | if (enddir != expdir) | ||
| 4665 | enddir[-1] = '/'; | ||
| 4666 | if (*endname == 0) { | ||
| 4667 | atend = 1; | ||
| 4668 | } else { | ||
| 4669 | atend = 0; | ||
| 4670 | *endname++ = '\0'; | ||
| 4671 | } | ||
| 4672 | matchdot = 0; | ||
| 4673 | p = start; | ||
| 4674 | if (*p == '\\') | ||
| 4675 | p++; | ||
| 4676 | if (*p == '.') | ||
| 4677 | matchdot++; | ||
| 4678 | while (! intpending && (dp = readdir(dirp)) != NULL) { | ||
| 4679 | if (dp->d_name[0] == '.' && ! matchdot) | ||
| 4680 | continue; | ||
| 4681 | if (pmatch(start, dp->d_name)) { | ||
| 4682 | if (atend) { | ||
| 4683 | strcpy(enddir, dp->d_name); | ||
| 4684 | addfname(expdir); | ||
| 4685 | } else { | ||
| 4686 | for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) | ||
| 4687 | continue; | ||
| 4688 | p[-1] = '/'; | ||
| 4689 | expmeta(p, endname); | ||
| 4690 | } | ||
| 4691 | } | ||
| 4692 | } | ||
| 4693 | closedir(dirp); | ||
| 4694 | if (! atend) | ||
| 4695 | endname[-1] = '/'; | ||
| 4696 | } | ||
| 4697 | |||
| 4698 | static struct strlist * | ||
| 4699 | msort(struct strlist *list, int len) | ||
| 4700 | { | ||
| 4701 | struct strlist *p, *q = NULL; | ||
| 4702 | struct strlist **lpp; | ||
| 4703 | int half; | ||
| 4704 | int n; | ||
| 4705 | |||
| 4706 | if (len <= 1) | ||
| 4707 | return list; | ||
| 4708 | half = len >> 1; | ||
| 4709 | p = list; | ||
| 4710 | for (n = half; --n >= 0; ) { | ||
| 4711 | q = p; | ||
| 4712 | p = p->next; | ||
| 4713 | } | ||
| 4714 | q->next = NULL; /* terminate first half of list */ | ||
| 4715 | q = msort(list, half); /* sort first half of list */ | ||
| 4716 | p = msort(p, len - half); /* sort second half */ | ||
| 4717 | lpp = &list; | ||
| 4718 | for (;;) { | ||
| 4719 | #if ENABLE_LOCALE_SUPPORT | ||
| 4720 | if (strcoll(p->text, q->text) < 0) | ||
| 4721 | #else | ||
| 4722 | if (strcmp(p->text, q->text) < 0) | ||
| 4723 | #endif | ||
| 4724 | { | ||
| 4725 | *lpp = p; | ||
| 4726 | lpp = &p->next; | ||
| 4727 | p = *lpp; | ||
| 4728 | if (p == NULL) { | ||
| 4729 | *lpp = q; | ||
| 4730 | break; | ||
| 4731 | } | ||
| 4732 | } else { | ||
| 4733 | *lpp = q; | ||
| 4734 | lpp = &q->next; | ||
| 4735 | q = *lpp; | ||
| 4736 | if (q == NULL) { | ||
| 4737 | *lpp = p; | ||
| 4738 | break; | ||
| 4739 | } | ||
| 4740 | } | ||
| 4741 | } | ||
| 4742 | return list; | ||
| 4743 | } | ||
| 4744 | |||
| 4745 | /* | ||
| 4746 | * Sort the results of file name expansion. It calculates the number of | ||
| 4747 | * strings to sort and then calls msort (short for merge sort) to do the | ||
| 4748 | * work. | ||
| 4749 | */ | ||
| 4750 | static struct strlist * | ||
| 4751 | expsort(struct strlist *str) | ||
| 4752 | { | ||
| 4753 | int len; | ||
| 4754 | struct strlist *sp; | ||
| 4755 | |||
| 4756 | len = 0; | ||
| 4757 | for (sp = str; sp; sp = sp->next) | ||
| 4758 | len++; | ||
| 4759 | return msort(str, len); | ||
| 4760 | } | ||
| 4761 | |||
| 4762 | static void | ||
| 4763 | expandmeta(struct strlist *str, int flag) | ||
| 4764 | { | ||
| 4765 | static const char metachars[] = { | ||
| 4766 | '*', '?', '[', 0 | ||
| 4767 | }; | ||
| 4768 | /* TODO - EXP_REDIR */ | ||
| 4769 | |||
| 4770 | while (str) { | ||
| 4771 | struct strlist **savelastp; | ||
| 4772 | struct strlist *sp; | ||
| 4773 | char *p; | ||
| 4774 | |||
| 4775 | if (fflag) | ||
| 4776 | goto nometa; | ||
| 4777 | if (!strpbrk(str->text, metachars)) | ||
| 4778 | goto nometa; | ||
| 4779 | savelastp = exparg.lastp; | ||
| 4780 | |||
| 4781 | INT_OFF; | ||
| 4782 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); | ||
| 4783 | { | ||
| 4784 | int i = strlen(str->text); | ||
| 4785 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | ||
| 4786 | } | ||
| 4787 | |||
| 4788 | expmeta(expdir, p); | ||
| 4789 | free(expdir); | ||
| 4790 | if (p != str->text) | ||
| 4791 | free(p); | ||
| 4792 | INT_ON; | ||
| 4793 | if (exparg.lastp == savelastp) { | ||
| 4794 | /* | ||
| 4795 | * no matches | ||
| 4796 | */ | ||
| 4797 | nometa: | ||
| 4798 | *exparg.lastp = str; | ||
| 4799 | rmescapes(str->text); | ||
| 4800 | exparg.lastp = &str->next; | ||
| 4801 | } else { | ||
| 4802 | *exparg.lastp = NULL; | ||
| 4803 | *savelastp = sp = expsort(*savelastp); | ||
| 4804 | while (sp->next != NULL) | ||
| 4805 | sp = sp->next; | ||
| 4806 | exparg.lastp = &sp->next; | ||
| 4807 | } | ||
| 4808 | str = str->next; | ||
| 4809 | } | ||
| 4810 | } | ||
| 4811 | |||
| 4812 | /* | ||
| 4813 | * Perform variable substitution and command substitution on an argument, | ||
| 4814 | * placing the resulting list of arguments in arglist. If EXP_FULL is true, | ||
| 4815 | * perform splitting and file name expansion. When arglist is NULL, perform | ||
| 4816 | * here document expansion. | ||
| 4817 | */ | ||
| 4818 | static void | ||
| 4819 | expandarg(union node *arg, struct arglist *arglist, int flag) | ||
| 4820 | { | ||
| 4821 | struct strlist *sp; | ||
| 4822 | char *p; | ||
| 4823 | |||
| 4824 | argbackq = arg->narg.backquote; | ||
| 4825 | STARTSTACKSTR(expdest); | ||
| 4826 | ifsfirst.next = NULL; | ||
| 4827 | ifslastp = NULL; | ||
| 4828 | argstr(arg->narg.text, flag); | ||
| 4829 | p = _STPUTC('\0', expdest); | ||
| 4830 | expdest = p - 1; | ||
| 4831 | if (arglist == NULL) { | ||
| 4832 | return; /* here document expanded */ | ||
| 4833 | } | ||
| 4834 | p = grabstackstr(p); | ||
| 4835 | exparg.lastp = &exparg.list; | ||
| 4836 | /* | ||
| 4837 | * TODO - EXP_REDIR | ||
| 4838 | */ | ||
| 4839 | if (flag & EXP_FULL) { | ||
| 4840 | ifsbreakup(p, &exparg); | ||
| 4841 | *exparg.lastp = NULL; | ||
| 4842 | exparg.lastp = &exparg.list; | ||
| 4843 | expandmeta(exparg.list, flag); | ||
| 4844 | } else { | ||
| 4845 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ | ||
| 4846 | rmescapes(p); | ||
| 4847 | sp = stalloc(sizeof(*sp)); | ||
| 4848 | sp->text = p; | ||
| 4849 | *exparg.lastp = sp; | ||
| 4850 | exparg.lastp = &sp->next; | ||
| 4851 | } | ||
| 4852 | if (ifsfirst.next) | ||
| 4853 | ifsfree(); | ||
| 4854 | *exparg.lastp = NULL; | ||
| 4855 | if (exparg.list) { | ||
| 4856 | *arglist->lastp = exparg.list; | ||
| 4857 | arglist->lastp = exparg.lastp; | ||
| 4858 | } | ||
| 4859 | } | ||
| 4860 | |||
| 4861 | /* | ||
| 4862 | * Expand shell variables and backquotes inside a here document. | ||
| 4863 | */ | ||
| 4864 | static void | ||
| 4865 | expandhere(union node *arg, int fd) | ||
| 4866 | { | ||
| 4867 | herefd = fd; | ||
| 4868 | expandarg(arg, (struct arglist *)NULL, 0); | ||
| 4869 | full_write(fd, stackblock(), expdest - (char *)stackblock()); | ||
| 4870 | } | ||
| 4871 | |||
| 4872 | /* | ||
| 4873 | * Returns true if the pattern matches the string. | ||
| 4874 | */ | ||
| 4875 | static int | ||
| 4876 | patmatch(char *pattern, const char *string) | ||
| 4877 | { | ||
| 4878 | return pmatch(preglob(pattern, 0, 0), string); | ||
| 4879 | } | ||
| 4880 | |||
| 4881 | /* | ||
| 4882 | * See if a pattern matches in a case statement. | ||
| 4883 | */ | ||
| 4884 | static int | ||
| 4885 | casematch(union node *pattern, char *val) | ||
| 4886 | { | ||
| 4887 | struct stackmark smark; | ||
| 4888 | int result; | ||
| 4889 | |||
| 4890 | setstackmark(&smark); | ||
| 4891 | argbackq = pattern->narg.backquote; | ||
| 4892 | STARTSTACKSTR(expdest); | ||
| 4893 | ifslastp = NULL; | ||
| 4894 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); | ||
| 4895 | STACKSTRNUL(expdest); | ||
| 4896 | result = patmatch(stackblock(), val); | ||
| 4897 | popstackmark(&smark); | ||
| 4898 | return result; | ||
| 4899 | } | ||
| 4900 | |||
| 4901 | |||
| 4902 | /* ============ eval.c */ | ||
| 3449 | 4903 | ||
| 3450 | /* flags in argument to evaltree */ | 4904 | /* flags in argument to evaltree */ |
| 3451 | #define EV_EXIT 01 /* exit after evaluating tree */ | 4905 | #define EV_EXIT 01 /* exit after evaluating tree */ |
| 3452 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | 4906 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
| 3453 | #define EV_BACKCMD 04 /* command executing within back quotes */ | 4907 | #define EV_BACKCMD 04 /* command executing within back quotes */ |
| 3454 | 4908 | ||
| 3455 | /* forward declarations - evaluation is faily recursive business... */ | 4909 | /* forward declarations - evaluation is fairly recursive business... */ |
| 3456 | static void evalloop(union node *, int); | 4910 | static void evalloop(union node *, int); |
| 3457 | static void evalfor(union node *, int); | 4911 | static void evalfor(union node *, int); |
| 3458 | static void evalcase(union node *, int); | 4912 | static void evalcase(union node *, int); |
| @@ -5218,1517 +6672,6 @@ commandcmd(int argc, char **argv) | |||
| 5218 | } | 6672 | } |
| 5219 | #endif | 6673 | #endif |
| 5220 | 6674 | ||
| 5221 | /* expand.c */ | ||
| 5222 | |||
| 5223 | /* | ||
| 5224 | * Routines to expand arguments to commands. We have to deal with | ||
| 5225 | * backquotes, shell variables, and file metacharacters. | ||
| 5226 | */ | ||
| 5227 | |||
| 5228 | /* | ||
| 5229 | * _rmescape() flags | ||
| 5230 | */ | ||
| 5231 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ | ||
| 5232 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | ||
| 5233 | #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ | ||
| 5234 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ | ||
| 5235 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | ||
| 5236 | |||
| 5237 | /* | ||
| 5238 | * Structure specifying which parts of the string should be searched | ||
| 5239 | * for IFS characters. | ||
| 5240 | */ | ||
| 5241 | |||
| 5242 | struct ifsregion { | ||
| 5243 | struct ifsregion *next; /* next region in list */ | ||
| 5244 | int begoff; /* offset of start of region */ | ||
| 5245 | int endoff; /* offset of end of region */ | ||
| 5246 | int nulonly; /* search for nul bytes only */ | ||
| 5247 | }; | ||
| 5248 | |||
| 5249 | /* output of current string */ | ||
| 5250 | static char *expdest; | ||
| 5251 | /* list of back quote expressions */ | ||
| 5252 | static struct nodelist *argbackq; | ||
| 5253 | /* first struct in list of ifs regions */ | ||
| 5254 | static struct ifsregion ifsfirst; | ||
| 5255 | /* last struct in list */ | ||
| 5256 | static struct ifsregion *ifslastp; | ||
| 5257 | /* holds expanded arg list */ | ||
| 5258 | static struct arglist exparg; | ||
| 5259 | |||
| 5260 | static void argstr(char *, int); | ||
| 5261 | static char *exptilde(char *, char *, int); | ||
| 5262 | static void expbackq(union node *, int, int); | ||
| 5263 | static const char *subevalvar(char *, char *, int, int, int, int, int); | ||
| 5264 | static char *evalvar(char *, int); | ||
| 5265 | static void strtodest(const char *, int, int); | ||
| 5266 | static void memtodest(const char *p, size_t len, int syntax, int quotes); | ||
| 5267 | static ssize_t varvalue(char *, int, int); | ||
| 5268 | static void recordregion(int, int, int); | ||
| 5269 | static void removerecordregions(int); | ||
| 5270 | static void ifsbreakup(char *, struct arglist *); | ||
| 5271 | static void ifsfree(void); | ||
| 5272 | static void expandmeta(struct strlist *, int); | ||
| 5273 | static int patmatch(char *, const char *); | ||
| 5274 | |||
| 5275 | static int cvtnum(arith_t); | ||
| 5276 | static size_t esclen(const char *, const char *); | ||
| 5277 | static char *scanleft(char *, char *, char *, char *, int, int); | ||
| 5278 | static char *scanright(char *, char *, char *, char *, int, int); | ||
| 5279 | static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN; | ||
| 5280 | |||
| 5281 | |||
| 5282 | #define pmatch(a, b) !fnmatch((a), (b), 0) | ||
| 5283 | /* | ||
| 5284 | * Prepare a pattern for a expmeta (internal glob(3)) call. | ||
| 5285 | * | ||
| 5286 | * Returns an stalloced string. | ||
| 5287 | */ | ||
| 5288 | static char * | ||
| 5289 | preglob(const char *pattern, int quoted, int flag) | ||
| 5290 | { | ||
| 5291 | flag |= RMESCAPE_GLOB; | ||
| 5292 | if (quoted) { | ||
| 5293 | flag |= RMESCAPE_QUOTED; | ||
| 5294 | } | ||
| 5295 | return _rmescapes((char *)pattern, flag); | ||
| 5296 | } | ||
| 5297 | |||
| 5298 | |||
| 5299 | static size_t | ||
| 5300 | esclen(const char *start, const char *p) | ||
| 5301 | { | ||
| 5302 | size_t esc = 0; | ||
| 5303 | |||
| 5304 | while (p > start && *--p == CTLESC) { | ||
| 5305 | esc++; | ||
| 5306 | } | ||
| 5307 | return esc; | ||
| 5308 | } | ||
| 5309 | |||
| 5310 | |||
| 5311 | /* | ||
| 5312 | * Expand shell variables and backquotes inside a here document. | ||
| 5313 | */ | ||
| 5314 | static void | ||
| 5315 | expandhere(union node *arg, int fd) | ||
| 5316 | { | ||
| 5317 | herefd = fd; | ||
| 5318 | expandarg(arg, (struct arglist *)NULL, 0); | ||
| 5319 | full_write(fd, stackblock(), expdest - (char *)stackblock()); | ||
| 5320 | } | ||
| 5321 | |||
| 5322 | |||
| 5323 | /* | ||
| 5324 | * Perform variable substitution and command substitution on an argument, | ||
| 5325 | * placing the resulting list of arguments in arglist. If EXP_FULL is true, | ||
| 5326 | * perform splitting and file name expansion. When arglist is NULL, perform | ||
| 5327 | * here document expansion. | ||
| 5328 | */ | ||
| 5329 | static void | ||
| 5330 | expandarg(union node *arg, struct arglist *arglist, int flag) | ||
| 5331 | { | ||
| 5332 | struct strlist *sp; | ||
| 5333 | char *p; | ||
| 5334 | |||
| 5335 | argbackq = arg->narg.backquote; | ||
| 5336 | STARTSTACKSTR(expdest); | ||
| 5337 | ifsfirst.next = NULL; | ||
| 5338 | ifslastp = NULL; | ||
| 5339 | argstr(arg->narg.text, flag); | ||
| 5340 | p = _STPUTC('\0', expdest); | ||
| 5341 | expdest = p - 1; | ||
| 5342 | if (arglist == NULL) { | ||
| 5343 | return; /* here document expanded */ | ||
| 5344 | } | ||
| 5345 | p = grabstackstr(p); | ||
| 5346 | exparg.lastp = &exparg.list; | ||
| 5347 | /* | ||
| 5348 | * TODO - EXP_REDIR | ||
| 5349 | */ | ||
| 5350 | if (flag & EXP_FULL) { | ||
| 5351 | ifsbreakup(p, &exparg); | ||
| 5352 | *exparg.lastp = NULL; | ||
| 5353 | exparg.lastp = &exparg.list; | ||
| 5354 | expandmeta(exparg.list, flag); | ||
| 5355 | } else { | ||
| 5356 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ | ||
| 5357 | rmescapes(p); | ||
| 5358 | sp = stalloc(sizeof(*sp)); | ||
| 5359 | sp->text = p; | ||
| 5360 | *exparg.lastp = sp; | ||
| 5361 | exparg.lastp = &sp->next; | ||
| 5362 | } | ||
| 5363 | if (ifsfirst.next) | ||
| 5364 | ifsfree(); | ||
| 5365 | *exparg.lastp = NULL; | ||
| 5366 | if (exparg.list) { | ||
| 5367 | *arglist->lastp = exparg.list; | ||
| 5368 | arglist->lastp = exparg.lastp; | ||
| 5369 | } | ||
| 5370 | } | ||
| 5371 | |||
| 5372 | |||
| 5373 | /* | ||
| 5374 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC | ||
| 5375 | * characters to allow for further processing. Otherwise treat | ||
| 5376 | * $@ like $* since no splitting will be performed. | ||
| 5377 | */ | ||
| 5378 | static void | ||
| 5379 | argstr(char *p, int flag) | ||
| 5380 | { | ||
| 5381 | static const char spclchars[] = { | ||
| 5382 | '=', | ||
| 5383 | ':', | ||
| 5384 | CTLQUOTEMARK, | ||
| 5385 | CTLENDVAR, | ||
| 5386 | CTLESC, | ||
| 5387 | CTLVAR, | ||
| 5388 | CTLBACKQ, | ||
| 5389 | CTLBACKQ | CTLQUOTE, | ||
| 5390 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 5391 | CTLENDARI, | ||
| 5392 | #endif | ||
| 5393 | 0 | ||
| 5394 | }; | ||
| 5395 | const char *reject = spclchars; | ||
| 5396 | int c; | ||
| 5397 | int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ | ||
| 5398 | int breakall = flag & EXP_WORD; | ||
| 5399 | int inquotes; | ||
| 5400 | size_t length; | ||
| 5401 | int startloc; | ||
| 5402 | |||
| 5403 | if (!(flag & EXP_VARTILDE)) { | ||
| 5404 | reject += 2; | ||
| 5405 | } else if (flag & EXP_VARTILDE2) { | ||
| 5406 | reject++; | ||
| 5407 | } | ||
| 5408 | inquotes = 0; | ||
| 5409 | length = 0; | ||
| 5410 | if (flag & EXP_TILDE) { | ||
| 5411 | char *q; | ||
| 5412 | |||
| 5413 | flag &= ~EXP_TILDE; | ||
| 5414 | tilde: | ||
| 5415 | q = p; | ||
| 5416 | if (*q == CTLESC && (flag & EXP_QWORD)) | ||
| 5417 | q++; | ||
| 5418 | if (*q == '~') | ||
| 5419 | p = exptilde(p, q, flag); | ||
| 5420 | } | ||
| 5421 | start: | ||
| 5422 | startloc = expdest - (char *)stackblock(); | ||
| 5423 | for (;;) { | ||
| 5424 | length += strcspn(p + length, reject); | ||
| 5425 | c = p[length]; | ||
| 5426 | if (c && (!(c & 0x80) | ||
| 5427 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 5428 | || c == CTLENDARI | ||
| 5429 | #endif | ||
| 5430 | )) { | ||
| 5431 | /* c == '=' || c == ':' || c == CTLENDARI */ | ||
| 5432 | length++; | ||
| 5433 | } | ||
| 5434 | if (length > 0) { | ||
| 5435 | int newloc; | ||
| 5436 | expdest = stack_nputstr(p, length, expdest); | ||
| 5437 | newloc = expdest - (char *)stackblock(); | ||
| 5438 | if (breakall && !inquotes && newloc > startloc) { | ||
| 5439 | recordregion(startloc, newloc, 0); | ||
| 5440 | } | ||
| 5441 | startloc = newloc; | ||
| 5442 | } | ||
| 5443 | p += length + 1; | ||
| 5444 | length = 0; | ||
| 5445 | |||
| 5446 | switch (c) { | ||
| 5447 | case '\0': | ||
| 5448 | goto breakloop; | ||
| 5449 | case '=': | ||
| 5450 | if (flag & EXP_VARTILDE2) { | ||
| 5451 | p--; | ||
| 5452 | continue; | ||
| 5453 | } | ||
| 5454 | flag |= EXP_VARTILDE2; | ||
| 5455 | reject++; | ||
| 5456 | /* fall through */ | ||
| 5457 | case ':': | ||
| 5458 | /* | ||
| 5459 | * sort of a hack - expand tildes in variable | ||
| 5460 | * assignments (after the first '=' and after ':'s). | ||
| 5461 | */ | ||
| 5462 | if (*--p == '~') { | ||
| 5463 | goto tilde; | ||
| 5464 | } | ||
| 5465 | continue; | ||
| 5466 | } | ||
| 5467 | |||
| 5468 | switch (c) { | ||
| 5469 | case CTLENDVAR: /* ??? */ | ||
| 5470 | goto breakloop; | ||
| 5471 | case CTLQUOTEMARK: | ||
| 5472 | /* "$@" syntax adherence hack */ | ||
| 5473 | if ( | ||
| 5474 | !inquotes && | ||
| 5475 | !memcmp(p, dolatstr, 4) && | ||
| 5476 | (p[4] == CTLQUOTEMARK || ( | ||
| 5477 | p[4] == CTLENDVAR && | ||
| 5478 | p[5] == CTLQUOTEMARK | ||
| 5479 | )) | ||
| 5480 | ) { | ||
| 5481 | p = evalvar(p + 1, flag) + 1; | ||
| 5482 | goto start; | ||
| 5483 | } | ||
| 5484 | inquotes = !inquotes; | ||
| 5485 | addquote: | ||
| 5486 | if (quotes) { | ||
| 5487 | p--; | ||
| 5488 | length++; | ||
| 5489 | startloc++; | ||
| 5490 | } | ||
| 5491 | break; | ||
| 5492 | case CTLESC: | ||
| 5493 | startloc++; | ||
| 5494 | length++; | ||
| 5495 | goto addquote; | ||
| 5496 | case CTLVAR: | ||
| 5497 | p = evalvar(p, flag); | ||
| 5498 | goto start; | ||
| 5499 | case CTLBACKQ: | ||
| 5500 | c = 0; | ||
| 5501 | case CTLBACKQ|CTLQUOTE: | ||
| 5502 | expbackq(argbackq->n, c, quotes); | ||
| 5503 | argbackq = argbackq->next; | ||
| 5504 | goto start; | ||
| 5505 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 5506 | case CTLENDARI: | ||
| 5507 | p--; | ||
| 5508 | expari(quotes); | ||
| 5509 | goto start; | ||
| 5510 | #endif | ||
| 5511 | } | ||
| 5512 | } | ||
| 5513 | breakloop: | ||
| 5514 | ; | ||
| 5515 | } | ||
| 5516 | |||
| 5517 | static char * | ||
| 5518 | exptilde(char *startp, char *p, int flag) | ||
| 5519 | { | ||
| 5520 | char c; | ||
| 5521 | char *name; | ||
| 5522 | struct passwd *pw; | ||
| 5523 | const char *home; | ||
| 5524 | int quotes = flag & (EXP_FULL | EXP_CASE); | ||
| 5525 | int startloc; | ||
| 5526 | |||
| 5527 | name = p + 1; | ||
| 5528 | |||
| 5529 | while ((c = *++p) != '\0') { | ||
| 5530 | switch (c) { | ||
| 5531 | case CTLESC: | ||
| 5532 | return startp; | ||
| 5533 | case CTLQUOTEMARK: | ||
| 5534 | return startp; | ||
| 5535 | case ':': | ||
| 5536 | if (flag & EXP_VARTILDE) | ||
| 5537 | goto done; | ||
| 5538 | break; | ||
| 5539 | case '/': | ||
| 5540 | case CTLENDVAR: | ||
| 5541 | goto done; | ||
| 5542 | } | ||
| 5543 | } | ||
| 5544 | done: | ||
| 5545 | *p = '\0'; | ||
| 5546 | if (*name == '\0') { | ||
| 5547 | home = lookupvar(homestr); | ||
| 5548 | } else { | ||
| 5549 | pw = getpwnam(name); | ||
| 5550 | if (pw == NULL) | ||
| 5551 | goto lose; | ||
| 5552 | home = pw->pw_dir; | ||
| 5553 | } | ||
| 5554 | if (!home || !*home) | ||
| 5555 | goto lose; | ||
| 5556 | *p = c; | ||
| 5557 | startloc = expdest - (char *)stackblock(); | ||
| 5558 | strtodest(home, SQSYNTAX, quotes); | ||
| 5559 | recordregion(startloc, expdest - (char *)stackblock(), 0); | ||
| 5560 | return p; | ||
| 5561 | lose: | ||
| 5562 | *p = c; | ||
| 5563 | return startp; | ||
| 5564 | } | ||
| 5565 | |||
| 5566 | |||
| 5567 | static void | ||
| 5568 | removerecordregions(int endoff) | ||
| 5569 | { | ||
| 5570 | if (ifslastp == NULL) | ||
| 5571 | return; | ||
| 5572 | |||
| 5573 | if (ifsfirst.endoff > endoff) { | ||
| 5574 | while (ifsfirst.next != NULL) { | ||
| 5575 | struct ifsregion *ifsp; | ||
| 5576 | INT_OFF; | ||
| 5577 | ifsp = ifsfirst.next->next; | ||
| 5578 | free(ifsfirst.next); | ||
| 5579 | ifsfirst.next = ifsp; | ||
| 5580 | INT_ON; | ||
| 5581 | } | ||
| 5582 | if (ifsfirst.begoff > endoff) | ||
| 5583 | ifslastp = NULL; | ||
| 5584 | else { | ||
| 5585 | ifslastp = &ifsfirst; | ||
| 5586 | ifsfirst.endoff = endoff; | ||
| 5587 | } | ||
| 5588 | return; | ||
| 5589 | } | ||
| 5590 | |||
| 5591 | ifslastp = &ifsfirst; | ||
| 5592 | while (ifslastp->next && ifslastp->next->begoff < endoff) | ||
| 5593 | ifslastp=ifslastp->next; | ||
| 5594 | while (ifslastp->next != NULL) { | ||
| 5595 | struct ifsregion *ifsp; | ||
| 5596 | INT_OFF; | ||
| 5597 | ifsp = ifslastp->next->next; | ||
| 5598 | free(ifslastp->next); | ||
| 5599 | ifslastp->next = ifsp; | ||
| 5600 | INT_ON; | ||
| 5601 | } | ||
| 5602 | if (ifslastp->endoff > endoff) | ||
| 5603 | ifslastp->endoff = endoff; | ||
| 5604 | } | ||
| 5605 | |||
| 5606 | |||
| 5607 | #if ENABLE_ASH_MATH_SUPPORT | ||
| 5608 | /* | ||
| 5609 | * Expand arithmetic expression. Backup to start of expression, | ||
| 5610 | * evaluate, place result in (backed up) result, adjust string position. | ||
| 5611 | */ | ||
| 5612 | static void | ||
| 5613 | expari(int quotes) | ||
| 5614 | { | ||
| 5615 | char *p, *start; | ||
| 5616 | int begoff; | ||
| 5617 | int flag; | ||
| 5618 | int len; | ||
| 5619 | |||
| 5620 | /* ifsfree(); */ | ||
| 5621 | |||
| 5622 | /* | ||
| 5623 | * This routine is slightly over-complicated for | ||
| 5624 | * efficiency. Next we scan backwards looking for the | ||
| 5625 | * start of arithmetic. | ||
| 5626 | */ | ||
| 5627 | start = stackblock(); | ||
| 5628 | p = expdest - 1; | ||
| 5629 | *p = '\0'; | ||
| 5630 | p--; | ||
| 5631 | do { | ||
| 5632 | int esc; | ||
| 5633 | |||
| 5634 | while (*p != CTLARI) { | ||
| 5635 | p--; | ||
| 5636 | #if DEBUG | ||
| 5637 | if (p < start) { | ||
| 5638 | ash_msg_and_raise_error("missing CTLARI (shouldn't happen)"); | ||
| 5639 | } | ||
| 5640 | #endif | ||
| 5641 | } | ||
| 5642 | |||
| 5643 | esc = esclen(start, p); | ||
| 5644 | if (!(esc % 2)) { | ||
| 5645 | break; | ||
| 5646 | } | ||
| 5647 | |||
| 5648 | p -= esc + 1; | ||
| 5649 | } while (1); | ||
| 5650 | |||
| 5651 | begoff = p - start; | ||
| 5652 | |||
| 5653 | removerecordregions(begoff); | ||
| 5654 | |||
| 5655 | flag = p[1]; | ||
| 5656 | |||
| 5657 | expdest = p; | ||
| 5658 | |||
| 5659 | if (quotes) | ||
| 5660 | rmescapes(p + 2); | ||
| 5661 | |||
| 5662 | len = cvtnum(dash_arith(p + 2)); | ||
| 5663 | |||
| 5664 | if (flag != '"') | ||
| 5665 | recordregion(begoff, begoff + len, 0); | ||
| 5666 | } | ||
| 5667 | #endif | ||
| 5668 | |||
| 5669 | |||
| 5670 | /* | ||
| 5671 | * Execute a command inside back quotes. If it's a builtin command, we | ||
| 5672 | * want to save its output in a block obtained from malloc. Otherwise | ||
| 5673 | * we fork off a subprocess and get the output of the command via a pipe. | ||
| 5674 | * Should be called with interrupts off. | ||
| 5675 | */ | ||
| 5676 | struct backcmd { /* result of evalbackcmd */ | ||
| 5677 | int fd; /* file descriptor to read from */ | ||
| 5678 | char *buf; /* buffer */ | ||
| 5679 | int nleft; /* number of chars in buffer */ | ||
| 5680 | struct job *jp; /* job structure for command */ | ||
| 5681 | }; | ||
| 5682 | |||
| 5683 | static void | ||
| 5684 | evalbackcmd(union node *n, struct backcmd *result) | ||
| 5685 | { | ||
| 5686 | int saveherefd; | ||
| 5687 | |||
| 5688 | result->fd = -1; | ||
| 5689 | result->buf = NULL; | ||
| 5690 | result->nleft = 0; | ||
| 5691 | result->jp = NULL; | ||
| 5692 | if (n == NULL) { | ||
| 5693 | goto out; | ||
| 5694 | } | ||
| 5695 | |||
| 5696 | saveherefd = herefd; | ||
| 5697 | herefd = -1; | ||
| 5698 | |||
| 5699 | { | ||
| 5700 | int pip[2]; | ||
| 5701 | struct job *jp; | ||
| 5702 | |||
| 5703 | if (pipe(pip) < 0) | ||
| 5704 | ash_msg_and_raise_error("Pipe call failed"); | ||
| 5705 | jp = makejob(n, 1); | ||
| 5706 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | ||
| 5707 | FORCE_INT_ON; | ||
| 5708 | close(pip[0]); | ||
| 5709 | if (pip[1] != 1) { | ||
| 5710 | close(1); | ||
| 5711 | copyfd(pip[1], 1); | ||
| 5712 | close(pip[1]); | ||
| 5713 | } | ||
| 5714 | eflag = 0; | ||
| 5715 | evaltreenr(n, EV_EXIT); | ||
| 5716 | /* NOTREACHED */ | ||
| 5717 | } | ||
| 5718 | close(pip[1]); | ||
| 5719 | result->fd = pip[0]; | ||
| 5720 | result->jp = jp; | ||
| 5721 | } | ||
| 5722 | herefd = saveherefd; | ||
| 5723 | out: | ||
| 5724 | TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", | ||
| 5725 | result->fd, result->buf, result->nleft, result->jp)); | ||
| 5726 | } | ||
| 5727 | |||
| 5728 | /* | ||
| 5729 | * Expand stuff in backwards quotes. | ||
| 5730 | */ | ||
| 5731 | static void | ||
| 5732 | expbackq(union node *cmd, int quoted, int quotes) | ||
| 5733 | { | ||
| 5734 | struct backcmd in; | ||
| 5735 | int i; | ||
| 5736 | char buf[128]; | ||
| 5737 | char *p; | ||
| 5738 | char *dest; | ||
| 5739 | int startloc; | ||
| 5740 | int syntax = quoted? DQSYNTAX : BASESYNTAX; | ||
| 5741 | struct stackmark smark; | ||
| 5742 | |||
| 5743 | INT_OFF; | ||
| 5744 | setstackmark(&smark); | ||
| 5745 | dest = expdest; | ||
| 5746 | startloc = dest - (char *)stackblock(); | ||
| 5747 | grabstackstr(dest); | ||
| 5748 | evalbackcmd(cmd, &in); | ||
| 5749 | popstackmark(&smark); | ||
| 5750 | |||
| 5751 | p = in.buf; | ||
| 5752 | i = in.nleft; | ||
| 5753 | if (i == 0) | ||
| 5754 | goto read; | ||
| 5755 | for (;;) { | ||
| 5756 | memtodest(p, i, syntax, quotes); | ||
| 5757 | read: | ||
| 5758 | if (in.fd < 0) | ||
| 5759 | break; | ||
| 5760 | i = safe_read(in.fd, buf, sizeof(buf)); | ||
| 5761 | TRACE(("expbackq: read returns %d\n", i)); | ||
| 5762 | if (i <= 0) | ||
| 5763 | break; | ||
| 5764 | p = buf; | ||
| 5765 | } | ||
| 5766 | |||
| 5767 | if (in.buf) | ||
| 5768 | free(in.buf); | ||
| 5769 | if (in.fd >= 0) { | ||
| 5770 | close(in.fd); | ||
| 5771 | back_exitstatus = waitforjob(in.jp); | ||
| 5772 | } | ||
| 5773 | INT_ON; | ||
| 5774 | |||
| 5775 | /* Eat all trailing newlines */ | ||
| 5776 | dest = expdest; | ||
| 5777 | for (; dest > (char *)stackblock() && dest[-1] == '\n';) | ||
| 5778 | STUNPUTC(dest); | ||
| 5779 | expdest = dest; | ||
| 5780 | |||
| 5781 | if (quoted == 0) | ||
| 5782 | recordregion(startloc, dest - (char *)stackblock(), 0); | ||
| 5783 | TRACE(("evalbackq: size=%d: \"%.*s\"\n", | ||
| 5784 | (dest - (char *)stackblock()) - startloc, | ||
| 5785 | (dest - (char *)stackblock()) - startloc, | ||
| 5786 | stackblock() + startloc)); | ||
| 5787 | } | ||
| 5788 | |||
| 5789 | |||
| 5790 | static char * | ||
| 5791 | scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes, | ||
| 5792 | int zero) | ||
| 5793 | { | ||
| 5794 | char *loc; | ||
| 5795 | char *loc2; | ||
| 5796 | char c; | ||
| 5797 | |||
| 5798 | loc = startp; | ||
| 5799 | loc2 = rmesc; | ||
| 5800 | do { | ||
| 5801 | int match; | ||
| 5802 | const char *s = loc2; | ||
| 5803 | c = *loc2; | ||
| 5804 | if (zero) { | ||
| 5805 | *loc2 = '\0'; | ||
| 5806 | s = rmesc; | ||
| 5807 | } | ||
| 5808 | match = pmatch(str, s); | ||
| 5809 | *loc2 = c; | ||
| 5810 | if (match) | ||
| 5811 | return loc; | ||
| 5812 | if (quotes && *loc == CTLESC) | ||
| 5813 | loc++; | ||
| 5814 | loc++; | ||
| 5815 | loc2++; | ||
| 5816 | } while (c); | ||
| 5817 | return 0; | ||
| 5818 | } | ||
| 5819 | |||
| 5820 | |||
| 5821 | static char * | ||
| 5822 | scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes, | ||
| 5823 | int zero) | ||
| 5824 | { | ||
| 5825 | int esc = 0; | ||
| 5826 | char *loc; | ||
| 5827 | char *loc2; | ||
| 5828 | |||
| 5829 | for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { | ||
| 5830 | int match; | ||
| 5831 | char c = *loc2; | ||
| 5832 | const char *s = loc2; | ||
| 5833 | if (zero) { | ||
| 5834 | *loc2 = '\0'; | ||
| 5835 | s = rmesc; | ||
| 5836 | } | ||
| 5837 | match = pmatch(str, s); | ||
| 5838 | *loc2 = c; | ||
| 5839 | if (match) | ||
| 5840 | return loc; | ||
| 5841 | loc--; | ||
| 5842 | if (quotes) { | ||
| 5843 | if (--esc < 0) { | ||
| 5844 | esc = esclen(startp, loc); | ||
| 5845 | } | ||
| 5846 | if (esc % 2) { | ||
| 5847 | esc--; | ||
| 5848 | loc--; | ||
| 5849 | } | ||
| 5850 | } | ||
| 5851 | } | ||
| 5852 | return 0; | ||
| 5853 | } | ||
| 5854 | |||
| 5855 | static const char * | ||
| 5856 | subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) | ||
| 5857 | { | ||
| 5858 | char *startp; | ||
| 5859 | char *loc; | ||
| 5860 | int saveherefd = herefd; | ||
| 5861 | struct nodelist *saveargbackq = argbackq; | ||
| 5862 | int amount; | ||
| 5863 | char *rmesc, *rmescend; | ||
| 5864 | int zero; | ||
| 5865 | char *(*scan)(char *, char *, char *, char *, int , int); | ||
| 5866 | |||
| 5867 | herefd = -1; | ||
| 5868 | argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); | ||
| 5869 | STPUTC('\0', expdest); | ||
| 5870 | herefd = saveherefd; | ||
| 5871 | argbackq = saveargbackq; | ||
| 5872 | startp = stackblock() + startloc; | ||
| 5873 | |||
| 5874 | switch (subtype) { | ||
| 5875 | case VSASSIGN: | ||
| 5876 | setvar(str, startp, 0); | ||
| 5877 | amount = startp - expdest; | ||
| 5878 | STADJUST(amount, expdest); | ||
| 5879 | return startp; | ||
| 5880 | |||
| 5881 | case VSQUESTION: | ||
| 5882 | varunset(p, str, startp, varflags); | ||
| 5883 | /* NOTREACHED */ | ||
| 5884 | } | ||
| 5885 | |||
| 5886 | subtype -= VSTRIMRIGHT; | ||
| 5887 | #if DEBUG | ||
| 5888 | if (subtype < 0 || subtype > 3) | ||
| 5889 | abort(); | ||
| 5890 | #endif | ||
| 5891 | |||
| 5892 | rmesc = startp; | ||
| 5893 | rmescend = stackblock() + strloc; | ||
| 5894 | if (quotes) { | ||
| 5895 | rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); | ||
| 5896 | if (rmesc != startp) { | ||
| 5897 | rmescend = expdest; | ||
| 5898 | startp = stackblock() + startloc; | ||
| 5899 | } | ||
| 5900 | } | ||
| 5901 | rmescend--; | ||
| 5902 | str = stackblock() + strloc; | ||
| 5903 | preglob(str, varflags & VSQUOTE, 0); | ||
| 5904 | |||
| 5905 | /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ | ||
| 5906 | zero = subtype >> 1; | ||
| 5907 | /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ | ||
| 5908 | scan = (subtype & 1) ^ zero ? scanleft : scanright; | ||
| 5909 | |||
| 5910 | loc = scan(startp, rmesc, rmescend, str, quotes, zero); | ||
| 5911 | if (loc) { | ||
| 5912 | if (zero) { | ||
| 5913 | memmove(startp, loc, str - loc); | ||
| 5914 | loc = startp + (str - loc) - 1; | ||
| 5915 | } | ||
| 5916 | *loc = '\0'; | ||
| 5917 | amount = loc - expdest; | ||
| 5918 | STADJUST(amount, expdest); | ||
| 5919 | } | ||
| 5920 | return loc; | ||
| 5921 | } | ||
| 5922 | |||
| 5923 | |||
| 5924 | /* | ||
| 5925 | * Expand a variable, and return a pointer to the next character in the | ||
| 5926 | * input string. | ||
| 5927 | */ | ||
| 5928 | static char * | ||
| 5929 | evalvar(char *p, int flag) | ||
| 5930 | { | ||
| 5931 | int subtype; | ||
| 5932 | int varflags; | ||
| 5933 | char *var; | ||
| 5934 | int patloc; | ||
| 5935 | int c; | ||
| 5936 | int startloc; | ||
| 5937 | ssize_t varlen; | ||
| 5938 | int easy; | ||
| 5939 | int quotes; | ||
| 5940 | int quoted; | ||
| 5941 | |||
| 5942 | quotes = flag & (EXP_FULL | EXP_CASE); | ||
| 5943 | varflags = *p++; | ||
| 5944 | subtype = varflags & VSTYPE; | ||
| 5945 | quoted = varflags & VSQUOTE; | ||
| 5946 | var = p; | ||
| 5947 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | ||
| 5948 | startloc = expdest - (char *)stackblock(); | ||
| 5949 | p = strchr(p, '=') + 1; | ||
| 5950 | |||
| 5951 | again: | ||
| 5952 | varlen = varvalue(var, varflags, flag); | ||
| 5953 | if (varflags & VSNUL) | ||
| 5954 | varlen--; | ||
| 5955 | |||
| 5956 | if (subtype == VSPLUS) { | ||
| 5957 | varlen = -1 - varlen; | ||
| 5958 | goto vsplus; | ||
| 5959 | } | ||
| 5960 | |||
| 5961 | if (subtype == VSMINUS) { | ||
| 5962 | vsplus: | ||
| 5963 | if (varlen < 0) { | ||
| 5964 | argstr( | ||
| 5965 | p, flag | EXP_TILDE | | ||
| 5966 | (quoted ? EXP_QWORD : EXP_WORD) | ||
| 5967 | ); | ||
| 5968 | goto end; | ||
| 5969 | } | ||
| 5970 | if (easy) | ||
| 5971 | goto record; | ||
| 5972 | goto end; | ||
| 5973 | } | ||
| 5974 | |||
| 5975 | if (subtype == VSASSIGN || subtype == VSQUESTION) { | ||
| 5976 | if (varlen < 0) { | ||
| 5977 | if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) { | ||
| 5978 | varflags &= ~VSNUL; | ||
| 5979 | /* | ||
| 5980 | * Remove any recorded regions beyond | ||
| 5981 | * start of variable | ||
| 5982 | */ | ||
| 5983 | removerecordregions(startloc); | ||
| 5984 | goto again; | ||
| 5985 | } | ||
| 5986 | goto end; | ||
| 5987 | } | ||
| 5988 | if (easy) | ||
| 5989 | goto record; | ||
| 5990 | goto end; | ||
| 5991 | } | ||
| 5992 | |||
| 5993 | if (varlen < 0 && uflag) | ||
| 5994 | varunset(p, var, 0, 0); | ||
| 5995 | |||
| 5996 | if (subtype == VSLENGTH) { | ||
| 5997 | cvtnum(varlen > 0 ? varlen : 0); | ||
| 5998 | goto record; | ||
| 5999 | } | ||
| 6000 | |||
| 6001 | if (subtype == VSNORMAL) { | ||
| 6002 | if (!easy) | ||
| 6003 | goto end; | ||
| 6004 | record: | ||
| 6005 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
| 6006 | goto end; | ||
| 6007 | } | ||
| 6008 | |||
| 6009 | #if DEBUG | ||
| 6010 | switch (subtype) { | ||
| 6011 | case VSTRIMLEFT: | ||
| 6012 | case VSTRIMLEFTMAX: | ||
| 6013 | case VSTRIMRIGHT: | ||
| 6014 | case VSTRIMRIGHTMAX: | ||
| 6015 | break; | ||
| 6016 | default: | ||
| 6017 | abort(); | ||
| 6018 | } | ||
| 6019 | #endif | ||
| 6020 | |||
| 6021 | if (varlen >= 0) { | ||
| 6022 | /* | ||
| 6023 | * Terminate the string and start recording the pattern | ||
| 6024 | * right after it | ||
| 6025 | */ | ||
| 6026 | STPUTC('\0', expdest); | ||
| 6027 | patloc = expdest - (char *)stackblock(); | ||
| 6028 | if (subevalvar(p, NULL, patloc, subtype, | ||
| 6029 | startloc, varflags, quotes) == 0) { | ||
| 6030 | int amount = expdest - ( | ||
| 6031 | (char *)stackblock() + patloc - 1 | ||
| 6032 | ); | ||
| 6033 | STADJUST(-amount, expdest); | ||
| 6034 | } | ||
| 6035 | /* Remove any recorded regions beyond start of variable */ | ||
| 6036 | removerecordregions(startloc); | ||
| 6037 | goto record; | ||
| 6038 | } | ||
| 6039 | |||
| 6040 | end: | ||
| 6041 | if (subtype != VSNORMAL) { /* skip to end of alternative */ | ||
| 6042 | int nesting = 1; | ||
| 6043 | for (;;) { | ||
| 6044 | c = *p++; | ||
| 6045 | if (c == CTLESC) | ||
| 6046 | p++; | ||
| 6047 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { | ||
| 6048 | if (varlen >= 0) | ||
| 6049 | argbackq = argbackq->next; | ||
| 6050 | } else if (c == CTLVAR) { | ||
| 6051 | if ((*p++ & VSTYPE) != VSNORMAL) | ||
| 6052 | nesting++; | ||
| 6053 | } else if (c == CTLENDVAR) { | ||
| 6054 | if (--nesting == 0) | ||
| 6055 | break; | ||
| 6056 | } | ||
| 6057 | } | ||
| 6058 | } | ||
| 6059 | return p; | ||
| 6060 | } | ||
| 6061 | |||
| 6062 | |||
| 6063 | /* | ||
| 6064 | * Put a string on the stack. | ||
| 6065 | */ | ||
| 6066 | static void | ||
| 6067 | memtodest(const char *p, size_t len, int syntax, int quotes) | ||
| 6068 | { | ||
| 6069 | char *q = expdest; | ||
| 6070 | |||
| 6071 | q = makestrspace(len * 2, q); | ||
| 6072 | |||
| 6073 | while (len--) { | ||
| 6074 | int c = SC2INT(*p++); | ||
| 6075 | if (!c) | ||
| 6076 | continue; | ||
| 6077 | if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) | ||
| 6078 | USTPUTC(CTLESC, q); | ||
| 6079 | USTPUTC(c, q); | ||
| 6080 | } | ||
| 6081 | |||
| 6082 | expdest = q; | ||
| 6083 | } | ||
| 6084 | |||
| 6085 | |||
| 6086 | static void | ||
| 6087 | strtodest(const char *p, int syntax, int quotes) | ||
| 6088 | { | ||
| 6089 | memtodest(p, strlen(p), syntax, quotes); | ||
| 6090 | } | ||
| 6091 | |||
| 6092 | |||
| 6093 | /* | ||
| 6094 | * Add the value of a specialized variable to the stack string. | ||
| 6095 | */ | ||
| 6096 | static ssize_t | ||
| 6097 | varvalue(char *name, int varflags, int flags) | ||
| 6098 | { | ||
| 6099 | int num; | ||
| 6100 | char *p; | ||
| 6101 | int i; | ||
| 6102 | int sep = 0; | ||
| 6103 | int sepq = 0; | ||
| 6104 | ssize_t len = 0; | ||
| 6105 | char **ap; | ||
| 6106 | int syntax; | ||
| 6107 | int quoted = varflags & VSQUOTE; | ||
| 6108 | int subtype = varflags & VSTYPE; | ||
| 6109 | int quotes = flags & (EXP_FULL | EXP_CASE); | ||
| 6110 | |||
| 6111 | if (quoted && (flags & EXP_FULL)) | ||
| 6112 | sep = 1 << CHAR_BIT; | ||
| 6113 | |||
| 6114 | syntax = quoted ? DQSYNTAX : BASESYNTAX; | ||
| 6115 | switch (*name) { | ||
| 6116 | case '$': | ||
| 6117 | num = rootpid; | ||
| 6118 | goto numvar; | ||
| 6119 | case '?': | ||
| 6120 | num = exitstatus; | ||
| 6121 | goto numvar; | ||
| 6122 | case '#': | ||
| 6123 | num = shellparam.nparam; | ||
| 6124 | goto numvar; | ||
| 6125 | case '!': | ||
| 6126 | num = backgndpid; | ||
| 6127 | if (num == 0) | ||
| 6128 | return -1; | ||
| 6129 | numvar: | ||
| 6130 | len = cvtnum(num); | ||
| 6131 | break; | ||
| 6132 | case '-': | ||
| 6133 | p = makestrspace(NOPTS, expdest); | ||
| 6134 | for (i = NOPTS - 1; i >= 0; i--) { | ||
| 6135 | if (optlist[i]) { | ||
| 6136 | USTPUTC(optletters(i), p); | ||
| 6137 | len++; | ||
| 6138 | } | ||
| 6139 | } | ||
| 6140 | expdest = p; | ||
| 6141 | break; | ||
| 6142 | case '@': | ||
| 6143 | if (sep) | ||
| 6144 | goto param; | ||
| 6145 | /* fall through */ | ||
| 6146 | case '*': | ||
| 6147 | sep = ifsset() ? SC2INT(ifsval()[0]) : ' '; | ||
| 6148 | if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK)) | ||
| 6149 | sepq = 1; | ||
| 6150 | param: | ||
| 6151 | ap = shellparam.p; | ||
| 6152 | if (!ap) | ||
| 6153 | return -1; | ||
| 6154 | while ((p = *ap++)) { | ||
| 6155 | size_t partlen; | ||
| 6156 | |||
| 6157 | partlen = strlen(p); | ||
| 6158 | len += partlen; | ||
| 6159 | |||
| 6160 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 6161 | memtodest(p, partlen, syntax, quotes); | ||
| 6162 | |||
| 6163 | if (*ap && sep) { | ||
| 6164 | char *q; | ||
| 6165 | |||
| 6166 | len++; | ||
| 6167 | if (subtype == VSPLUS || subtype == VSLENGTH) { | ||
| 6168 | continue; | ||
| 6169 | } | ||
| 6170 | q = expdest; | ||
| 6171 | if (sepq) | ||
| 6172 | STPUTC(CTLESC, q); | ||
| 6173 | STPUTC(sep, q); | ||
| 6174 | expdest = q; | ||
| 6175 | } | ||
| 6176 | } | ||
| 6177 | return len; | ||
| 6178 | case '0': | ||
| 6179 | case '1': | ||
| 6180 | case '2': | ||
| 6181 | case '3': | ||
| 6182 | case '4': | ||
| 6183 | case '5': | ||
| 6184 | case '6': | ||
| 6185 | case '7': | ||
| 6186 | case '8': | ||
| 6187 | case '9': | ||
| 6188 | num = atoi(name); | ||
| 6189 | if (num < 0 || num > shellparam.nparam) | ||
| 6190 | return -1; | ||
| 6191 | p = num ? shellparam.p[num - 1] : arg0; | ||
| 6192 | goto value; | ||
| 6193 | default: | ||
| 6194 | p = lookupvar(name); | ||
| 6195 | value: | ||
| 6196 | if (!p) | ||
| 6197 | return -1; | ||
| 6198 | |||
| 6199 | len = strlen(p); | ||
| 6200 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
| 6201 | memtodest(p, len, syntax, quotes); | ||
| 6202 | return len; | ||
| 6203 | } | ||
| 6204 | |||
| 6205 | if (subtype == VSPLUS || subtype == VSLENGTH) | ||
| 6206 | STADJUST(-len, expdest); | ||
| 6207 | return len; | ||
| 6208 | } | ||
| 6209 | |||
| 6210 | |||
| 6211 | /* | ||
| 6212 | * Record the fact that we have to scan this region of the | ||
| 6213 | * string for IFS characters. | ||
| 6214 | */ | ||
| 6215 | static void | ||
| 6216 | recordregion(int start, int end, int nulonly) | ||
| 6217 | { | ||
| 6218 | struct ifsregion *ifsp; | ||
| 6219 | |||
| 6220 | if (ifslastp == NULL) { | ||
| 6221 | ifsp = &ifsfirst; | ||
| 6222 | } else { | ||
| 6223 | INT_OFF; | ||
| 6224 | ifsp = ckmalloc(sizeof(*ifsp)); | ||
| 6225 | ifsp->next = NULL; | ||
| 6226 | ifslastp->next = ifsp; | ||
| 6227 | INT_ON; | ||
| 6228 | } | ||
| 6229 | ifslastp = ifsp; | ||
| 6230 | ifslastp->begoff = start; | ||
| 6231 | ifslastp->endoff = end; | ||
| 6232 | ifslastp->nulonly = nulonly; | ||
| 6233 | } | ||
| 6234 | |||
| 6235 | |||
| 6236 | /* | ||
| 6237 | * Break the argument string into pieces based upon IFS and add the | ||
| 6238 | * strings to the argument list. The regions of the string to be | ||
| 6239 | * searched for IFS characters have been stored by recordregion. | ||
| 6240 | */ | ||
| 6241 | static void | ||
| 6242 | ifsbreakup(char *string, struct arglist *arglist) | ||
| 6243 | { | ||
| 6244 | struct ifsregion *ifsp; | ||
| 6245 | struct strlist *sp; | ||
| 6246 | char *start; | ||
| 6247 | char *p; | ||
| 6248 | char *q; | ||
| 6249 | const char *ifs, *realifs; | ||
| 6250 | int ifsspc; | ||
| 6251 | int nulonly; | ||
| 6252 | |||
| 6253 | start = string; | ||
| 6254 | if (ifslastp != NULL) { | ||
| 6255 | ifsspc = 0; | ||
| 6256 | nulonly = 0; | ||
| 6257 | realifs = ifsset() ? ifsval() : defifs; | ||
| 6258 | ifsp = &ifsfirst; | ||
| 6259 | do { | ||
| 6260 | p = string + ifsp->begoff; | ||
| 6261 | nulonly = ifsp->nulonly; | ||
| 6262 | ifs = nulonly ? nullstr : realifs; | ||
| 6263 | ifsspc = 0; | ||
| 6264 | while (p < string + ifsp->endoff) { | ||
| 6265 | q = p; | ||
| 6266 | if (*p == CTLESC) | ||
| 6267 | p++; | ||
| 6268 | if (!strchr(ifs, *p)) { | ||
| 6269 | p++; | ||
| 6270 | continue; | ||
| 6271 | } | ||
| 6272 | if (!nulonly) | ||
| 6273 | ifsspc = (strchr(defifs, *p) != NULL); | ||
| 6274 | /* Ignore IFS whitespace at start */ | ||
| 6275 | if (q == start && ifsspc) { | ||
| 6276 | p++; | ||
| 6277 | start = p; | ||
| 6278 | continue; | ||
| 6279 | } | ||
| 6280 | *q = '\0'; | ||
| 6281 | sp = stalloc(sizeof(*sp)); | ||
| 6282 | sp->text = start; | ||
| 6283 | *arglist->lastp = sp; | ||
| 6284 | arglist->lastp = &sp->next; | ||
| 6285 | p++; | ||
| 6286 | if (!nulonly) { | ||
| 6287 | for (;;) { | ||
| 6288 | if (p >= string + ifsp->endoff) { | ||
| 6289 | break; | ||
| 6290 | } | ||
| 6291 | q = p; | ||
| 6292 | if (*p == CTLESC) | ||
| 6293 | p++; | ||
| 6294 | if (strchr(ifs, *p) == NULL ) { | ||
| 6295 | p = q; | ||
| 6296 | break; | ||
| 6297 | } else if (strchr(defifs, *p) == NULL) { | ||
| 6298 | if (ifsspc) { | ||
| 6299 | p++; | ||
| 6300 | ifsspc = 0; | ||
| 6301 | } else { | ||
| 6302 | p = q; | ||
| 6303 | break; | ||
| 6304 | } | ||
| 6305 | } else | ||
| 6306 | p++; | ||
| 6307 | } | ||
| 6308 | } | ||
| 6309 | start = p; | ||
| 6310 | } /* while */ | ||
| 6311 | ifsp = ifsp->next; | ||
| 6312 | } while (ifsp != NULL); | ||
| 6313 | if (nulonly) | ||
| 6314 | goto add; | ||
| 6315 | } | ||
| 6316 | |||
| 6317 | if (!*start) | ||
| 6318 | return; | ||
| 6319 | |||
| 6320 | add: | ||
| 6321 | sp = stalloc(sizeof(*sp)); | ||
| 6322 | sp->text = start; | ||
| 6323 | *arglist->lastp = sp; | ||
| 6324 | arglist->lastp = &sp->next; | ||
| 6325 | } | ||
| 6326 | |||
| 6327 | static void | ||
| 6328 | ifsfree(void) | ||
| 6329 | { | ||
| 6330 | struct ifsregion *p; | ||
| 6331 | |||
| 6332 | INT_OFF; | ||
| 6333 | p = ifsfirst.next; | ||
| 6334 | do { | ||
| 6335 | struct ifsregion *ifsp; | ||
| 6336 | ifsp = p->next; | ||
| 6337 | free(p); | ||
| 6338 | p = ifsp; | ||
| 6339 | } while (p); | ||
| 6340 | ifslastp = NULL; | ||
| 6341 | ifsfirst.next = NULL; | ||
| 6342 | INT_ON; | ||
| 6343 | } | ||
| 6344 | |||
| 6345 | static void expmeta(char *, char *); | ||
| 6346 | static struct strlist *expsort(struct strlist *); | ||
| 6347 | static struct strlist *msort(struct strlist *, int); | ||
| 6348 | |||
| 6349 | static char *expdir; | ||
| 6350 | |||
| 6351 | |||
| 6352 | static void | ||
| 6353 | expandmeta(struct strlist *str, int flag) | ||
| 6354 | { | ||
| 6355 | static const char metachars[] = { | ||
| 6356 | '*', '?', '[', 0 | ||
| 6357 | }; | ||
| 6358 | /* TODO - EXP_REDIR */ | ||
| 6359 | |||
| 6360 | while (str) { | ||
| 6361 | struct strlist **savelastp; | ||
| 6362 | struct strlist *sp; | ||
| 6363 | char *p; | ||
| 6364 | |||
| 6365 | if (fflag) | ||
| 6366 | goto nometa; | ||
| 6367 | if (!strpbrk(str->text, metachars)) | ||
| 6368 | goto nometa; | ||
| 6369 | savelastp = exparg.lastp; | ||
| 6370 | |||
| 6371 | INT_OFF; | ||
| 6372 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); | ||
| 6373 | { | ||
| 6374 | int i = strlen(str->text); | ||
| 6375 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | ||
| 6376 | } | ||
| 6377 | |||
| 6378 | expmeta(expdir, p); | ||
| 6379 | free(expdir); | ||
| 6380 | if (p != str->text) | ||
| 6381 | free(p); | ||
| 6382 | INT_ON; | ||
| 6383 | if (exparg.lastp == savelastp) { | ||
| 6384 | /* | ||
| 6385 | * no matches | ||
| 6386 | */ | ||
| 6387 | nometa: | ||
| 6388 | *exparg.lastp = str; | ||
| 6389 | rmescapes(str->text); | ||
| 6390 | exparg.lastp = &str->next; | ||
| 6391 | } else { | ||
| 6392 | *exparg.lastp = NULL; | ||
| 6393 | *savelastp = sp = expsort(*savelastp); | ||
| 6394 | while (sp->next != NULL) | ||
| 6395 | sp = sp->next; | ||
| 6396 | exparg.lastp = &sp->next; | ||
| 6397 | } | ||
| 6398 | str = str->next; | ||
| 6399 | } | ||
| 6400 | } | ||
| 6401 | |||
| 6402 | |||
| 6403 | /* | ||
| 6404 | * Add a file name to the list. | ||
| 6405 | */ | ||
| 6406 | static void | ||
| 6407 | addfname(const char *name) | ||
| 6408 | { | ||
| 6409 | struct strlist *sp; | ||
| 6410 | |||
| 6411 | sp = stalloc(sizeof(*sp)); | ||
| 6412 | sp->text = ststrdup(name); | ||
| 6413 | *exparg.lastp = sp; | ||
| 6414 | exparg.lastp = &sp->next; | ||
| 6415 | } | ||
| 6416 | |||
| 6417 | |||
| 6418 | /* | ||
| 6419 | * Do metacharacter (i.e. *, ?, [...]) expansion. | ||
| 6420 | */ | ||
| 6421 | static void | ||
| 6422 | expmeta(char *enddir, char *name) | ||
| 6423 | { | ||
| 6424 | char *p; | ||
| 6425 | const char *cp; | ||
| 6426 | char *start; | ||
| 6427 | char *endname; | ||
| 6428 | int metaflag; | ||
| 6429 | struct stat statb; | ||
| 6430 | DIR *dirp; | ||
| 6431 | struct dirent *dp; | ||
| 6432 | int atend; | ||
| 6433 | int matchdot; | ||
| 6434 | |||
| 6435 | metaflag = 0; | ||
| 6436 | start = name; | ||
| 6437 | for (p = name; *p; p++) { | ||
| 6438 | if (*p == '*' || *p == '?') | ||
| 6439 | metaflag = 1; | ||
| 6440 | else if (*p == '[') { | ||
| 6441 | char *q = p + 1; | ||
| 6442 | if (*q == '!') | ||
| 6443 | q++; | ||
| 6444 | for (;;) { | ||
| 6445 | if (*q == '\\') | ||
| 6446 | q++; | ||
| 6447 | if (*q == '/' || *q == '\0') | ||
| 6448 | break; | ||
| 6449 | if (*++q == ']') { | ||
| 6450 | metaflag = 1; | ||
| 6451 | break; | ||
| 6452 | } | ||
| 6453 | } | ||
| 6454 | } else if (*p == '\\') | ||
| 6455 | p++; | ||
| 6456 | else if (*p == '/') { | ||
| 6457 | if (metaflag) | ||
| 6458 | goto out; | ||
| 6459 | start = p + 1; | ||
| 6460 | } | ||
| 6461 | } | ||
| 6462 | out: | ||
| 6463 | if (metaflag == 0) { /* we've reached the end of the file name */ | ||
| 6464 | if (enddir != expdir) | ||
| 6465 | metaflag++; | ||
| 6466 | p = name; | ||
| 6467 | do { | ||
| 6468 | if (*p == '\\') | ||
| 6469 | p++; | ||
| 6470 | *enddir++ = *p; | ||
| 6471 | } while (*p++); | ||
| 6472 | if (metaflag == 0 || lstat(expdir, &statb) >= 0) | ||
| 6473 | addfname(expdir); | ||
| 6474 | return; | ||
| 6475 | } | ||
| 6476 | endname = p; | ||
| 6477 | if (name < start) { | ||
| 6478 | p = name; | ||
| 6479 | do { | ||
| 6480 | if (*p == '\\') | ||
| 6481 | p++; | ||
| 6482 | *enddir++ = *p++; | ||
| 6483 | } while (p < start); | ||
| 6484 | } | ||
| 6485 | if (enddir == expdir) { | ||
| 6486 | cp = "."; | ||
| 6487 | } else if (enddir == expdir + 1 && *expdir == '/') { | ||
| 6488 | cp = "/"; | ||
| 6489 | } else { | ||
| 6490 | cp = expdir; | ||
| 6491 | enddir[-1] = '\0'; | ||
| 6492 | } | ||
| 6493 | dirp = opendir(cp); | ||
| 6494 | if (dirp == NULL) | ||
| 6495 | return; | ||
| 6496 | if (enddir != expdir) | ||
| 6497 | enddir[-1] = '/'; | ||
| 6498 | if (*endname == 0) { | ||
| 6499 | atend = 1; | ||
| 6500 | } else { | ||
| 6501 | atend = 0; | ||
| 6502 | *endname++ = '\0'; | ||
| 6503 | } | ||
| 6504 | matchdot = 0; | ||
| 6505 | p = start; | ||
| 6506 | if (*p == '\\') | ||
| 6507 | p++; | ||
| 6508 | if (*p == '.') | ||
| 6509 | matchdot++; | ||
| 6510 | while (! intpending && (dp = readdir(dirp)) != NULL) { | ||
| 6511 | if (dp->d_name[0] == '.' && ! matchdot) | ||
| 6512 | continue; | ||
| 6513 | if (pmatch(start, dp->d_name)) { | ||
| 6514 | if (atend) { | ||
| 6515 | strcpy(enddir, dp->d_name); | ||
| 6516 | addfname(expdir); | ||
| 6517 | } else { | ||
| 6518 | for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) | ||
| 6519 | continue; | ||
| 6520 | p[-1] = '/'; | ||
| 6521 | expmeta(p, endname); | ||
| 6522 | } | ||
| 6523 | } | ||
| 6524 | } | ||
| 6525 | closedir(dirp); | ||
| 6526 | if (! atend) | ||
| 6527 | endname[-1] = '/'; | ||
| 6528 | } | ||
| 6529 | |||
| 6530 | |||
| 6531 | /* | ||
| 6532 | * Sort the results of file name expansion. It calculates the number of | ||
| 6533 | * strings to sort and then calls msort (short for merge sort) to do the | ||
| 6534 | * work. | ||
| 6535 | */ | ||
| 6536 | static struct strlist * | ||
| 6537 | expsort(struct strlist *str) | ||
| 6538 | { | ||
| 6539 | int len; | ||
| 6540 | struct strlist *sp; | ||
| 6541 | |||
| 6542 | len = 0; | ||
| 6543 | for (sp = str; sp; sp = sp->next) | ||
| 6544 | len++; | ||
| 6545 | return msort(str, len); | ||
| 6546 | } | ||
| 6547 | |||
| 6548 | |||
| 6549 | static struct strlist * | ||
| 6550 | msort(struct strlist *list, int len) | ||
| 6551 | { | ||
| 6552 | struct strlist *p, *q = NULL; | ||
| 6553 | struct strlist **lpp; | ||
| 6554 | int half; | ||
| 6555 | int n; | ||
| 6556 | |||
| 6557 | if (len <= 1) | ||
| 6558 | return list; | ||
| 6559 | half = len >> 1; | ||
| 6560 | p = list; | ||
| 6561 | for (n = half; --n >= 0; ) { | ||
| 6562 | q = p; | ||
| 6563 | p = p->next; | ||
| 6564 | } | ||
| 6565 | q->next = NULL; /* terminate first half of list */ | ||
| 6566 | q = msort(list, half); /* sort first half of list */ | ||
| 6567 | p = msort(p, len - half); /* sort second half */ | ||
| 6568 | lpp = &list; | ||
| 6569 | for (;;) { | ||
| 6570 | #if ENABLE_LOCALE_SUPPORT | ||
| 6571 | if (strcoll(p->text, q->text) < 0) | ||
| 6572 | #else | ||
| 6573 | if (strcmp(p->text, q->text) < 0) | ||
| 6574 | #endif | ||
| 6575 | { | ||
| 6576 | *lpp = p; | ||
| 6577 | lpp = &p->next; | ||
| 6578 | p = *lpp; | ||
| 6579 | if (p == NULL) { | ||
| 6580 | *lpp = q; | ||
| 6581 | break; | ||
| 6582 | } | ||
| 6583 | } else { | ||
| 6584 | *lpp = q; | ||
| 6585 | lpp = &q->next; | ||
| 6586 | q = *lpp; | ||
| 6587 | if (q == NULL) { | ||
| 6588 | *lpp = p; | ||
| 6589 | break; | ||
| 6590 | } | ||
| 6591 | } | ||
| 6592 | } | ||
| 6593 | return list; | ||
| 6594 | } | ||
| 6595 | |||
| 6596 | |||
| 6597 | /* | ||
| 6598 | * Returns true if the pattern matches the string. | ||
| 6599 | */ | ||
| 6600 | static int patmatch(char *pattern, const char *string) | ||
| 6601 | { | ||
| 6602 | return pmatch(preglob(pattern, 0, 0), string); | ||
| 6603 | } | ||
| 6604 | |||
| 6605 | |||
| 6606 | /* | ||
| 6607 | * Remove any CTLESC characters from a string. | ||
| 6608 | */ | ||
| 6609 | static char * | ||
| 6610 | _rmescapes(char *str, int flag) | ||
| 6611 | { | ||
| 6612 | char *p, *q, *r; | ||
| 6613 | static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; | ||
| 6614 | unsigned inquotes; | ||
| 6615 | int notescaped; | ||
| 6616 | int globbing; | ||
| 6617 | |||
| 6618 | p = strpbrk(str, qchars); | ||
| 6619 | if (!p) { | ||
| 6620 | return str; | ||
| 6621 | } | ||
| 6622 | q = p; | ||
| 6623 | r = str; | ||
| 6624 | if (flag & RMESCAPE_ALLOC) { | ||
| 6625 | size_t len = p - str; | ||
| 6626 | size_t fulllen = len + strlen(p) + 1; | ||
| 6627 | |||
| 6628 | if (flag & RMESCAPE_GROW) { | ||
| 6629 | r = makestrspace(fulllen, expdest); | ||
| 6630 | } else if (flag & RMESCAPE_HEAP) { | ||
| 6631 | r = ckmalloc(fulllen); | ||
| 6632 | } else { | ||
| 6633 | r = stalloc(fulllen); | ||
| 6634 | } | ||
| 6635 | q = r; | ||
| 6636 | if (len > 0) { | ||
| 6637 | q = memcpy(q, str, len) + len; | ||
| 6638 | } | ||
| 6639 | } | ||
| 6640 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; | ||
| 6641 | globbing = flag & RMESCAPE_GLOB; | ||
| 6642 | notescaped = globbing; | ||
| 6643 | while (*p) { | ||
| 6644 | if (*p == CTLQUOTEMARK) { | ||
| 6645 | inquotes = ~inquotes; | ||
| 6646 | p++; | ||
| 6647 | notescaped = globbing; | ||
| 6648 | continue; | ||
| 6649 | } | ||
| 6650 | if (*p == '\\') { | ||
| 6651 | /* naked back slash */ | ||
| 6652 | notescaped = 0; | ||
| 6653 | goto copy; | ||
| 6654 | } | ||
| 6655 | if (*p == CTLESC) { | ||
| 6656 | p++; | ||
| 6657 | if (notescaped && inquotes && *p != '/') { | ||
| 6658 | *q++ = '\\'; | ||
| 6659 | } | ||
| 6660 | } | ||
| 6661 | notescaped = globbing; | ||
| 6662 | copy: | ||
| 6663 | *q++ = *p++; | ||
| 6664 | } | ||
| 6665 | *q = '\0'; | ||
| 6666 | if (flag & RMESCAPE_GROW) { | ||
| 6667 | expdest = r; | ||
| 6668 | STADJUST(q - r + 1, expdest); | ||
| 6669 | } | ||
| 6670 | return r; | ||
| 6671 | } | ||
| 6672 | |||
| 6673 | |||
| 6674 | /* | ||
| 6675 | * See if a pattern matches in a case statement. | ||
| 6676 | */ | ||
| 6677 | static int | ||
| 6678 | casematch(union node *pattern, char *val) | ||
| 6679 | { | ||
| 6680 | struct stackmark smark; | ||
| 6681 | int result; | ||
| 6682 | |||
| 6683 | setstackmark(&smark); | ||
| 6684 | argbackq = pattern->narg.backquote; | ||
| 6685 | STARTSTACKSTR(expdest); | ||
| 6686 | ifslastp = NULL; | ||
| 6687 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); | ||
| 6688 | STACKSTRNUL(expdest); | ||
| 6689 | result = patmatch(stackblock(), val); | ||
| 6690 | popstackmark(&smark); | ||
| 6691 | return result; | ||
| 6692 | } | ||
| 6693 | |||
| 6694 | |||
| 6695 | /* | ||
| 6696 | * Our own itoa(). | ||
| 6697 | */ | ||
| 6698 | static int | ||
| 6699 | cvtnum(arith_t num) | ||
| 6700 | { | ||
| 6701 | int len; | ||
| 6702 | |||
| 6703 | expdest = makestrspace(32, expdest); | ||
| 6704 | #if ENABLE_ASH_MATH_SUPPORT_64 | ||
| 6705 | len = fmtstr(expdest, 32, "%lld", (long long) num); | ||
| 6706 | #else | ||
| 6707 | len = fmtstr(expdest, 32, "%ld", num); | ||
| 6708 | #endif | ||
| 6709 | STADJUST(len, expdest); | ||
| 6710 | return len; | ||
| 6711 | } | ||
| 6712 | |||
| 6713 | static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN; | ||
| 6714 | static void | ||
| 6715 | varunset(const char *end, const char *var, const char *umsg, int varflags) | ||
| 6716 | { | ||
| 6717 | const char *msg; | ||
| 6718 | const char *tail; | ||
| 6719 | |||
| 6720 | tail = nullstr; | ||
| 6721 | msg = "parameter not set"; | ||
| 6722 | if (umsg) { | ||
| 6723 | if (*end == CTLENDVAR) { | ||
| 6724 | if (varflags & VSNUL) | ||
| 6725 | tail = " or null"; | ||
| 6726 | } else | ||
| 6727 | msg = umsg; | ||
| 6728 | } | ||
| 6729 | ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); | ||
| 6730 | } | ||
| 6731 | |||
| 6732 | 6675 | ||
| 6733 | /* ============ input.c | 6676 | /* ============ input.c |
| 6734 | * | 6677 | * |
| @@ -6901,7 +6844,6 @@ preadbuffer(void) | |||
| 6901 | return SC2INT(*parsenextc++); | 6844 | return SC2INT(*parsenextc++); |
| 6902 | } | 6845 | } |
| 6903 | 6846 | ||
| 6904 | |||
| 6905 | #define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer()) | 6847 | #define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer()) |
| 6906 | 6848 | ||
| 6907 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 6849 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
| @@ -7142,7 +7084,7 @@ setinputstring(char *string) | |||
| 7142 | } | 7084 | } |
| 7143 | 7085 | ||
| 7144 | 7086 | ||
| 7145 | /* jobs.c */ | 7087 | /* ============ jobs.c */ |
| 7146 | 7088 | ||
| 7147 | /* mode flags for set_curjob */ | 7089 | /* mode flags for set_curjob */ |
| 7148 | #define CUR_DELETE 2 | 7090 | #define CUR_DELETE 2 |
| @@ -8500,12 +8442,11 @@ stoppedjobs(void) | |||
| 8500 | } | 8442 | } |
| 8501 | 8443 | ||
| 8502 | 8444 | ||
| 8503 | #if ENABLE_ASH_MAIL | 8445 | /* ============ mail.c |
| 8504 | /* mail.c */ | 8446 | * |
| 8505 | 8447 | * Routines to check for mail. | |
| 8506 | /* | ||
| 8507 | * Routines to check for mail. (Perhaps make part of main.c?) | ||
| 8508 | */ | 8448 | */ |
| 8449 | #if ENABLE_ASH_MAIL | ||
| 8509 | 8450 | ||
| 8510 | #define MAXMBOXES 10 | 8451 | #define MAXMBOXES 10 |
| 8511 | 8452 | ||
| @@ -8514,7 +8455,6 @@ static time_t mailtime[MAXMBOXES]; | |||
| 8514 | /* Set if MAIL or MAILPATH is changed. */ | 8455 | /* Set if MAIL or MAILPATH is changed. */ |
| 8515 | static int mail_var_path_changed; | 8456 | static int mail_var_path_changed; |
| 8516 | 8457 | ||
| 8517 | |||
| 8518 | /* | 8458 | /* |
| 8519 | * Print appropriate message(s) if mail has arrived. | 8459 | * Print appropriate message(s) if mail has arrived. |
| 8520 | * If mail_var_path_changed is set, | 8460 | * If mail_var_path_changed is set, |
| @@ -8561,15 +8501,16 @@ chkmail(void) | |||
| 8561 | popstackmark(&smark); | 8501 | popstackmark(&smark); |
| 8562 | } | 8502 | } |
| 8563 | 8503 | ||
| 8564 | |||
| 8565 | static void | 8504 | static void |
| 8566 | changemail(const char *val) | 8505 | changemail(const char *val) |
| 8567 | { | 8506 | { |
| 8568 | mail_var_path_changed++; | 8507 | mail_var_path_changed++; |
| 8569 | } | 8508 | } |
| 8570 | |||
| 8571 | #endif /* ASH_MAIL */ | 8509 | #endif /* ASH_MAIL */ |
| 8572 | 8510 | ||
| 8511 | |||
| 8512 | /* ============ ??? */ | ||
| 8513 | |||
| 8573 | /* | 8514 | /* |
| 8574 | * Take commands from a file. To be compatible we should do a path | 8515 | * Take commands from a file. To be compatible we should do a path |
| 8575 | * search for the file, which is necessary to find sub-commands. | 8516 | * search for the file, which is necessary to find sub-commands. |
| @@ -8679,7 +8620,6 @@ calcsize(union node *n) | |||
| 8679 | }; | 8620 | }; |
| 8680 | } | 8621 | } |
| 8681 | 8622 | ||
| 8682 | |||
| 8683 | static void | 8623 | static void |
| 8684 | sizenodelist(struct nodelist *lp) | 8624 | sizenodelist(struct nodelist *lp) |
| 8685 | { | 8625 | { |
| @@ -8690,7 +8630,6 @@ sizenodelist(struct nodelist *lp) | |||
| 8690 | } | 8630 | } |
| 8691 | } | 8631 | } |
| 8692 | 8632 | ||
| 8693 | |||
| 8694 | static union node * | 8633 | static union node * |
| 8695 | copynode(union node *n) | 8634 | copynode(union node *n) |
| 8696 | { | 8635 | { |
| @@ -8780,7 +8719,6 @@ copynode(union node *n) | |||
| 8780 | return new; | 8719 | return new; |
| 8781 | } | 8720 | } |
| 8782 | 8721 | ||
| 8783 | |||
| 8784 | static struct nodelist * | 8722 | static struct nodelist * |
| 8785 | copynodelist(struct nodelist *lp) | 8723 | copynodelist(struct nodelist *lp) |
| 8786 | { | 8724 | { |
| @@ -8799,7 +8737,6 @@ copynodelist(struct nodelist *lp) | |||
| 8799 | return start; | 8737 | return start; |
| 8800 | } | 8738 | } |
| 8801 | 8739 | ||
| 8802 | |||
| 8803 | static char * | 8740 | static char * |
| 8804 | nodeckstrdup(char *s) | 8741 | nodeckstrdup(char *s) |
| 8805 | { | 8742 | { |
| @@ -8810,6 +8747,37 @@ nodeckstrdup(char *s) | |||
| 8810 | return rtn; | 8747 | return rtn; |
| 8811 | } | 8748 | } |
| 8812 | 8749 | ||
| 8750 | /* | ||
| 8751 | * Controls whether the shell is interactive or not. | ||
| 8752 | */ | ||
| 8753 | static void | ||
| 8754 | setinteractive(int on) | ||
| 8755 | { | ||
| 8756 | static int is_interactive; | ||
| 8757 | |||
| 8758 | if (++on == is_interactive) | ||
| 8759 | return; | ||
| 8760 | is_interactive = on; | ||
| 8761 | setsignal(SIGINT); | ||
| 8762 | setsignal(SIGQUIT); | ||
| 8763 | setsignal(SIGTERM); | ||
| 8764 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | ||
| 8765 | if (is_interactive > 1) { | ||
| 8766 | /* Looks like they want an interactive shell */ | ||
| 8767 | static smallint do_banner; | ||
| 8768 | |||
| 8769 | if (!do_banner) { | ||
| 8770 | out1fmt( | ||
| 8771 | "\n\n" | ||
| 8772 | "%s Built-in shell (ash)\n" | ||
| 8773 | "Enter 'help' for a list of built-in commands." | ||
| 8774 | "\n\n", | ||
| 8775 | BB_BANNER); | ||
| 8776 | do_banner = 1; | ||
| 8777 | } | ||
| 8778 | } | ||
| 8779 | #endif | ||
| 8780 | } | ||
| 8813 | 8781 | ||
| 8814 | #if ENABLE_FEATURE_EDITING_VI | 8782 | #if ENABLE_FEATURE_EDITING_VI |
| 8815 | #define setvimode(on) do { \ | 8783 | #define setvimode(on) do { \ |
| @@ -8867,7 +8835,6 @@ setoption(int flag, int val) | |||
| 8867 | /* NOTREACHED */ | 8835 | /* NOTREACHED */ |
| 8868 | } | 8836 | } |
| 8869 | 8837 | ||
| 8870 | |||
| 8871 | /* | 8838 | /* |
| 8872 | * Process shell options. The global variable argptr contains a pointer | 8839 | * Process shell options. The global variable argptr contains a pointer |
| 8873 | * to the argument list; we advance it past the options. | 8840 | * to the argument list; we advance it past the options. |
| @@ -8921,7 +8888,6 @@ options(int cmdline) | |||
| 8921 | } | 8888 | } |
| 8922 | } | 8889 | } |
| 8923 | 8890 | ||
| 8924 | |||
| 8925 | /* | 8891 | /* |
| 8926 | * Set the shell parameters. | 8892 | * Set the shell parameters. |
| 8927 | */ | 8893 | */ |
| @@ -8948,7 +8914,6 @@ setparam(char **argv) | |||
| 8948 | #endif | 8914 | #endif |
| 8949 | } | 8915 | } |
| 8950 | 8916 | ||
| 8951 | |||
| 8952 | /* | 8917 | /* |
| 8953 | * Free the list of positional parameters. | 8918 | * Free the list of positional parameters. |
| 8954 | */ | 8919 | */ |
| @@ -8964,7 +8929,6 @@ freeparam(volatile struct shparam *param) | |||
| 8964 | } | 8929 | } |
| 8965 | } | 8930 | } |
| 8966 | 8931 | ||
| 8967 | |||
| 8968 | /* | 8932 | /* |
| 8969 | * The shift builtin command. | 8933 | * The shift builtin command. |
| 8970 | */ | 8934 | */ |
| @@ -8995,7 +8959,6 @@ shiftcmd(int argc, char **argv) | |||
| 8995 | return 0; | 8959 | return 0; |
| 8996 | } | 8960 | } |
| 8997 | 8961 | ||
| 8998 | |||
| 8999 | /* | 8962 | /* |
| 9000 | * POSIX requires that 'set' (but not export or readonly) output the | 8963 | * POSIX requires that 'set' (but not export or readonly) output the |
| 9001 | * variables in lexicographic order - by the locale's collating order (sigh). | 8964 | * variables in lexicographic order - by the locale's collating order (sigh). |
| @@ -9045,7 +9008,6 @@ setcmd(int argc, char **argv) | |||
| 9045 | return 0; | 9008 | return 0; |
| 9046 | } | 9009 | } |
| 9047 | 9010 | ||
| 9048 | |||
| 9049 | #if ENABLE_LOCALE_SUPPORT | 9011 | #if ENABLE_LOCALE_SUPPORT |
| 9050 | static void | 9012 | static void |
| 9051 | change_lc_all(const char *value) | 9013 | change_lc_all(const char *value) |
| @@ -9083,7 +9045,6 @@ change_random(const char *value) | |||
| 9083 | } | 9045 | } |
| 9084 | #endif | 9046 | #endif |
| 9085 | 9047 | ||
| 9086 | |||
| 9087 | #if ENABLE_ASH_GETOPTS | 9048 | #if ENABLE_ASH_GETOPTS |
| 9088 | static int | 9049 | static int |
| 9089 | getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) | 9050 | getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) |
| @@ -9173,7 +9134,6 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
| 9173 | return done; | 9134 | return done; |
| 9174 | } | 9135 | } |
| 9175 | 9136 | ||
| 9176 | |||
| 9177 | /* | 9137 | /* |
| 9178 | * The getopts builtin. Shellparam.optnext points to the next argument | 9138 | * The getopts builtin. Shellparam.optnext points to the next argument |
| 9179 | * to be processed. Shellparam.optptr points to the next character to | 9139 | * to be processed. Shellparam.optptr points to the next character to |
| @@ -10877,7 +10837,7 @@ readcmdfile(char *name) | |||
| 10877 | } | 10837 | } |
| 10878 | 10838 | ||
| 10879 | 10839 | ||
| 10880 | /* redir.c */ | 10840 | /* ============ redir.c */ |
| 10881 | 10841 | ||
| 10882 | /* | 10842 | /* |
| 10883 | * Code for dealing with input/output redirection. | 10843 | * Code for dealing with input/output redirection. |
| @@ -10948,7 +10908,6 @@ noclobberopen(const char *fname) | |||
| 10948 | return -1; | 10908 | return -1; |
| 10949 | } | 10909 | } |
| 10950 | 10910 | ||
| 10951 | |||
| 10952 | /* | 10911 | /* |
| 10953 | * Handle here documents. Normally we fork off a process to write the | 10912 | * Handle here documents. Normally we fork off a process to write the |
| 10954 | * data to a pipe. If the document is short, we can stuff the data in | 10913 | * data to a pipe. If the document is short, we can stuff the data in |
| @@ -11139,7 +11098,6 @@ redirect(union node *redir, int flags) | |||
| 11139 | preverrout_fd = sv->renamed[2]; | 11098 | preverrout_fd = sv->renamed[2]; |
| 11140 | } | 11099 | } |
| 11141 | 11100 | ||
| 11142 | |||
| 11143 | /* | 11101 | /* |
| 11144 | * Undo the effects of the last redirection. | 11102 | * Undo the effects of the last redirection. |
| 11145 | */ | 11103 | */ |
| @@ -11186,7 +11144,6 @@ clearredir(int drop) | |||
| 11186 | } | 11144 | } |
| 11187 | } | 11145 | } |
| 11188 | 11146 | ||
| 11189 | |||
| 11190 | /* | 11147 | /* |
| 11191 | * Copy a file descriptor to be >= to. Returns -1 | 11148 | * Copy a file descriptor to be >= to. Returns -1 |
| 11192 | * if the source file descriptor is closed, EMPTY if there are no unused | 11149 | * if the source file descriptor is closed, EMPTY if there are no unused |
| @@ -11228,7 +11185,7 @@ redirectsafe(union node *redir, int flags) | |||
| 11228 | } | 11185 | } |
| 11229 | 11186 | ||
| 11230 | 11187 | ||
| 11231 | /* trap.c */ | 11188 | /* ============ trap.c */ |
| 11232 | 11189 | ||
| 11233 | /* | 11190 | /* |
| 11234 | * The trap builtin. | 11191 | * The trap builtin. |
| @@ -11280,7 +11237,6 @@ trapcmd(int argc, char **argv) | |||
| 11280 | return 0; | 11237 | return 0; |
| 11281 | } | 11238 | } |
| 11282 | 11239 | ||
| 11283 | |||
| 11284 | /* | 11240 | /* |
| 11285 | * Clear traps on a fork. | 11241 | * Clear traps on a fork. |
| 11286 | */ | 11242 | */ |
| @@ -11301,7 +11257,6 @@ clear_traps(void) | |||
| 11301 | } | 11257 | } |
| 11302 | } | 11258 | } |
| 11303 | 11259 | ||
| 11304 | |||
| 11305 | /* | 11260 | /* |
| 11306 | * Set the signal handler for the specified signal. The routine figures | 11261 | * Set the signal handler for the specified signal. The routine figures |
| 11307 | * out what it should be set to. | 11262 | * out what it should be set to. |
| @@ -11389,7 +11344,6 @@ setsignal(int signo) | |||
| 11389 | sigaction(signo, &act, 0); | 11344 | sigaction(signo, &act, 0); |
| 11390 | } | 11345 | } |
| 11391 | 11346 | ||
| 11392 | |||
| 11393 | /* | 11347 | /* |
| 11394 | * Called to execute a trap. Perhaps we should avoid entering new trap | 11348 | * Called to execute a trap. Perhaps we should avoid entering new trap |
| 11395 | * handlers while we are executing a trap handler. | 11349 | * handlers while we are executing a trap handler. |
| @@ -11424,37 +11378,8 @@ dotrap(void) | |||
| 11424 | return skip; | 11378 | return skip; |
| 11425 | } | 11379 | } |
| 11426 | 11380 | ||
| 11427 | /* | ||
| 11428 | * Controls whether the shell is interactive or not. | ||
| 11429 | */ | ||
| 11430 | static void | ||
| 11431 | setinteractive(int on) | ||
| 11432 | { | ||
| 11433 | static int is_interactive; | ||
| 11434 | |||
| 11435 | if (++on == is_interactive) | ||
| 11436 | return; | ||
| 11437 | is_interactive = on; | ||
| 11438 | setsignal(SIGINT); | ||
| 11439 | setsignal(SIGQUIT); | ||
| 11440 | setsignal(SIGTERM); | ||
| 11441 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | ||
| 11442 | if (is_interactive > 1) { | ||
| 11443 | /* Looks like they want an interactive shell */ | ||
| 11444 | static smallint do_banner; | ||
| 11445 | 11381 | ||
| 11446 | if (!do_banner) { | 11382 | /* ============ Builtins */ |
| 11447 | out1fmt( | ||
| 11448 | "\n\n" | ||
| 11449 | "%s Built-in shell (ash)\n" | ||
| 11450 | "Enter 'help' for a list of built-in commands." | ||
| 11451 | "\n\n", | ||
| 11452 | BB_BANNER); | ||
| 11453 | do_banner = 1; | ||
| 11454 | } | ||
| 11455 | } | ||
| 11456 | #endif | ||
| 11457 | } | ||
| 11458 | 11383 | ||
| 11459 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | 11384 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET |
| 11460 | /* | 11385 | /* |
| @@ -11489,41 +11414,6 @@ helpcmd(int argc, char **argv) | |||
| 11489 | #endif /* FEATURE_SH_EXTRA_QUIET */ | 11414 | #endif /* FEATURE_SH_EXTRA_QUIET */ |
| 11490 | 11415 | ||
| 11491 | /* | 11416 | /* |
| 11492 | * Called to exit the shell. | ||
| 11493 | */ | ||
| 11494 | static void | ||
| 11495 | exitshell(void) | ||
| 11496 | { | ||
| 11497 | struct jmploc loc; | ||
| 11498 | char *p; | ||
| 11499 | int status; | ||
| 11500 | |||
| 11501 | status = exitstatus; | ||
| 11502 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); | ||
| 11503 | if (setjmp(loc.loc)) { | ||
| 11504 | if (exception == EXEXIT) | ||
| 11505 | /* dash bug: it just does _exit(exitstatus) here | ||
| 11506 | * but we have to do setjobctl(0) first! | ||
| 11507 | * (bug is still not fixed in dash-0.5.3 - if you run dash | ||
| 11508 | * under Midnight Commander, on exit from dash MC is backgrounded) */ | ||
| 11509 | status = exitstatus; | ||
| 11510 | goto out; | ||
| 11511 | } | ||
| 11512 | exception_handler = &loc; | ||
| 11513 | p = trap[0]; | ||
| 11514 | if (p) { | ||
| 11515 | trap[0] = NULL; | ||
| 11516 | evalstring(p, 0); | ||
| 11517 | } | ||
| 11518 | flush_stdout_stderr(); | ||
| 11519 | out: | ||
| 11520 | setjobctl(0); | ||
| 11521 | _exit(status); | ||
| 11522 | /* NOTREACHED */ | ||
| 11523 | } | ||
| 11524 | |||
| 11525 | |||
| 11526 | /* | ||
| 11527 | * The export and readonly commands. | 11417 | * The export and readonly commands. |
| 11528 | */ | 11418 | */ |
| 11529 | static int | 11419 | static int |
| @@ -11559,7 +11449,6 @@ exportcmd(int argc, char **argv) | |||
| 11559 | return 0; | 11449 | return 0; |
| 11560 | } | 11450 | } |
| 11561 | 11451 | ||
| 11562 | |||
| 11563 | /* | 11452 | /* |
| 11564 | * Make a variable a local variable. When a variable is made local, it's | 11453 | * Make a variable a local variable. When a variable is made local, it's |
| 11565 | * value and flags are saved in a localvar structure. The saved values | 11454 | * value and flags are saved in a localvar structure. The saved values |
| @@ -11607,7 +11496,6 @@ mklocal(char *name) | |||
| 11607 | INT_ON; | 11496 | INT_ON; |
| 11608 | } | 11497 | } |
| 11609 | 11498 | ||
| 11610 | |||
| 11611 | /* | 11499 | /* |
| 11612 | * The "local" command. | 11500 | * The "local" command. |
| 11613 | */ | 11501 | */ |
| @@ -11623,7 +11511,6 @@ localcmd(int argc, char **argv) | |||
| 11623 | return 0; | 11511 | return 0; |
| 11624 | } | 11512 | } |
| 11625 | 11513 | ||
| 11626 | |||
| 11627 | /* | 11514 | /* |
| 11628 | * The unset builtin command. We unset the function before we unset the | 11515 | * The unset builtin command. We unset the function before we unset the |
| 11629 | * variable to allow a function to be unset when there is a readonly variable | 11516 | * variable to allow a function to be unset when there is a readonly variable |
| @@ -11713,7 +11600,6 @@ dash_arith(const char *s) | |||
| 11713 | return result; | 11600 | return result; |
| 11714 | } | 11601 | } |
| 11715 | 11602 | ||
| 11716 | |||
| 11717 | /* | 11603 | /* |
| 11718 | * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. | 11604 | * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. |
| 11719 | * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. | 11605 | * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. |
| @@ -11737,9 +11623,9 @@ letcmd(int argc, char **argv) | |||
| 11737 | } | 11623 | } |
| 11738 | #endif /* ASH_MATH_SUPPORT */ | 11624 | #endif /* ASH_MATH_SUPPORT */ |
| 11739 | 11625 | ||
| 11740 | /* miscbltin.c */ | ||
| 11741 | 11626 | ||
| 11742 | /* | 11627 | /* ============ miscbltin.c |
| 11628 | * | ||
| 11743 | * Miscellaneous builtins. | 11629 | * Miscellaneous builtins. |
| 11744 | */ | 11630 | */ |
| 11745 | 11631 | ||
| @@ -11749,7 +11635,6 @@ letcmd(int argc, char **argv) | |||
| 11749 | typedef enum __rlimit_resource rlim_t; | 11635 | typedef enum __rlimit_resource rlim_t; |
| 11750 | #endif | 11636 | #endif |
| 11751 | 11637 | ||
| 11752 | |||
| 11753 | /* | 11638 | /* |
| 11754 | * The read builtin. The -e option causes backslashes to escape the | 11639 | * The read builtin. The -e option causes backslashes to escape the |
| 11755 | * following character. | 11640 | * following character. |
| @@ -11938,7 +11823,6 @@ readcmd(int argc, char **argv) | |||
| 11938 | return status; | 11823 | return status; |
| 11939 | } | 11824 | } |
| 11940 | 11825 | ||
| 11941 | |||
| 11942 | static int | 11826 | static int |
| 11943 | umaskcmd(int argc, char **argv) | 11827 | umaskcmd(int argc, char **argv) |
| 11944 | { | 11828 | { |
| @@ -12190,6 +12074,8 @@ ulimitcmd(int argc, char **argv) | |||
| 12190 | } | 12074 | } |
| 12191 | 12075 | ||
| 12192 | 12076 | ||
| 12077 | /* ============ Math support */ | ||
| 12078 | |||
| 12193 | #if ENABLE_ASH_MATH_SUPPORT | 12079 | #if ENABLE_ASH_MATH_SUPPORT |
| 12194 | 12080 | ||
| 12195 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> | 12081 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
| @@ -12645,7 +12531,6 @@ static const char op_tokens[] = { | |||
| 12645 | /* ptr to ")" */ | 12531 | /* ptr to ")" */ |
| 12646 | #define endexpression &op_tokens[sizeof(op_tokens)-7] | 12532 | #define endexpression &op_tokens[sizeof(op_tokens)-7] |
| 12647 | 12533 | ||
| 12648 | |||
| 12649 | static arith_t | 12534 | static arith_t |
| 12650 | arith(const char *expr, int *perrcode) | 12535 | arith(const char *expr, int *perrcode) |
| 12651 | { | 12536 | { |
| @@ -12837,14 +12722,43 @@ arith(const char *expr, int *perrcode) | |||
| 12837 | #endif /* ASH_MATH_SUPPORT */ | 12722 | #endif /* ASH_MATH_SUPPORT */ |
| 12838 | 12723 | ||
| 12839 | 12724 | ||
| 12840 | /* ============ main() and helpers | 12725 | /* ============ main() and helpers */ |
| 12841 | * | 12726 | |
| 12842 | * Main routine. We initialize things, parse the arguments, execute | 12727 | /* |
| 12843 | * profiles if we're a login shell, and then call cmdloop to execute | 12728 | * Called to exit the shell. |
| 12844 | * commands. The setjmp call sets up the location to jump to when an | ||
| 12845 | * exception occurs. When an exception occurs the variable "state" | ||
| 12846 | * is used to figure out how far we had gotten. | ||
| 12847 | */ | 12729 | */ |
| 12730 | static void exitshell(void) ATTRIBUTE_NORETURN; | ||
| 12731 | static void | ||
| 12732 | exitshell(void) | ||
| 12733 | { | ||
| 12734 | struct jmploc loc; | ||
| 12735 | char *p; | ||
| 12736 | int status; | ||
| 12737 | |||
| 12738 | status = exitstatus; | ||
| 12739 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); | ||
| 12740 | if (setjmp(loc.loc)) { | ||
| 12741 | if (exception == EXEXIT) | ||
| 12742 | /* dash bug: it just does _exit(exitstatus) here | ||
| 12743 | * but we have to do setjobctl(0) first! | ||
| 12744 | * (bug is still not fixed in dash-0.5.3 - if you run dash | ||
| 12745 | * under Midnight Commander, on exit from dash MC is backgrounded) */ | ||
| 12746 | status = exitstatus; | ||
| 12747 | goto out; | ||
| 12748 | } | ||
| 12749 | exception_handler = &loc; | ||
| 12750 | p = trap[0]; | ||
| 12751 | if (p) { | ||
| 12752 | trap[0] = NULL; | ||
| 12753 | evalstring(p, 0); | ||
| 12754 | } | ||
| 12755 | flush_stdout_stderr(); | ||
| 12756 | out: | ||
| 12757 | setjobctl(0); | ||
| 12758 | _exit(status); | ||
| 12759 | /* NOTREACHED */ | ||
| 12760 | } | ||
| 12761 | |||
| 12848 | static void | 12762 | static void |
| 12849 | init(void) | 12763 | init(void) |
| 12850 | { | 12764 | { |
| @@ -12981,6 +12895,13 @@ static short profile_buf[16384]; | |||
| 12981 | extern int etext(); | 12895 | extern int etext(); |
| 12982 | #endif | 12896 | #endif |
| 12983 | 12897 | ||
| 12898 | /* | ||
| 12899 | * Main routine. We initialize things, parse the arguments, execute | ||
| 12900 | * profiles if we're a login shell, and then call cmdloop to execute | ||
| 12901 | * commands. The setjmp call sets up the location to jump to when an | ||
| 12902 | * exception occurs. When an exception occurs the variable "state" | ||
| 12903 | * is used to figure out how far we had gotten. | ||
| 12904 | */ | ||
| 12984 | int ash_main(int argc, char **argv); | 12905 | int ash_main(int argc, char **argv); |
| 12985 | int ash_main(int argc, char **argv) | 12906 | int ash_main(int argc, char **argv) |
| 12986 | { | 12907 | { |
