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


Ignore:
Timestamp:
Dec 20, 2016, 4:07:32 PM (9 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:
131 added
10 deleted
12 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;
  • branches/3.3/mindi-busybox/shell/ash_test/ash-quoting/dollar_squote_bash1.right

    r2725 r3621  
    11a   b
     2$'a\tb'
    23a
    34b c
  • branches/3.3/mindi-busybox/shell/ash_test/ash-quoting/dollar_squote_bash1.tests

    r2725 r3621  
    11echo $'a\tb'
     2echo "$'a\tb'"
    23echo $'a\nb' $'c\nd''ef'
    34echo $'a\'b' $'c\"d' $'e\\f'
  • branches/3.3/mindi-busybox/shell/ash_test/printenv.c

    r2725 r3621  
    2525   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
    2626
     27#include <stdio.h>
    2728#include <stdlib.h>
    2829#include <string.h>
  • branches/3.3/mindi-busybox/shell/ash_test/run-all

    r2725 r3621  
    3232        #*) echo $x ; sh $x ;;
    3333        *)
     34        echo -n "$1/$x: "
    3435        sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
    35         { echo "$1/$x: ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "$1/$x: fail";
     36        { echo "ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "fail";
    3637        ;;
    3738    esac
     
    4344    name="${x%%.tests}"
    4445    test -f "$name.right" || continue
     46    echo -n "$1/$x: "
    4547    {
    4648        "$THIS_SH" "./$x" >"$name.xx" 2>&1
    4749        diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
    4850        && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
    49     } && echo "$1/$x: ok" || echo "$1/$x: fail"
     51    } && echo "ok" || echo "fail"
    5052    done
    5153    )
  • branches/3.3/mindi-busybox/shell/hush.c

    r3232 r3621  
    9292# include <fnmatch.h>
    9393#endif
     94#include <sys/utsname.h> /* for setting $HOSTNAME */
    9495
    9596#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
     
    442443    DEFINITELY_ASSIGNMENT = 1,
    443444    NOT_ASSIGNMENT        = 2,
    444     /* Not an assigment, but next word may be: "if v=xyz cmd;" */
     445    /* Not an assignment, but next word may be: "if v=xyz cmd;" */
    445446    WORD_IS_KEYWORD       = 3,
    446447};
     
    851852static int builtin_help(char **argv) FAST_FUNC;
    852853#endif
     854#if MAX_HISTORY && ENABLE_FEATURE_EDITING
     855static int builtin_history(char **argv) FAST_FUNC;
     856#endif
    853857#if ENABLE_HUSH_LOCAL
    854858static int builtin_local(char **argv) FAST_FUNC;
     
    920924    BLTIN("help"     , builtin_help    , NULL),
    921925#endif
     926#if MAX_HISTORY && ENABLE_FEATURE_EDITING
     927    BLTIN("history"  , builtin_history , "Show command history"),
     928#endif
    922929#if ENABLE_HUSH_JOB
    923930    BLTIN("jobs"     , builtin_jobs    , "List jobs"),
     
    939946#endif
    940947    BLTIN("trap"     , builtin_trap    , "Trap signals"),
     948    BLTIN("true"     , builtin_true    , NULL),
    941949    BLTIN("type"     , builtin_type    , "Show command type"),
    942950    BLTIN("ulimit"   , shell_builtin_ulimit  , "Control resource limits"),
     
    13051313 * pipe.
    13061314 *
    1307  * Wait builtin in interruptible by signals for which user trap is set
     1315 * Wait builtin is interruptible by signals for which user trap is set
    13081316 * or by SIGINT in interactive shell.
    13091317 *
     
    13841392 *
    13851393 * Problem: the above approach makes it unwieldy to catch signals while
    1386  * we are in read builtin, of while we read commands from stdin:
     1394 * we are in read builtin, or while we read commands from stdin:
    13871395 * masked signals are not visible!
    13881396 *
     
    13931401 * We are interested in: signals which need to have special handling
    13941402 * as described above, and all signals which have traps set.
    1395  * Signals are rocorded in pending_set.
     1403 * Signals are recorded in pending_set.
    13961404 * After each pipe execution, we extract any pending signals
    13971405 * and act on them.
     
    14701478}
    14711479
     1480static void hush_exit(int exitcode) NORETURN;
     1481static void fflush_and__exit(void) NORETURN;
     1482static void restore_ttypgrp_and__exit(void) NORETURN;
     1483
     1484static void restore_ttypgrp_and__exit(void)
     1485{
     1486    /* xfunc has failed! die die die */
     1487    /* no EXIT traps, this is an escape hatch! */
     1488    G.exiting = 1;
     1489    hush_exit(xfunc_error_retval);
     1490}
     1491
     1492/* Needed only on some libc:
     1493 * It was observed that on exit(), fgetc'ed buffered data
     1494 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
     1495 * With the net effect that even after fork(), not vfork(),
     1496 * exit() in NOEXECed applet in "sh SCRIPT":
     1497 *  noexec_applet_here
     1498 *  echo END_OF_SCRIPT
     1499 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
     1500 * This makes "echo END_OF_SCRIPT" executed twice.
     1501 * Similar problems can be seen with die_if_script() -> xfunc_die()
     1502 * and in `cmd` handling.
     1503 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
     1504 */
     1505static void fflush_and__exit(void)
     1506{
     1507    fflush_all();
     1508    _exit(xfunc_error_retval);
     1509}
     1510
    14721511#if ENABLE_HUSH_JOB
    14731512
    14741513/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
    1475 # define disable_restore_tty_pgrp_on_exit() (die_sleep = 0)
     1514# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
    14761515/* After [v]fork, in parent: restore tty pgrp on xfunc death */
    1477 # define enable_restore_tty_pgrp_on_exit()  (die_sleep = -1)
     1516# define enable_restore_tty_pgrp_on_exit()  (die_func = restore_ttypgrp_and__exit)
    14781517
    14791518/* Restores tty foreground process group, and exits.
     
    14811520 * (will resend signal to itself, producing correct exit state)
    14821521 * or called directly with -EXITCODE.
    1483  * We also call it if xfunc is exiting. */
     1522 * We also call it if xfunc is exiting.
     1523 */
    14841524static void sigexit(int sig) NORETURN;
    14851525static void sigexit(int sig)
     
    15361576
    15371577/* Restores tty foreground process group, and exits. */
    1538 static void hush_exit(int exitcode) NORETURN;
    15391578static void hush_exit(int exitcode)
    15401579{
     
    15721611#endif
    15731612
     1613    fflush_all();
    15741614#if ENABLE_HUSH_JOB
    1575     fflush_all();
    15761615    sigexit(- (exitcode & 0xff));
    15771616#else
    1578     exit(exitcode);
     1617    _exit(exitcode);
    15791618#endif
    15801619}
     
    17491788    struct variable **var_pp;
    17501789    struct variable *cur;
     1790    char *free_me = NULL;
    17511791    char *eq_sign;
    17521792    int name_len;
     
    17651805            continue;
    17661806        }
     1807
    17671808        /* We found an existing var with this name */
    17681809        if (cur->flg_read_only) {
     
    18131854                goto free_and_exp;
    18141855            }
    1815         } else {
    1816             /* max_len == 0 signifies "malloced" var, which we can
    1817              * (and has to) free */
    1818             free(cur->varstr);
    1819         }
    1820         cur->max_len = 0;
     1856            /* Can't reuse */
     1857            cur->max_len = 0;
     1858            goto set_str_and_exp;
     1859        }
     1860        /* max_len == 0 signifies "malloced" var, which we can
     1861         * (and have to) free. But we can't free(cur->varstr) here:
     1862         * if cur->flg_export is 1, it is in the environment.
     1863         * We should either unsetenv+free, or wait until putenv,
     1864         * then putenv(new)+free(old).
     1865         */
     1866        free_me = cur->varstr;
    18211867        goto set_str_and_exp;
    18221868    }
     
    18451891            /* unsetenv was already done */
    18461892        } else {
     1893            int i;
    18471894            debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
    1848             return putenv(cur->varstr);
    1849         }
    1850     }
     1895            i = putenv(cur->varstr);
     1896            /* only now we can free old exported malloced string */
     1897            free(free_me);
     1898            return i;
     1899        }
     1900    }
     1901    free(free_me);
    18511902    return 0;
    18521903}
     
    19662017    }
    19672018    return old;
     2019}
     2020
     2021
     2022/*
     2023 * Unicode helper
     2024 */
     2025static void reinit_unicode_for_hush(void)
     2026{
     2027    /* Unicode support should be activated even if LANG is set
     2028     * _during_ shell execution, not only if it was set when
     2029     * shell was started. Therefore, re-check LANG every time:
     2030     */
     2031    if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
     2032     || ENABLE_UNICODE_USING_LOCALE
     2033        ) {
     2034        const char *s = get_local_var_value("LC_ALL");
     2035        if (!s) s = get_local_var_value("LC_CTYPE");
     2036        if (!s) s = get_local_var_value("LANG");
     2037        reinit_unicode(s);
     2038    }
    19682039}
    19692040
     
    20352106     * is actually being read */
    20362107    do {
    2037         /* Unicode support should be activated even if LANG is set
    2038          * _during_ shell execution, not only if it was set when
    2039          * shell was started. Therefore, re-check LANG every time:
    2040          */
    2041         reinit_unicode(get_local_var_value("LANG"));
    2042 
     2108        reinit_unicode_for_hush();
    20432109        G.flag_SIGINT = 0;
    20442110        /* buglet: SIGINT will not make new prompt to appear _at once_,
     
    31393205        old->command->cmd_type = CMD_NORMAL;
    31403206# if !BB_MMU
    3141         o_addstr(&old->as_string, ctx->as_string.data);
    3142         o_free_unsafe(&ctx->as_string);
    3143         old->command->group_as_string = xstrdup(old->as_string.data);
    3144         debug_printf_parse("pop, remembering as:'%s'\n",
    3145                 old->command->group_as_string);
     3207        /* At this point, the compound command's string is in
     3208         * ctx->as_string... except for the leading keyword!
     3209         * Consider this example: "echo a | if true; then echo a; fi"
     3210         * ctx->as_string will contain "true; then echo a; fi",
     3211         * with "if " remaining in old->as_string!
     3212         */
     3213        {
     3214            char *str;
     3215            int len = old->as_string.length;
     3216            /* Concatenate halves */
     3217            o_addstr(&old->as_string, ctx->as_string.data);
     3218            o_free_unsafe(&ctx->as_string);
     3219            /* Find where leading keyword starts in first half */
     3220            str = old->as_string.data + len;
     3221            if (str > old->as_string.data)
     3222                str--; /* skip whitespace after keyword */
     3223            while (str > old->as_string.data && isalpha(str[-1]))
     3224                str--;
     3225            /* Ugh, we're done with this horrid hack */
     3226            old->command->group_as_string = xstrdup(str);
     3227            debug_printf_parse("pop, remembering as:'%s'\n",
     3228                    old->command->group_as_string);
     3229        }
    31463230# endif
    31473231        *ctx = *old;   /* physical copy */
     
    42264310            }
    42274311#if !BB_MMU
    4228             debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
     4312            debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
    42294313            if (pstring)
    42304314                *pstring = ctx.as_string.data;
     
    43774461                o_free(&dest);
    43784462#if !BB_MMU
    4379                 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
     4463                debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
    43804464                if (pstring)
    43814465                    *pstring = ctx.as_string.data;
     
    46174701                 * "echo foo 2| cat" yields "foo 2". */
    46184702                done_command(&ctx);
    4619 #if !BB_MMU
    4620                 o_reset_to_empty_unquoted(&ctx.as_string);
    4621 #endif
    46224703            }
    46234704            goto new_cmd;
     
    50185099    /* Handle any expansions */
    50195100    if (exp_op == 'L') {
     5101        reinit_unicode_for_hush();
    50205102        debug_printf_expand("expand: length(%s)=", val);
    5021         val = utoa(val ? strlen(val) : 0);
     5103        val = utoa(val ? unicode_strlen(val) : 0);
    50225104        debug_printf_expand("%s\n", val);
    50235105    } else if (exp_op) {
     
    53675449            }
    53685450            break;
    5369 
    53705451        } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
    53715452
     
    58615942         */
    58625943        s = skip_whitespace(s);
    5863         if (strncmp(s, "trap", 4) == 0
     5944        if (is_prefixed_with(s, "trap")
    58645945         && skip_whitespace(s + 4)[0] == '\0'
    58655946        ) {
    58665947            static const char *const argv[] = { NULL, NULL };
    58675948            builtin_trap((char**)argv);
    5868             exit(0); /* not _exit() - we need to fflush */
     5949            fflush_all(); /* important */
     5950            _exit(0);
    58695951        }
    58705952# if BB_MMU
     
    64196501 * Don't exit() here.  If you don't exec, use _exit instead.
    64206502 * The at_exit handlers apparently confuse the calling process,
    6421  * in particular stdin handling.  Not sure why? -- because of vfork! (vda) */
     6503 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
     6504 */
    64226505static void pseudo_exec_argv(nommu_save_t *nommu_save,
    64236506        char **argv, int assignment_cnt,
     
    67576840                        if (i == fg_pipe->num_cmds-1)
    67586841                            /* TODO: use strsignal() instead for bash compat? but that's bloat... */
    6759                             printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
     6842                            puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
    67606843                        /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
    67616844                        /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
     
    73557438                debug_printf_exec("skipped cmd because of || or &&\n");
    73567439                last_followup = pi->followup;
    7357                 continue;
     7440                goto dont_check_jobs_but_continue;
    73587441            }
    73597442        }
     
    74947577                        /* else: e.g. "continue 2" should *break* once, *then* continue */
    74957578                    } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
    7496                     if (G.depth_break_continue != 0 || fbc == BC_BREAK)
    7497                         goto check_jobs_and_break;
     7579                    if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
     7580                        checkjobs(NULL);
     7581                        break;
     7582                    }
    74987583                    /* "continue": simulate end of loop */
    74997584                    rword = RES_DONE;
     
    75037588#if ENABLE_HUSH_FUNCTIONS
    75047589                if (G.flag_return_in_progress == 1) {
    7505                     /* same as "goto check_jobs_and_break" */
    75067590                    checkjobs(NULL);
    75077591                    break;
     
    75457629            cond_code = rcode;
    75467630#endif
     7631 check_jobs_and_continue:
     7632        checkjobs(NULL);
     7633 dont_check_jobs_but_continue: ;
    75477634#if ENABLE_HUSH_LOOPS
    75487635        /* Beware of "while false; true; do ..."! */
     
    75567643                    G.last_exitcode = rcode = EXIT_SUCCESS;
    75577644                    debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
    7558                     goto check_jobs_and_break;
     7645                    break;
    75597646                }
    75607647            }
     
    75627649                if (!rcode) {
    75637650                    debug_printf_exec(": until expr is true: breaking\n");
    7564  check_jobs_and_break:
    7565                     checkjobs(NULL);
    75667651                    break;
    75677652                }
     
    75697654        }
    75707655#endif
    7571 
    7572  check_jobs_and_continue:
    7573         checkjobs(NULL);
    75747656    } /* for (pi) */
    75757657
     
    77417823    if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
    77427824        G.last_exitcode = EXIT_SUCCESS;
     7825
    77437826#if ENABLE_HUSH_FAST
    77447827    G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
     
    77787861    /* Export PWD */
    77797862    set_pwd_var(/*exp:*/ 1);
     7863
     7864#if ENABLE_HUSH_BASH_COMPAT
     7865    /* Set (but not export) HOSTNAME unless already set */
     7866    if (!get_local_var_value("HOSTNAME")) {
     7867        struct utsname uts;
     7868        uname(&uts);
     7869        set_local_var_from_halves("HOSTNAME", uts.nodename);
     7870    }
    77807871    /* bash also exports SHLVL and _,
    77817872     * and sets (but doesn't export) the following variables:
     
    77867877     * MACHTYPE=i386-pc-linux-gnu
    77877878     * OSTYPE=linux-gnu
    7788      * HOSTNAME=<xxxxxxxxxx>
    77897879     * PPID=<NNNNN> - we also do it elsewhere
    77907880     * EUID=<NNNNN>
     
    78147904     * PS4='+ '
    78157905     */
     7906#endif
    78167907
    78177908#if ENABLE_FEATURE_EDITING
     
    78227913    cmdedit_update_prompt();
    78237914
    7824     if (setjmp(die_jmp)) {
    7825         /* xfunc has failed! die die die */
    7826         /* no EXIT traps, this is an escape hatch! */
    7827         G.exiting = 1;
    7828         hush_exit(xfunc_error_retval);
    7829     }
     7915    die_func = restore_ttypgrp_and__exit;
    78307916
    78317917    /* Shell is non-interactive at first. We need to call
     
    80858171            tcsetpgrp(G_interactive_fd, getpid());
    80868172        }
    8087         /* -1 is special - makes xfuncs longjmp, not exit
    8088          * (we reset die_sleep = 0 whereever we [v]fork) */
    8089         enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
     8173        enable_restore_tty_pgrp_on_exit();
    80908174
    80918175# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
     
    86258709    }
    86268710    bb_putchar('\n');
     8711    return EXIT_SUCCESS;
     8712}
     8713#endif
     8714
     8715#if MAX_HISTORY && ENABLE_FEATURE_EDITING
     8716static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
     8717{
     8718    show_history(G.line_input_state);
    86278719    return EXIT_SUCCESS;
    86288720}
     
    88818973    if (!input) {
    88828974        /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
     8975        /* POSIX: non-interactive shell should abort here,
     8976         * not merely fail. So far no one complained :)
     8977         */
    88838978        return EXIT_FAILURE;
    88848979    }
     
    88908985    G.flag_return_in_progress = -1;
    88918986#endif
    8892     save_and_replace_G_args(&sv, argv);
     8987    if (argv[1])
     8988        save_and_replace_G_args(&sv, argv);
    88938989
    88948990    parse_and_run_file(input);
    88958991    fclose(input);
    88968992
    8897     restore_G_args(&sv, argv);
     8993    if (argv[1])
     8994        restore_G_args(&sv, argv);
    88988995#if ENABLE_HUSH_FUNCTIONS
    88998996    G.flag_return_in_progress = sv_flg;
     
    89089005    mode_t mask;
    89099006
     9007    rc = 1;
    89109008    mask = umask(0);
    89119009    argv = skip_dash_dash(argv);
     
    89139011        mode_t old_mask = mask;
    89149012
    8915         mask ^= 0777;
    8916         rc = bb_parse_mode(argv[0], &mask);
    8917         mask ^= 0777;
    8918         if (rc == 0) {
     9013        /* numeric umasks are taken as-is */
     9014        /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
     9015        if (!isdigit(argv[0][0]))
     9016            mask ^= 0777;
     9017        mask = bb_parse_mode(argv[0], mask);
     9018        if (!isdigit(argv[0][0]))
     9019            mask ^= 0777;
     9020        if ((unsigned)mask > 0777) {
    89199021            mask = old_mask;
    89209022            /* bash messages:
     
    89239025             */
    89249026            bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
     9027            rc = 0;
    89259028        }
    89269029    } else {
    8927         rc = 1;
    89289030        /* Mimic bash */
    89299031        printf("%04o\n", (unsigned) mask);
     
    90559157        }
    90569158        if (waitpid(pid, &status, 0) == pid) {
     9159            ret = WEXITSTATUS(status);
    90579160            if (WIFSIGNALED(status))
    90589161                ret = 128 + WTERMSIG(status);
    9059             else if (WIFEXITED(status))
    9060                 ret = WEXITSTATUS(status);
    9061             else /* wtf? */
    9062                 ret = EXIT_FAILURE;
    90639162        } else {
    90649163            bb_perror_msg("wait %s", *argv);
  • branches/3.3/mindi-busybox/shell/hush_test/hush-misc/func_args1.tests

    r2725 r3621  
    1 # UNFIXED BUG
    2 
    31f() { echo "'f $1 $2 $3' called"; }
    42
  • branches/3.3/mindi-busybox/shell/math.c

    r3232 r3621  
    416416        else if (right_side_val == 0)
    417417            return "divide by zero";
    418         else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
    419             rez /= right_side_val;
    420         else if (op == TOK_REM || op == TOK_REM_ASSIGN)
    421             rez %= right_side_val;
     418        else if (op == TOK_DIV || op == TOK_DIV_ASSIGN
     419              || op == TOK_REM || op == TOK_REM_ASSIGN) {
     420            /*
     421             * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))'
     422             *
     423             * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1
     424             * and thus is not representable.
     425             * Some CPUs segfault trying such op.
     426             * Others overflow MAX_POSITIVE_INT+1 to
     427             * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000).
     428             * Make sure to at least not SEGV here:
     429             */
     430            if (right_side_val == -1
     431             && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */
     432            ) {
     433                right_side_val = 1;
     434            }
     435            if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
     436                rez /= right_side_val;
     437            else {
     438                rez %= right_side_val;
     439            }
     440        }
    422441    }
    423442
     
    495514#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
    496515
    497 const char* FAST_FUNC
    498 endofname(const char *name)
    499 {
    500     if (!is_name(*name))
    501         return name;
    502     while (*++name) {
    503         if (!is_in_name(*name))
    504             break;
    505     }
    506     return name;
    507 }
    508 
    509516static arith_t FAST_FUNC
    510517evaluate_string(arith_state_t *math_state, const char *expr)
  • branches/3.3/mindi-busybox/shell/math.h

    r2725 r3621  
    7474#endif
    7575
    76 /* ash's and hush's endofname is the same, so... */
    77 # define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
    78 # define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
    79 const char* FAST_FUNC endofname(const char *name);
    80 
    8176typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
    8277typedef void        FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
  • branches/3.3/mindi-busybox/shell/random.c

    r2725 r3621  
    77 * Licensed under GPLv2, see file LICENSE in this source tree.
    88 */
    9 #include "libbb.h"
    10 #include "random.h"
     9
     10/* For testing against dieharder, you need only random.{c,h}
     11 * Howto:
     12 * gcc -O2 -Wall -DRANDTEST random.c -o random
     13 * ./random | dieharder -g 200 -a
     14 */
     15
     16#if !defined RANDTEST
     17
     18# include "libbb.h"
     19# include "random.h"
     20# define RAND_BASH_MASK 0x7fff
     21
     22#else
     23# include <stdint.h>
     24# include <unistd.h>
     25# include <stdio.h>
     26# include <time.h>
     27# define FAST_FUNC /* nothing */
     28# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
     29# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
     30# define monotonic_us() time(NULL)
     31# include "random.h"
     32# define RAND_BASH_MASK 0xffffffff /* off */
     33#endif
    1134
    1235uint32_t FAST_FUNC
    1336next_random(random_t *rnd)
    1437{
    15     /* Galois LFSR parameter */
    16     /* Taps at 32 31 29 1: */
     38    /* Galois LFSR parameter:
     39     * Taps at 32 31 29 1:
     40     */
    1741    enum { MASK = 0x8000000b };
    1842    /* Another example - taps at 32 31 30 10: */
    19     /* MASK = 0x00400007 */
     43    /* enum { MASK = 0x00400007 }; */
     44
     45    /* Xorshift parameters:
     46     * Choices for a,b,c: 10,13,10; 8,9,22; 2,7,3; 23,3,24
     47     * (given by algorithm author)
     48     */
     49        enum {
     50                a = 2,
     51                b = 7,
     52                c = 3,
     53        };
    2054
    2155    uint32_t t;
     
    2862    }
    2963
    30     /* LCG has period of 2^32 and alternating lowest bit */
     64    /* LCG: period of 2^32, but quite weak:
     65     * bit 0 alternates beetween 0 and 1 (pattern of length 2)
     66     * bit 1 has a repeating pattern of length 4
     67     * bit 2 has a repeating pattern of length 8
     68     * etc...
     69     */
    3170    rnd->LCG = 1664525 * rnd->LCG + 1013904223;
    32     /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
     71
     72    /* Galois LFSR:
     73     * period of 2^32-1 = 3 * 5 * 17 * 257 * 65537.
     74     * Successive values are right-shifted one bit
     75     * and possibly xored with a sparse constant.
     76     */
    3377    t = (rnd->galois_LFSR << 1);
    3478    if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */
    3579        t ^= MASK;
    3680    rnd->galois_LFSR = t;
    37     /* Both are weak, combining them gives better randomness
    38      * and ~2^64 period. & 0x7fff is probably bash compat
    39      * for $RANDOM range. Combining with subtraction is
    40      * just for fun. + and ^ would work equally well. */
    41     t = (t - rnd->LCG) & 0x7fff;
    4281
    43     return t;
     82    /* http://en.wikipedia.org/wiki/Xorshift
     83     * Moderately good statistical properties:
     84     * fails the following "dieharder -g 200 -a" tests:
     85     *       diehard_operm5|   0
     86     *         diehard_oqso|   0
     87     * diehard_count_1s_byt|   0
     88     *     diehard_3dsphere|   3
     89     *      diehard_squeeze|   0
     90     *         diehard_runs|   0
     91     *         diehard_runs|   0
     92     *        diehard_craps|   0
     93     *        diehard_craps|   0
     94     * rgb_minimum_distance|   3
     95     * rgb_minimum_distance|   4
     96     * rgb_minimum_distance|   5
     97     *     rgb_permutations|   3
     98     *     rgb_permutations|   4
     99     *     rgb_permutations|   5
     100     *         dab_filltree|  32
     101     *         dab_filltree|  32
     102     *         dab_monobit2|  12
     103     */
     104 again:
     105    t = rnd->xs64_x ^ (rnd->xs64_x << a);
     106    rnd->xs64_x = rnd->xs64_y;
     107    rnd->xs64_y = rnd->xs64_y ^ (rnd->xs64_y >> c) ^ t ^ (t >> b);
     108    /*
     109     * Period 2^64-1 = 2^32+1 * 2^32-1 has a common divisor with Galois LFSR.
     110     * By skipping two possible states (0x1 and 0x2) we reduce period to
     111     * 2^64-3 = 13 * 3889 * 364870227143809 which has no common divisors:
     112     */
     113    if (rnd->xs64_y == 0 && rnd->xs64_x <= 2)
     114        goto again;
     115
     116    /* Combined LCG + Galois LFSR rng has 2^32 * 2^32-1 period.
     117     * Strength:
     118     * individually, both are extremely weak cryptographycally;
     119     * when combined, they fail the following "dieharder -g 200 -a" tests:
     120     *     diehard_rank_6x8|   0
     121     *         diehard_oqso|   0
     122     *          diehard_dna|   0
     123     * diehard_count_1s_byt|   0
     124     *          rgb_bitdist|   2
     125     *         dab_monobit2|  12
     126     *
     127     * Combining them with xorshift-64 increases period to
     128     * 2^32 * 2^32-1 * 2^64-3
     129     * which is about 2^128, or in base 10 ~3.40*10^38.
     130     * Strength of the combination:
     131     * passes all "dieharder -g 200 -a" tests.
     132     *
     133     * Combining with subtraction and addition is just for fun.
     134     * It does not add meaningful strength, could use xor operation instead.
     135     */
     136    t = rnd->galois_LFSR - rnd->LCG + rnd->xs64_y;
     137
     138    /* bash compat $RANDOM range: */
     139    return t & RAND_BASH_MASK;
    44140}
     141
     142#ifdef RANDTEST
     143static random_t rnd;
     144
     145int main(int argc, char **argv)
     146{
     147    int i;
     148    uint32_t buf[4096];
     149
     150    for (;;) {
     151        for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) {
     152            buf[i] = next_random(&rnd);
     153        }
     154        write(1, buf, sizeof(buf));
     155    }
     156
     157        return 0;
     158}
     159
     160#endif
  • branches/3.3/mindi-busybox/shell/random.h

    r2725 r3621  
    1313
    1414typedef struct random_t {
    15     /* Random number generators */
    16     int32_t galois_LFSR; /* Galois LFSR (fast but weak). signed! */
    17     uint32_t LCG;        /* LCG (fast but weak) */
     15    /* State of random number generators: */
     16
     17    /* Galois LFSR (fast but weak) */
     18    int32_t galois_LFSR; /* must be signed! */
     19
     20    /* LCG (fast but weak) */
     21    uint32_t LCG;
     22
     23    /* 64-bit xorshift (fast, moderate strength) */
     24    uint32_t xs64_x;
     25    uint32_t xs64_y;
    1826} random_t;
    1927
     
    2230
    2331#define INIT_RANDOM_T(rnd, nonzero, v) \
    24     ((rnd)->galois_LFSR = (nonzero), (rnd)->LCG = (v))
     32    ((rnd)->galois_LFSR = (rnd)->xs64_x = (nonzero), (rnd)->LCG = (rnd)->xs64_y = (v))
    2533
    2634#define CLEAR_RANDOM_T(rnd) \
  • branches/3.3/mindi-busybox/shell/shell_common.c

    r3232 r3621  
    3838/* read builtin */
    3939
    40 /* Needs to be interruptible: shell mush handle traps and shell-special signals
     40/* Needs to be interruptible: shell must handle traps and shell-special signals
    4141 * while inside read. To implement this, be sure to not loop on EINTR
    4242 * and return errno == EINTR reliably.
     
    329329
    330330/* "-": treat args as parameters of option with ASCII code 1 */
    331 static const char ulimit_opt_string[] = "-HSa"
     331static const char ulimit_opt_string[] ALIGN1 = "-HSa"
    332332#ifdef RLIMIT_FSIZE
    333333            "f::"
     
    381381
    382382    if (val == RLIM_INFINITY)
    383         printf("unlimited\n");
     383        puts("unlimited");
    384384    else {
    385385        val >>= l->factor_shift;
     
    494494            break;
    495495        }
    496 
    497496    } /* while (there are options) */
    498497
Note: See TracChangeset for help on using the changeset viewer.