Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/shell/ash.c


Ignore:
Timestamp:
Dec 20, 2016, 4:07:32 PM (7 years ago)
Author:
Bruno Cornec
Message:

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

Location:
branches/3.3
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • branches/3.3/mindi-busybox/shell/ash.c

    r3232 r3621  
    3838#define JOBS ENABLE_ASH_JOB_CONTROL
    3939
    40 #include <paths.h>
    4140#include <setjmp.h>
    4241#include <fnmatch.h>
    4342#include <sys/times.h>
     43#include <sys/utsname.h> /* for setting $HOSTNAME */
    4444
    4545#include "busybox.h" /* for applet_names */
     
    143143//config:     Enable support for test builtin in ash.
    144144//config:
     145//config:config ASH_HELP
     146//config:   bool "help builtin"
     147//config:   default y
     148//config:   depends on ASH
     149//config:   help
     150//config:     Enable help builtin in ash.
     151//config:
    145152//config:config ASH_CMDCMD
    146153//config:   bool "'command' command to override shell builtins"
     
    386393#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
    387394
     395#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
     396#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
     397
    388398static int isdigit_str9(const char *str)
    389399{
     
    527537}
    528538
     539/* Was called outcslow(c,FILE*), but c was always '\n' */
    529540static void
    530 outcslow(int c, FILE *dest)
     541newline_and_flush(FILE *dest)
    531542{
    532543    INT_OFF;
    533     putc(c, dest);
     544    putc('\n', dest);
    534545    fflush(dest);
    535546    INT_ON;
     
    588599#define CTLENDVAR    ((unsigned char)'\203')
    589600#define CTLBACKQ     ((unsigned char)'\204')
    590 #define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
    591 /*      CTLBACKQ | CTLQUOTE == '\205' */
    592601#define CTLARI       ((unsigned char)'\206')    /* arithmetic expression */
    593602#define CTLENDARI    ((unsigned char)'\207')
     
    598607#define VSTYPE  0x0f            /* type of variable substitution */
    599608#define VSNUL   0x10            /* colon--treat the empty string as unset */
    600 #define VSQUOTE 0x80            /* inside double quotes--suppress splitting */
    601609
    602610/* values of VSTYPE field */
     
    618626
    619627static const char dolatstr[] ALIGN1 = {
    620     CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
     628    CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
    621629};
     630#define DOLATSTRLEN 6
    622631
    623632#define NCMD      0
     
    855864        case CTLESC: c = 'e'; goto backslash;
    856865        case CTLVAR: c = 'v'; goto backslash;
    857         case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
    858866        case CTLBACKQ: c = 'q'; goto backslash;
    859         case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
    860867 backslash:
    861868            putc('\\', tracefile);
     
    10201027            break;
    10211028        case CTLBACKQ:
    1022         case CTLBACKQ|CTLQUOTE:
    10231029            putc('$', fp);
    10241030            putc('(', fp);
     
    11971203    }
    11981204    vfprintf(stderr, msg, ap);
    1199     outcslow('\n', stderr);
     1205    newline_and_flush(stderr);
    12001206}
    12011207
     
    20092015#endif
    20102016
    2011 /* math.h has these, otherwise define our private copies */
    2012 #if !ENABLE_SH_MATH_SUPPORT
    2013 #define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
    2014 #define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
    2015 /*
    2016  * Return the pointer to the first char which is not part of a legal variable name
    2017  * (a letter or underscore followed by letters, underscores, and digits).
    2018  */
    2019 static const char*
    2020 endofname(const char *name)
    2021 {
    2022     if (!is_name(*name))
    2023         return name;
    2024     while (*++name) {
    2025         if (!is_in_name(*name))
    2026             break;
    2027     }
    2028     return name;
    2029 }
    2030 #endif
    2031 
    20322017/*
    20332018 * Compares two strings up to the first = or '\0'.  The first
     
    20412026
    20422027    while ((c = *p) == (d = *q)) {
    2043         if (!c || c == '=')
     2028        if (c == '\0' || c == '=')
    20442029            goto out;
    20452030        p++;
     
    21392124}
    21402125
     2126static void reinit_unicode_for_ash(void)
     2127{
     2128    /* Unicode support should be activated even if LANG is set
     2129     * _during_ shell execution, not only if it was set when
     2130     * shell was started. Therefore, re-check LANG every time:
     2131     */
     2132    if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
     2133     || ENABLE_UNICODE_USING_LOCALE
     2134    ) {
     2135        const char *s = lookupvar("LC_ALL");
     2136        if (!s) s = lookupvar("LC_CTYPE");
     2137        if (!s) s = lookupvar("LANG");
     2138        reinit_unicode(s);
     2139    }
     2140}
     2141
    21412142/*
    21422143 * Search the environment of a builtin command.
     
    22422243
    22432244static void FAST_FUNC
    2244 setvar2(const char *name, const char *val)
     2245setvar0(const char *name, const char *val)
    22452246{
    22462247    setvar(name, val, 0);
     
    23052306            INT_ON;
    23062307        } else {
    2307             setvar(s, 0, 0);
     2308            setvar0(s, NULL);
    23082309            vp->flags &= ~VEXPORT;
    23092310        }
     
    25552556            USTPUTC('/', new);
    25562557        }
    2557         p = strtok(0, "/");
     2558        p = strtok(NULL, "/");
    25582559    }
    25592560    if (new > lim)
     
    27502751# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
    27512752#endif
    2752 static const uint16_t S_I_T[] = {
     2753static const uint16_t S_I_T[] ALIGN2 = {
    27532754#if ENABLE_ASH_ALIAS
    27542755    SIT_ITEM(CSPCL   , CIGN     , CIGN , CIGN   ),    /* 0, PEOA */
     
    28522853#else   /* !USE_SIT_FUNCTION */
    28532854
    2854 static const uint8_t syntax_index_table[] = {
     2855static const uint8_t syntax_index_table[] ALIGN1 = {
    28552856    /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
    28562857    /*   0      */ CWORD_CWORD_CWORD_CWORD,
     
    33363337#define SHOW_PIDS       0x02    /* show individual pids, not just one line per job */
    33373338#define SHOW_CHANGED    0x04    /* only jobs whose state has changed */
     3339#define SHOW_STDERR     0x08    /* print to stderr (else stdout) */
    33383340
    33393341/*
     
    36423644    if (is_number(p)) {
    36433645        num = atoi(p);
    3644         if (num < njobs) {
     3646        if (num <= njobs) {
    36453647            jp = jobtab + num - 1;
    36463648            if (jp->used)
     
    38503852    for (ps = jp->ps + 1; ps < psend; ps++)
    38513853        printf(" | %s", ps->ps_cmd);
    3852     outcslow('\n', stdout);
     3854    newline_and_flush(stdout);
    38533855    flush_stdout_stderr();
    38543856}
     
    39103912
    39113913static int
    3912 sprint_status(char *s, int status, int sigonly)
     3914sprint_status48(char *s, int status, int sigonly)
    39133915{
    39143916    int col;
     
    39173919    col = 0;
    39183920    if (!WIFEXITED(status)) {
    3919 #if JOBS
    3920         if (WIFSTOPPED(status))
     3921        if (JOBS && WIFSTOPPED(status))
    39213922            st = WSTOPSIG(status);
    39223923        else
    3923 #endif
    39243924            st = WTERMSIG(status);
    39253925        if (sigonly) {
    39263926            if (st == SIGINT || st == SIGPIPE)
    39273927                goto out;
    3928 #if JOBS
    3929             if (WIFSTOPPED(status))
     3928            if (JOBS && WIFSTOPPED(status))
    39303929                goto out;
    3931 #endif
    39323930        }
    39333931        st &= 0x7f;
     
    39353933        col = fmtstr(s, 32, strsignal(st));
    39363934        if (WCOREDUMP(status)) {
    3937             col += fmtstr(s + col, 16, " (core dumped)");
     3935            strcpy(s + col, " (core dumped)");
     3936            col += sizeof(" (core dumped)")-1;
    39383937        }
    39393938    } else if (!sigonly) {
    39403939        st = WEXITSTATUS(status);
    3941         if (st)
    3942             col = fmtstr(s, 16, "Done(%d)", st);
    3943         else
    3944             col = fmtstr(s, 16, "Done");
     3940        col = fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
    39453941    }
    39463942 out:
     
    39553951    struct job *jp;
    39563952    struct job *thisjob;
    3957     int state;
    39583953
    39593954    TRACE(("dowait(0x%x) called\n", wait_flags));
     
    39733968    thisjob = NULL;
    39743969    for (jp = curjob; jp; jp = jp->prev_job) {
     3970        int jobstate;
    39753971        struct procstat *ps;
    39763972        struct procstat *psend;
    39773973        if (jp->state == JOBDONE)
    39783974            continue;
    3979         state = JOBDONE;
     3975        jobstate = JOBDONE;
    39803976        ps = jp->ps;
    39813977        psend = ps + jp->nprocs;
     
    39893985            }
    39903986            if (ps->ps_status == -1)
    3991                 state = JOBRUNNING;
     3987                jobstate = JOBRUNNING;
    39923988#if JOBS
    3993             if (state == JOBRUNNING)
     3989            if (jobstate == JOBRUNNING)
    39943990                continue;
    39953991            if (WIFSTOPPED(ps->ps_status)) {
    39963992                jp->stopstatus = ps->ps_status;
    3997                 state = JOBSTOPPED;
     3993                jobstate = JOBSTOPPED;
    39983994            }
    39993995#endif
    40003996        } while (++ps < psend);
    4001         if (thisjob)
    4002             goto gotjob;
    4003     }
     3997        if (!thisjob)
     3998            continue;
     3999
     4000        /* Found the job where one of its processes changed its state.
     4001         * Is there at least one live and running process in this job? */
     4002        if (jobstate != JOBRUNNING) {
     4003            /* No. All live processes in the job are stopped
     4004             * (JOBSTOPPED) or there are no live processes (JOBDONE)
     4005             */
     4006            thisjob->changed = 1;
     4007            if (thisjob->state != jobstate) {
     4008                TRACE(("Job %d: changing state from %d to %d\n",
     4009                    jobno(thisjob), thisjob->state, jobstate));
     4010                thisjob->state = jobstate;
    40044011#if JOBS
    4005     if (!WIFSTOPPED(status))
    4006 #endif
     4012                if (jobstate == JOBSTOPPED)
     4013                    set_curjob(thisjob, CUR_STOPPED);
     4014#endif
     4015            }
     4016        }
     4017        goto out;
     4018    }
     4019    /* The process wasn't found in job list */
     4020    if (JOBS && !WIFSTOPPED(status))
    40074021        jobless--;
    4008     goto out;
    4009 
    4010  gotjob:
    4011     if (state != JOBRUNNING) {
    4012         thisjob->changed = 1;
    4013 
    4014         if (thisjob->state != state) {
    4015             TRACE(("Job %d: changing state from %d to %d\n",
    4016                 jobno(thisjob), thisjob->state, state));
    4017             thisjob->state = state;
    4018 #if JOBS
    4019             if (state == JOBSTOPPED) {
    4020                 set_curjob(thisjob, CUR_STOPPED);
    4021             }
    4022 #endif
    4023         }
    4024     }
    4025 
    40264022 out:
    40274023    INT_ON;
     
    40314027        int len;
    40324028
    4033         len = sprint_status(s, status, 1);
     4029        len = sprint_status48(s, status, 1);
    40344030        if (len) {
    40354031            s[len] = '\n';
     
    40524048#if JOBS
    40534049static void
    4054 showjob(FILE *out, struct job *jp, int mode)
     4050showjob(struct job *jp, int mode)
    40554051{
    40564052    struct procstat *ps;
     
    40584054    int col;
    40594055    int indent_col;
    4060     char s[80];
     4056    char s[16 + 16 + 48];
     4057    FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
    40614058
    40624059    ps = jp->ps;
     
    40884085        if (jp->state == JOBSTOPPED)
    40894086            status = jp->stopstatus;
    4090         col += sprint_status(s + col, status, 0);
     4087        col += sprint_status48(s + col, status, 0);
    40914088    }
    40924089    /* By now, "[JOBID]*  [maybe PID] STATUS" is printed */
     
    41154112        );
    41164113    } while (++ps != psend);
    4117     outcslow('\n', out);
     4114    newline_and_flush(out);
    41184115
    41194116    jp->changed = 0;
     
    41304127 */
    41314128static void
    4132 showjobs(FILE *out, int mode)
     4129showjobs(int mode)
    41334130{
    41344131    struct job *jp;
     
    41424139    for (jp = curjob; jp; jp = jp->prev_job) {
    41434140        if (!(mode & SHOW_CHANGED) || jp->changed) {
    4144             showjob(out, jp, mode);
     4141            showjob(jp, mode);
    41454142        }
    41464143    }
     
    41634160    if (*argv) {
    41644161        do
    4165             showjob(stdout, getjob(*argv, 0), mode);
     4162            showjob(getjob(*argv, 0), mode);
    41664163        while (*++argv);
    41674164    } else {
    4168         showjobs(stdout, mode);
     4165        showjobs(mode);
    41694166    }
    41704167
     
    44084405            else
    44094406                str = "${";
    4410             if (!(subtype & VSQUOTE) == !(quoted & 1))
    4411                 goto dostr;
    4412             quoted ^= 1;
    4413             c = '"';
    4414             break;
     4407            goto dostr;
    44154408        case CTLENDVAR:
    44164409            str = "\"}" + !(quoted & 1);
     
    44204413        case CTLBACKQ:
    44214414            str = "$(...)";
    4422             goto dostr;
    4423         case CTLBACKQ+CTLQUOTE:
    4424             str = "\"$(...)\"";
    44254415            goto dostr;
    44264416#if ENABLE_SH_MATH_SUPPORT
     
    47584748         */
    47594749        /* Save trap handler strings for trap builtin to print */
    4760         trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
     4750        trap_ptr = xmemdup(trap, sizeof(trap));
    47614751        /* Fall through into clearing traps */
    47624752    }
     
    54195409    int i;
    54205410
    5421     if (--g_nullredirs >= 0)
     5411    if (--g_nullredirs >= 0 || redirlist == NULL)
    54225412        return;
    54235413    INT_OFF;
     
    55005490
    55015491    math_state.lookupvar = lookupvar;
    5502     math_state.setvar    = setvar2;
     5492    math_state.setvar    = setvar0;
    55035493    //math_state.endofname = endofname;
    55045494
     
    55215511#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
    55225512#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
    5523 #define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
     5513#define EXP_QPAT        0x20    /* pattern in quoted parameter expansion */
    55245514#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
    55255515#define EXP_WORD        0x80    /* expand word in parameter expansion */
    5526 #define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
     5516#define EXP_QUOTED      0x100   /* expand word in double quotes */
    55275517/*
    55285518 * rmescape() flags
     
    55305520#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
    55315521#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
    5532 #define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
    55335522#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
    55345523#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */
     5524#define RMESCAPE_SLASH  0x20    /* Stop globbing after slash */
     5525
     5526/* Add CTLESC when necessary. */
     5527#define QUOTES_ESC     (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR)
     5528/* Do not skip NUL characters. */
     5529#define QUOTES_KEEPNUL EXP_TILDE
    55355530
    55365531/*
     
    55745569    int len;
    55755570
    5576     expdest = makestrspace(32, expdest);
    5577     len = fmtstr(expdest, 32, ARITH_FMT, num);
     5571    expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest);
     5572    len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num);
    55785573    STADJUST(len, expdest);
    55795574    return len;
     
    55975592rmescapes(char *str, int flag)
    55985593{
    5599     static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
     5594    static const char qchars[] ALIGN1 = {
     5595        IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' };
    56005596
    56015597    char *p, *q, *r;
     
    56035599    unsigned protect_against_glob;
    56045600    unsigned globbing;
    5605 
    5606     p = strpbrk(str, qchars);
     5601    IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;)
     5602
     5603    p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash));
    56075604    if (!p)
    56085605        return str;
     
    56315628    }
    56325629
    5633     inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
     5630    inquotes = 0;
    56345631    globbing = flag & RMESCAPE_GLOB;
    56355632    protect_against_glob = globbing;
    56365633    while (*p) {
    56375634        if ((unsigned char)*p == CTLQUOTEMARK) {
    5638 // TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
    5639 // (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
    56405635// Note: both inquotes and protect_against_glob only affect whether
    56415636// CTLESC,<ch> gets converted to <ch> or to \<ch>
     
    56455640            continue;
    56465641        }
    5647         if (*p == '\\') {
     5642        if ((unsigned char)*p == CTLESC) {
     5643            p++;
     5644            if (protect_against_glob) {
     5645                *q++ = '\\';
     5646            }
     5647        } else if (*p == '\\' && !inquotes) {
    56485648            /* naked back slash */
    56495649            protect_against_glob = 0;
    56505650            goto copy;
    56515651        }
    5652         if ((unsigned char)*p == CTLESC) {
    5653             p++;
    5654             if (protect_against_glob && inquotes && *p != '/') {
    5655                 *q++ = '\\';
    5656             }
    5657         }
     5652#if ENABLE_ASH_BASH_COMPAT
     5653        else if (*p == '/' && slash) {
     5654            /* stop handling globbing and mark location of slash */
     5655            globbing = slash = 0;
     5656            *p = CTLESC;
     5657        }
     5658#endif
    56585659        protect_against_glob = globbing;
    56595660 copy:
     
    56755676 */
    56765677static char *
    5677 preglob(const char *pattern, int quoted, int flag)
    5678 {
    5679     flag |= RMESCAPE_GLOB;
    5680     if (quoted) {
    5681         flag |= RMESCAPE_QUOTED;
    5682     }
    5683     return rmescapes((char *)pattern, flag);
     5678preglob(const char *pattern, int flag)
     5679{
     5680    return rmescapes((char *)pattern, flag | RMESCAPE_GLOB);
    56845681}
    56855682
     
    56905687memtodest(const char *p, size_t len, int syntax, int quotes)
    56915688{
    5692     char *q = expdest;
    5693 
    5694     q = makestrspace(quotes ? len * 2 : len, q);
    5695 
    5696     while (len--) {
     5689    char *q;
     5690
     5691    if (!len)
     5692        return;
     5693
     5694    q = makestrspace((quotes & QUOTES_ESC) ? len * 2 : len, expdest);
     5695
     5696    do {
    56975697        unsigned char c = *p++;
    5698         if (c == '\0')
     5698        if (c) {
     5699            int n = SIT(c, syntax);
     5700            if ((quotes & QUOTES_ESC) &&
     5701                    ((n == CCTL) ||
     5702                    (((quotes & EXP_FULL) || syntax != BASESYNTAX) &&
     5703                    n == CBACK)))
     5704                USTPUTC(CTLESC, q);
     5705        } else if (!(quotes & QUOTES_KEEPNUL))
    56995706            continue;
    5700         if (quotes) {
    5701             int n = SIT(c, syntax);
    5702             if (n == CCTL || n == CBACK)
    5703                 USTPUTC(CTLESC, q);
    5704         }
    57055707        USTPUTC(c, q);
    5706     }
     5708    } while (--len);
    57075709
    57085710    expdest = q;
    57095711}
    57105712
    5711 static void
     5713static size_t
    57125714strtodest(const char *p, int syntax, int quotes)
    57135715{
    5714     memtodest(p, strlen(p), syntax, quotes);
     5716    size_t len = strlen(p);
     5717    memtodest(p, len, syntax, quotes);
     5718    return len;
    57155719}
    57165720
     
    57855789    struct passwd *pw;
    57865790    const char *home;
    5787     int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
    5788     int startloc;
     5791    int quotes = flags & QUOTES_ESC;
    57895792
    57905793    name = p + 1;
     
    58185821        goto lose;
    58195822    *p = c;
    5820     startloc = expdest - (char *)stackblock();
    58215823    strtodest(home, SQSYNTAX, quotes);
    5822     recordregion(startloc, expdest - (char *)stackblock(), 0);
    58235824    return p;
    58245825 lose:
     
    58935894 */
    58945895static void
    5895 expbackq(union node *cmd, int quoted, int quotes)
     5896expbackq(union node *cmd, int flag)
    58965897{
    58975898    struct backcmd in;
     
    59015902    char *dest;
    59025903    int startloc;
    5903     int syntax = quoted ? DQSYNTAX : BASESYNTAX;
     5904    int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
    59045905    struct stackmark smark;
    59055906
     
    59175918        goto read;
    59185919    for (;;) {
    5919         memtodest(p, i, syntax, quotes);
     5920        memtodest(p, i, syntax, flag & QUOTES_ESC);
    59205921 read:
    59215922        if (in.fd < 0)
    59225923            break;
    5923         i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
     5924        i = nonblock_immune_read(in.fd, buf, sizeof(buf));
    59245925        TRACE(("expbackq: read returns %d\n", i));
    59255926        if (i <= 0)
     
    59415942    expdest = dest;
    59425943
    5943     if (quoted == 0)
     5944    if (!(flag & EXP_QUOTED))
    59445945        recordregion(startloc, dest - (char *)stackblock(), 0);
    59455946    TRACE(("evalbackq: size:%d:'%.*s'\n",
     
    59555956 */
    59565957static void
    5957 expari(int quotes)
     5958expari(int flag)
    59585959{
    59595960    char *p, *start;
    59605961    int begoff;
    5961     int flag;
    59625962    int len;
    59635963
     
    59975997    removerecordregions(begoff);
    59985998
    5999     flag = p[1];
    6000 
    60015999    expdest = p;
    60026000
    6003     if (quotes)
    6004         rmescapes(p + 2, 0);
    6005 
    6006     len = cvtnum(ash_arith(p + 2));
    6007 
    6008     if (flag != '"')
     6001    if (flag & QUOTES_ESC)
     6002        rmescapes(p + 1, 0);
     6003
     6004    len = cvtnum(ash_arith(p + 1));
     6005
     6006    if (!(flag & EXP_QUOTED))
    60096007        recordregion(begoff, begoff + len, 0);
    60106008}
     
    60346032        CTLVAR,
    60356033        CTLBACKQ,
    6036         CTLBACKQ | CTLQUOTE,
    60376034#if ENABLE_SH_MATH_SUPPORT
    60386035        CTLENDARI,
     
    60416038    };
    60426039    const char *reject = spclchars;
    6043     int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
    6044     int breakall = flags & EXP_WORD;
     6040    int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
    60456041    int inquotes;
    60466042    size_t length;
     
    60606056 tilde:
    60616057        q = p;
    6062         if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
    6063             q++;
    60646058        if (*q == '~')
    60656059            p = exptilde(p, q, flags);
     
    61186112            goto breakloop;
    61196113        case CTLQUOTEMARK:
     6114            inquotes ^= EXP_QUOTED;
    61206115            /* "$@" syntax adherence hack */
    6121             if (!inquotes
    6122              && memcmp(p, dolatstr, 4) == 0
    6123              && (  p[4] == (char)CTLQUOTEMARK
    6124                 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
    6125                 )
    6126             ) {
    6127                 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
     6116            if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
     6117                p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1;
    61286118                goto start;
    61296119            }
    6130             inquotes = !inquotes;
    61316120 addquote:
    6132             if (quotes) {
     6121            if (flags & QUOTES_ESC) {
    61336122                p--;
    61346123                length++;
     
    61396128            startloc++;
    61406129            length++;
     6130
     6131            /*
     6132             * Quoted parameter expansion pattern: remove quote
     6133             * unless inside inner quotes or we have a literal
     6134             * backslash.
     6135             */
     6136            if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
     6137                EXP_QPAT && *p != '\\')
     6138                break;
     6139
    61416140            goto addquote;
    61426141        case CTLVAR:
    61436142            TRACE(("argstr: evalvar('%s')\n", p));
    6144             p = evalvar(p, flags, var_str_list);
     6143            p = evalvar(p, flags | inquotes, var_str_list);
    61456144            TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
    61466145            goto start;
    61476146        case CTLBACKQ:
    6148             c = '\0';
    6149         case CTLBACKQ|CTLQUOTE:
    6150             expbackq(argbackq->n, c, quotes);
     6147            expbackq(argbackq->n, flags | inquotes);
    61516148            argbackq = argbackq->next;
    61526149            goto start;
     
    61546151        case CTLENDARI:
    61556152            p--;
    6156             expari(quotes);
     6153            expari(flags | inquotes);
    61576154            goto start;
    61586155#endif
     
    62846281}
    62856282
    6286 #if ENABLE_ASH_BASH_COMPAT
    6287 static char *
    6288 parse_sub_pattern(char *arg, int varflags)
    6289 {
    6290     char *idx, *repl = NULL;
    6291     unsigned char c;
    6292 
    6293     //char *org_arg = arg;
    6294     //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
    6295     idx = arg;
    6296     while (1) {
    6297         c = *arg;
    6298         if (!c)
    6299             break;
    6300         if (c == '/') {
    6301             /* Only the first '/' seen is our separator */
    6302             if (!repl) {
    6303                 repl = idx + 1;
    6304                 c = '\0';
    6305             }
    6306         }
    6307         *idx++ = c;
    6308         arg++;
    6309         /*
    6310          * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
    6311          * The result is a_\_z_c (not a\_\_z_c)!
    6312          *
    6313          * Enable debug prints in this function and you'll see:
    6314          * ash: arg:'\\b/_\\_z_' varflags:d
    6315          * ash: pattern:'\\b' repl:'_\_z_'
    6316          * That is, \\b is interpreted as \\b, but \\_ as \_!
    6317          * IOW: search pattern and replace string treat backslashes
    6318          * differently! That is the reason why we check repl below:
    6319          */
    6320         if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
    6321             arg++; /* skip both '\', not just first one */
    6322     }
    6323     *idx = c; /* NUL */
    6324     //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
    6325 
    6326     return repl;
    6327 }
    6328 #endif /* ENABLE_ASH_BASH_COMPAT */
    6329 
    63306283static const char *
    63316284subevalvar(char *p, char *varname, int strloc, int subtype,
    6332         int startloc, int varflags, int quotes, struct strlist *var_str_list)
     6285        int startloc, int varflags, int flag, struct strlist *var_str_list)
    63336286{
    63346287    struct nodelist *saveargbackq = argbackq;
     6288    int quotes = flag & QUOTES_ESC;
    63356289    char *startp;
    63366290    char *loc;
    63376291    char *rmesc, *rmescend;
    63386292    char *str;
    6339     IF_ASH_BASH_COMPAT(const char *repl = NULL;)
     6293    IF_ASH_BASH_COMPAT(char *repl = NULL;)
    63406294    IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
    63416295    int saveherefd = herefd;
     
    63496303
    63506304    herefd = -1;
    6351     argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
     6305    argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
     6306            (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0),
    63526307            var_str_list);
    63536308    STPUTC('\0', expdest);
     
    63586313    switch (subtype) {
    63596314    case VSASSIGN:
    6360         setvar(varname, startp, 0);
     6315        setvar0(varname, startp);
    63616316        amount = startp - expdest;
    63626317        STADJUST(amount, expdest);
     
    64066361            }
    64076362        }
    6408         if (pos >= orig_len) {
     6363        if (pos < 0) {
     6364            /* ${VAR:$((-n)):l} starts n chars from the end */
     6365            pos = orig_len + pos;
     6366        }
     6367        if ((unsigned)pos >= orig_len) {
     6368            /* apart from obvious ${VAR:999999:l},
     6369             * covers ${VAR:$((-9999999)):l} - result is ""
     6370             * (bash-compat)
     6371             */
    64096372            pos = 0;
    64106373            len = 0;
     
    64536416    rmescend--;
    64546417    str = (char *)stackblock() + strloc;
    6455     preglob(str, varflags & VSQUOTE, 0);
     6418    /*
     6419     * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
     6420     * The result is a_\_z_c (not a\_\_z_c)!
     6421     *
     6422     * The search pattern and replace string treat backslashes differently!
     6423     * RMESCAPE_SLASH causes preglob to work differently on the pattern
     6424     * and string.  It's only used on the first call.
     6425     */
     6426    preglob(str, IF_ASH_BASH_COMPAT(
     6427        (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ?
     6428            RMESCAPE_SLASH :) 0);
    64566429
    64576430#if ENABLE_ASH_BASH_COMPAT
     
    64616434
    64626435        if (!repl) {
    6463             repl = parse_sub_pattern(str, varflags);
    6464             //bb_error_msg("repl:'%s'", repl);
    6465             if (!repl)
     6436            if ((repl=strchr(str, CTLESC)))
     6437                *repl++ = '\0';
     6438            else
    64666439                repl = nullstr;
    64676440        }
     6441        //bb_error_msg("str:'%s' repl:'%s'", str, repl);
    64686442
    64696443        /* If there's no pattern to match, return the expansion unmolested */
     
    65986572    int num;
    65996573    int i;
    6600     int sepq = 0;
    66016574    ssize_t len = 0;
     6575    int sep;
     6576    int quoted = flags & EXP_QUOTED;
    66026577    int subtype = varflags & VSTYPE;
    6603     int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
    6604     int quoted = varflags & VSQUOTE;
     6578    int discard = subtype == VSPLUS || subtype == VSLENGTH;
     6579    int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
    66056580    int syntax = quoted ? DQSYNTAX : BASESYNTAX;
     6581
     6582    sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
    66066583
    66076584    switch (*name) {
     
    66396616    case '@': {
    66406617        char **ap;
    6641         int sep;
     6618        char sepc;
    66426619
    66436620        if (quoted && (flags & EXP_FULL)) {
     
    66496626    case '*':
    66506627        sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
    6651         i = SIT(sep, syntax);
    6652         if (quotes && (i == CCTL || i == CBACK))
    6653             sepq = 1;
    66546628 param:
    66556629        ap = shellparam.p;
     6630        sepc = sep;
    66566631        if (!ap)
    66576632            return -1;
    66586633        while ((p = *ap++) != NULL) {
    6659             size_t partlen;
    6660 
    6661             partlen = strlen(p);
    6662             len += partlen;
    6663 
    6664             if (!(subtype == VSPLUS || subtype == VSLENGTH))
    6665                 memtodest(p, partlen, syntax, quotes);
     6634            len += strtodest(p, syntax, quotes);
    66666635
    66676636            if (*ap && sep) {
    6668                 char *q;
    6669 
    66706637                len++;
    6671                 if (subtype == VSPLUS || subtype == VSLENGTH) {
    6672                     continue;
    6673                 }
    6674                 q = expdest;
    6675                 if (sepq)
    6676                     STPUTC(CTLESC, q);
    6677                 /* note: may put NUL despite sep != 0
    6678                  * (see sep = 1 << CHAR_BIT above) */
    6679                 STPUTC(sep, q);
    6680                 expdest = q;
     6638                memtodest(&sepc, 1, syntax, quotes);
    66816639            }
    66826640        }
    6683         return len;
     6641        break;
    66846642    } /* case '@' and '*' */
    66856643    case '0':
     
    67306688            return -1;
    67316689
    6732         len = strlen(p);
    6733         if (!(subtype == VSPLUS || subtype == VSLENGTH))
    6734             memtodest(p, len, syntax, quotes);
    6735         return len;
    6736     }
    6737 
    6738     if (subtype == VSPLUS || subtype == VSLENGTH)
     6690        len = strtodest(p, syntax, quotes);
     6691#if ENABLE_UNICODE_SUPPORT
     6692        if (subtype == VSLENGTH && len > 0) {
     6693            reinit_unicode_for_ash();
     6694            if (unicode_status == UNICODE_ON) {
     6695                STADJUST(-len, expdest);
     6696                discard = 0;
     6697                len = unicode_strlen(p);
     6698            }
     6699        }
     6700#endif
     6701        break;
     6702    }
     6703
     6704    if (discard)
    67396705        STADJUST(-len, expdest);
    67406706    return len;
     
    67506716    char varflags;
    67516717    char subtype;
    6752     char quoted;
     6718    int quoted;
    67536719    char easy;
    67546720    char *var;
     
    67596725    varflags = (unsigned char) *p++;
    67606726    subtype = varflags & VSTYPE;
    6761     quoted = varflags & VSQUOTE;
     6727    quoted = flags & EXP_QUOTED;
    67626728    var = p;
    67636729    easy = (!quoted || (*var == '@' && shellparam.nparam));
     
    67806746            argstr(
    67816747                p,
    6782                 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
     6748                flags | EXP_TILDE | EXP_WORD,
    67836749                var_str_list
    67846750            );
     
    67946760            if (subevalvar(p, var, /* strloc: */ 0,
    67956761                    subtype, startloc, varflags,
    6796                     /* quotes: */ 0,
     6762                    /* quotes: */ flags & ~QUOTES_ESC,
    67976763                    var_str_list)
    67986764            ) {
     
    68516817        patloc = expdest - (char *)stackblock();
    68526818        if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
    6853                 startloc, varflags,
    6854                 /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
    6855                 var_str_list)
    6856         ) {
     6819                startloc, varflags, flags, var_str_list)) {
    68576820            int amount = expdest - (
    68586821                (char *)stackblock() + patloc - 1
     
    68736836            if (c == CTLESC)
    68746837                p++;
    6875             else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
     6838            else if (c == CTLBACKQ) {
    68766839                if (varlen >= 0)
    68776840                    argbackq = argbackq->next;
     
    70286991    int atend;
    70296992    int matchdot;
     6993    int esc;
    70306994
    70316995    metaflag = 0;
    70326996    start = name;
    7033     for (p = name; *p; p++) {
     6997    for (p = name; esc = 0, *p; p += esc + 1) {
    70346998        if (*p == '*' || *p == '?')
    70356999            metaflag = 1;
     
    70487012                }
    70497013            }
    7050         } else if (*p == '\\')
    7051             p++;
    7052         else if (*p == '/') {
    7053             if (metaflag)
    7054                 goto out;
    7055             start = p + 1;
    7056         }
    7057     }
    7058  out:
     7014        } else {
     7015            if (*p == '\\')
     7016                esc++;
     7017            if (p[esc] == '/') {
     7018                if (metaflag)
     7019                    break;
     7020                start = p + esc + 1;
     7021            }
     7022        }
     7023    }
    70597024    if (metaflag == 0) {    /* we've reached the end of the file name */
    70607025        if (enddir != expdir)
     
    70967061    } else {
    70977062        atend = 0;
    7098         *endname++ = '\0';
     7063        *endname = '\0';
     7064        endname += esc + 1;
    70997065    }
    71007066    matchdot = 0;
     
    71217087    closedir(dirp);
    71227088    if (!atend)
    7123         endname[-1] = '/';
     7089        endname[-esc - 1] = esc ? '\\' : '/';
    71247090}
    71257091
     
    72097175
    72107176        INT_OFF;
    7211         p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
     7177        p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
    72127178        {
    72137179            int i = strlen(str->text);
     
    72997265{
    73007266    herefd = fd;
    7301     expandarg(arg, (struct arglist *)NULL, 0);
     7267    expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
    73027268    full_write(fd, stackblock(), expdest - (char *)stackblock());
    73037269}
     
    73097275patmatch(char *pattern, const char *string)
    73107276{
    7311     return pmatch(preglob(pattern, 0, 0), string);
     7277    return pmatch(preglob(pattern, 0), string);
    73127278}
    73137279
     
    77627728    builtinloc = idx_bltin;
    77637729}
    7764 
    7765 #define TEOF 0
    7766 #define TNL 1
    7767 #define TREDIR 2
    7768 #define TWORD 3
    7769 #define TSEMI 4
    7770 #define TBACKGND 5
    7771 #define TAND 6
    7772 #define TOR 7
    7773 #define TPIPE 8
    7774 #define TLP 9
    7775 #define TRP 10
    7776 #define TENDCASE 11
    7777 #define TENDBQUOTE 12
    7778 #define TNOT 13
    7779 #define TCASE 14
    7780 #define TDO 15
    7781 #define TDONE 16
    7782 #define TELIF 17
    7783 #define TELSE 18
    7784 #define TESAC 19
    7785 #define TFI 20
    7786 #define TFOR 21
    7787 #define TIF 22
    7788 #define TIN 23
    7789 #define TTHEN 24
    7790 #define TUNTIL 25
    7791 #define TWHILE 26
    7792 #define TBEGIN 27
    7793 #define TEND 28
     7730enum {
     7731    TEOF,
     7732    TNL,
     7733    TREDIR,
     7734    TWORD,
     7735    TSEMI,
     7736    TBACKGND,
     7737    TAND,
     7738    TOR,
     7739    TPIPE,
     7740    TLP,
     7741    TRP,
     7742    TENDCASE,
     7743    TENDBQUOTE,
     7744    TNOT,
     7745    TCASE,
     7746    TDO,
     7747    TDONE,
     7748    TELIF,
     7749    TELSE,
     7750    TESAC,
     7751    TFI,
     7752    TFOR,
     7753#if ENABLE_ASH_BASH_COMPAT
     7754    TFUNCTION,
     7755#endif
     7756    TIF,
     7757    TIN,
     7758    TTHEN,
     7759    TUNTIL,
     7760    TWHILE,
     7761    TBEGIN,
     7762    TEND
     7763};
    77947764typedef smallint token_id_t;
    77957765
     
    78207790    "\1fi",
    78217791    "\0for",
     7792#if ENABLE_ASH_BASH_COMPAT
     7793    "\0function",
     7794#endif
    78227795    "\0if",
    78237796    "\0in",
     
    78487821 */
    78497822static int
    7850 describe_command(char *command, int describe_command_verbose)
     7823describe_command(char *command, const char *path, int describe_command_verbose)
    78517824{
    78527825    struct cmdentry entry;
     
    78557828    const struct alias *ap;
    78567829#endif
    7857     const char *path = pathval();
     7830
     7831    path = path ? path : pathval();
    78587832
    78597833    if (describe_command_verbose) {
     
    79557929    }
    79567930    while (argv[i]) {
    7957         err |= describe_command(argv[i++], verbose);
     7931        err |= describe_command(argv[i++], NULL, verbose);
    79587932    }
    79597933    return err;
     
    79697943        VERIFY_VERBOSE = 2,
    79707944    } verify = 0;
     7945    const char *path = NULL;
    79717946
    79727947    while ((c = nextopt("pvV")) != '\0')
     
    79797954            abort();
    79807955#endif
     7956        else
     7957            path = bb_default_path;
    79817958    /* Mimic bash: just "command -v" doesn't complain, it's a nop */
    79827959    if (verify && (*argptr != NULL)) {
    7983         return describe_command(*argptr, verify - VERIFY_BRIEF);
     7960        return describe_command(*argptr, path, verify - VERIFY_BRIEF);
    79847961    }
    79857962
     
    80017978#define EV_BACKCMD 04           /* command executing within back quotes */
    80027979
    8003 static const uint8_t nodesize[N_NUMBER] = {
     7980static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
    80047981    [NCMD     ] = SHELL_ALIGN(sizeof(struct ncmd)),
    80057982    [NPIPE    ] = SHELL_ALIGN(sizeof(struct npipe)),
     
    84548431            (flags | ((is_or >> 1) - 1)) & EV_TESTED
    84558432        );
    8456         if (!exitstatus == is_or)
     8433        if ((!exitstatus) == is_or)
    84578434            break;
    84588435        if (!evalskip) {
     
    85628539    arglist.lastp = &arglist.list;
    85638540    for (argp = n->nfor.args; argp; argp = argp->narg.next) {
    8564         expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
     8541        expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
    85658542        /* XXX */
    85668543        if (evalskip)
     
    85738550    flags &= EV_TESTED;
    85748551    for (sp = arglist.list; sp; sp = sp->next) {
    8575         setvar(n->nfor.var, sp->text, 0);
     8552        setvar0(n->nfor.var, sp->text);
    85768553        evaltree(n->nfor.body, flags);
    85778554        if (evalskip) {
     
    87988775            /* note: ash and hush share this string */
    87998776            out1fmt("\n\n%s %s\n"
    8800                 "Enter 'help' for a list of built-in commands."
    8801                 "\n\n",
     8777                IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
     8778                "\n",
    88028779                bb_banner,
    88038780                "built-in shell (ash)"
     
    89148891        cp = *++argv;
    89158892        if (!cp)
    8916             return 0;
     8893            return NULL;
    89178894        if (*cp++ != '-')
    89188895            break;
     
    89218898            break;
    89228899        if (c == '-' && !*cp) {
    8923             argv++;
     8900            if (!*++argv)
     8901                return NULL;
    89248902            break;
    89258903        }
     
    89318909            default:
    89328910                /* run 'typecmd' for other options */
    8933                 return 0;
     8911                return NULL;
    89348912            }
    89358913            c = *cp++;
     
    89448922 * value and flags are saved in a localvar structure.  The saved values
    89458923 * will be restored when the shell function returns.  We handle the name
    8946  * "-" as a special case.
     8924 * "-" as a special case: it makes changes to "set +-options" local
     8925 * (options will be restored on return from the function).
    89478926 */
    89488927static void
     
    89528931    struct var **vpp;
    89538932    struct var *vp;
     8933    char *eq = strchr(name, '=');
    89548934
    89558935    INT_OFF;
    8956     lvp = ckzalloc(sizeof(struct localvar));
     8936    /* Cater for duplicate "local". Examples:
     8937     * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
     8938     * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
     8939     */
     8940    lvp = localvars;
     8941    while (lvp) {
     8942        if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
     8943            if (eq)
     8944                setvareq(name, 0);
     8945            /* else:
     8946             * it's a duplicate "local VAR" declaration, do nothing
     8947             */
     8948            return;
     8949        }
     8950        lvp = lvp->next;
     8951    }
     8952
     8953    lvp = ckzalloc(sizeof(*lvp));
    89578954    if (LONE_DASH(name)) {
    89588955        char *p;
     
    89618958        vp = NULL;
    89628959    } else {
    8963         char *eq;
    8964 
    89658960        vpp = hashvar(name);
    89668961        vp = *findvar(vpp, name);
    8967         eq = strchr(name, '=');
    89688962        if (vp == NULL) {
     8963            /* variable did not exist yet */
    89698964            if (eq)
    89708965                setvareq(name, VSTRFIXED);
     
    89768971            lvp->text = vp->var_text;
    89778972            lvp->flags = vp->flags;
     8973            /* make sure neither "struct var" nor string gets freed
     8974             * during (un)setting:
     8975             */
    89788976            vp->flags |= VSTRFIXED|VTEXTFIXED;
    89798977            if (eq)
    89808978                setvareq(name, 0);
     8979            else
     8980                /* "local VAR" unsets VAR: */
     8981                setvar0(name, NULL);
    89818982        }
    89828983    }
     
    89948995{
    89958996    char *name;
     8997
     8998    if (!funcnest)
     8999        ash_msg_and_raise_error("not in a function");
    89969000
    89979001    argv = argptr;
     
    90499053static int getoptscmd(int, char **) FAST_FUNC;
    90509054#endif
    9051 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
     9055#if ENABLE_ASH_HELP
    90529056static int helpcmd(int, char **) FAST_FUNC;
     9057#endif
     9058#if MAX_HISTORY
     9059static int historycmd(int, char **) FAST_FUNC;
    90539060#endif
    90549061#if ENABLE_SH_MATH_SUPPORT
     
    91229129#endif
    91239130    { BUILTIN_NOSPEC        "hash"    , hashcmd    },
    9124 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
     9131#if ENABLE_ASH_HELP
    91259132    { BUILTIN_NOSPEC        "help"    , helpcmd    },
     9133#endif
     9134#if MAX_HISTORY
     9135    { BUILTIN_NOSPEC        "history" , historycmd },
    91269136#endif
    91279137#if JOBS
     
    94319441            int exit_status;
    94329442            int i = exception_type;
    9433             if (i == EXEXIT)
     9443            if (i == EXEXIT || i == EXEXEC)
    94349444                goto raise;
    94359445            exit_status = 2;
     
    94549464            goto raise;
    94559465        break;
    9456 
    94579466    } /* switch */
    94589467
     
    94649473         * However I implemented that within libedit itself.
    94659474         */
    9466         setvar("_", lastarg, 0);
     9475        setvar0("_", lastarg);
    94679476    }
    94689477    popstackmark(&smark);
     
    96509659 retry:
    96519660    if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
    9652         nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
     9661        nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
    96539662    else {
    96549663        int timeout = -1;
     
    96669675        line_input_state->path_lookup = pathval();
    96679676# endif
    9668         /* Unicode support should be activated even if LANG is set
    9669          * _during_ shell execution, not only if it was set when
    9670          * shell was started. Therefore, re-check LANG every time:
    9671          */
    9672         reinit_unicode(lookupvar("LANG"));
     9677        reinit_unicode_for_ash();
    96739678        nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
    96749679        if (nr == 0) {
     
    96899694# if ENABLE_ASH_IDLE_TIMEOUT
    96909695            else if (errno == EAGAIN && timeout > 0) {
    9691                 printf("\007timed out waiting for input: auto-logout\n");
     9696                puts("\007timed out waiting for input: auto-logout");
    96929697                exitshell();
    96939698            }
     
    96969701    }
    96979702#else
    9698     nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
     9703    nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
    96999704#endif
    97009705
     
    1004110046#if ENABLE_ASH_MAIL
    1004210047
    10043 #define MAXMBOXES 10
    10044 
    10045 /* times of mailboxes */
    10046 static time_t mailtime[MAXMBOXES];
     10048/* Hash of mtimes of mailboxes */
     10049static unsigned mailtime_hash;
    1004710050/* Set if MAIL or MAILPATH is changed. */
    1004810051static smallint mail_var_path_changed;
     
    1006010063    char *p;
    1006110064    char *q;
    10062     time_t *mtp;
     10065    unsigned new_hash;
    1006310066    struct stackmark smark;
    1006410067    struct stat statb;
     
    1006610069    setstackmark(&smark);
    1006710070    mpath = mpathset() ? mpathval() : mailval();
    10068     for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
     10071    new_hash = 0;
     10072    for (;;) {
    1006910073        p = path_advance(&mpath, nullstr);
    1007010074        if (p == NULL)
     
    1008010084        q[-1] = '\0';                   /* delete trailing '/' */
    1008110085        if (stat(p, &statb) < 0) {
    10082             *mtp = 0;
    1008310086            continue;
    1008410087        }
    10085         if (!mail_var_path_changed && statb.st_mtime != *mtp) {
    10086             fprintf(
    10087                 stderr, "%s\n",
    10088                 pathopt ? pathopt : "you have mail"
    10089             );
    10090         }
    10091         *mtp = statb.st_mtime;
     10088        /* Very simplistic "hash": just a sum of all mtimes */
     10089        new_hash += (unsigned)statb.st_mtime;
     10090    }
     10091    if (!mail_var_path_changed && mailtime_hash != new_hash) {
     10092        if (mailtime_hash != 0)
     10093            out2str("you have mail\n");
     10094        mailtime_hash = new_hash;
    1009210095    }
    1009310096    mail_var_path_changed = 0;
     
    1036110364    int done = 0;
    1036210365    int err = 0;
    10363     char s[12];
     10366    char sbuf[2];
    1036410367    char **optnext;
     10368
     10369    sbuf[1] = '\0';
    1036510370
    1036610371    if (*param_optind < 1)
     
    1039010395        if (*q == '\0') {
    1039110396            if (optstr[0] == ':') {
    10392                 s[0] = c;
    10393                 s[1] = '\0';
    10394                 err |= setvarsafe("OPTARG", s, 0);
     10397                sbuf[0] = c;
     10398                /*sbuf[1] = '\0'; - already is */
     10399                err |= setvarsafe("OPTARG", sbuf, 0);
    1039510400            } else {
    1039610401                fprintf(stderr, "Illegal option -%c\n", c);
     
    1040710412        if (*p == '\0' && (p = *optnext) == NULL) {
    1040810413            if (optstr[0] == ':') {
    10409                 s[0] = c;
    10410                 s[1] = '\0';
    10411                 err |= setvarsafe("OPTARG", s, 0);
     10414                sbuf[0] = c;
     10415                /*sbuf[1] = '\0'; - already is */
     10416                err |= setvarsafe("OPTARG", sbuf, 0);
    1041210417                c = ':';
    1041310418            } else {
     
    1042810433    *optoff = p ? p - *(optnext - 1) : -1;
    1042910434    *param_optind = optnext - optfirst + 1;
    10430     fmtstr(s, sizeof(s), "%d", *param_optind);
    10431     err |= setvarsafe("OPTIND", s, VNOFUNC);
    10432     s[0] = c;
    10433     s[1] = '\0';
    10434     err |= setvarsafe(optvar, s, 0);
     10435    err |= setvarsafe("OPTIND", itoa(*param_optind), VNOFUNC);
     10436    sbuf[0] = c;
     10437    /*sbuf[1] = '\0'; - already is */
     10438    err |= setvarsafe(optvar, sbuf, 0);
    1043510439    if (err) {
    1043610440        *param_optind = 1;
     
    1048510489
    1048610490static smallint tokpushback;           /* last token pushed back */
    10487 static smallint parsebackquote;        /* nonzero if we are inside backquotes */
    1048810491static smallint quoteflag;             /* set if (part of) last token was quoted */
    1048910492static token_id_t lasttoken;           /* last token read (integer id Txxx) */
     
    1053010533static union node *parse_command(void);
    1053110534static void parseheredoc(void);
    10532 static char peektoken(void);
     10535static int peektoken(void);
    1053310536static int readtoken(void);
    1053410537
     
    1053910542    int tok;
    1054010543
    10541     checkkwd = CHKNL | CHKKWD | CHKALIAS;
    10542     if (nlflag == 2 && peektoken())
    10543         return NULL;
    1054410544    n1 = NULL;
    1054510545    for (;;) {
     10546        switch (peektoken()) {
     10547        case TNL:
     10548            if (!(nlflag & 1))
     10549                break;
     10550            parseheredoc();
     10551            return n1;
     10552
     10553        case TEOF:
     10554            if (!n1 && (nlflag & 1))
     10555                n1 = NODE_EOF;
     10556            parseheredoc();
     10557            return n1;
     10558        }
     10559
     10560        checkkwd = CHKNL | CHKKWD | CHKALIAS;
     10561        if (nlflag == 2 && tokname_array[peektoken()][0])
     10562            return n1;
     10563        nlflag |= 2;
     10564
    1054610565        n2 = andor();
    1054710566        tok = readtoken();
     
    1056910588        }
    1057010589        switch (tok) {
     10590        case TNL:
     10591        case TEOF:
     10592            tokpushback = 1;
     10593            /* fall through */
    1057110594        case TBACKGND:
    1057210595        case TSEMI:
    10573             tok = readtoken();
    10574             /* fall through */
    10575         case TNL:
    10576             if (tok == TNL) {
    10577                 parseheredoc();
    10578                 if (nlflag == 1)
    10579                     return n1;
    10580             } else {
    10581                 tokpushback = 1;
    10582             }
    10583             checkkwd = CHKNL | CHKKWD | CHKALIAS;
    10584             if (peektoken())
    10585                 return n1;
    1058610596            break;
    10587         case TEOF:
    10588             if (heredoclist)
    10589                 parseheredoc();
    10590             else
    10591                 pungetc();              /* push back EOF on input */
    10592             return n1;
    1059310597        default:
    10594             if (nlflag == 1)
     10598            if ((nlflag & 1))
    1059510599                raise_error_unexpected_syntax(-1);
    1059610600            tokpushback = 1;
     
    1076710771#if ENABLE_ASH_BASH_COMPAT
    1076810772    smallint double_brackets_flag = 0;
     10773    smallint function_flag = 0;
    1076910774#endif
    1077010775
     
    1078310788        switch (t) {
    1078410789#if ENABLE_ASH_BASH_COMPAT
     10790        case TFUNCTION:
     10791            if (peektoken() != TWORD)
     10792                raise_error_unexpected_syntax(TWORD);
     10793            function_flag = 1;
     10794            break;
    1078510795        case TAND: /* "&&" */
    1078610796        case TOR: /* "||" */
     
    1081110821                savecheckkwd = 0;
    1081210822            }
     10823#if ENABLE_ASH_BASH_COMPAT
     10824            if (function_flag) {
     10825                checkkwd = CHKNL | CHKKWD;
     10826                switch (peektoken()) {
     10827                case TBEGIN:
     10828                case TIF:
     10829                case TCASE:
     10830                case TUNTIL:
     10831                case TWHILE:
     10832                case TFOR:
     10833                    goto do_func;
     10834                case TLP:
     10835                    function_flag = 0;
     10836                    break;
     10837                case TWORD:
     10838                    if (strcmp("[[", wordtext) == 0)
     10839                        goto do_func;
     10840                    /* fall through */
     10841                default:
     10842                    raise_error_unexpected_syntax(-1);
     10843                }
     10844            }
     10845#endif
    1081310846            break;
    1081410847        case TREDIR:
     
    1081810851            break;
    1081910852        case TLP:
     10853 IF_ASH_BASH_COMPAT(do_func:)
    1082010854            if (args && app == &args->narg.next
    1082110855             && !vars && !redir
     
    1082510859
    1082610860                /* We have a function */
    10827                 if (readtoken() != TRP)
     10861                if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP)
    1082810862                    raise_error_unexpected_syntax(TRP);
    1082910863                name = n->narg.text;
     
    1083810872                return n;
    1083910873            }
     10874            IF_ASH_BASH_COMPAT(function_flag = 0;)
    1084010875            /* fall through */
    1084110876        default:
     
    1092010955        n1->type = NFOR;
    1092110956        n1->nfor.var = wordtext;
    10922         checkkwd = CHKKWD | CHKALIAS;
     10957        checkkwd = CHKNL | CHKKWD | CHKALIAS;
    1092310958        if (readtoken() == TIN) {
    1092410959            app = &ap;
     
    1094710982             * that the original Bourne shell only allowed NL).
    1094810983             */
    10949             if (lasttoken != TNL && lasttoken != TSEMI)
     10984            if (lasttoken != TSEMI)
    1095010985                tokpushback = 1;
    1095110986        }
     
    1096611001        n2->narg.text = wordtext;
    1096711002        n2->narg.backquote = backquotelist;
    10968         do {
    10969             checkkwd = CHKKWD | CHKALIAS;
    10970         } while (readtoken() == TNL);
    10971         if (lasttoken != TIN)
     11003        checkkwd = CHKNL | CHKKWD | CHKALIAS;
     11004        if (readtoken() != TIN)
    1097211005            raise_error_unexpected_syntax(TIN);
    1097311006        cpp = &n1->ncase.cases;
     
    1102011053        t = TEND;
    1102111054        break;
     11055    IF_ASH_BASH_COMPAT(case TFUNCTION:)
    1102211056    case TWORD:
    1102311057    case TREDIR:
     
    1113811172    IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
    1113911173
    11140 #if __GNUC__
    11141     /* Avoid longjmp clobbering */
    11142     (void) &out;
    11143     (void) &quotef;
    11144     (void) &dblquote;
    11145     (void) &varnest;
    11146     (void) &arinest;
    11147     (void) &parenlevel;
    11148     (void) &dqvarnest;
    11149     (void) &oldstyle;
    11150     (void) &prevsyntax;
    11151     (void) &syntax;
    11152 #endif
    1115311174    startlinno = g_parsefile->linno;
    1115411175    bqlist = NULL;
     
    1122011241                 && (c != '"' || eofmark != NULL)
    1122111242                ) {
    11222                     USTPUTC(CTLESC, out);
    1122311243                    USTPUTC('\\', out);
    1122411244                }
    11225                 if (SIT(c, SQSYNTAX) == CCTL)
    11226                     USTPUTC(CTLESC, out);
     11245                USTPUTC(CTLESC, out);
    1122711246                USTPUTC(c, out);
    1122811247                quotef = 1;
     
    1124211261        case CENDQUOTE:
    1124311262            IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
    11244             if (eofmark != NULL && arinest == 0
    11245              && varnest == 0
    11246             ) {
     11263            if (eofmark != NULL && varnest == 0) {
    1124711264                USTPUTC(c, out);
    1124811265            } else {
     
    1127811295            } else {
    1127911296                if (pgetc() == ')') {
     11297                    c = CTLENDARI;
    1128011298                    if (--arinest == 0) {
    1128111299                        syntax = prevsyntax;
    11282                         dblquote = (syntax == DQSYNTAX);
    11283                         c = CTLENDARI;
    1128411300                    }
    1128511301                } else {
     
    1132311339        raise_error_syntax("missing '))'");
    1132411340#endif
    11325     if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
     11341    if (syntax != BASESYNTAX && eofmark == NULL)
    1132611342        raise_error_syntax("unterminated quoted string");
    1132711343    if (varnest != 0) {
     
    1148511501    ) {
    1148611502#if ENABLE_ASH_BASH_COMPAT
    11487         if (c == '\'')
     11503        if (syntax != DQSYNTAX && c == '\'')
    1148811504            bash_dollar_squote = 1;
    1148911505        else
     
    1160011616            pungetc();
    1160111617        }
    11602         if (dblquote || arinest)
    11603             flags |= VSQUOTE;
    1160411618        ((unsigned char *)stackblock())[typeloc] = subtype | flags;
    1160511619        if (subtype != VSNORMAL) {
    1160611620            varnest++;
    11607             if (dblquote || arinest) {
     11621            if (dblquote) {
    1160811622                dqvarnest++;
    1160911623            }
     
    1162111635parsebackq: {
    1162211636    struct nodelist **nlpp;
    11623     smallint savepbq;
    1162411637    union node *n;
    11625     char *volatile str;
    11626     struct jmploc jmploc;
    11627     struct jmploc *volatile savehandler;
     11638    char *str;
    1162811639    size_t savelen;
    1162911640    smallint saveprompt = 0;
    1163011641
    11631 #ifdef __GNUC__
    11632     (void) &saveprompt;
    11633 #endif
    11634     savepbq = parsebackquote;
    11635     if (setjmp(jmploc.loc)) {
    11636         free(str);
    11637         parsebackquote = 0;
    11638         exception_handler = savehandler;
    11639         longjmp(exception_handler->loc, 1);
    11640     }
    11641     INT_OFF;
    1164211642    str = NULL;
    1164311643    savelen = out - (char *)stackblock();
    1164411644    if (savelen > 0) {
    11645         str = ckmalloc(savelen);
     11645        str = alloca(savelen);
    1164611646        memcpy(str, stackblock(), savelen);
    1164711647    }
    11648     savehandler = exception_handler;
    11649     exception_handler = &jmploc;
    11650     INT_ON;
    1165111648    if (oldstyle) {
    1165211649        /* We must read until the closing backquote, giving special
     
    1171911716    *nlpp = stzalloc(sizeof(**nlpp));
    1172011717    /* (*nlpp)->next = NULL; - stzalloc did it */
    11721     parsebackquote = oldstyle;
    1172211718
    1172311719    if (oldstyle) {
     
    1174811744        memcpy(out, str, savelen);
    1174911745        STADJUST(savelen, out);
    11750         INT_OFF;
    11751         free(str);
    11752         str = NULL;
    11753         INT_ON;
    11754     }
    11755     parsebackquote = savepbq;
    11756     exception_handler = savehandler;
    11757     if (arinest || dblquote)
    11758         USTPUTC(CTLBACKQ | CTLQUOTE, out);
    11759     else
    11760         USTPUTC(CTLBACKQ, out);
     11746    }
     11747    USTPUTC(CTLBACKQ, out);
    1176111748    if (oldstyle)
    1176211749        goto parsebackq_oldreturn;
     
    1177211759        prevsyntax = syntax;
    1177311760        syntax = ARISYNTAX;
    11774         USTPUTC(CTLARI, out);
    11775         if (dblquote)
    11776             USTPUTC('"', out);
    11777         else
    11778             USTPUTC(' ', out);
    11779     } else {
    11780         /*
    11781          * we collapse embedded arithmetic expansion to
    11782          * parenthesis, which should be equivalent
    11783          */
    11784         USTPUTC('(', out);
    11785     }
     11761    }
     11762    USTPUTC(CTLARI, out);
    1178611763    goto parsearith_return;
    1178711764}
    1178811765#endif
    11789 
    1179011766} /* end of readtoken */
    1179111767
     
    1195811934{
    1195911935    int t;
     11936    int kwd = checkkwd;
    1196011937#if DEBUG
    1196111938    smallint alreadyseen = tokpushback;
     
    1197111948     * eat newlines
    1197211949     */
    11973     if (checkkwd & CHKNL) {
     11950    if (kwd & CHKNL) {
    1197411951        while (t == TNL) {
    1197511952            parseheredoc();
     
    1198511962     * check for keywords
    1198611963     */
    11987     if (checkkwd & CHKKWD) {
     11964    if (kwd & CHKKWD) {
    1198811965        const char *const *pp;
    1198911966
     
    1201911996}
    1202011997
    12021 static char
     11998static int
    1202211999peektoken(void)
    1202312000{
     
    1202612003    t = readtoken();
    1202712004    tokpushback = 1;
    12028     return tokname_array[t][0];
     12005    return t;
    1202912006}
    1203012007
     
    1203612013parsecmd(int interact)
    1203712014{
    12038     int t;
    12039 
    1204012015    tokpushback = 0;
     12016    checkkwd = 0;
     12017    heredoclist = 0;
    1204112018    doprompt = interact;
    1204212019    setprompt_if(doprompt, doprompt);
    1204312020    needprompt = 0;
    12044     t = readtoken();
    12045     if (t == TEOF)
    12046         return NODE_EOF;
    12047     if (t == TNL)
    12048         return NULL;
    12049     tokpushback = 1;
    1205012021    return list(1);
    1205112022}
     
    1209812069    n.narg.backquote = backquotelist;
    1209912070
    12100     expandarg(&n, NULL, 0);
     12071    expandarg(&n, NULL, EXP_QUOTED);
    1210112072    return stackblock();
    1210212073}
     
    1218012151#if JOBS
    1218112152        if (doing_jobctl)
    12182             showjobs(stderr, SHOW_CHANGED);
     12153            showjobs(SHOW_CHANGED|SHOW_STDERR);
    1218312154#endif
    1218412155        inter = 0;
     
    1227712248    exitstatus = 0;
    1227812249
     12250    /* This aborts if file isn't found, which is POSIXly correct.
     12251     * bash returns exitcode 1 instead.
     12252     */
    1227912253    fullname = find_dot_file(argv[1]);
    12280 
    1228112254    argv += 2;
    1228212255    argc -= 2;
     
    1228812261    };
    1228912262
     12263    /* This aborts if file can't be opened, which is POSIXly correct.
     12264     * bash returns exitcode 1 instead.
     12265     */
    1229012266    setinputfile(fullname, INPUT_PUSH_FILE);
    1229112267    commandname = fullname;
     
    1259512571/* ============ Builtins */
    1259612572
    12597 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
    12598 /*
    12599  * Lists available builtins
    12600  */
     12573#if ENABLE_ASH_HELP
    1260112574static int FAST_FUNC
    1260212575helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     
    1261612589        }
    1261712590    }
    12618 #if ENABLE_FEATURE_SH_STANDALONE
     12591# if ENABLE_FEATURE_SH_STANDALONE
    1261912592    {
    1262012593        const char *a = applet_names;
     
    1262512598                col = 0;
    1262612599            }
    12627             a += strlen(a) + 1;
    12628         }
    12629     }
    12630 #endif
     12600            while (*a++ != '\0')
     12601                continue;
     12602        }
     12603    }
     12604# endif
    1263112605    out1fmt("\n\n");
    1263212606    return EXIT_SUCCESS;
    1263312607}
    12634 #endif /* FEATURE_SH_EXTRA_QUIET */
     12608#endif
     12609
     12610#if MAX_HISTORY
     12611static int FAST_FUNC
     12612historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     12613{
     12614    show_history(line_input_state);
     12615    return EXIT_SUCCESS;
     12616}
     12617#endif
    1263512618
    1263612619/*
     
    1274912732    struct tms buf;
    1275012733
    12751     clk_tck = sysconf(_SC_CLK_TCK);
     12734    clk_tck = bb_clk_tck();
    1275212735    times(&buf);
    1275312736
     
    1284412827     */
    1284512828    INT_OFF;
    12846     r = shell_builtin_read(setvar2,
     12829    r = shell_builtin_read(setvar0,
    1284712830        argptr,
    1284812831        bltinlookup("IFS"), /* can be NULL */
     
    1286212845
    1286312846static int FAST_FUNC
    12864 umaskcmd(int argc UNUSED_PARAM, char **argv)
    12865 {
    12866     static const char permuser[3] ALIGN1 = "ugo";
    12867     static const char permmode[3] ALIGN1 = "rwx";
    12868     static const short permmask[] ALIGN2 = {
    12869         S_IRUSR, S_IWUSR, S_IXUSR,
    12870         S_IRGRP, S_IWGRP, S_IXGRP,
    12871         S_IROTH, S_IWOTH, S_IXOTH
    12872     };
    12873 
    12874     /* TODO: use bb_parse_mode() instead */
    12875 
    12876     char *ap;
     12847umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     12848{
     12849    static const char permuser[3] ALIGN1 = "ogu";
     12850
    1287712851    mode_t mask;
    12878     int i;
    1287912852    int symbolic_mode = 0;
    1288012853
     
    1288812861    INT_ON;
    1288912862
    12890     ap = *argptr;
    12891     if (ap == NULL) {
     12863    if (*argptr == NULL) {
    1289212864        if (symbolic_mode) {
    12893             char buf[18];
     12865            char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
    1289412866            char *p = buf;
    12895 
    12896             for (i = 0; i < 3; i++) {
    12897                 int j;
    12898 
     12867            int i;
     12868
     12869            i = 2;
     12870            for (;;) {
     12871                *p++ = ',';
    1289912872                *p++ = permuser[i];
    1290012873                *p++ = '=';
    12901                 for (j = 0; j < 3; j++) {
    12902                     if ((mask & permmask[3 * i + j]) == 0) {
    12903                         *p++ = permmode[j];
    12904                     }
    12905                 }
    12906                 *p++ = ',';
     12874                /* mask is 0..0uuugggooo. i=2 selects uuu bits */
     12875                if (!(mask & 0400)) *p++ = 'r';
     12876                if (!(mask & 0200)) *p++ = 'w';
     12877                if (!(mask & 0100)) *p++ = 'x';
     12878                mask <<= 3;
     12879                if (--i < 0)
     12880                    break;
    1290712881            }
    12908             *--p = 0;
    12909             puts(buf);
     12882            *p = '\0';
     12883            puts(buf + 1);
    1291012884        } else {
    12911             out1fmt("%.4o\n", mask);
     12885            out1fmt("%04o\n", mask);
    1291212886        }
    1291312887    } else {
    12914         if (isdigit((unsigned char) *ap)) {
    12915             mask = 0;
    12916             do {
    12917                 if (*ap >= '8' || *ap < '0')
    12918                     ash_msg_and_raise_error(msg_illnum, argv[1]);
    12919                 mask = (mask << 3) + (*ap - '0');
    12920             } while (*++ap != '\0');
    12921             umask(mask);
    12922         } else {
    12923             mask = ~mask & 0777;
    12924             if (!bb_parse_mode(ap, &mask)) {
    12925                 ash_msg_and_raise_error("illegal mode: %s", ap);
    12926             }
    12927             umask(~mask & 0777);
    12928         }
     12888        char *modestr = *argptr;
     12889                /* numeric umasks are taken as-is */
     12890                /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
     12891        if (!isdigit(modestr[0]))
     12892            mask ^= 0777;
     12893        mask = bb_parse_mode(modestr, mask);
     12894        if ((unsigned)mask > 0777) {
     12895            ash_msg_and_raise_error("illegal mode: %s", modestr);
     12896        }
     12897        if (!isdigit(modestr[0]))
     12898            mask ^= 0777;
     12899        umask(mask);
    1292912900    }
    1293012901    return 0;
     
    1300512976        }
    1300612977
    13007         setvar("PPID", utoa(getppid()), 0);
    13008 
     12978        setvar0("PPID", utoa(getppid()));
     12979#if ENABLE_ASH_BASH_COMPAT
     12980        p = lookupvar("SHLVL");
     12981        setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
     12982        if (!lookupvar("HOSTNAME")) {
     12983            struct utsname uts;
     12984            uname(&uts);
     12985            setvar0("HOSTNAME", uts.nodename);
     12986        }
     12987#endif
    1300912988        p = lookupvar("PWD");
    1301012989        if (p) {
     
    1318513164        }
    1318613165        if (e == EXINT) {
    13187             outcslow('\n', stderr);
     13166            newline_and_flush(stderr);
    1318813167        }
    1318913168
     
    1321013189    procargs(argv);
    1321113190
    13212 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
    13213     if (iflag) {
    13214         const char *hp = lookupvar("HISTFILE");
    13215         if (!hp) {
    13216             hp = lookupvar("HOME");
    13217             if (hp) {
    13218                 char *defhp = concat_path_file(hp, ".ash_history");
    13219                 setvar("HISTFILE", defhp, 0);
    13220                 free(defhp);
    13221             }
    13222         }
    13223     }
    13224 #endif
    1322513191    if (argv[0] && argv[0][0] == '-')
    1322613192        isloginsh = 1;
    1322713193    if (isloginsh) {
     13194        const char *hp;
     13195
    1322813196        state = 1;
    1322913197        read_profile("/etc/profile");
    1323013198 state1:
    1323113199        state = 2;
    13232         read_profile(".profile");
     13200        hp = lookupvar("HOME");
     13201        if (hp) {
     13202            hp = concat_path_file(hp, ".profile");
     13203            read_profile(hp);
     13204            free((char*)hp);
     13205        }
    1323313206    }
    1323413207 state2:
     
    1326213235        if (iflag) {
    1326313236            const char *hp = lookupvar("HISTFILE");
     13237            if (!hp) {
     13238                hp = lookupvar("HOME");
     13239                if (hp) {
     13240                    hp = concat_path_file(hp, ".ash_history");
     13241                    setvar0("HISTFILE", hp);
     13242                    free((char*)hp);
     13243                    hp = lookupvar("HISTFILE");
     13244                }
     13245            }
    1326413246            if (hp)
    1326513247                line_input_state->hist_file = hp;
Note: See TracChangeset for help on using the changeset viewer.