aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorvda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-02-23 01:05:52 +0000
committervda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-02-23 01:05:52 +0000
commita02e9ff74128cf5dc6bcba7142687e6c94447670 (patch)
treee5dfedc7e1ff7cc4c72d26ba4f3ec3f2ab0b1d1d /shell
parent25abda587e7485fe6e42bfe731c553d1b2ba7e97 (diff)
downloadbusybox-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.c3237
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
331static void 331static void
332outstr(const char *p, FILE *file) 332outstr(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
2356struct 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
2375static void expandarg(union node *, struct arglist *, int);
2376#define rmescapes(p) _rmescapes((p), 0)
2377static char *_rmescapes(char *, int);
2378static int casematch(union node *, char *);
2379
2380#if ENABLE_ASH_MATH_SUPPORT
2381static 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);
3210static void clear_traps(void); 3179static void clear_traps(void);
3211static void setsignal(int); 3180static void setsignal(int);
3212static int dotrap(void); 3181static int dotrap(void);
3213static void setinteractive(int);
3214static void exitshell(void) ATTRIBUTE_NORETURN;
3215 3182
3216 3183
3217static int is_safe_applet(char *name) 3184static 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 */
3445struct 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
3452struct arglist {
3453 struct strlist *list;
3454 struct strlist **lastp;
3455};
3456
3457/* output of current string */
3458static char *expdest;
3459/* list of back quote expressions */
3460static struct nodelist *argbackq;
3461/* first struct in list of ifs regions */
3462static struct ifsregion ifsfirst;
3463/* last struct in list */
3464static struct ifsregion *ifslastp;
3465/* holds expanded arg list */
3466static struct arglist exparg;
3467
3468/*
3469 * Our own itoa().
3470 */
3471static int
3472cvtnum(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
3486static size_t
3487esclen(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 */
3500static 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 */
3572static char *
3573preglob(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 */
3585static void
3586memtodest(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
3604static void
3605strtodest(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 */
3614static void
3615recordregion(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
3634static void
3635removerecordregions(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
3673static char *
3674exptilde(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 */
3728struct 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: */
3736static int back_exitstatus; /* exit status of backquoted command */
3737#define EV_EXIT 01 /* exit after evaluating tree */
3738static void evaltree(union node *, int);
3739
3740static void
3741evalbackcmd(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 */
3788static void
3789expbackq(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 */
3851static void
3852expari(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 */
3909static 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 */
3916static void
3917argstr(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
4055static char *
4056scanleft(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
4085static char *
4086scanright(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
4119static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
4120static void
4121varunset(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
4138static const char *
4139subevalvar(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 */
4209static ssize_t
4210varvalue(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 */
4327static char *
4328evalvar(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 */
4466static void
4467ifsbreakup(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
4552static void
4553ifsfree(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 */
4573static void
4574addfname(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
4584static char *expdir;
4585
4586/*
4587 * Do metacharacter (i.e. *, ?, [...]) expansion.
4588 */
4589static void
4590expmeta(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
4698static struct strlist *
4699msort(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 */
4750static struct strlist *
4751expsort(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
4762static void
4763expandmeta(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 */
4818static void
4819expandarg(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 */
4864static void
4865expandhere(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 */
4875static int
4876patmatch(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 */
4884static int
4885casematch(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... */
3456static void evalloop(union node *, int); 4910static void evalloop(union node *, int);
3457static void evalfor(union node *, int); 4911static void evalfor(union node *, int);
3458static void evalcase(union node *, int); 4912static 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
5242struct 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 */
5250static char *expdest;
5251/* list of back quote expressions */
5252static struct nodelist *argbackq;
5253/* first struct in list of ifs regions */
5254static struct ifsregion ifsfirst;
5255/* last struct in list */
5256static struct ifsregion *ifslastp;
5257/* holds expanded arg list */
5258static struct arglist exparg;
5259
5260static void argstr(char *, int);
5261static char *exptilde(char *, char *, int);
5262static void expbackq(union node *, int, int);
5263static const char *subevalvar(char *, char *, int, int, int, int, int);
5264static char *evalvar(char *, int);
5265static void strtodest(const char *, int, int);
5266static void memtodest(const char *p, size_t len, int syntax, int quotes);
5267static ssize_t varvalue(char *, int, int);
5268static void recordregion(int, int, int);
5269static void removerecordregions(int);
5270static void ifsbreakup(char *, struct arglist *);
5271static void ifsfree(void);
5272static void expandmeta(struct strlist *, int);
5273static int patmatch(char *, const char *);
5274
5275static int cvtnum(arith_t);
5276static size_t esclen(const char *, const char *);
5277static char *scanleft(char *, char *, char *, char *, int, int);
5278static char *scanright(char *, char *, char *, char *, int, int);
5279static 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 */
5288static char *
5289preglob(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
5299static size_t
5300esclen(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 */
5314static void
5315expandhere(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 */
5329static void
5330expandarg(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 */
5378static void
5379argstr(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
5517static char *
5518exptilde(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
5567static void
5568removerecordregions(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 */
5612static void
5613expari(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 */
5676struct 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
5683static void
5684evalbackcmd(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 */
5731static void
5732expbackq(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
5790static char *
5791scanleft(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
5821static char *
5822scanright(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
5855static const char *
5856subevalvar(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 */
5928static char *
5929evalvar(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 */
6066static void
6067memtodest(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
6086static void
6087strtodest(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 */
6096static ssize_t
6097varvalue(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 */
6215static void
6216recordregion(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 */
6241static void
6242ifsbreakup(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
6327static void
6328ifsfree(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
6345static void expmeta(char *, char *);
6346static struct strlist *expsort(struct strlist *);
6347static struct strlist *msort(struct strlist *, int);
6348
6349static char *expdir;
6350
6351
6352static void
6353expandmeta(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 */
6406static void
6407addfname(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 */
6421static void
6422expmeta(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 */
6536static struct strlist *
6537expsort(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
6549static struct strlist *
6550msort(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 */
6600static 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 */
6609static 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 */
6677static int
6678casematch(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 */
6698static int
6699cvtnum(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
6713static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
6714static void
6715varunset(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. */
8515static int mail_var_path_changed; 8456static 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
8565static void 8504static void
8566changemail(const char *val) 8505changemail(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
8683static void 8623static void
8684sizenodelist(struct nodelist *lp) 8624sizenodelist(struct nodelist *lp)
8685{ 8625{
@@ -8690,7 +8630,6 @@ sizenodelist(struct nodelist *lp)
8690 } 8630 }
8691} 8631}
8692 8632
8693
8694static union node * 8633static union node *
8695copynode(union node *n) 8634copynode(union node *n)
8696{ 8635{
@@ -8780,7 +8719,6 @@ copynode(union node *n)
8780 return new; 8719 return new;
8781} 8720}
8782 8721
8783
8784static struct nodelist * 8722static struct nodelist *
8785copynodelist(struct nodelist *lp) 8723copynodelist(struct nodelist *lp)
8786{ 8724{
@@ -8799,7 +8737,6 @@ copynodelist(struct nodelist *lp)
8799 return start; 8737 return start;
8800} 8738}
8801 8739
8802
8803static char * 8740static char *
8804nodeckstrdup(char *s) 8741nodeckstrdup(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 */
8753static void
8754setinteractive(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
9050static void 9012static void
9051change_lc_all(const char *value) 9013change_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
9088static int 9049static int
9089getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) 9050getopts(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 */
11430static void
11431setinteractive(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 */
11494static void
11495exitshell(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 */
11529static int 11419static 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)
11749typedef enum __rlimit_resource rlim_t; 11635typedef 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
11942static int 11826static int
11943umaskcmd(int argc, char **argv) 11827umaskcmd(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
12649static arith_t 12534static arith_t
12650arith(const char *expr, int *perrcode) 12535arith(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 */
12730static void exitshell(void) ATTRIBUTE_NORETURN;
12731static void
12732exitshell(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
12848static void 12762static void
12849init(void) 12763init(void)
12850{ 12764{
@@ -12981,6 +12895,13 @@ static short profile_buf[16384];
12981extern int etext(); 12895extern 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 */
12984int ash_main(int argc, char **argv); 12905int ash_main(int argc, char **argv);
12985int ash_main(int argc, char **argv) 12906int ash_main(int argc, char **argv)
12986{ 12907{