| /* vi: set sw=4 ts=4: */ |
| /* |
| * ash shell port for busybox |
| * |
| * Copyright (c) 1989, 1991, 1993, 1994 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> |
| * was re-ported from NetBSD and debianized. |
| * |
| * |
| * This code is derived from software contributed to Berkeley by |
| * Kenneth Almquist. |
| * |
| * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
| * |
| * Original BSD copyright notice is retained at the end of this file. |
| */ |
| |
| /* |
| * rewrite arith.y to micro stack based cryptic algorithm by |
| * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
| * |
| * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support |
| * dynamic variables. |
| * |
| * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be |
| * used in busybox and size optimizations, |
| * rewrote arith (see notes to this), added locale support, |
| * rewrote dynamic variables. |
| * |
| */ |
| |
| /* |
| * The follow should be set to reflect the type of system you have: |
| * JOBS -> 1 if you have Berkeley job control, 0 otherwise. |
| * define SYSV if you are running under System V. |
| * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) |
| * define DEBUG=2 to compile in and turn on debugging. |
| * |
| * When debugging is on, debugging info will be written to ./trace and |
| * a quit signal will generate a core dump. |
| */ |
| #define DEBUG 0 |
| #define IFS_BROKEN |
| #define PROFILE 0 |
| #if ENABLE_ASH_JOB_CONTROL |
| #define JOBS 1 |
| #else |
| #define JOBS 0 |
| #endif |
| |
| #if DEBUG |
| #define _GNU_SOURCE |
| #endif |
| #include "busybox.h" /* for struct bb_applet */ |
| #include <paths.h> |
| #include <setjmp.h> |
| #include <fnmatch.h> |
| #if JOBS || ENABLE_ASH_READ_NCHARS |
| #include <termios.h> |
| #endif |
| extern char **environ; |
| |
| #if defined(__uClinux__) |
| #error "Do not even bother, ash will not run on uClinux" |
| #endif |
| |
| |
| /* ============ Misc helpers */ |
| |
| #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) |
| |
| /* C99 say: "char" declaration may be signed or unsigned default */ |
| #define signed_char2int(sc) ((int)((signed char)sc)) |
| |
| |
| /* ============ Shell options */ |
| |
| static const char *const optletters_optnames[] = { |
| "e" "errexit", |
| "f" "noglob", |
| "I" "ignoreeof", |
| "i" "interactive", |
| "m" "monitor", |
| "n" "noexec", |
| "s" "stdin", |
| "x" "xtrace", |
| "v" "verbose", |
| "C" "noclobber", |
| "a" "allexport", |
| "b" "notify", |
| "u" "nounset", |
| "\0" "vi" |
| #if DEBUG |
| ,"\0" "nolog" |
| ,"\0" "debug" |
| #endif |
| }; |
| |
| #define optletters(n) optletters_optnames[(n)][0] |
| #define optnames(n) (&optletters_optnames[(n)][1]) |
| |
| enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; |
| |
| static char optlist[NOPTS] ALIGN1; |
| |
| #define eflag optlist[0] |
| #define fflag optlist[1] |
| #define Iflag optlist[2] |
| #define iflag optlist[3] |
| #define mflag optlist[4] |
| #define nflag optlist[5] |
| #define sflag optlist[6] |
| #define xflag optlist[7] |
| #define vflag optlist[8] |
| #define Cflag optlist[9] |
| #define aflag optlist[10] |
| #define bflag optlist[11] |
| #define uflag optlist[12] |
| #define viflag optlist[13] |
| #if DEBUG |
| #define nolog optlist[14] |
| #define debug optlist[15] |
| #endif |
| |
| |
| /* ============ Misc data */ |
| |
| static char nullstr[1] ALIGN1; /* zero length string */ |
| static const char homestr[] ALIGN1 = "HOME"; |
| static const char snlfmt[] ALIGN1 = "%s\n"; |
| static const char illnum[] ALIGN1 = "Illegal number: %s"; |
| |
| static char *minusc; /* argument to -c option */ |
| |
| /* pid of main shell */ |
| static int rootpid; |
| /* shell level: 0 for the main shell, 1 for its children, and so on */ |
| static int shlvl; |
| #define rootshell (!shlvl) |
| /* trap handler commands */ |
| static char *trap[NSIG]; |
| static smallint isloginsh; |
| /* current value of signal */ |
| static char sigmode[NSIG - 1]; |
| /* indicates specified signal received */ |
| static char gotsig[NSIG - 1]; |
| static char *arg0; /* value of $0 */ |
| |
| |
| /* ============ Interrupts / exceptions */ |
| |
| /* |
| * We enclose jmp_buf in a structure so that we can declare pointers to |
| * jump locations. The global variable handler contains the location to |
| * jump to when an exception occurs, and the global variable exception |
| * contains a code identifying the exception. To implement nested |
| * exception handlers, the user should save the value of handler on entry |
| * to an inner scope, set handler to point to a jmploc structure for the |
| * inner scope, and restore handler on exit from the scope. |
| */ |
| struct jmploc { |
| jmp_buf loc; |
| }; |
| static struct jmploc *exception_handler; |
| static int exception; |
| /* exceptions */ |
| #define EXINT 0 /* SIGINT received */ |
| #define EXERROR 1 /* a generic error */ |
| #define EXSHELLPROC 2 /* execute a shell procedure */ |
| #define EXEXEC 3 /* command execution failed */ |
| #define EXEXIT 4 /* exit the shell */ |
| #define EXSIG 5 /* trapped signal in wait(1) */ |
| static volatile int suppressint; |
| static volatile sig_atomic_t intpending; |
| /* do we generate EXSIG events */ |
| static int exsig; |
| /* last pending signal */ |
| static volatile sig_atomic_t pendingsig; |
| |
| /* |
| * Sigmode records the current value of the signal handlers for the various |
| * modes. A value of zero means that the current handler is not known. |
| * S_HARD_IGN indicates that the signal was ignored on entry to the shell, |
| */ |
| |
| #define S_DFL 1 /* default signal handling (SIG_DFL) */ |
| #define S_CATCH 2 /* signal is caught */ |
| #define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
| #define S_HARD_IGN 4 /* signal is ignored permenantly */ |
| #define S_RESET 5 /* temporary - to reset a hard ignored sig */ |
| |
| /* |
| * These macros allow the user to suspend the handling of interrupt signals |
| * over a period of time. This is similar to SIGHOLD to or sigblock, but |
| * much more efficient and portable. (But hacking the kernel is so much |
| * more fun than worrying about efficiency and portability. :-)) |
| */ |
| #define INT_OFF \ |
| do { \ |
| suppressint++; \ |
| xbarrier(); \ |
| } while (0) |
| |
| /* |
| * Called to raise an exception. Since C doesn't include exceptions, we |
| * just do a longjmp to the exception handler. The type of exception is |
| * stored in the global variable "exception". |
| */ |
| static void raise_exception(int) ATTRIBUTE_NORETURN; |
| static void |
| raise_exception(int e) |
| { |
| #if DEBUG |
| if (exception_handler == NULL) |
| abort(); |
| #endif |
| INT_OFF; |
| exception = e; |
| longjmp(exception_handler->loc, 1); |
| } |
| |
| /* |
| * Called from trap.c when a SIGINT is received. (If the user specifies |
| * that SIGINT is to be trapped or ignored using the trap builtin, then |
| * this routine is not called.) Suppressint is nonzero when interrupts |
| * are held using the INT_OFF macro. (The test for iflag is just |
| * defensive programming.) |
| */ |
| static void raise_interrupt(void) ATTRIBUTE_NORETURN; |
| static void |
| raise_interrupt(void) |
| { |
| int i; |
| sigset_t mask; |
| |
| intpending = 0; |
| /* Signal is not automatically re-enabled after it is raised, |
| * do it ourself */ |
| sigemptyset(&mask); |
| sigprocmask(SIG_SETMASK, &mask, 0); |
| /* pendingsig = 0; - now done in onsig() */ |
| |
| i = EXSIG; |
| if (gotsig[SIGINT - 1] && !trap[SIGINT]) { |
| if (!(rootshell && iflag)) { |
| signal(SIGINT, SIG_DFL); |
| raise(SIGINT); |
| } |
| i = EXINT; |
| } |
| raise_exception(i); |
| /* NOTREACHED */ |
| } |
| |
| #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
| static void |
| int_on(void) |
| { |
| if (--suppressint == 0 && intpending) { |
| raise_interrupt(); |
| } |
| } |
| #define INT_ON int_on() |
| static void |
| force_int_on(void) |
| { |
| suppressint = 0; |
| if (intpending) |
| raise_interrupt(); |
| } |
| #define FORCE_INT_ON force_int_on() |
| #else |
| #define INT_ON \ |
| do { \ |
| xbarrier(); \ |
| if (--suppressint == 0 && intpending) \ |
| raise_interrupt(); \ |
| } while (0) |
| #define FORCE_INT_ON \ |
| do { \ |
| xbarrier(); \ |
| suppressint = 0; \ |
| if (intpending) \ |
| raise_interrupt(); \ |
| } while (0) |
| #endif /* ASH_OPTIMIZE_FOR_SIZE */ |
| |
| #define SAVE_INT(v) ((v) = suppressint) |
| |
| #define RESTORE_INT(v) \ |
| do { \ |
| xbarrier(); \ |
| suppressint = (v); \ |
| if (suppressint == 0 && intpending) \ |
| raise_interrupt(); \ |
| } while (0) |
| |
| #define EXSIGON \ |
| do { \ |
| exsig++; \ |
| xbarrier(); \ |
| if (pendingsig) \ |
| raise_exception(EXSIG); \ |
| } while (0) |
| /* EXSIG is turned off by evalbltin(). */ |
| |
| /* |
| * Ignore a signal. Only one usage site - in forkchild() |
| */ |
| static void |
| ignoresig(int signo) |
| { |
| if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { |
| signal(signo, SIG_IGN); |
| } |
| sigmode[signo - 1] = S_HARD_IGN; |
| } |
| |
| /* |
| * Signal handler. Only one usage site - in setsignal() |
| */ |
| static void |
| onsig(int signo) |
| { |
| gotsig[signo - 1] = 1; |
| pendingsig = signo; |
| |
| if (exsig || (signo == SIGINT && !trap[SIGINT])) { |
| if (!suppressint) { |
| pendingsig = 0; |
| raise_interrupt(); |
| } |
| intpending = 1; |
| } |
| } |
| |
| |
| /* ============ Stdout/stderr output */ |
| |
| static void |
| outstr(const char *p, FILE *file) |
| { |
| INT_OFF; |
| fputs(p, file); |
| INT_ON; |
| } |
| |
| static void |
| flush_stdout_stderr(void) |
| { |
| INT_OFF; |
| fflush(stdout); |
| fflush(stderr); |
| INT_ON; |
| } |
| |
| static void |
| flush_stderr(void) |
| { |
| INT_OFF; |
| fflush(stderr); |
| INT_ON; |
| } |
| |
| static void |
| outcslow(int c, FILE *dest) |
| { |
| INT_OFF; |
| putc(c, dest); |
| fflush(dest); |
| INT_ON; |
| } |
| |
| static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); |
| static int |
| out1fmt(const char *fmt, ...) |
| { |
| va_list ap; |
| int r; |
| |
| INT_OFF; |
| va_start(ap, fmt); |
| r = vprintf(fmt, ap); |
| va_end(ap); |
| INT_ON; |
| return r; |
| } |
| |
| static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4))); |
| static int |
| fmtstr(char *outbuf, size_t length, const char *fmt, ...) |
| { |
| va_list ap; |
| int ret; |
| |
| va_start(ap, fmt); |
| INT_OFF; |
| ret = vsnprintf(outbuf, length, fmt, ap); |
| va_end(ap); |
| INT_ON; |
| return ret; |
| } |
| |
| static void |
| out1str(const char *p) |
| { |
| outstr(p, stdout); |
| } |
| |
| static void |
| out2str(const char *p) |
| { |
| outstr(p, stderr); |
| flush_stderr(); |
| } |
| |
| |
| /* ============ Parser structures */ |
| |
| /* control characters in argument strings */ |
| #define CTLESC '\201' /* escape next character */ |
| #define CTLVAR '\202' /* variable defn */ |
| #define CTLENDVAR '\203' |
| #define CTLBACKQ '\204' |
| #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ |
| /* CTLBACKQ | CTLQUOTE == '\205' */ |
| #define CTLARI '\206' /* arithmetic expression */ |
| #define CTLENDARI '\207' |
| #define CTLQUOTEMARK '\210' |
| |
| /* variable substitution byte (follows CTLVAR) */ |
| #define VSTYPE 0x0f /* type of variable substitution */ |
| #define VSNUL 0x10 /* colon--treat the empty string as unset */ |
| #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ |
| |
| /* values of VSTYPE field */ |
| #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ |
| #define VSMINUS 0x2 /* ${var-text} */ |
| #define VSPLUS 0x3 /* ${var+text} */ |
| #define VSQUESTION 0x4 /* ${var?message} */ |
| #define VSASSIGN 0x5 /* ${var=text} */ |
| #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ |
| #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ |
| #define VSTRIMLEFT 0x8 /* ${var#pattern} */ |
| #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ |
| #define VSLENGTH 0xa /* ${#var} */ |
| |
| static const char dolatstr[] ALIGN1 = { |
| CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' |
| }; |
| |
| #define NCMD 0 |
| #define NPIPE 1 |
| #define NREDIR 2 |
| #define NBACKGND 3 |
| #define NSUBSHELL 4 |
| #define NAND 5 |
| #define NOR 6 |
| #define NSEMI 7 |
| #define NIF 8 |
| #define NWHILE 9 |
| #define NUNTIL 10 |
| #define NFOR 11 |
| #define NCASE 12 |
| #define NCLIST 13 |
| #define NDEFUN 14 |
| #define NARG 15 |
| #define NTO 16 |
| #define NCLOBBER 17 |
| #define NFROM 18 |
| #define NFROMTO 19 |
| #define NAPPEND 20 |
| #define NTOFD 21 |
| #define NFROMFD 22 |
| #define NHERE 23 |
| #define NXHERE 24 |
| #define NNOT 25 |
| |
| union node; |
| |
| struct ncmd { |
| int type; |
| union node *assign; |
| union node *args; |
| union node *redirect; |
| }; |
| |
| struct npipe { |
| int type; |
| int backgnd; |
| struct nodelist *cmdlist; |
| }; |
| |
| struct nredir { |
| int type; |
| union node *n; |
| union node *redirect; |
| }; |
| |
| struct nbinary { |
| int type; |
| union node *ch1; |
| union node *ch2; |
| }; |
| |
| struct nif { |
| int type; |
| union node *test; |
| union node *ifpart; |
| union node *elsepart; |
| }; |
| |
| struct nfor { |
| int type; |
| union node *args; |
| union node *body; |
| char *var; |
| }; |
| |
| struct ncase { |
| int type; |
| union node *expr; |
| union node *cases; |
| }; |
| |
| struct nclist { |
| int type; |
| union node *next; |
| union node *pattern; |
| union node *body; |
| }; |
| |
| struct narg { |
| int type; |
| union node *next; |
| char *text; |
| struct nodelist *backquote; |
| }; |
| |
| struct nfile { |
| int type; |
| union node *next; |
| int fd; |
| union node *fname; |
| char *expfname; |
| }; |
| |
| struct ndup { |
| int type; |
| union node *next; |
| int fd; |
| int dupfd; |
| union node *vname; |
| }; |
| |
| struct nhere { |
| int type; |
| union node *next; |
| int fd; |
| union node *doc; |
| }; |
| |
| struct nnot { |
| int type; |
| union node *com; |
| }; |
| |
| union node { |
| int type; |
| struct ncmd ncmd; |
| struct npipe npipe; |
| struct nredir nredir; |
| struct nbinary nbinary; |
| struct nif nif; |
| struct nfor nfor; |
| struct ncase ncase; |
| struct nclist nclist; |
| struct narg narg; |
| struct nfile nfile; |
| struct ndup ndup; |
| struct nhere nhere; |
| struct nnot nnot; |
| }; |
| |
| struct nodelist { |
| struct nodelist *next; |
| union node *n; |
| }; |
| |
| struct funcnode { |
| int count; |
| union node n; |
| }; |
| |
| /* |
| * Free a parse tree. |
| */ |
| static void |
| freefunc(struct funcnode *f) |
| { |
| if (f && --f->count < 0) |
| free(f); |
| } |
| |
| |
| /* ============ Debugging output */ |
| |
| #if DEBUG |
| |
| static FILE *tracefile; |
| |
| static void |
| trace_printf(const char *fmt, ...) |
| { |
| va_list va; |
| |
| if (debug != 1) |
| return; |
| va_start(va, fmt); |
| vfprintf(tracefile, fmt, va); |
| va_end(va); |
| } |
| |
| static void |
| trace_vprintf(const char *fmt, va_list va) |
| { |
| if (debug != 1) |
| return; |
| vfprintf(tracefile, fmt, va); |
| } |
| |
| static void |
| trace_puts(const char *s) |
| { |
| if (debug != 1) |
| return; |
| fputs(s, tracefile); |
| } |
| |
| static void |
| trace_puts_quoted(char *s) |
| { |
| char *p; |
| char c; |
| |
| if (debug != 1) |
| return; |
| putc('"', tracefile); |
| for (p = s; *p; p++) { |
| switch (*p) { |
| case '\n': c = 'n'; goto backslash; |
| case '\t': c = 't'; goto backslash; |
| case '\r': c = 'r'; goto backslash; |
| case '"': c = '"'; goto backslash; |
| case '\\': c = '\\'; goto backslash; |
| case CTLESC: c = 'e'; goto backslash; |
| case CTLVAR: c = 'v'; goto backslash; |
| case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; |
| case CTLBACKQ: c = 'q'; goto backslash; |
| case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; |
| backslash: |
| putc('\\', tracefile); |
| putc(c, tracefile); |
| break; |
| default: |
| if (*p >= ' ' && *p <= '~') |
| putc(*p, tracefile); |
| else { |
| putc('\\', tracefile); |
| putc(*p >> 6 & 03, tracefile); |
| putc(*p >> 3 & 07, tracefile); |
| putc(*p & 07, tracefile); |
| } |
| break; |
| } |
| } |
| putc('"', tracefile); |
| } |
| |
| static void |
| trace_puts_args(char **ap) |
| { |
| if (debug != 1) |
| return; |
| if (!*ap) |
| return; |
| while (1) { |
| trace_puts_quoted(*ap); |
| if (!*++ap) { |
| putc('\n', tracefile); |
| break; |
| } |
| putc(' ', tracefile); |
| } |
| } |
| |
| static void |
| opentrace(void) |
| { |
| char s[100]; |
| #ifdef O_APPEND |
| int flags; |
| #endif |
| |
| if (debug != 1) { |
| if (tracefile) |
| fflush(tracefile); |
| /* leave open because libedit might be using it */ |
| return; |
| } |
| strcpy(s, "./trace"); |
| if (tracefile) { |
| if (!freopen(s, "a", tracefile)) { |
| fprintf(stderr, "Can't re-open %s\n", s); |
| debug = 0; |
| return; |
| } |
| } else { |
| tracefile = fopen(s, "a"); |
| if (tracefile == NULL) { |
| fprintf(stderr, "Can't open %s\n", s); |
| debug = 0; |
| return; |
| } |
| } |
| #ifdef O_APPEND |
| flags = fcntl(fileno(tracefile), F_GETFL); |
| if (flags >= 0) |
| fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); |
| #endif |
| setlinebuf(tracefile); |
| fputs("\nTracing started.\n", tracefile); |
| } |
| |
| static void |
| indent(int amount, char *pfx, FILE *fp) |
| { |
| int i; |
| |
| for (i = 0; i < amount; i++) { |
| if (pfx && i == amount - 1) |
| fputs(pfx, fp); |
| putc('\t', fp); |
| } |
| } |
| |
| /* little circular references here... */ |
| static void shtree(union node *n, int ind, char *pfx, FILE *fp); |
| |
| static void |
| sharg(union node *arg, FILE *fp) |
| { |
| char *p; |
| struct nodelist *bqlist; |
| int subtype; |
| |
| if (arg->type != NARG) { |
| out1fmt("<node type %d>\n", arg->type); |
| abort(); |
| } |
| bqlist = arg->narg.backquote; |
| for (p = arg->narg.text; *p; p++) { |
| switch (*p) { |
| case CTLESC: |
| putc(*++p, fp); |
| break; |
| case CTLVAR: |
| putc('$', fp); |
| putc('{', fp); |
| subtype = *++p; |
| if (subtype == VSLENGTH) |
| putc('#', fp); |
| |
| while (*p != '=') |
| putc(*p++, fp); |
| |
| if (subtype & VSNUL) |
| putc(':', fp); |
| |
| switch (subtype & VSTYPE) { |
| case VSNORMAL: |
| putc('}', fp); |
| break; |
| case VSMINUS: |
| putc('-', fp); |
| break; |
| case VSPLUS: |
| putc('+', fp); |
| break; |
| case VSQUESTION: |
| putc('?', fp); |
| break; |
| case VSASSIGN: |
| putc('=', fp); |
| break; |
| case VSTRIMLEFT: |
| putc('#', fp); |
| break; |
| case VSTRIMLEFTMAX: |
| putc('#', fp); |
| putc('#', fp); |
| break; |
| case VSTRIMRIGHT: |
| putc('%', fp); |
| break; |
| case VSTRIMRIGHTMAX: |
| putc('%', fp); |
| putc('%', fp); |
| break; |
| case VSLENGTH: |
| break; |
| default: |
| out1fmt("<subtype %d>", subtype); |
| } |
| break; |
| case CTLENDVAR: |
| putc('}', fp); |
| break; |
| case CTLBACKQ: |
| case CTLBACKQ|CTLQUOTE: |
| putc('$', fp); |
| putc('(', fp); |
| shtree(bqlist->n, -1, NULL, fp); |
| putc(')', fp); |
| break; |
| default: |
| putc(*p, fp); |
| break; |
| } |
| } |
| } |
| |
| static void |
| shcmd(union node *cmd, FILE *fp) |
| { |
| union node *np; |
| int first; |
| const char *s; |
| int dftfd; |
| |
| first = 1; |
| for (np = cmd->ncmd.args; np; np = np->narg.next) { |
| if (!first) |
| putc(' ', fp); |
| sharg(np, fp); |
| first = 0; |
| } |
| for (np = cmd->ncmd.redirect; np; np = np->nfile.next) { |
| if (!first) |
| putc(' ', fp); |
| dftfd = 0; |
| switch (np->nfile.type) { |
| case NTO: s = ">>"+1; dftfd = 1; break; |
| case NCLOBBER: s = ">|"; dftfd = 1; break; |
| case NAPPEND: s = ">>"; dftfd = 1; break; |
| case NTOFD: s = ">&"; dftfd = 1; break; |
| case NFROM: s = "<"; break; |
| case NFROMFD: s = "<&"; break; |
| case NFROMTO: s = "<>"; break; |
| default: s = "*error*"; break; |
| } |
| if (np->nfile.fd != dftfd) |
| fprintf(fp, "%d", np->nfile.fd); |
| fputs(s, fp); |
| if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { |
| fprintf(fp, "%d", np->ndup.dupfd); |
| } else { |
| sharg(np->nfile.fname, fp); |
| } |
| first = 0; |
| } |
| } |
| |
| static void |
| shtree(union node *n, int ind, char *pfx, FILE *fp) |
| { |
| struct nodelist *lp; |
| const char *s; |
| |
| if (n == NULL) |
| return; |
| |
| indent(ind, pfx, fp); |
| switch (n->type) { |
| case NSEMI: |
| s = "; "; |
| goto binop; |
| case NAND: |
| s = " && "; |
| goto binop; |
| case NOR: |
| s = " || "; |
| binop: |
| shtree(n->nbinary.ch1, ind, NULL, fp); |
| /* if (ind < 0) */ |
| fputs(s, fp); |
| shtree(n->nbinary.ch2, ind, NULL, fp); |
| break; |
| case NCMD: |
| shcmd(n, fp); |
| if (ind >= 0) |
| putc('\n', fp); |
| break; |
| case NPIPE: |
| for (lp = n->npipe.cmdlist; lp; lp = lp->next) { |
| shcmd(lp->n, fp); |
| if (lp->next) |
| fputs(" | ", fp); |
| } |
| if (n->npipe.backgnd) |
| fputs(" &", fp); |
| if (ind >= 0) |
| putc('\n', fp); |
| break; |
| default: |
| fprintf(fp, "<node type %d>", n->type); |
| if (ind >= 0) |
| putc('\n', fp); |
| break; |
| } |
| } |
| |
| static void |
| showtree(union node *n) |
| { |
| trace_puts("showtree called\n"); |
| shtree(n, 1, NULL, stdout); |
| } |
| |
| #define TRACE(param) trace_printf param |
| #define TRACEV(param) trace_vprintf param |
| |
| #else |
| |
| #define TRACE(param) |
| #define TRACEV(param) |
| |
| #endif /* DEBUG */ |
| |
| |
| /* ============ Parser data */ |
| |
| /* |
| * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. |
| */ |
| struct strlist { |
| struct strlist *next; |
| char *text; |
| }; |
| |
| #if ENABLE_ASH_ALIAS |
| struct alias; |
| #endif |
| |
| struct strpush { |
| struct strpush *prev; /* preceding string on stack */ |
| char *prevstring; |
| int prevnleft; |
| #if ENABLE_ASH_ALIAS |
| struct alias *ap; /* if push was associated with an alias */ |
| #endif |
| char *string; /* remember the string since it may change */ |
| }; |
| |
| struct parsefile { |
| struct parsefile *prev; /* preceding file on stack */ |
| int linno; /* current line */ |
| int fd; /* file descriptor (or -1 if string) */ |
| int nleft; /* number of chars left in this line */ |
| int lleft; /* number of chars left in this buffer */ |
| char *nextc; /* next char in buffer */ |
| char *buf; /* input buffer */ |
| struct strpush *strpush; /* for pushing strings at this level */ |
| struct strpush basestrpush; /* so pushing one is fast */ |
| }; |
| |
| static struct parsefile basepf; /* top level input file */ |
| static struct parsefile *parsefile = &basepf; /* current input file */ |
| static int startlinno; /* line # where last token started */ |
| static char *commandname; /* currently executing command */ |
| static struct strlist *cmdenviron; /* environment for builtin command */ |
| static int exitstatus; /* exit status of last command */ |
| |
| |
| /* ============ Message printing */ |
| |
| static void |
| ash_vmsg(const char *msg, va_list ap) |
| { |
| fprintf(stderr, "%s: ", arg0); |
| if (commandname) { |
| if (strcmp(arg0, commandname)) |
| fprintf(stderr, "%s: ", commandname); |
| if (!iflag || parsefile->fd) |
| fprintf(stderr, "line %d: ", startlinno); |
| } |
| vfprintf(stderr, msg, ap); |
| outcslow('\n', stderr); |
| } |
| |
| /* |
| * Exverror is called to raise the error exception. If the second argument |
| * is not NULL then error prints an error message using printf style |
| * formatting. It then raises the error exception. |
| */ |
| static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN; |
| static void |
| ash_vmsg_and_raise(int cond, const char *msg, va_list ap) |
| { |
| #if DEBUG |
| if (msg) { |
| TRACE(("ash_vmsg_and_raise(%d, \"", cond)); |
| TRACEV((msg, ap)); |
| TRACE(("\") pid=%d\n", getpid())); |
| } else |
| TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid())); |
| if (msg) |
| #endif |
| ash_vmsg(msg, ap); |
| |
| flush_stdout_stderr(); |
| raise_exception(cond); |
| /* NOTREACHED */ |
| } |
| |
| static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN; |
| static void |
| ash_msg_and_raise_error(const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| ash_vmsg_and_raise(EXERROR, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN; |
| static void |
| ash_msg_and_raise(int cond, const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| ash_vmsg_and_raise(cond, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| /* |
| * error/warning routines for external builtins |
| */ |
| static void |
| ash_msg(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| ash_vmsg(fmt, ap); |
| va_end(ap); |
| } |
| |
| /* |
| * Return a string describing an error. The returned string may be a |
| * pointer to a static buffer that will be overwritten on the next call. |
| * Action describes the operation that got the error. |
| */ |
| static const char * |
| errmsg(int e, const char *em) |
| { |
| if (e == ENOENT || e == ENOTDIR) { |
| return em; |
| } |
| return strerror(e); |
| } |
| |
| |
| /* ============ Memory allocation */ |
| |
| /* |
| * It appears that grabstackstr() will barf with such alignments |
| * because stalloc() will return a string allocated in a new stackblock. |
| */ |
| #define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) |
| enum { |
| /* Most machines require the value returned from malloc to be aligned |
| * in some way. The following macro will get this right |
| * on many machines. */ |
| SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1, |
| /* Minimum size of a block */ |
| MINSIZE = SHELL_ALIGN(504), |
| }; |
| |
| struct stack_block { |
| struct stack_block *prev; |
| char space[MINSIZE]; |
| }; |
| |
| struct stackmark { |
| struct stack_block *stackp; |
| char *stacknxt; |
| size_t stacknleft; |
| struct stackmark *marknext; |
| }; |
| |
| static struct stack_block stackbase; |
| static struct stack_block *stackp = &stackbase; |
| static struct stackmark *markp; |
| static char *stacknxt = stackbase.space; |
| static size_t stacknleft = MINSIZE; |
| static char *sstrend = stackbase.space + MINSIZE; |
| static int herefd = -1; |
| |
| #define stackblock() ((void *)stacknxt) |
| #define stackblocksize() stacknleft |
| |
| static void * |
| ckrealloc(void * p, size_t nbytes) |
| { |
| p = realloc(p, nbytes); |
| if (!p) |
| ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| return p; |
| } |
| |
| static void * |
| ckmalloc(size_t nbytes) |
| { |
| return ckrealloc(NULL, nbytes); |
| } |
| |
| /* |
| * Make a copy of a string in safe storage. |
| */ |
| static char * |
| ckstrdup(const char *s) |
| { |
| char *p = strdup(s); |
| if (!p) |
| ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| return p; |
| } |
| |
| /* |
| * Parse trees for commands are allocated in lifo order, so we use a stack |
| * to make this more efficient, and also to avoid all sorts of exception |
| * handling code to handle interrupts in the middle of a parse. |
| * |
| * The size 504 was chosen because the Ultrix malloc handles that size |
| * well. |
| */ |
| static void * |
| stalloc(size_t nbytes) |
| { |
| char *p; |
| size_t aligned; |
| |
| aligned = SHELL_ALIGN(nbytes); |
| if (aligned > stacknleft) { |
| size_t len; |
| size_t blocksize; |
| struct stack_block *sp; |
| |
| blocksize = aligned; |
| if (blocksize < MINSIZE) |
| blocksize = MINSIZE; |
| len = sizeof(struct stack_block) - MINSIZE + blocksize; |
| if (len < blocksize) |
| ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| INT_OFF; |
| sp = ckmalloc(len); |
| sp->prev = stackp; |
| stacknxt = sp->space; |
| stacknleft = blocksize; |
| sstrend = stacknxt + blocksize; |
| stackp = sp; |
| INT_ON; |
| } |
| p = stacknxt; |
| stacknxt += aligned; |
| stacknleft -= aligned; |
| return p; |
| } |
| |
| static void |
| stunalloc(void *p) |
| { |
| #if DEBUG |
| if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { |
| write(2, "stunalloc\n", 10); |
| abort(); |
| } |
| #endif |
| stacknleft += stacknxt - (char *)p; |
| stacknxt = p; |
| } |
| |
| /* |
| * Like strdup but works with the ash stack. |
| */ |
| static char * |
| ststrdup(const char *p) |
| { |
| size_t len = strlen(p) + 1; |
| return memcpy(stalloc(len), p, len); |
| } |
| |
| static void |
| setstackmark(struct stackmark *mark) |
| { |
| mark->stackp = stackp; |
| mark->stacknxt = stacknxt; |
| mark->stacknleft = stacknleft; |
| mark->marknext = markp; |
| markp = mark; |
| } |
| |
| static void |
| popstackmark(struct stackmark *mark) |
| { |
| struct stack_block *sp; |
| |
| if (!mark->stackp) |
| return; |
| |
| INT_OFF; |
| markp = mark->marknext; |
| while (stackp != mark->stackp) { |
| sp = stackp; |
| stackp = sp->prev; |
| free(sp); |
| } |
| stacknxt = mark->stacknxt; |
| stacknleft = mark->stacknleft; |
| sstrend = mark->stacknxt + mark->stacknleft; |
| INT_ON; |
| } |
| |
| /* |
| * When the parser reads in a string, it wants to stick the string on the |
| * stack and only adjust the stack pointer when it knows how big the |
| * string is. Stackblock (defined in stack.h) returns a pointer to a block |
| * of space on top of the stack and stackblocklen returns the length of |
| * this block. Growstackblock will grow this space by at least one byte, |
| * possibly moving it (like realloc). Grabstackblock actually allocates the |
| * part of the block that has been used. |
| */ |
| static void |
| growstackblock(void) |
| { |
| size_t newlen; |
| |
| newlen = stacknleft * 2; |
| if (newlen < stacknleft) |
| ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| if (newlen < 128) |
| newlen += 128; |
| |
| if (stacknxt == stackp->space && stackp != &stackbase) { |
| struct stack_block *oldstackp; |
| struct stackmark *xmark; |
| struct stack_block *sp; |
| struct stack_block *prevstackp; |
| size_t grosslen; |
| |
| INT_OFF; |
| oldstackp = stackp; |
| sp = stackp; |
| prevstackp = sp->prev; |
| grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
| sp = ckrealloc(sp, grosslen); |
| sp->prev = prevstackp; |
| stackp = sp; |
| stacknxt = sp->space; |
| stacknleft = newlen; |
| sstrend = sp->space + newlen; |
| |
| /* |
| * Stack marks pointing to the start of the old block |
| * must be relocated to point to the new block |
| */ |
| xmark = markp; |
| while (xmark != NULL && xmark->stackp == oldstackp) { |
| xmark->stackp = stackp; |
| xmark->stacknxt = stacknxt; |
| xmark->stacknleft = stacknleft; |
| xmark = xmark->marknext; |
| } |
| INT_ON; |
| } else { |
| char *oldspace = stacknxt; |
| int oldlen = stacknleft; |
| char *p = stalloc(newlen); |
| |
| /* free the space we just allocated */ |
| stacknxt = memcpy(p, oldspace, oldlen); |
| stacknleft += newlen; |
| } |
| } |
| |
| static void |
| grabstackblock(size_t len) |
| { |
| len = SHELL_ALIGN(len); |
| stacknxt += len; |
| stacknleft -= len; |
| } |
| |
| /* |
| * The following routines are somewhat easier to use than the above. |
| * The user declares a variable of type STACKSTR, which may be declared |
| * to be a register. The macro STARTSTACKSTR initializes things. Then |
| * the user uses the macro STPUTC to add characters to the string. In |
| * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is |
| * grown as necessary. When the user is done, she can just leave the |
| * string there and refer to it using stackblock(). Or she can allocate |
| * the space for it using grabstackstr(). If it is necessary to allow |
| * someone else to use the stack temporarily and then continue to grow |
| * the string, the user should use grabstack to allocate the space, and |
| * then call ungrabstr(p) to return to the previous mode of operation. |
| * |
| * USTPUTC is like STPUTC except that it doesn't check for overflow. |
| * CHECKSTACKSPACE can be called before USTPUTC to ensure that there |
| * is space for at least one character. |
| */ |
| static void * |
| growstackstr(void) |
| { |
| size_t len = stackblocksize(); |
| if (herefd >= 0 && len >= 1024) { |
| full_write(herefd, stackblock(), len); |
| return stackblock(); |
| } |
| growstackblock(); |
| return stackblock() + len; |
| } |
| |
| /* |
| * Called from CHECKSTRSPACE. |
| */ |
| static char * |
| makestrspace(size_t newlen, char *p) |
| { |
| size_t len = p - stacknxt; |
| size_t size = stackblocksize(); |
| |
| for (;;) { |
| size_t nleft; |
| |
| size = stackblocksize(); |
| nleft = size - len; |
| if (nleft >= newlen) |
| break; |
| growstackblock(); |
| } |
| return stackblock() + len; |
| } |
| |
| static char * |
| stack_nputstr(const char *s, size_t n, char *p) |
| { |
| p = makestrspace(n, p); |
| p = memcpy(p, s, n) + n; |
| return p; |
| } |
| |
| static char * |
| stack_putstr(const char *s, char *p) |
| { |
| return stack_nputstr(s, strlen(s), p); |
| } |
| |
| static char * |
| _STPUTC(int c, char *p) |
| { |
| if (p == sstrend) |
| p = growstackstr(); |
| *p++ = c; |
| return p; |
| } |
| |
| #define STARTSTACKSTR(p) ((p) = stackblock()) |
| #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) |
| #define CHECKSTRSPACE(n, p) \ |
| do { \ |
| char *q = (p); \ |
| size_t l = (n); \ |
| size_t m = sstrend - q; \ |
| if (l > m) \ |
| (p) = makestrspace(l, q); \ |
| } while (0) |
| #define USTPUTC(c, p) (*p++ = (c)) |
| #define STACKSTRNUL(p) \ |
| do { \ |
| if ((p) == sstrend) \ |
| p = growstackstr(); \ |
| *p = '\0'; \ |
| } while (0) |
| #define STUNPUTC(p) (--p) |
| #define STTOPC(p) (p[-1]) |
| #define STADJUST(amount, p) (p += (amount)) |
| |
| #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) |
| #define ungrabstackstr(s, p) stunalloc((s)) |
| #define stackstrend() ((void *)sstrend) |
| |
| |
| /* ============ String helpers */ |
| |
| /* |
| * prefix -- see if pfx is a prefix of string. |
| */ |
| static char * |
| prefix(const char *string, const char *pfx) |
| { |
| while (*pfx) { |
| if (*pfx++ != *string++) |
| return 0; |
| } |
| return (char *) string; |
| } |
| |
| /* |
| * Check for a valid number. This should be elsewhere. |
| */ |
| static int |
| is_number(const char *p) |
| { |
| do { |
| if (!isdigit(*p)) |
| return 0; |
| } while (*++p != '\0'); |
| return 1; |
| } |
| |
| /* |
| * Convert a string of digits to an integer, printing an error message on |
| * failure. |
| */ |
| static int |
| number(const char *s) |
| { |
| if (!is_number(s)) |
| ash_msg_and_raise_error(illnum, s); |
| return atoi(s); |
| } |
| |
| /* |
| * Produce a possibly single quoted string suitable as input to the shell. |
| * The return string is allocated on the stack. |
| */ |
| static char * |
| single_quote(const char *s) |
| { |
| char *p; |
| |
| STARTSTACKSTR(p); |
| |
| do { |
| char *q; |
| size_t len; |
| |
| len = strchrnul(s, '\'') - s; |
| |
| q = p = makestrspace(len + 3, p); |
| |
| *q++ = '\''; |
| q = memcpy(q, s, len) + len; |
| *q++ = '\''; |
| s += len; |
| |
| STADJUST(q - p, p); |
| |
| len = strspn(s, "'"); |
| if (!len) |
| break; |
| |
| q = p = makestrspace(len + 3, p); |
| |
| *q++ = '"'; |
| q = memcpy(q, s, len) + len; |
| *q++ = '"'; |
| s += len; |
| |
| STADJUST(q - p, p); |
| } while (*s); |
| |
| USTPUTC(0, p); |
| |
| return stackblock(); |
| } |
| |
| |
| /* ============ nextopt */ |
| |
| static char **argptr; /* argument list for builtin commands */ |
| static char *optionarg; /* set by nextopt (like getopt) */ |
| static char *optptr; /* used by nextopt */ |
| |
| /* |
| * XXX - should get rid of. have all builtins use getopt(3). the |
| * library getopt must have the BSD extension static variable "optreset" |
| * otherwise it can't be used within the shell safely. |
| * |
| * Standard option processing (a la getopt) for builtin routines. The |
| * only argument that is passed to nextopt is the option string; the |
| * other arguments are unnecessary. It return the character, or '\0' on |
| * end of input. |
| */ |
| static int |
| nextopt(const char *optstring) |
| { |
| char *p; |
| const char *q; |
| char c; |
| |
| p = optptr; |
| if (p == NULL || *p == '\0') { |
| p = *argptr; |
| if (p == NULL || *p != '-' || *++p == '\0') |
| return '\0'; |
| argptr++; |
| if (LONE_DASH(p)) /* check for "--" */ |
| return '\0'; |
| } |
| c = *p++; |
| for (q = optstring; *q != c; ) { |
| if (*q == '\0') |
| ash_msg_and_raise_error("illegal option -%c", c); |
| if (*++q == ':') |
| q++; |
| } |
| if (*++q == ':') { |
| if (*p == '\0' && (p = *argptr++) == NULL) |
| ash_msg_and_raise_error("no arg for -%c option", c); |
| optionarg = p; |
| p = NULL; |
| } |
| optptr = p; |
| return c; |
| } |
| |
| |
| /* ============ Math support definitions */ |
| |
| #if ENABLE_ASH_MATH_SUPPORT_64 |
| typedef int64_t arith_t; |
| #define arith_t_type long long |
| #else |
| typedef long arith_t; |
| #define arith_t_type long |
| #endif |
| |
| #if ENABLE_ASH_MATH_SUPPORT |
| static arith_t dash_arith(const char *); |
| static arith_t arith(const char *expr, int *perrcode); |
| #endif |
| |
| #if ENABLE_ASH_RANDOM_SUPPORT |
| static unsigned long rseed; |
| #ifndef DYNAMIC_VAR |
| #define DYNAMIC_VAR |
| #endif |
| #endif |
| |
| |
| /* ============ Shell variables */ |
| |
| /* flags */ |
| #define VEXPORT 0x01 /* variable is exported */ |
| #define VREADONLY 0x02 /* variable cannot be modified */ |
| #define VSTRFIXED 0x04 /* variable struct is statically allocated */ |
| #define VTEXTFIXED 0x08 /* text is statically allocated */ |
| #define VSTACK 0x10 /* text is allocated on the stack */ |
| #define VUNSET 0x20 /* the variable is not set */ |
| #define VNOFUNC 0x40 /* don't call the callback function */ |
| #define VNOSET 0x80 /* do not set variable - just readonly test */ |
| #define VNOSAVE 0x100 /* when text is on the heap before setvareq */ |
| #ifdef DYNAMIC_VAR |
| # define VDYNAMIC 0x200 /* dynamic variable */ |
| #else |
| # define VDYNAMIC 0 |
| #endif |
| |
| #ifdef IFS_BROKEN |
| static const char defifsvar[] ALIGN1 = "IFS= \t\n"; |
| #define defifs (defifsvar + 4) |
| #else |
| static const char defifs[] ALIGN1 = " \t\n"; |
| #endif |
| |
| struct shparam { |
| int nparam; /* # of positional parameters (without $0) */ |
| unsigned char malloc; /* if parameter list dynamically allocated */ |
| char **p; /* parameter list */ |
| #if ENABLE_ASH_GETOPTS |
| int optind; /* next parameter to be processed by getopts */ |
| int optoff; /* used by getopts */ |
| #endif |
| }; |
| |
| static struct shparam shellparam; /* $@ current positional parameters */ |
| |
| /* |
| * Free the list of positional parameters. |
| */ |
| static void |
| freeparam(volatile struct shparam *param) |
| { |
| char **ap; |
| |
| if (param->malloc) { |
| for (ap = param->p; *ap; ap++) |
| free(*ap); |
| free(param->p); |
| } |
| } |
| |
| #if ENABLE_ASH_GETOPTS |
| static void |
| getoptsreset(const char *value) |
| { |
| shellparam.optind = number(value); |
| shellparam.optoff = -1; |
| } |
| #endif |
| |
| struct var { |
| struct var *next; /* next entry in hash list */ |
| int flags; /* flags are defined above */ |
| const char *text; /* name=value */ |
| void (*func)(const char *); /* function to be called when */ |
| /* the variable gets set/unset */ |
| }; |
| |
| struct localvar { |
| struct localvar *next; /* next local variable in list */ |
| struct var *vp; /* the variable that was made local */ |
| int flags; /* saved flags */ |
| const char *text; /* saved text */ |
| }; |
| |
| /* Forward decls for varinit[] */ |
| #if ENABLE_LOCALE_SUPPORT |
| static void |
| change_lc_all(const char *value) |
| { |
| if (value && *value != '\0') |
| setlocale(LC_ALL, value); |
| } |
| static void |
| change_lc_ctype(const char *value) |
| { |
| if (value && *value != '\0') |
| setlocale(LC_CTYPE, value); |
| } |
| #endif |
| #if ENABLE_ASH_MAIL |
| static void chkmail(void); |
| static void changemail(const char *); |
| #endif |
| static void changepath(const char *); |
| #if ENABLE_ASH_RANDOM_SUPPORT |
| static void change_random(const char *); |
| #endif |
| |
| static struct var varinit[] = { |
| #ifdef IFS_BROKEN |
| { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL }, |
| #else |
| { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL }, |
| #endif |
| #if ENABLE_ASH_MAIL |
| { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, |
| { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, |
| #endif |
| { NULL, VSTRFIXED|VTEXTFIXED, bb_PATH_root_path, changepath }, |
| { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL }, |
| { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL }, |
| { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL }, |
| #if ENABLE_ASH_GETOPTS |
| { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, |
| #endif |
| #if ENABLE_ASH_RANDOM_SUPPORT |
| { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, |
| #endif |
| #if ENABLE_LOCALE_SUPPORT |
| { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, |
| { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, |
| #endif |
| #if ENABLE_FEATURE_EDITING_SAVEHISTORY |
| { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, |
| #endif |
| }; |
| |
| #define vifs varinit[0] |
| #if ENABLE_ASH_MAIL |
| #define vmail (&vifs)[1] |
| #define vmpath (&vmail)[1] |
| #else |
| #define vmpath vifs |
| #endif |
| #define vpath (&vmpath)[1] |
| #define vps1 (&vpath)[1] |
| #define vps2 (&vps1)[1] |
| #define vps4 (&vps2)[1] |
| #define voptind (&vps4)[1] |
| #if ENABLE_ASH_GETOPTS |
| #define vrandom (&voptind)[1] |
| #else |
| #define vrandom (&vps4)[1] |
| #endif |
| |
| /* |
| * The following macros access the values of the above variables. |
| * They have to skip over the name. They return the null string |
| * for unset variables. |
| */ |
| #define ifsval() (vifs.text + 4) |
| #define ifsset() ((vifs.flags & VUNSET) == 0) |
| #define mailval() (vmail.text + 5) |
| #define mpathval() (vmpath.text + 9) |
| #define pathval() (vpath.text + 5) |
| #define ps1val() (vps1.text + 4) |
| #define ps2val() (vps2.text + 4) |
| #define ps4val() (vps4.text + 4) |
| #define optindval() (voptind.text + 7) |
| |
| #define mpathset() ((vmpath.flags & VUNSET) == 0) |
| |
| /* |
| * The parsefile structure pointed to by the global variable parsefile |
| * contains information about the current file being read. |
| */ |
| struct redirtab { |
| struct redirtab *next; |
| int renamed[10]; |
| int nullredirs; |
| }; |
| |
| static struct redirtab *redirlist; |
| static int nullredirs; |
| static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ |
| |
| #define VTABSIZE 39 |
| |
| static struct var *vartab[VTABSIZE]; |
| |
| #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) |
| #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
| |
| /* |
| * Return of a legal variable name (a letter or underscore followed by zero or |
| * more letters, underscores, and digits). |
| */ |
| static char * |
| endofname(const char *name) |
| { |
| char *p; |
| |
| p = (char *) name; |
| if (!is_name(*p)) |
| return p; |
| while (*++p) { |
| if (!is_in_name(*p)) |
| break; |
| } |
| return p; |
| } |
| |
| /* |
| * Compares two strings up to the first = or '\0'. The first |
| * string must be terminated by '='; the second may be terminated by |
| * either '=' or '\0'. |
| */ |
| static int |
| varcmp(const char *p, const char *q) |
| { |
| int c, d; |
| |
| while ((c = *p) == (d = *q)) { |
| if (!c || c == '=') |
| goto out; |
| p++; |
| q++; |
| } |
| if (c == '=') |
| c = '\0'; |
| if (d == '=') |
| d = '\0'; |
| out: |
| return c - d; |
| } |
| |
| static int |
| varequal(const char *a, const char *b) |
| { |
| return !varcmp(a, b); |
| } |
| |
| /* |
| * Find the appropriate entry in the hash table from the name. |
| */ |
| static struct var ** |
| hashvar(const char *p) |
| { |
| unsigned hashval; |
| |
| hashval = ((unsigned char) *p) << 4; |
| while (*p && *p != '=') |
| hashval += (unsigned char) *p++; |
| return &vartab[hashval % VTABSIZE]; |
| } |
| |
| static int |
| vpcmp(const void *a, const void *b) |
| { |
| return varcmp(*(const char **)a, *(const char **)b); |
| } |
| |
| /* |
| * This routine initializes the builtin variables. |
| */ |
| static void |
| initvar(void) |
| { |
| struct var *vp; |
| struct var *end; |
| struct var **vpp; |
| |
| /* |
| * PS1 depends on uid |
| */ |
| #if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT |
| vps1.text = "PS1=\\w \\$ "; |
| #else |
| if (!geteuid()) |
| vps1.text = "PS1=# "; |
| #endif |
| vp = varinit; |
| end = vp + ARRAY_SIZE(varinit); |
| do { |
| vpp = hashvar(vp->text); |
| vp->next = *vpp; |
| *vpp = vp; |
| } while (++vp < end); |
| } |
| |
| static struct var ** |
| findvar(struct var **vpp, const char *name) |
| { |
| for (; *vpp; vpp = &(*vpp)->next) { |
| if (varequal((*vpp)->text, name)) { |
| break; |
| } |
| } |
| return vpp; |
| } |
| |
| /* |
| * Find the value of a variable. Returns NULL if not set. |
| */ |
| static char * |
| lookupvar(const char *name) |
| { |
| struct var *v; |
| |
| v = *findvar(hashvar(name), name); |
| if (v) { |
| #ifdef DYNAMIC_VAR |
| /* |
| * Dynamic variables are implemented roughly the same way they are |
| * in bash. Namely, they're "special" so long as they aren't unset. |
| * As soon as they're unset, they're no longer dynamic, and dynamic |
| * lookup will no longer happen at that point. -- PFM. |
| */ |
| if ((v->flags & VDYNAMIC)) |
| (*v->func)(NULL); |
| #endif |
| if (!(v->flags & VUNSET)) |
| return strchrnul(v->text, '=') + 1; |
| } |
| return NULL; |
| } |
| |
| /* |
| * Search the environment of a builtin command. |
| */ |
| static char * |
| bltinlookup(const char *name) |
| { |
| struct strlist *sp; |
| |
| for (sp = cmdenviron; sp; sp = sp->next) { |
| if (varequal(sp->text, name)) |
| return strchrnul(sp->text, '=') + 1; |
| } |
| return lookupvar(name); |
| } |
| |
| /* |
| * Same as setvar except that the variable and value are passed in |
| * the first argument as name=value. Since the first argument will |
| * be actually stored in the table, it should not be a string that |
| * will go away. |
| * Called with interrupts off. |
| */ |
| static void |
| setvareq(char *s, int flags) |
| { |
| struct var *vp, **vpp; |
| |
| vpp = hashvar(s); |
| flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
| vp = *findvar(vpp, s); |
| if (vp) { |
| if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { |
| const char *n; |
| |
| if (flags & VNOSAVE) |
| free(s); |
| n = vp->text; |
| ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); |
| } |
| |
| if (flags & VNOSET) |
| return; |
| |
| if (vp->func && (flags & VNOFUNC) == 0) |
| (*vp->func)(strchrnul(s, '=') + 1); |
| |
| if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
| free((char*)vp->text); |
| |
| flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); |
| } else { |
| if (flags & VNOSET) |
| return; |
| /* not found */ |
| vp = ckmalloc(sizeof(*vp)); |
| vp->next = *vpp; |
| vp->func = NULL; |
| *vpp = vp; |
| } |
| if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) |
| s = ckstrdup(s); |
| vp->text = s; |
| vp->flags = flags; |
| } |
| |
| /* |
| * Set the value of a variable. The flags argument is ored with the |
| * flags of the variable. If val is NULL, the variable is unset. |
| */ |
| static void |
| setvar(const char *name, const char *val, int flags) |
| { |
| char *p, *q; |
| size_t namelen; |
| char *nameeq; |
| size_t vallen; |
| |
| q = endofname(name); |
| p = strchrnul(q, '='); |
| namelen = p - name; |
| if (!namelen || p != q) |
| ash_msg_and_raise_error("%.*s: bad variable name", namelen, name); |
| vallen = 0; |
| if (val == NULL) { |
| flags |= VUNSET; |
| } else { |
| vallen = strlen(val); |
| } |
| INT_OFF; |
| nameeq = ckmalloc(namelen + vallen + 2); |
| p = memcpy(nameeq, name, namelen) + namelen; |
| if (val) { |
| *p++ = '='; |
| p = memcpy(p, val, vallen) + vallen; |
| } |
| *p = '\0'; |
| setvareq(nameeq, flags | VNOSAVE); |
| INT_ON; |
| } |
| |
| #if ENABLE_ASH_GETOPTS |
| /* |
| * Safe version of setvar, returns 1 on success 0 on failure. |
| */ |
| static int |
| setvarsafe(const char *name, const char *val, int flags) |
| { |
| int err; |
| volatile int saveint; |
| struct jmploc *volatile savehandler = exception_handler; |
| struct jmploc jmploc; |
| |
| SAVE_INT(saveint); |
| if (setjmp(jmploc.loc)) |
| err = 1; |
| else { |
| exception_handler = &jmploc; |
| setvar(name, val, flags); |
| err = 0; |
| } |
| exception_handler = savehandler; |
| RESTORE_INT(saveint); |
| return err; |
| } |
| #endif |
| |
| /* |
| * Unset the specified variable. |
| */ |
| static int |
| unsetvar(const char *s) |
| { |
| struct var **vpp; |
| struct var *vp; |
| int retval; |
| |
| vpp = findvar(hashvar(s), s); |
| vp = *vpp; |
| retval = 2; |
| if (vp) { |
| int flags = vp->flags; |
| |
| retval = 1; |
| if (flags & VREADONLY) |
| goto out; |
| #ifdef DYNAMIC_VAR |
| vp->flags &= ~VDYNAMIC; |
| #endif |
| if (flags & VUNSET) |
| goto ok; |
| if ((flags & VSTRFIXED) == 0) { |
| INT_OFF; |
| if ((flags & (VTEXTFIXED|VSTACK)) == 0) |
| free((char*)vp->text); |
| *vpp = vp->next; |
| free(vp); |
| INT_ON; |
| } else { |
| setvar(s, 0, 0); |
| vp->flags &= ~VEXPORT; |
| } |
| ok: |
| retval = 0; |
| } |
| out: |
| return retval; |
| } |
| |
| /* |
| * Process a linked list of variable assignments. |
| */ |
| static void |
| listsetvar(struct strlist *list_set_var, int flags) |
| { |
| struct strlist *lp = list_set_var; |
| |
| if (!lp) |
| return; |
| INT_OFF; |
| do { |
| setvareq(lp->text, flags); |
| lp = lp->next; |
| } while (lp); |
| INT_ON; |
| } |
| |
| /* |
| * Generate a list of variables satisfying the given conditions. |
| */ |
| static char ** |
| listvars(int on, int off, char ***end) |
| { |
| struct var **vpp; |
| struct var *vp; |
| char **ep; |
| int mask; |
| |
| STARTSTACKSTR(ep); |
| vpp = vartab; |
| mask = on | off; |
| do { |
| for (vp = *vpp; vp; vp = vp->next) { |
| if ((vp->flags & mask) == on) { |
| if (ep == stackstrend()) |
| ep = growstackstr(); |
| *ep++ = (char *) vp->text; |
| } |
| } |
| } while (++vpp < vartab + VTABSIZE); |
| if (ep == stackstrend()) |
| ep = growstackstr(); |
| if (end) |
| *end = ep; |
| *ep++ = NULL; |
| return grabstackstr(ep); |
| } |
| |
| |
| /* ============ Path search helper |
| * |
| * The variable path (passed by reference) should be set to the start |
| * of the path before the first call; padvance will update |
| * this value as it proceeds. Successive calls to padvance will return |
| * the possible path expansions in sequence. If an option (indicated by |
| * a percent sign) appears in the path entry then the global variable |
| * pathopt will be set to point to it; otherwise pathopt will be set to |
| * NULL. |
| */ |
| static const char *pathopt; /* set by padvance */ |
| |
| static char * |
| padvance(const char **path, const char *name) |
| { |
| const char *p; |
| char *q; |
| const char *start; |
| size_t len; |
| |
| if (*path == NULL) |
| return NULL; |
| start = *path; |
| for (p = start; *p && *p != ':' && *p != '%'; p++); |
| len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ |
| while (stackblocksize() < len) |
| growstackblock(); |
| q = stackblock(); |
| if (p != start) { |
| memcpy(q, start, p - start); |
| q += p - start; |
| *q++ = '/'; |
| } |
| strcpy(q, name); |
| pathopt = NULL; |
| if (*p == '%') { |
| pathopt = ++p; |
| while (*p && *p != ':') p++; |
| } |
| if (*p == ':') |
| *path = p + 1; |
| else |
| *path = NULL; |
| return stalloc(len); |
| } |
| |
| |
| /* ============ Prompt */ |
| |
| static smallint doprompt; /* if set, prompt the user */ |
| static smallint needprompt; /* true if interactive and at start of line */ |
| |
| #if ENABLE_FEATURE_EDITING |
| static line_input_t *line_input_state; |
| static const char *cmdedit_prompt; |
| static void |
| putprompt(const char *s) |
| { |
| if (ENABLE_ASH_EXPAND_PRMT) { |
| free((char*)cmdedit_prompt); |
| cmdedit_prompt = ckstrdup(s); |
| return; |
| } |
| cmdedit_prompt = s; |
| } |
| #else |
| static void |
| putprompt(const char *s) |
| { |
| out2str(s); |
| } |
| #endif |
| |
| #if ENABLE_ASH_EXPAND_PRMT |
| /* expandstr() needs parsing machinery, so it is far away ahead... */ |
| static const char *expandstr(const char *ps); |
| #else |
| #define expandstr(s) s |
| #endif |
| |
| static void |
| setprompt(int whichprompt) |
| { |
| const char *prompt; |
| #if ENABLE_ASH_EXPAND_PRMT |
| struct stackmark smark; |
| #endif |
| |
| needprompt = 0; |
| |
| switch (whichprompt) { |
| case 1: |
| prompt = ps1val(); |
| break; |
| case 2: |
| prompt = ps2val(); |
| break; |
| default: /* 0 */ |
| prompt = nullstr; |
| } |
| #if ENABLE_ASH_EXPAND_PRMT |
| setstackmark(&smark); |
| stalloc(stackblocksize()); |
| #endif |
| putprompt(expandstr(prompt)); |
| #if ENABLE_ASH_EXPAND_PRMT |
| popstackmark(&smark); |
| #endif |
| } |
| |
| |
| /* ============ The cd and pwd commands */ |
| |
| #define CD_PHYSICAL 1 |
| #define CD_PRINT 2 |
| |
| static int docd(const char *, int); |
| |
| static char *curdir = nullstr; /* current working directory */ |
| static char *physdir = nullstr; /* physical working directory */ |
| |
| static int |
| cdopt(void) |
| { |
| int flags = 0; |
| int i, j; |
| |
| j = 'L'; |
| while ((i = nextopt("LP"))) { |
| if (i != j) { |
| flags ^= CD_PHYSICAL; |
| j = i; |
| } |
| } |
| |
| return flags; |
| } |
| |
| /* |
| * Update curdir (the name of the current directory) in response to a |
| * cd command. |
| */ |
| static const char * |
| updatepwd(const char *dir) |
| { |
| char *new; |
| char *p; |
| char *cdcomppath; |
| const char *lim; |
| |
| cdcomppath = ststrdup(dir); |
| STARTSTACKSTR(new); |
| if (*dir != '/') { |
| if (curdir == nullstr) |
| return 0; |
| new = stack_putstr(curdir, new); |
| } |
| new = makestrspace(strlen(dir) + 2, new); |
| lim = stackblock() + 1; |
| if (*dir != '/') { |
| if (new[-1] != '/') |
| USTPUTC('/', new); |
| if (new > lim && *lim == '/') |
| lim++; |
| } else { |
| USTPUTC('/', new); |
| cdcomppath++; |
| if (dir[1] == '/' && dir[2] != '/') { |
| USTPUTC('/', new); |
| cdcomppath++; |
| lim++; |
| } |
| } |
| p = strtok(cdcomppath, "/"); |
| while (p) { |
| switch (*p) { |
| case '.': |
| if (p[1] == '.' && p[2] == '\0') { |
| while (new > lim) { |
| STUNPUTC(new); |
| if (new[-1] == '/') |
| break; |
| } |
| break; |
| } |
| if (p[1] == '\0') |
| break; |
| /* fall through */ |
| default: |
| new = stack_putstr(p, new); |
| USTPUTC('/', new); |
| } |
| p = strtok(0, "/"); |
| } |
| if (new > lim) |
| STUNPUTC(new); |
| *new = 0; |
| return stackblock(); |
| } |
| |
| /* |
| * Find out what the current directory is. If we already know the current |
| * directory, this routine returns immediately. |
| */ |
| static char * |
| getpwd(void) |
| { |
| char *dir = getcwd(0, 0); |
| return dir ? dir : nullstr; |
| } |
| |
| static void |
| setpwd(const char *val, int setold) |
| { |
| char *oldcur, *dir; |
| |
| oldcur = dir = curdir; |
| |
| if (setold) { |
| setvar("OLDPWD", oldcur, VEXPORT); |
| } |
| INT_OFF; |
| if (physdir != nullstr) { |
| if (physdir != oldcur) |
| free(physdir); |
| physdir = nullstr; |
| } |
| if (oldcur == val || !val) { |
| char *s = getpwd(); |
| physdir = s; |
| if (!val) |
| dir = s; |
| } else |
| dir = ckstrdup(val); |
| if (oldcur != dir && oldcur != nullstr) { |
| free(oldcur); |
| } |
| curdir = dir; |
| INT_ON; |
| setvar("PWD", dir, VEXPORT); |
| } |
| |
| static void hashcd(void); |
| |
| /* |
| * Actually do the chdir. We also call hashcd to let the routines in exec.c |
| * know that the current directory has changed. |
| */ |
| static int |
| docd(const char *dest, int flags) |
| { |
| const char *dir = 0; |
| int err; |
| |
| TRACE(("docd(\"%s\", %d) called\n", dest, flags)); |
| |
| INT_OFF; |
| if (!(flags & CD_PHYSICAL)) { |
| dir = updatepwd(dest); |
| if (dir) |
| dest = dir; |
| } |
| err = chdir(dest); |
| if (err) |
| goto out; |
| setpwd(dir, 1); |
| hashcd(); |
| out: |
| INT_ON; |
| return err; |
| } |
| |
| static int |
| cdcmd(int argc, char **argv) |
| { |
| const char *dest; |
| const char *path; |
| const char *p; |
| char c; |
| struct stat statb; |
| int flags; |
| |
| flags = cdopt(); |
| dest = *argptr; |
| if (!dest) |
| dest = bltinlookup(homestr); |
| else if (LONE_DASH(dest)) { |
| dest = bltinlookup("OLDPWD"); |
| flags |= CD_PRINT; |
| } |
| if (!dest) |
| dest = nullstr; |
| if (*dest == '/') |
| goto step7; |
| if (*dest == '.') { |
| c = dest[1]; |
| dotdot: |
| switch (c) { |
| case '\0': |
| case '/': |
| goto step6; |
| case '.': |
| c = dest[2]; |
| if (c != '.') |
| goto dotdot; |
| } |
| } |
| if (!*dest) |
| dest = "."; |
| path = bltinlookup("CDPATH"); |
| if (!path) { |
| step6: |
| step7: |
| p = dest; |
| goto docd; |
| } |
| do { |
| c = *path; |
| p = padvance(&path, dest); |
| if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { |
| if (c && c != ':') |
| flags |= CD_PRINT; |
| docd: |
| if (!docd(p, flags)) |
| goto out; |
| break; |
| } |
| } while (path); |
| ash_msg_and_raise_error("can't cd to %s", dest); |
| /* NOTREACHED */ |
| out: |
| if (flags & CD_PRINT) |
| out1fmt(snlfmt, curdir); |
| return 0; |
| } |
| |
| static int |
| pwdcmd(int argc, char **argv) |
| { |
| int flags; |
| const char *dir = curdir; |
| |
| flags = cdopt(); |
| if (flags) { |
| if (physdir == nullstr) |
| setpwd(dir, 0); |
| dir = physdir; |
| } |
| out1fmt(snlfmt, dir); |
| return 0; |
| } |
| |
| |
| /* ============ ... */ |
| |
| #define IBUFSIZ COMMON_BUFSIZE |
| #define basebuf bb_common_bufsiz1 /* buffer for top level input file */ |
| |
| /* Syntax classes */ |
| #define CWORD 0 /* character is nothing special */ |
| #define CNL 1 /* newline character */ |
| #define CBACK 2 /* a backslash character */ |
| #define CSQUOTE 3 /* single quote */ |
| #define CDQUOTE 4 /* double quote */ |
| #define CENDQUOTE 5 /* a terminating quote */ |
| #define CBQUOTE 6 /* backwards single quote */ |
| #define CVAR 7 /* a dollar sign */ |
| #define CENDVAR 8 /* a '}' character */ |
| #define CLP 9 /* a left paren in arithmetic */ |
| #define CRP 10 /* a right paren in arithmetic */ |
| #define CENDFILE 11 /* end of file */ |
| #define CCTL 12 /* like CWORD, except it must be escaped */ |
| #define CSPCL 13 /* these terminate a word */ |
| #define CIGN 14 /* character should be ignored */ |
| |
| #if ENABLE_ASH_ALIAS |
| #define SYNBASE 130 |
| #define PEOF -130 |
| #define PEOA -129 |
| #define PEOA_OR_PEOF PEOA |
| #else |
| #define SYNBASE 129 |
| #define PEOF -129 |
| #define PEOA_OR_PEOF PEOF |
| #endif |
| |
| /* number syntax index */ |
| #define BASESYNTAX 0 /* not in quotes */ |
| #define DQSYNTAX 1 /* in double quotes */ |
| #define SQSYNTAX 2 /* in single quotes */ |
| #define ARISYNTAX 3 /* in arithmetic */ |
| #define PSSYNTAX 4 /* prompt */ |
| |
| #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
| #define USE_SIT_FUNCTION |
| #endif |
| |
| #if ENABLE_ASH_MATH_SUPPORT |
| static const char S_I_T[][4] = { |
| #if ENABLE_ASH_ALIAS |
| { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */ |
| #endif |
| { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */ |
| { CNL, CNL, CNL, CNL }, /* 2, \n */ |
| { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */ |
| { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */ |
| { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */ |
| { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */ |
| { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */ |
| { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */ |
| { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */ |
| { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */ |
| { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */ |
| #ifndef USE_SIT_FUNCTION |
| { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */ |
| { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */ |
| { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */ |
| #endif |
| }; |
| #else |
| static const char S_I_T[][3] = { |
| #if ENABLE_ASH_ALIAS |
| { CSPCL, CIGN, CIGN }, /* 0, PEOA */ |
| #endif |
| { CSPCL, CWORD, CWORD }, /* 1, ' ' */ |
| { CNL, CNL, CNL }, /* 2, \n */ |
| { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */ |
| { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */ |
| { CVAR, CVAR, CWORD }, /* 5, $ */ |
| { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */ |
| { CSPCL, CWORD, CWORD }, /* 7, ( */ |
| { CSPCL, CWORD, CWORD }, /* 8, ) */ |
| { CBACK, CBACK, CCTL }, /* 9, \ */ |
| { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */ |
| { CENDVAR, CENDVAR, CWORD }, /* 11, } */ |
| #ifndef USE_SIT_FUNCTION |
| { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */ |
| { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */ |
| { CCTL, CCTL, CCTL } /* 14, CTLESC ... */ |
| #endif |
| }; |
| #endif /* ASH_MATH_SUPPORT */ |
| |
| #ifdef USE_SIT_FUNCTION |
| |
| static int |
| SIT(int c, int syntax) |
| { |
| static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; |
| #if ENABLE_ASH_ALIAS |
| static const char syntax_index_table[] ALIGN1 = { |
| 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ |
| 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ |
| 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ |
| 11, 3 /* "}~" */ |
| }; |
| #else |
| static const char syntax_index_table[] ALIGN1 = { |
| 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ |
| 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ |
| 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ |
| 10, 2 /* "}~" */ |
| }; |
| #endif |
| const char *s; |
| int indx; |
| |
| if (c == PEOF) /* 2^8+2 */ |
| return CENDFILE; |
| #if ENABLE_ASH_ALIAS |
| if (c == PEOA) /* 2^8+1 */ |
| indx = 0; |
| else |
| #endif |
| #define U_C(c) ((unsigned char)(c)) |
| |
| if ((unsigned char)c >= (unsigned char)(CTLESC) |
| && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) |
| ) { |
| return CCTL; |
| } else { |
| s = strchr(spec_symbls, c); |
| if (s == NULL || *s == '\0') |
| return CWORD; |
| indx = syntax_index_table[(s - spec_symbls)]; |
| } |
| return S_I_T[indx][syntax]; |
| } |
| |
| #else /* !USE_SIT_FUNCTION */ |
| |
| #if ENABLE_ASH_ALIAS |
| #define CSPCL_CIGN_CIGN_CIGN 0 |
| #define CSPCL_CWORD_CWORD_CWORD 1 |
| #define CNL_CNL_CNL_CNL 2 |
| #define CWORD_CCTL_CCTL_CWORD 3 |
| #define CDQUOTE_CENDQUOTE_CWORD_CWORD 4 |
| #define CVAR_CVAR_CWORD_CVAR 5 |
| #define CSQUOTE_CWORD_CENDQUOTE_CWORD 6 |
| #define CSPCL_CWORD_CWORD_CLP 7 |
| #define CSPCL_CWORD_CWORD_CRP 8 |
| #define CBACK_CBACK_CCTL_CBACK 9 |
| #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10 |
| #define CENDVAR_CENDVAR_CWORD_CENDVAR 11 |
| #define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12 |
| #define CWORD_CWORD_CWORD_CWORD 13 |
| #define CCTL_CCTL_CCTL_CCTL 14 |
| #else |
| #define CSPCL_CWORD_CWORD_CWORD 0 |
| #define CNL_CNL_CNL_CNL 1 |
| #define CWORD_CCTL_CCTL_CWORD 2 |
| #define CDQUOTE_CENDQUOTE_CWORD_CWORD 3 |
| #define CVAR_CVAR_CWORD_CVAR 4 |
| #define CSQUOTE_CWORD_CENDQUOTE_CWORD 5 |
| #define CSPCL_CWORD_CWORD_CLP 6 |
| #define CSPCL_CWORD_CWORD_CRP 7 |
| #define CBACK_CBACK_CCTL_CBACK 8 |
| #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9 |
| #define CENDVAR_CENDVAR_CWORD_CENDVAR 10 |
| #define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11 |
| #define CWORD_CWORD_CWORD_CWORD 12 |
| #define CCTL_CCTL_CCTL_CCTL 13 |
| #endif |
| |
| static const char syntax_index_table[258] = { |
| /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ |
| /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, |
| #if ENABLE_ASH_ALIAS |
| /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN, |
| #endif |
| /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD, |
| /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL, |
| /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL, |
| /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, |
| /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, |
| /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, |
| /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL, |
| /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, |
| /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, |
| /* 11 -119 */ CWORD_CWORD_CWORD_CWORD, |
| /* 12 -118 */ CWORD_CWORD_CWORD_CWORD, |
| /* 13 -117 */ CWORD_CWORD_CWORD_CWORD, |
| /* 14 -116 */ CWORD_CWORD_CWORD_CWORD, |
| /* 15 -115 */ CWORD_CWORD_CWORD_CWORD, |
| /* 16 -114 */ CWORD_CWORD_CWORD_CWORD, |
| /* 17 -113 */ CWORD_CWORD_CWORD_CWORD, |
| /* 18 -112 */ CWORD_CWORD_CWORD_CWORD, |
| /* 19 -111 */ CWORD_CWORD_CWORD_CWORD, |
| /* 20 -110 */ CWORD_CWORD_CWORD_CWORD, |
| /* 21 -109 */ CWORD_CWORD_CWORD_CWORD, |
| /* 22 -108 */ CWORD_CWORD_CWORD_CWORD, |
| /* 23 -107 */ CWORD_CWORD_CWORD_CWORD, |
| /* 24 -106 */ CWORD_CWORD_CWORD_CWORD, |
| /* 25 -105 */ CWORD_CWORD_CWORD_CWORD, |
| /* 26 -104 */ CWORD_CWORD_CWORD_CWORD, |
| /* 27 -103 */ CWORD_CWORD_CWORD_CWORD, |
| /* 28 -102 */ CWORD_CWORD_CWORD_CWORD, |
| /* 29 -101 */ CWORD_CWORD_CWORD_CWORD, |
| /* 30 -100 */ CWORD_CWORD_CWORD_CWORD, |
| /* 31 -99 */ CWORD_CWORD_CWORD_CWORD, |
| /* 32 -98 */ CWORD_CWORD_CWORD_CWORD, |
| /* 33 -97 */ CWORD_CWORD_CWORD_CWORD, |
| /* 34 -96 */ CWORD_CWORD_CWORD_CWORD, |
| /* 35 -95 */ CWORD_CWORD_CWORD_CWORD, |
| /* 36 -94 */ CWORD_CWORD_CWORD_CWORD, |
| /* 37 -93 */ CWORD_CWORD_CWORD_CWORD, |
| /* 38 -92 */ CWORD_CWORD_CWORD_CWORD, |
| /* 39 -91 */ CWORD_CWORD_CWORD_CWORD, |
| /* 40 -90 */ CWORD_CWORD_CWORD_CWORD, |
| /* 41 -89 */ CWORD_CWORD_CWORD_CWORD, |
| /* 42 -88 */ CWORD_CWORD_CWORD_CWORD, |
| /* 43 -87 */ CWORD_CWORD_CWORD_CWORD, |
| /* 44 -86 */ CWORD_CWORD_CWORD_CWORD, |
| /* 45 -85 */ CWORD_CWORD_CWORD_CWORD, |
| /* 46 -84 */ CWORD_CWORD_CWORD_CWORD, |
| /* 47 -83 */ CWORD_CWORD_CWORD_CWORD, |
| /* 48 -82 */ CWORD_CWORD_CWORD_CWORD, |
| /* 49 -81 */ CWORD_CWORD_CWORD_CWORD, |
| /* 50 -80 */ CWORD_CWORD_CWORD_CWORD, |
| /* 51 -79 */ CWORD_CWORD_CWORD_CWORD, |
| /* 52 -78 */ CWORD_CWORD_CWORD_CWORD, |
| /* 53 -77 */ CWORD_CWORD_CWORD_CWORD, |
| /* 54 -76 */ CWORD_CWORD_CWORD_CWORD, |
| /* 55 -75 */ CWORD_CWORD_CWORD_CWORD, |
| /* 56 -74 */ CWORD_CWORD_CWORD_CWORD, |
| /* 57 -73 */ CWORD_CWORD_CWORD_CWORD, |
| /* 58 -72 */ CWORD_CWORD_CWORD_CWORD, |
| /* 59 -71 */ CWORD_CWORD_CWORD_CWORD, |
| /* 60 -70 */ CWORD_CWORD_CWORD_CWORD, |
| /* 61 -69 */ CWORD_CWORD_CWORD_CWORD, |
| /* 62 -68 */ CWORD_CWORD_CWORD_CWORD, |
| /* 63 -67 */ CWORD_CWORD_CWORD_CWORD, |
| /* 64 -66 */ CWORD_CWORD_CWORD_CWORD, |
| /* 65 -65 */ CWORD_CWORD_CWORD_CWORD, |
| /* 66 -64 */ CWORD_CWORD_CWORD_CWORD, |
| /* 67 -63 */ CWORD_CWORD_CWORD_CWORD, |
| /* 68 -62 */ CWORD_CWORD_CWORD_CWORD, |
| /* 69 -61 */ CWORD_CWORD_CWORD_CWORD, |
| /* 70 -60 */ CWORD_CWORD_CWORD_CWORD, |
| /* 71 -59 */ CWORD_CWORD_CWORD_CWORD, |
| /* 72 -58 */ CWORD_CWORD_CWORD_CWORD, |
| /* 73 -57 */ CWORD_CWORD_CWORD_CWORD, |
| /* 74 -56 */ CWORD_CWORD_CWORD_CWORD, |
| /* 75 -55 */ CWORD_CWORD_CWORD_CWORD, |
| /* 76 -54 */ CWORD_CWORD_CWORD_CWORD, |
| /* 77 -53 */ CWORD_CWORD_CWORD_CWORD, |
| /* 78 -52 */ CWORD_CWORD_CWORD_CWORD, |
| /* 79 -51 */ CWORD_CWORD_CWORD_CWORD, |
| /* 80 -50 */ CWORD_CWORD_CWORD_CWORD, |
| /* 81 -49 */ CWORD_CWORD_CWORD_CWORD, |
| /* 82 -48 */ CWORD_CWORD_CWORD_CWORD, |
| /* 83 -47 */ CWORD_CWORD_CWORD_CWORD, |
| /* 84 -46 */ CWORD_CWORD_CWORD_CWORD, |
| /* 85 -45 */ CWORD_CWORD_CWORD_CWORD, |
| /* 86 -44 */ CWORD_CWORD_CWORD_CWORD, |
| /* 87 -43 */ CWORD_CWORD_CWORD_CWORD, |
| /* 88 -42 */ CWORD_CWORD_CWORD_CWORD, |
| /* 89 -41 */ CWORD_CWORD_CWORD_CWORD, |
| /* 90 -40 */ CWORD_CWORD_CWORD_CWORD, |
| /* 91 -39 */ CWORD_CWORD_CWORD_CWORD, |
| /* 92 -38 */ CWORD_CWORD_CWORD_CWORD, |
| /* 93 -37 */ CWORD_CWORD_CWORD_CWORD, |
| /* 94 -36 */ CWORD_CWORD_CWORD_CWORD, |
| /* 95 -35 */ CWORD_CWORD_CWORD_CWORD, |
| /* 96 -34 */ CWORD_CWORD_CWORD_CWORD, |
| /* 97 -33 */ CWORD_CWORD_CWORD_CWORD, |
| /* 98 -32 */ CWORD_CWORD_CWORD_CWORD, |
| /* 99 -31 */ CWORD_CWORD_CWORD_CWORD, |
| /* 100 -30 */ CWORD_CWORD_CWORD_CWORD, |
| /* 101 -29 */ CWORD_CWORD_CWORD_CWORD, |
| /* 102 -28 */ CWORD_CWORD_CWORD_CWORD, |
| /* 103 -27 */ CWORD_CWORD_CWORD_CWORD, |
| /* 104 -26 */ CWORD_CWORD_CWORD_CWORD, |
| /* 105 -25 */ CWORD_CWORD_CWORD_CWORD, |
| /* 106 -24 */ CWORD_CWORD_CWORD_CWORD, |
| /* 107 -23 */ CWORD_CWORD_CWORD_CWORD, |
| /* 108 -22 */ CWORD_CWORD_CWORD_CWORD, |
| /* 109 -21 */ CWORD_CWORD_CWORD_CWORD, |
| /* 110 -20 */ CWORD_CWORD_CWORD_CWORD, |
| /* 111 -19 */ CWORD_CWORD_CWORD_CWORD, |
| /* 112 -18 */ CWORD_CWORD_CWORD_CWORD, |
| /* 113 -17 */ CWORD_CWORD_CWORD_CWORD, |
| /* 114 -16 */ CWORD_CWORD_CWORD_CWORD, |
| /* 115 -15 */ CWORD_CWORD_CWORD_CWORD, |
| /* 116 -14 */ CWORD_CWORD_CWORD_CWORD, |
| /* 117 -13 */ CWORD_CWORD_CWORD_CWORD, |
| /* 118 -12 */ CWORD_CWORD_CWORD_CWORD, |
| /* 119 -11 */ CWORD_CWORD_CWORD_CWORD, |
| /* 120 -10 */ CWORD_CWORD_CWORD_CWORD, |
| /* 121 -9 */ CWORD_CWORD_CWORD_CWORD, |
| /* 122 -8 */ CWORD_CWORD_CWORD_CWORD, |
| /* 123 -7 */ CWORD_CWORD_CWORD_CWORD, |
| /* 124 -6 */ CWORD_CWORD_CWORD_CWORD, |
| /* 125 -5 */ CWORD_CWORD_CWORD_CWORD, |
| /* 126 -4 */ CWORD_CWORD_CWORD_CWORD, |
| /* 127 -3 */ CWORD_CWORD_CWORD_CWORD, |
| /* 128 -2 */ CWORD_CWORD_CWORD_CWORD, |
| /* 129 -1 */ CWORD_CWORD_CWORD_CWORD, |
| /* 130 0 */ CWORD_CWORD_CWORD_CWORD, |
| /* 131 1 */ CWORD_CWORD_CWORD_CWORD, |
| /* 132 2 */ CWORD_CWORD_CWORD_CWORD, |
| /* 133 3 */ CWORD_CWORD_CWORD_CWORD, |
| /* 134 4 */ CWORD_CWORD_CWORD_CWORD, |
| /* 135 5 */ CWORD_CWORD_CWORD_CWORD, |
| /* 136 6 */ CWORD_CWORD_CWORD_CWORD, |
| /* 137 7 */ CWORD_CWORD_CWORD_CWORD, |
| /* 138 8 */ CWORD_CWORD_CWORD_CWORD, |
| /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 140 10 "\n" */ CNL_CNL_CNL_CNL, |
| /* 141 11 */ CWORD_CWORD_CWORD_CWORD, |
| /* 142 12 */ CWORD_CWORD_CWORD_CWORD, |
| /* 143 13 */ CWORD_CWORD_CWORD_CWORD, |
| /* 144 14 */ CWORD_CWORD_CWORD_CWORD, |
| /* 145 15 */ CWORD_CWORD_CWORD_CWORD, |
| /* 146 16 */ CWORD_CWORD_CWORD_CWORD, |
| /* 147 17 */ CWORD_CWORD_CWORD_CWORD, |
| /* 148 18 */ CWORD_CWORD_CWORD_CWORD, |
| /* 149 19 */ CWORD_CWORD_CWORD_CWORD, |
| /* 150 20 */ CWORD_CWORD_CWORD_CWORD, |
| /* 151 21 */ CWORD_CWORD_CWORD_CWORD, |
| /* 152 22 */ CWORD_CWORD_CWORD_CWORD, |
| /* 153 23 */ CWORD_CWORD_CWORD_CWORD, |
| /* 154 24 */ CWORD_CWORD_CWORD_CWORD, |
| /* 155 25 */ CWORD_CWORD_CWORD_CWORD, |
| /* 156 26 */ CWORD_CWORD_CWORD_CWORD, |
| /* 157 27 */ CWORD_CWORD_CWORD_CWORD, |
| /* 158 28 */ CWORD_CWORD_CWORD_CWORD, |
| /* 159 29 */ CWORD_CWORD_CWORD_CWORD, |
| /* 160 30 */ CWORD_CWORD_CWORD_CWORD, |
| /* 161 31 */ CWORD_CWORD_CWORD_CWORD, |
| /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD, |
| /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD, |
| /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, |
| /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD, |
| /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR, |
| /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD, |
| /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, |
| /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP, |
| /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP, |
| /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD, |
| /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD, |
| /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD, |
| /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD, |
| /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD, |
| /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD, |
| /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD, |
| /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD, |
| /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD, |
| /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD, |
| /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD, |
| /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD, |
| /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD, |
| /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD, |
| /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD, |
| /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD, |
| /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD, |
| /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD, |
| /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD, |
| /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD, |
| /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD, |
| /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD, |
| /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD, |
| /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD, |
| /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD, |
| /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD, |
| /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD, |
| /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD, |
| /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD, |
| /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD, |
| /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD, |
| /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD, |
| /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD, |
| /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD, |
| /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD, |
| /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD, |
| /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD, |
| /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD, |
| /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD, |
| /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD, |
| /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD, |
| /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD, |
| /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD, |
| /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD, |
| /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD, |
| /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD, |
| /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD, |
| /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK, |
| /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD, |
| /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD, |
| /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD, |
| /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, |
| /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD, |
| /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD, |
| /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD, |
| /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD, |
| /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD, |
| /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD, |
| /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD, |
| /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD, |
| /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD, |
| /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD, |
| /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD, |
| /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD, |
| /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD, |
| /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD, |
| /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD, |
| /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD, |
| /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD, |
| /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD, |
| /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD, |
| /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD, |
| /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD, |
| /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD, |
| /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD, |
| /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD, |
| /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD, |
| /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD, |
| /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD, |
| /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD, |
| /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, |
| /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD, |
| /* 257 127 */ CWORD_CWORD_CWORD_CWORD, |
| }; |
| |
| #define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]) |
| |
| #endif /* USE_SIT_FUNCTION */ |
| |
| |
| /* ============ Alias handling */ |
| |
| #if ENABLE_ASH_ALIAS |
| |
| #define ALIASINUSE 1 |
| #define ALIASDEAD 2 |
| |
| #define ATABSIZE 39 |
| |
| struct alias { |
| struct alias *next; |
| char *name; |
| char *val; |
| int flag; |
| }; |
| |
| static struct alias *atab[ATABSIZE]; |
| |
| static struct alias ** |
| __lookupalias(const char *name) { |
| unsigned int hashval; |
| struct alias **app; |
| const char *p; |
| unsigned int ch; |
| |
| p = name; |
| |
| ch = (unsigned char)*p; |
| hashval = ch << 4; |
| while (ch) { |
| hashval += ch; |
| ch = (unsigned char)*++p; |
| } |
| app = &atab[hashval % ATABSIZE]; |
| |
| for (; *app; app = &(*app)->next) { |
| if (strcmp(name, (*app)->name) == 0) { |
| break; |
| } |
| } |
| |
| return app; |
| } |
| |
| static struct alias * |
| lookupalias(const char *name, int check) |
| { |
| struct alias *ap = *__lookupalias(name); |
| |
| if (check && ap && (ap->flag & ALIASINUSE)) |
| return NULL; |
| return ap; |
| } |
| |
| static struct alias * |
| freealias(struct alias *ap) |
| { |
| struct alias *next; |
| |
| if (ap->flag & ALIASINUSE) { |
| ap->flag |= ALIASDEAD; |
| return ap; |
| } |
| |
| next = ap->next; |
| free(ap->name); |
| free(ap->val); |
| free(ap); |
| return next; |
| } |
| |
| static void |
| setalias(const char *name, const char *val) |
| { |
| struct alias *ap, **app; |
| |
| app = __lookupalias(name); |
| ap = *app; |
| INT_OFF; |
| if (ap) { |
| if (!(ap->flag & ALIASINUSE)) { |
| free(ap->val); |
| } |
| ap->val = ckstrdup(val); |
| ap->flag &= ~ALIASDEAD; |
| } else { |
| /* not found */ |
| ap = ckmalloc(sizeof(struct alias)); |
| ap->name = ckstrdup(name); |
| ap->val = ckstrdup(val); |
| ap->flag = 0; |
| ap->next = 0; |
| *app = ap; |
| } |
| INT_ON; |
| } |
| |
| static int |
| unalias(const char *name) |
| { |
| struct alias **app; |
| |
| app = __lookupalias(name); |
| |
| if (*app) { |
| INT_OFF; |
| *app = freealias(*app); |
| INT_ON; |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void |
| rmaliases(void) |
| { |
| struct alias *ap, **app; |
| int i; |
| |
| INT_OFF; |
| for (i = 0; i < ATABSIZE; i++) { |
| app = &atab[i]; |
| for (ap = *app; ap; ap = *app) { |
| *app = freealias(*app); |
| if (ap == *app) { |
| app = &ap->next; |
| } |
| } |
| } |
| INT_ON; |
| } |
| |
| static void |
| printalias(const struct alias *ap) |
| { |
| out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); |
| } |
| |
| /* |
| * TODO - sort output |
| */ |
| static int |
| aliascmd(int argc, char **argv) |
| { |
| char *n, *v; |
| int ret = 0; |
| struct alias *ap; |
| |
| if (argc == 1) { |
| int i; |
| |
| for (i = 0; i < ATABSIZE; i++) |
| for (ap = atab[i]; ap; ap = ap->next) { |
| printalias(ap); |
| } |
| return 0; |
| } |
| while ((n = *++argv) != NULL) { |
| v = strchr(n+1, '='); |
| if (v == NULL) { /* n+1: funny ksh stuff */ |
| ap = *__lookupalias(n); |
| if (ap == NULL) { |
| fprintf(stderr, "%s: %s not found\n", "alias", n); |
| ret = 1; |
| } else |
| printalias(ap); |
| } else { |
| *v++ = '\0'; |
| setalias(n, v); |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int |
| unaliascmd(int argc, char **argv) |
| { |
| int i; |
| |
| while ((i = nextopt("a")) != '\0') { |
| if (i == 'a') { |
| rmaliases(); |
| return 0; |
| } |
| } |
| for (i = 0; *argptr; argptr++) { |
| if (unalias(*argptr)) { |
| fprintf(stderr, "%s: %s not found\n", "unalias", *argptr); |
| i = 1; |
| } |
| } |
| |
| return i; |
| } |
| |
| #endif /* ASH_ALIAS */ |
| |
| |
| /* ============ jobs.c */ |
| |
| /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
| #define FORK_FG 0 |
| #define FORK_BG 1 |
| #define FORK_NOJOB 2 |
| |
| /* mode flags for showjob(s) */ |
| #define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ |
| #define SHOW_PID 0x04 /* include process pid */ |
| #define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ |
| |
| /* |
| * A job structure contains information about a job. A job is either a |
| * single process or a set of processes contained in a pipeline. In the |
| * latter case, pidlist will be non-NULL, and will point to a -1 terminated |
| * array of pids. |
| */ |
| |
| struct procstat { |
| pid_t pid; /* process id */ |
| int status; /* last process status from wait() */ |
| char *cmd; /* text of command being run */ |
| }; |
| |
| struct job { |
| struct procstat ps0; /* status of process */ |
| struct procstat *ps; /* status or processes when more than one */ |
| #if JOBS |
| int stopstatus; /* status of a stopped job */ |
| #endif |
| uint32_t |
| nprocs: 16, /* number of processes */ |
| state: 8, |
| #define JOBRUNNING 0 /* at least one proc running */ |
| #define JOBSTOPPED 1 /* all procs are stopped */ |
| #define JOBDONE 2 /* all procs are completed */ |
| #if JOBS |
| sigint: 1, /* job was killed by SIGINT */ |
| jobctl: 1, /* job running under job control */ |
| #endif |
| waited: 1, /* true if this entry has been waited for */ |
| used: 1, /* true if this entry is in used */ |
| changed: 1; /* true if status has changed */ |
| struct job *prev_job; /* previous job */ |
| }; |
| |
| static pid_t backgndpid; /* pid of last background process */ |
| static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ |
| |
| static struct job *makejob(union node *, int); |
| static int forkshell(struct job *, union node *, int); |
| static int waitforjob(struct job *); |
| |
| #if !JOBS |
| enum { jobctl = 0 }; |
| #define setjobctl(on) do {} while (0) |
| #else |
| static smallint jobctl; /* true if doing job control */ |
| static void setjobctl(int); |
| #endif |
| |
| /* |
| * Set the signal handler for the specified signal. The routine figures |
| * out what it should be set to. |
| */ |
| static void |
| setsignal(int signo) |
| { |
| int action; |
| char *t, tsig; |
| struct sigaction act; |
| |
| t = trap[signo]; |
| if (t == NULL) |
| action = S_DFL; |
| else if (*t != '\0') |
| action = S_CATCH; |
| else |
| action = S_IGN; |
| if (rootshell && action == S_DFL) { |
| switch (signo) { |
| case SIGINT: |
| if (iflag || minusc || sflag == 0) |
| action = S_CATCH; |
| break; |
| case SIGQUIT: |
| #if DEBUG |
| if (debug) |
| break; |
| #endif |
| /* FALLTHROUGH */ |
| case SIGTERM: |
| if (iflag) |
| action = S_IGN; |
| break; |
| #if JOBS |
| case SIGTSTP: |
| case SIGTTOU: |
| if (mflag) |
| action = S_IGN; |
| break; |
| #endif |
| } |
| } |
| |
| t = &sigmode[signo - |