diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 01:05:52 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 01:05:52 +0000 |
commit | a02e9ff74128cf5dc6bcba7142687e6c94447670 (patch) | |
tree | e5dfedc7e1ff7cc4c72d26ba4f3ec3f2ab0b1d1d /shell | |
parent | 25abda587e7485fe6e42bfe731c553d1b2ba7e97 (diff) | |
download | busybox-w32-a02e9ff74128cf5dc6bcba7142687e6c94447670.tar.gz busybox-w32-a02e9ff74128cf5dc6bcba7142687e6c94447670.tar.bz2 busybox-w32-a02e9ff74128cf5dc6bcba7142687e6c94447670.zip |
ash: cleanup part 9
git-svn-id: svn://busybox.net/trunk/busybox@17961 69ca8d6d-28ef-0310-b511-8ec308f3f277
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 | { |