| /* 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-2003 Herbert Xu <herbert@debian.org> |
| * was re-ported from NetBSD and debianized. |
| * |
| * |
| * This code is derived from software contributed to Berkeley by |
| * Kenneth Almquist. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * 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-2004 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 IFS_BROKEN |
| |
| #define PROFILE 0 |
| |
| #ifdef DEBUG |
| #define _GNU_SOURCE |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/cdefs.h> |
| #include <sys/ioctl.h> |
| #include <sys/param.h> |
| #include <sys/resource.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <assert.h> |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <paths.h> |
| #include <setjmp.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <sysexits.h> |
| #include <time.h> |
| #include <fnmatch.h> |
| |
| |
| #include "busybox.h" |
| #include "pwd_.h" |
| |
| #ifdef CONFIG_ASH_JOB_CONTROL |
| #define JOBS 1 |
| #else |
| #undef JOBS |
| #endif |
| |
| #if JOBS |
| #include <termios.h> |
| #endif |
| |
| #include "cmdedit.h" |
| |
| #ifdef __GLIBC__ |
| /* glibc sucks */ |
| static int *dash_errno; |
| #undef errno |
| #define errno (*dash_errno) |
| #endif |
| |
| #if defined(__uClinux__) |
| #error "Do not even bother, ash will not run on uClinux" |
| #endif |
| |
| #ifdef DEBUG |
| #define _DIAGASSERT(assert_expr) assert(assert_expr) |
| #else |
| #define _DIAGASSERT(assert_expr) |
| #endif |
| |
| |
| #ifdef CONFIG_ASH_ALIAS |
| /* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */ |
| |
| #define ALIASINUSE 1 |
| #define ALIASDEAD 2 |
| |
| struct alias { |
| struct alias *next; |
| char *name; |
| char *val; |
| int flag; |
| }; |
| |
| static struct alias *lookupalias(const char *, int); |
| static int aliascmd(int, char **); |
| static int unaliascmd(int, char **); |
| static void rmaliases(void); |
| static int unalias(const char *); |
| static void printalias(const struct alias *); |
| #endif |
| |
| /* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */ |
| |
| |
| static void setpwd(const char *, int); |
| |
| /* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */ |
| |
| |
| /* |
| * Types of operations (passed to the errmsg routine). |
| */ |
| |
| |
| static const char not_found_msg[] = "%s: not found"; |
| |
| |
| #define E_OPEN "No such file" /* opening a file */ |
| #define E_CREAT "Directory nonexistent" /* creating a file */ |
| #define E_EXEC not_found_msg+4 /* executing a program */ |
| |
| /* |
| * 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 exeception. 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 *handler; |
| static int exception; |
| static volatile int suppressint; |
| static volatile sig_atomic_t intpending; |
| |
| static int exerrno; /* Last exec error, error for EXEXEC */ |
| |
| /* 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) */ |
| |
| |
| /* do we generate EXSIG events */ |
| static int exsig; |
| /* last pending signal */ |
| static volatile sig_atomic_t pendingsigs; |
| |
| /* |
| * 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 barrier() ({ __asm__ __volatile__ ("": : :"memory"); }) |
| #define INTOFF \ |
| ({ \ |
| suppressint++; \ |
| barrier(); \ |
| 0; \ |
| }) |
| #define SAVEINT(v) ((v) = suppressint) |
| #define RESTOREINT(v) \ |
| ({ \ |
| barrier(); \ |
| if ((suppressint = (v)) == 0 && intpending) onint(); \ |
| 0; \ |
| }) |
| #define EXSIGON() \ |
| ({ \ |
| exsig++; \ |
| barrier(); \ |
| if (pendingsigs) \ |
| exraise(EXSIG); \ |
| 0; \ |
| }) |
| /* EXSIG is turned off by evalbltin(). */ |
| |
| |
| static void exraise(int) __attribute__((__noreturn__)); |
| static void onint(void) __attribute__((__noreturn__)); |
| |
| static void error(const char *, ...) __attribute__((__noreturn__)); |
| static void exerror(int, const char *, ...) __attribute__((__noreturn__)); |
| |
| static void sh_warnx(const char *, ...); |
| |
| #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE |
| static void |
| inton(void) { |
| if (--suppressint == 0 && intpending) { |
| onint(); |
| } |
| } |
| #define INTON inton() |
| static void forceinton(void) |
| { |
| suppressint = 0; |
| if (intpending) |
| onint(); |
| } |
| #define FORCEINTON forceinton() |
| #else |
| #define INTON \ |
| ({ \ |
| barrier(); \ |
| if (--suppressint == 0 && intpending) onint(); \ |
| 0; \ |
| }) |
| #define FORCEINTON \ |
| ({ \ |
| barrier(); \ |
| suppressint = 0; \ |
| if (intpending) onint(); \ |
| 0; \ |
| }) |
| #endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */ |
| |
| /* |
| * BSD setjmp saves the signal mask, which violates ANSI C and takes time, |
| * so we use _setjmp instead. |
| */ |
| |
| #if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__) |
| #define setjmp(jmploc) _setjmp(jmploc) |
| #define longjmp(jmploc, val) _longjmp(jmploc, val) |
| #endif |
| |
| /* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */ |
| |
| struct strlist { |
| struct strlist *next; |
| char *text; |
| }; |
| |
| |
| struct arglist { |
| struct strlist *list; |
| struct strlist **lastp; |
| }; |
| |
| /* |
| * expandarg() flags |
| */ |
| #define EXP_FULL 0x1 /* perform word splitting & file globbing */ |
| #define EXP_TILDE 0x2 /* do normal tilde expansion */ |
| #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ |
| #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ |
| #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
| #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ |
| #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ |
| #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
| #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
| |
| |
| union node; |
| static void expandarg(union node *, struct arglist *, int); |
| #define rmescapes(p) _rmescapes((p), 0) |
| static char *_rmescapes(char *, int); |
| static int casematch(union node *, char *); |
| |
| #ifdef CONFIG_ASH_MATH_SUPPORT |
| static void expari(int); |
| #endif |
| |
| /* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */ |
| |
| static char *commandname; /* currently executing command */ |
| static struct strlist *cmdenviron; /* environment for builtin command */ |
| static int exitstatus; /* exit status of last command */ |
| static int back_exitstatus; /* exit status of backquoted command */ |
| |
| |
| struct backcmd { /* result of evalbackcmd */ |
| int fd; /* file descriptor to read from */ |
| char *buf; /* buffer */ |
| int nleft; /* number of chars in buffer */ |
| struct job *jp; /* job structure for command */ |
| }; |
| |
| /* |
| * This file was generated by the mknodes program. |
| */ |
| |
| #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 |
| |
| |
| |
| 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; |
| }; |
| |
| |
| static void freefunc(struct funcnode *); |
| /* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */ |
| |
| /* control characters in argument strings */ |
| #define CTL_FIRST '\201' /* first 'special' character */ |
| #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' |
| #define CTL_LAST '\210' /* last 'special' character */ |
| |
| /* 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} */ |
| |
| /* values of checkkwd variable */ |
| #define CHKALIAS 0x1 |
| #define CHKKWD 0x2 |
| #define CHKNL 0x4 |
| |
| #define IBUFSIZ (BUFSIZ + 1) |
| |
| /* |
| * NEOF is returned by parsecmd when it encounters an end of file. It |
| * must be distinct from NULL, so we use the address of a variable that |
| * happens to be handy. |
| */ |
| static int plinno = 1; /* input line number */ |
| |
| /* number of characters left in input buffer */ |
| static int parsenleft; /* copy of parsefile->nleft */ |
| static int parselleft; /* copy of parsefile->lleft */ |
| |
| /* next character in input buffer */ |
| static char *parsenextc; /* copy of parsefile->nextc */ |
| |
| struct strpush { |
| struct strpush *prev; /* preceding string on stack */ |
| char *prevstring; |
| int prevnleft; |
| #ifdef CONFIG_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 char basebuf[IBUFSIZ]; /* buffer for top level input file */ |
| static struct parsefile *parsefile = &basepf; /* current input file */ |
| |
| |
| static int tokpushback; /* last token pushed back */ |
| #define NEOF ((union node *)&tokpushback) |
| static int parsebackquote; /* nonzero if we are inside backquotes */ |
| static int doprompt; /* if set, prompt the user */ |
| static int needprompt; /* true if interactive and at start of line */ |
| static int lasttoken; /* last token read */ |
| static char *wordtext; /* text of last word returned by readtoken */ |
| static int checkkwd; |
| static struct nodelist *backquotelist; |
| static union node *redirnode; |
| static struct heredoc *heredoc; |
| static int quoteflag; /* set if (part of) last token was quoted */ |
| static int startlinno; /* line # where last token started */ |
| |
| static union node *parsecmd(int); |
| static void fixredir(union node *, const char *, int); |
| static const char *const *findkwd(const char *); |
| static char *endofname(const char *); |
| |
| /* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| typedef void *pointer; |
| |
| static char nullstr[1]; /* zero length string */ |
| static const char spcstr[] = " "; |
| static const char snlfmt[] = "%s\n"; |
| static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; |
| static const char illnum[] = "Illegal number: %s"; |
| static const char homestr[] = "HOME"; |
| |
| #ifdef DEBUG |
| #define TRACE(param) trace param |
| #define TRACEV(param) tracev param |
| #else |
| #define TRACE(param) |
| #define TRACEV(param) |
| #endif |
| |
| #if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) |
| #define __builtin_expect(x, expected_value) (x) |
| #endif |
| |
| #define likely(x) __builtin_expect((x),1) |
| |
| |
| #define TEOF 0 |
| #define TNL 1 |
| #define TREDIR 2 |
| #define TWORD 3 |
| #define TSEMI 4 |
| #define TBACKGND 5 |
| #define TAND 6 |
| #define TOR 7 |
| #define TPIPE 8 |
| #define TLP 9 |
| #define TRP 10 |
| #define TENDCASE 11 |
| #define TENDBQUOTE 12 |
| #define TNOT 13 |
| #define TCASE 14 |
| #define TDO 15 |
| #define TDONE 16 |
| #define TELIF 17 |
| #define TELSE 18 |
| #define TESAC 19 |
| #define TFI 20 |
| #define TFOR 21 |
| #define TIF 22 |
| #define TIN 23 |
| #define TTHEN 24 |
| #define TUNTIL 25 |
| #define TWHILE 26 |
| #define TBEGIN 27 |
| #define TEND 28 |
| |
| /* first char is indicating which tokens mark the end of a list */ |
| static const char *const tokname_array[] = { |
| "\1end of file", |
| "\0newline", |
| "\0redirection", |
| "\0word", |
| "\0;", |
| "\0&", |
| "\0&&", |
| "\0||", |
| "\0|", |
| "\0(", |
| "\1)", |
| "\1;;", |
| "\1`", |
| #define KWDOFFSET 13 |
| /* the following are keywords */ |
| "\0!", |
| "\0case", |
| "\1do", |
| "\1done", |
| "\1elif", |
| "\1else", |
| "\1esac", |
| "\1fi", |
| "\0for", |
| "\0if", |
| "\0in", |
| "\1then", |
| "\0until", |
| "\0while", |
| "\0{", |
| "\1}", |
| }; |
| |
| static const char *tokname(int tok) |
| { |
| static char buf[16]; |
| |
| if (tok >= TSEMI) |
| buf[0] = '"'; |
| sprintf(buf + (tok >= TSEMI), "%s%c", |
| tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); |
| return buf; |
| } |
| |
| /* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */ |
| |
| /* |
| * Most machines require the value returned from malloc to be aligned |
| * in some way. The following macro will get this right on many machines. |
| */ |
| |
| #define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) |
| /* |
| * 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) |
| |
| /* |
| * This file was generated by the mksyntax program. |
| */ |
| |
| |
| /* 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 */ |
| |
| #ifdef CONFIG_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 |
| |
| #define is_digit(c) ((unsigned)((c) - '0') <= 9) |
| #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) |
| #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
| |
| /* |
| * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise |
| * (assuming ascii char codes, as the original implementation did) |
| */ |
| #define is_special(c) \ |
| ( (((unsigned int)c) - 33 < 32) \ |
| && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1)) |
| |
| #define digit_val(c) ((c) - '0') |
| |
| /* |
| * This file was generated by the mksyntax program. |
| */ |
| |
| #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE |
| #define USE_SIT_FUNCTION |
| #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 */ |
| |
| #ifdef CONFIG_ASH_MATH_SUPPORT |
| static const char S_I_T[][4] = { |
| #ifdef CONFIG_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] = { |
| #ifdef CONFIG_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 /* CONFIG_ASH_MATH_SUPPORT */ |
| |
| #ifdef USE_SIT_FUNCTION |
| |
| #define U_C(c) ((unsigned char)(c)) |
| |
| static int SIT(int c, int syntax) |
| { |
| static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; |
| #ifdef CONFIG_ASH_ALIAS |
| static const char syntax_index_table[] = { |
| 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[] = { |
| 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; |
| #ifdef CONFIG_ASH_ALIAS |
| if (c == PEOA) /* 2^8+1 */ |
| indx = 0; |
| else |
| #endif |
| if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK)) |
| return CCTL; |
| else { |
| s = strchr(spec_symbls, c); |
| if (s == 0 || *s == 0) |
| return CWORD; |
| indx = syntax_index_table[(s - spec_symbls)]; |
| } |
| return S_I_T[indx][syntax]; |
| } |
| |
| #else /* USE_SIT_FUNCTION */ |
| |
| #define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax] |
| |
| #ifdef CONFIG_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, |
| #ifdef CONFIG_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, |
| }; |
| |
| #endif /* USE_SIT_FUNCTION */ |
| |
| /* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */ |
| |
| |
| #define ATABSIZE 39 |
| |
| static int funcblocksize; /* size of structures in function */ |
| static int funcstringsize; /* size of strings in node */ |
| static pointer funcblock; /* block to allocate function from */ |
| static char *funcstring; /* block to allocate strings from */ |
| |
| static const short nodesize[26] = { |
| SHELL_ALIGN(sizeof (struct ncmd)), |
| SHELL_ALIGN(sizeof (struct npipe)), |
| SHELL_ALIGN(sizeof (struct nredir)), |
| SHELL_ALIGN(sizeof (struct nredir)), |
| SHELL_ALIGN(sizeof (struct nredir)), |
| SHELL_ALIGN(sizeof (struct nbinary)), |
| SHELL_ALIGN(sizeof (struct nbinary)), |
| SHELL_ALIGN(sizeof (struct nbinary)), |
| SHELL_ALIGN(sizeof (struct nif)), |
| SHELL_ALIGN(sizeof (struct nbinary)), |
| SHELL_ALIGN(sizeof (struct nbinary)), |
| SHELL_ALIGN(sizeof (struct nfor)), |
| SHELL_ALIGN(sizeof (struct ncase)), |
| SHELL_ALIGN(sizeof (struct nclist)), |
| SHELL_ALIGN(sizeof (struct narg)), |
| SHELL_ALIGN(sizeof (struct narg)), |
| SHELL_ALIGN(sizeof (struct nfile)), |
| SHELL_ALIGN(sizeof (struct nfile)), |
| SHELL_ALIGN(sizeof (struct nfile)), |
| SHELL_ALIGN(sizeof (struct nfile)), |
| SHELL_ALIGN(sizeof (struct nfile)), |
| SHELL_ALIGN(sizeof (struct ndup)), |
| SHELL_ALIGN(sizeof (struct ndup)), |
| SHELL_ALIGN(sizeof (struct nhere)), |
| SHELL_ALIGN(sizeof (struct nhere)), |
| SHELL_ALIGN(sizeof (struct nnot)), |
| }; |
| |
| |
| static void calcsize(union node *); |
| static void sizenodelist(struct nodelist *); |
| static union node *copynode(union node *); |
| static struct nodelist *copynodelist(struct nodelist *); |
| static char *nodesavestr(char *); |
| |
| |
| |
| static void evalstring(char *); |
| union node; /* BLETCH for ansi C */ |
| static void evaltree(union node *, int); |
| static void evalbackcmd(union node *, struct backcmd *); |
| |
| /* in_function returns nonzero if we are currently evaluating a function */ |
| #define in_function() funcnest |
| static int evalskip; /* set if we are skipping commands */ |
| static int skipcount; /* number of levels to skip */ |
| static int funcnest; /* depth of function calls */ |
| |
| /* reasons for skipping commands (see comment on breakcmd routine) */ |
| #define SKIPBREAK 1 |
| #define SKIPCONT 2 |
| #define SKIPFUNC 3 |
| #define SKIPFILE 4 |
| |
| /* |
| * This file was generated by the mkbuiltins program. |
| */ |
| |
| #ifdef JOBS |
| static int bgcmd(int, char **); |
| #endif |
| static int breakcmd(int, char **); |
| static int cdcmd(int, char **); |
| #ifdef CONFIG_ASH_CMDCMD |
| static int commandcmd(int, char **); |
| #endif |
| static int dotcmd(int, char **); |
| static int evalcmd(int, char **); |
| static int execcmd(int, char **); |
| static int exitcmd(int, char **); |
| static int exportcmd(int, char **); |
| static int falsecmd(int, char **); |
| #ifdef JOBS |
| static int fgcmd(int, char **); |
| #endif |
| #ifdef CONFIG_ASH_GETOPTS |
| static int getoptscmd(int, char **); |
| #endif |
| static int hashcmd(int, char **); |
| #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET |
| static int helpcmd(int argc, char **argv); |
| #endif |
| #ifdef JOBS |
| static int jobscmd(int, char **); |
| #endif |
| #ifdef CONFIG_ASH_MATH_SUPPORT |
| static int letcmd(int, char **); |
| #endif |
| static int localcmd(int, char **); |
| static int pwdcmd(int, char **); |
| static int readcmd(int, char **); |
| static int returncmd(int, char **); |
| static int setcmd(int, char **); |
| static int shiftcmd(int, char **); |
| static int timescmd(int, char **); |
| static int trapcmd(int, char **); |
| static int truecmd(int, char **); |
| static int typecmd(int, char **); |
| static int umaskcmd(int, char **); |
| static int unsetcmd(int, char **); |
| static int waitcmd(int, char **); |
| static int ulimitcmd(int, char **); |
| #ifdef JOBS |
| static int killcmd(int, char **); |
| #endif |
| |
| /* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */ |
| |
| #ifdef CONFIG_ASH_MAIL |
| static void chkmail(void); |
| static void changemail(const char *); |
| #endif |
| |
| /* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| /* values of cmdtype */ |
| #define CMDUNKNOWN -1 /* no entry in table for command */ |
| #define CMDNORMAL 0 /* command is an executable program */ |
| #define CMDFUNCTION 1 /* command is a shell function */ |
| #define CMDBUILTIN 2 /* command is a shell builtin */ |
| |
| struct builtincmd { |
| const char *name; |
| int (*builtin)(int, char **); |
| /* unsigned flags; */ |
| }; |
| |
| #ifdef CONFIG_ASH_CMDCMD |
| # ifdef JOBS |
| # ifdef CONFIG_ASH_ALIAS |
| # define COMMANDCMD (builtincmd + 7) |
| # define EXECCMD (builtincmd + 10) |
| # else |
| # define COMMANDCMD (builtincmd + 6) |
| # define EXECCMD (builtincmd + 9) |
| # endif |
| # else /* ! JOBS */ |
| # ifdef CONFIG_ASH_ALIAS |
| # define COMMANDCMD (builtincmd + 6) |
| # define EXECCMD (builtincmd + 9) |
| # else |
| # define COMMANDCMD (builtincmd + 5) |
| # define EXECCMD (builtincmd + 8) |
| # endif |
| # endif /* JOBS */ |
| #else /* ! CONFIG_ASH_CMDCMD */ |
| # ifdef JOBS |
| # ifdef CONFIG_ASH_ALIAS |
| # define EXECCMD (builtincmd + 9) |
| # else |
| # define EXECCMD (builtincmd + 8) |
| # endif |
| # else /* ! JOBS */ |
| # ifdef CONFIG_ASH_ALIAS |
| # define EXECCMD (builtincmd + 8) |
| # else |
| # define EXECCMD (builtincmd + 7) |
| # endif |
| # endif /* JOBS */ |
| #endif /* CONFIG_ASH_CMDCMD */ |
| |
| #define BUILTIN_NOSPEC "0" |
| #define BUILTIN_SPECIAL "1" |
| #define BUILTIN_REGULAR "2" |
| #define BUILTIN_SPEC_REG "3" |
| #define BUILTIN_ASSIGN "4" |
| #define BUILTIN_SPEC_ASSG "5" |
| #define BUILTIN_REG_ASSG "6" |
| #define BUILTIN_SPEC_REG_ASSG "7" |
| |
| #define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1) |
| #define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2) |
| |
| static const struct builtincmd builtincmd[] = { |
| { BUILTIN_SPEC_REG ".", dotcmd }, |
| { BUILTIN_SPEC_REG ":", truecmd }, |
| #ifdef CONFIG_ASH_ALIAS |
| { BUILTIN_REG_ASSG "alias", aliascmd }, |
| #endif |
| #ifdef JOBS |
| { BUILTIN_REGULAR "bg", bgcmd }, |
| #endif |
| { BUILTIN_SPEC_REG "break", breakcmd }, |
| { BUILTIN_REGULAR "cd", cdcmd }, |
| { BUILTIN_NOSPEC "chdir", cdcmd }, |
| #ifdef CONFIG_ASH_CMDCMD |
| { BUILTIN_REGULAR "command", commandcmd }, |
| #endif |
| { BUILTIN_SPEC_REG "continue", breakcmd }, |
| { BUILTIN_SPEC_REG "eval", evalcmd }, |
| { BUILTIN_SPEC_REG "exec", execcmd }, |
| { BUILTIN_SPEC_REG "exit", exitcmd }, |
| { BUILTIN_SPEC_REG_ASSG "export", exportcmd }, |
| { BUILTIN_REGULAR "false", falsecmd }, |
| #ifdef JOBS |
| { BUILTIN_REGULAR "fg", fgcmd }, |
| #endif |
| #ifdef CONFIG_ASH_GETOPTS |
| { BUILTIN_REGULAR "getopts", getoptscmd }, |
| #endif |
| { BUILTIN_NOSPEC "hash", hashcmd }, |
| #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET |
| { BUILTIN_NOSPEC "help", helpcmd }, |
| #endif |
| #ifdef JOBS |
| { BUILTIN_REGULAR "jobs", jobscmd }, |
| { BUILTIN_REGULAR "kill", killcmd }, |
| #endif |
| #ifdef CONFIG_ASH_MATH_SUPPORT |
| { BUILTIN_NOSPEC "let", letcmd }, |
| #endif |
| { BUILTIN_ASSIGN "local", localcmd }, |
| { BUILTIN_NOSPEC "pwd", pwdcmd }, |
| { BUILTIN_REGULAR "read", readcmd }, |
| { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd }, |
| { BUILTIN_SPEC_REG "return", returncmd }, |
| { BUILTIN_SPEC_REG "set", setcmd }, |
| { BUILTIN_SPEC_REG "shift", shiftcmd }, |
| { BUILTIN_SPEC_REG "times", timescmd }, |
| { BUILTIN_SPEC_REG "trap", trapcmd }, |
| { BUILTIN_REGULAR "true", truecmd }, |
| { BUILTIN_NOSPEC "type", typecmd }, |
| { BUILTIN_NOSPEC "ulimit", ulimitcmd }, |
| { BUILTIN_REGULAR "umask", umaskcmd }, |
| #ifdef CONFIG_ASH_ALIAS |
| { BUILTIN_REGULAR "unalias", unaliascmd }, |
| #endif |
| { BUILTIN_SPEC_REG "unset", unsetcmd }, |
| { BUILTIN_REGULAR "wait", waitcmd }, |
| }; |
| |
| #define NUMBUILTINS (sizeof (builtincmd) / sizeof (struct builtincmd) ) |
| |
| |
| |
| struct cmdentry { |
| int cmdtype; |
| union param { |
| int index; |
| const struct builtincmd *cmd; |
| struct funcnode *func; |
| } u; |
| }; |
| |
| |
| /* action to find_command() */ |
| #define DO_ERR 0x01 /* prints errors */ |
| #define DO_ABS 0x02 /* checks absolute paths */ |
| #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ |
| #define DO_ALTPATH 0x08 /* using alternate path */ |
| #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ |
| |
| static const char *pathopt; /* set by padvance */ |
| |
| static void shellexec(char **, const char *, int) |
| __attribute__((__noreturn__)); |
| static char *padvance(const char **, const char *); |
| static void find_command(char *, struct cmdentry *, int, const char *); |
| static struct builtincmd *find_builtin(const char *); |
| static void hashcd(void); |
| static void changepath(const char *); |
| static void defun(char *, union node *); |
| static void unsetfunc(const char *); |
| |
| #ifdef CONFIG_ASH_MATH_SUPPORT |
| static long dash_arith(const char *); |
| #endif |
| |
| #ifdef CONFIG_ASH_RANDOM_SUPPORT |
| static unsigned long rseed; |
| static void change_random(const char *); |
| # ifndef DYNAMIC_VAR |
| # define DYNAMIC_VAR |
| # endif |
| #endif |
| |
| /* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */ |
| |
| static void reset(void); |
| |
| /* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| /* |
| * 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 |
| |
| 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 */ |
| }; |
| |
| |
| static struct localvar *localvars; |
| |
| /* |
| * Shell variables. |
| */ |
| |
| #ifdef CONFIG_ASH_GETOPTS |
| static void getoptsreset(const char *); |
| #endif |
| |
| #ifdef CONFIG_LOCALE_SUPPORT |
| #include <locale.h> |
| static void change_lc_all(const char *value); |
| static void change_lc_ctype(const char *value); |
| #endif |
| |
| |
| #define VTABSIZE 39 |
| |
| static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin"; |
| #ifdef IFS_BROKEN |
| static const char defifsvar[] = "IFS= \t\n"; |
| #define defifs (defifsvar + 4) |
| #else |
| static const char defifs[] = " \t\n"; |
| #endif |
| |
| |
| static struct var varinit[] = { |
| #ifdef IFS_BROKEN |
| { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, |
| #else |
| { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, |
| #endif |
| |
| #ifdef CONFIG_ASH_MAIL |
| { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, |
| { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, |
| #endif |
| |
| { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, |
| { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, |
| { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, |
| { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, |
| #ifdef CONFIG_ASH_GETOPTS |
| { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, |
| #endif |
| #ifdef CONFIG_ASH_RANDOM_SUPPORT |
| {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, |
| #endif |
| #ifdef CONFIG_LOCALE_SUPPORT |
| {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, |
| {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, |
| #endif |
| #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY |
| {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, |
| #endif |
| }; |
| |
| #define vifs varinit[0] |
| #ifdef CONFIG_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] |
| #ifdef CONFIG_ASH_GETOPTS |
| #define vrandom (&voptind)[1] |
| #else |
| #define vrandom (&vps4)[1] |
| #endif |
| #define defpath (defpathvar + 5) |
| |
| /* |
| * 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) |
| |
| static void setvar(const char *, const char *, int); |
| static void setvareq(char *, int); |
| static void listsetvar(struct strlist *, int); |
| static char *lookupvar(const char *); |
| static char *bltinlookup(const char *); |
| static char **listvars(int, int, char ***); |
| #define environment() listvars(VEXPORT, VUNSET, 0) |
| static int showvars(const char *, int, int); |
| static void poplocalvars(void); |
| static int unsetvar(const char *); |
| #ifdef CONFIG_ASH_GETOPTS |
| static int setvarsafe(const char *, const char *, int); |
| #endif |
| static int varcmp(const char *, const char *); |
| static struct var **hashvar(const char *); |
| |
| |
| static inline int varequal(const char *a, const char *b) { |
| return !varcmp(a, b); |
| } |
| |
| |
| static int loopnest; /* current loop nesting level */ |
| |
| /* |
| * 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; |
| |
| extern char **environ; |
| |
| /* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */ |
| |
| |
| static void outstr(const char *, FILE *); |
| static void outcslow(int, FILE *); |
| static void flushall(void); |
| static void flusherr(void); |
| static int out1fmt(const char *, ...) |
| __attribute__((__format__(__printf__,1,2))); |
| static int fmtstr(char *, size_t, const char *, ...) |
| __attribute__((__format__(__printf__,3,4))); |
| |
| static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ |
| |
| |
| static void out1str(const char *p) |
| { |
| outstr(p, stdout); |
| } |
| |
| static void out2str(const char *p) |
| { |
| outstr(p, stderr); |
| flusherr(); |
| } |
| |
| /* |
| * Initialization code. |
| */ |
| |
| /* |
| * This routine initializes the builtin variables. |
| */ |
| |
| static inline void |
| initvar(void) |
| { |
| struct var *vp; |
| struct var *end; |
| struct var **vpp; |
| |
| /* |
| * PS1 depends on uid |
| */ |
| #if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT) |
| vps1.text = "PS1=\\w \\$ "; |
| #else |
| if (!geteuid()) |
| vps1.text = "PS1=# "; |
| #endif |
| vp = varinit; |
| end = vp + sizeof(varinit) / sizeof(varinit[0]); |
| do { |
| vpp = hashvar(vp->text); |
| vp->next = *vpp; |
| *vpp = vp; |
| } while (++vp < end); |
| } |
| |
| static inline void |
| init(void) |
| { |
| |
| /* from input.c: */ |
| { |
| basepf.nextc = basepf.buf = basebuf; |
| } |
| |
| /* from trap.c: */ |
| { |
| signal(SIGCHLD, SIG_DFL); |
| } |
| |
| /* from var.c: */ |
| { |
| char **envp; |
| char ppid[32]; |
| |
| initvar(); |
| for (envp = environ ; *envp ; envp++) { |
| if (strchr(*envp, '=')) { |
| setvareq(*envp, VEXPORT|VTEXTFIXED); |
| } |
| } |
| |
| snprintf(ppid, sizeof(ppid), "%d", (int) getppid()); |
| setvar("PPID", ppid, 0); |
| setpwd(0, 0); |
| } |
| } |
| |
| /* PEOF (the end of file marker) */ |
| |
| /* |
| * The input line number. Input.c just defines this variable, and saves |
| * and restores it when files are pushed and popped. The user of this |
| * package must set its value. |
| */ |
| |
| static int pgetc(void); |
| static int pgetc2(void); |
| static int preadbuffer(void); |
| static void pungetc(void); |
| static void pushstring(char *, void *); |
| static void popstring(void); |
| static void setinputfile(const char *, int); |
| static void setinputfd(int, int); |
| static void setinputstring(char *); |
| static void popfile(void); |
| static void popallfiles(void); |
| static void closescript(void); |
| |
| |
| /* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| |
| /* 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 int job_warning; /* user was warned about stopped jobs */ |
| #if JOBS |
| static int jobctl; /* true if doing job control */ |
| #endif |
| |
| static struct job *makejob(union node *, int); |
| static int forkshell(struct job *, union node *, int); |
| static int waitforjob(struct job *); |
| static int stoppedjobs(void); |
| |
| #if ! JOBS |
| #define setjobctl(on) /* do nothing */ |
| #else |
| static void setjobctl(int); |
| static void showjobs(FILE *, int); |
| #endif |
| |
| /* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */ |
| |
| |
| /* pid of main shell */ |
| static int rootpid; |
| /* true if we aren't a child of the main shell */ |
| static int rootshell; |
| |
| static void readcmdfile(char *); |
| static void cmdloop(int); |
| |
| /* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| |
| struct stackmark { |
| struct stack_block *stackp; |
| char *stacknxt; |
| size_t stacknleft; |
| struct stackmark *marknext; |
| }; |
| |
| /* minimum size of a block */ |
| #define MINSIZE SHELL_ALIGN(504) |
| |
| struct stack_block { |
| struct stack_block *prev; |
| char space[MINSIZE]; |
| }; |
| |
| 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; |
| |
| |
| static pointer ckmalloc(size_t); |
| static pointer ckrealloc(pointer, size_t); |
| static char *savestr(const char *); |
| static pointer stalloc(size_t); |
| static void stunalloc(pointer); |
| static void setstackmark(struct stackmark *); |
| static void popstackmark(struct stackmark *); |
| static void growstackblock(void); |
| static void *growstackstr(void); |
| static char *makestrspace(size_t, char *); |
| static char *stnputs(const char *, size_t, char *); |
| static char *stputs(const char *, char *); |
| |
| |
| static inline char *_STPUTC(char c, char *p) { |
| if (p == sstrend) |
| p = growstackstr(); |
| *p++ = c; |
| return p; |
| } |
| |
| #define stackblock() ((void *)stacknxt) |
| #define stackblocksize() stacknleft |
| #define STARTSTACKSTR(p) ((p) = stackblock()) |
| #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) |
| #define CHECKSTRSPACE(n, p) \ |
| ({ \ |
| char *q = (p); \ |
| size_t l = (n); \ |
| size_t m = sstrend - q; \ |
| if (l > m) \ |
| (p) = makestrspace(l, q); \ |
| 0; \ |
| }) |
| #define USTPUTC(c, p) (*p++ = (c)) |
| #define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\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) |
| |
| #define ckfree(p) free((pointer)(p)) |
| |
| /* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */ |
| |
| |
| #define DOLATSTRLEN 4 |
| |
| static char *prefix(const char *, const char *); |
| static int number(const char *); |
| static int is_number(const char *); |
| static char *single_quote(const char *); |
| static char *sstrdup(const char *); |
| |
| #define equal(s1, s2) (strcmp(s1, s2) == 0) |
| #define scopy(s1, s2) ((void)strcpy(s2, s1)) |
| |
| /* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| struct shparam { |
| int nparam; /* # of positional parameters (without $0) */ |
| unsigned char malloc; /* if parameter list dynamically allocated */ |
| char **p; /* parameter list */ |
| #ifdef CONFIG_ASH_GETOPTS |
| int optind; /* next parameter to be processed by getopts */ |
| int optoff; /* used by getopts */ |
| #endif |
| }; |
| |
| |
| #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 qflag optlist[13] |
| |
| #ifdef DEBUG |
| #define nolog optlist[14] |
| #define debug optlist[15] |
| #define NOPTS 16 |
| #else |
| #define NOPTS 14 |
| #endif |
| |
| /* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| |
| static const char *const optletters_optnames[NOPTS] = { |
| "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", |
| "q" "quietprofile", |
| #ifdef DEBUG |
| "\0" "nolog", |
| "\0" "debug", |
| #endif |
| }; |
| |
| #define optletters(n) optletters_optnames[(n)][0] |
| #define optnames(n) (&optletters_optnames[(n)][1]) |
| |
| |
| static char optlist[NOPTS]; |
| |
| |
| static char *arg0; /* value of $0 */ |
| static struct shparam shellparam; /* $@ current positional parameters */ |
| static char **argptr; /* argument list for builtin commands */ |
| static char *optionarg; /* set by nextopt (like getopt) */ |
| static char *optptr; /* used by nextopt */ |
| |
| static char *minusc; /* argument to -c option */ |
| |
| |
| static void procargs(int, char **); |
| static void optschanged(void); |
| static void setparam(char **); |
| static void freeparam(volatile struct shparam *); |
| static int shiftcmd(int, char **); |
| static int setcmd(int, char **); |
| static int nextopt(const char *); |
| |
| /* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */ |
| |
| /* flags passed to redirect */ |
| #define REDIR_PUSH 01 /* save previous values of file descriptors */ |
| #define REDIR_SAVEFD2 03 /* set preverrout */ |
| |
| union node; |
| static void redirect(union node *, int); |
| static void popredir(int); |
| static void clearredir(int); |
| static int copyfd(int, int); |
| static int redirectsafe(union node *, int); |
| |
| /* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */ |
| |
| |
| #ifdef DEBUG |
| static void showtree(union node *); |
| static void trace(const char *, ...); |
| static void tracev(const char *, va_list); |
| static void trargs(char **); |
| static void trputc(int); |
| static void trputs(const char *); |
| static void opentrace(void); |
| #endif |
| |
| /* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */ |
| |
| |
| /* trap handler commands */ |
| static char *trap[NSIG]; |
| /* current value of signal */ |
| static char sigmode[NSIG - 1]; |
| /* indicates specified signal received */ |
| static char gotsig[NSIG - 1]; |
| |
| static void clear_traps(void); |
| static void setsignal(int); |
| static void ignoresig(int); |
| static void onsig(int); |
| static void dotrap(void); |
| static void setinteractive(int); |
| static void exitshell(void) __attribute__((__noreturn__)); |
| static int decode_signal(const char *, int); |
| |
| /* |
| * This routine is called when an error or an interrupt occurs in an |
| * interactive shell and control is returned to the main command loop. |
| */ |
| |
| static void |
| reset(void) |
| { |
| /* from eval.c: */ |
| { |
| evalskip = 0; |
| loopnest = 0; |
| funcnest = 0; |
| } |
| |
| /* from input.c: */ |
| { |
| parselleft = parsenleft = 0; /* clear input buffer */ |
| popallfiles(); |
| } |
| |
| /* from parser.c: */ |
| { |
| tokpushback = 0; |
| checkkwd = 0; |
| } |
| |
| /* from redir.c: */ |
| { |
| clearredir(0); |
| } |
| |
| } |
| |
| #ifdef CONFIG_ASH_ALIAS |
| static struct alias *atab[ATABSIZE]; |
| |
| static void setalias(const char *, const char *); |
| static struct alias *freealias(struct alias *); |
| static struct alias **__lookupalias(const char *); |
| |
| static void |
| setalias(const char *name, const char *val) |
| { |
| struct alias *ap, **app; |
| |
| app = __lookupalias(name); |
| ap = *app; |
| INTOFF; |
| if (ap) { |
| if (!(ap->flag & ALIASINUSE)) { |
| ckfree(ap->val); |
| } |
| ap->val = savestr(val); |
| ap->flag &= ~ALIASDEAD; |
| } else { |
| /* not found */ |
| ap = ckmalloc(sizeof (struct alias)); |
| ap->name = savestr(name); |
| ap->val = savestr(val); |
| ap->flag = 0; |
| ap->next = 0; |
| *app = ap; |
| } |
| INTON; |
| } |
| |
| static int |
| unalias(const char *name) |
| { |
| struct alias **app; |
| |
| app = __lookupalias(name); |
| |
| if (*app) { |
| INTOFF; |
| *app = freealias(*app); |
| INTON; |
| return (0); |
| } |
| |
| return (1); |
| } |
| |
| static void |
| rmaliases(void) |
| { |
| struct alias *ap, **app; |
| int i; |
| |
| INTOFF; |
| for (i = 0; i < ATABSIZE; i++) { |
| app = &atab[i]; |
| for (ap = *app; ap; ap = *app) { |
| *app = freealias(*app); |
| if (ap == *app) { |
| app = &ap->next; |
| } |
| } |
| } |
| INTON; |
| } |
| |
| static struct alias * |
| lookupalias(const char *name, int check) |
| { |
| struct alias *ap = *__lookupalias(name); |
| |
| if (check && ap && (ap->flag & ALIASINUSE)) |
| return (NULL); |
| return (ap); |
| } |
| |
| /* |
| * 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) { |
| if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ |
| if ((ap = *__lookupalias(n)) == 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); |
| } |
| |
| static struct alias * |
| freealias(struct alias *ap) { |
| struct alias *next; |
| |
| if (ap->flag & ALIASINUSE) { |
| ap->flag |= ALIASDEAD; |
| return ap; |
| } |
| |
| next = ap->next; |
| ckfree(ap->name); |
| ckfree(ap->val); |
| ckfree(ap); |
| return next; |
| } |
| |
| static void |
| printalias(const struct alias *ap) { |
| out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); |
| } |
| |
| 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 (equal(name, (*app)->name)) { |
| break; |
| } |
| } |
| |
| return app; |
| } |
| #endif /* CONFIG_ASH_ALIAS */ |
| |
| |
| /* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */ |
| |
| /* |
| * The cd and pwd commands. |
| */ |
| |
| #define CD_PHYSICAL 1 |
| #define CD_PRINT 2 |
| |
| static int docd(const char *, int); |
| static int cdopt(void); |
| |
| 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; |
| } |
| |
| 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 (dest[0] == '-' && dest[1] == '\0') { |
| dest = bltinlookup("OLDPWD"); |
| flags |= CD_PRINT; |
| goto step7; |
| } |
| 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 = "."; |
| if (!(path = bltinlookup("CDPATH"))) { |
| 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); |
| error("can't cd to %s", dest); |
| /* NOTREACHED */ |
| out: |
| if (flags & CD_PRINT) |
| out1fmt(snlfmt, curdir); |
| return 0; |
| } |
| |
| |
| /* |
| * Update curdir (the name of the current directory) in response to a |
| * cd command. |
| */ |
| |
| static inline const char * |
| updatepwd(const char *dir) |
| { |
| char *new; |
| char *p; |
| char *cdcomppath; |
| const char *lim; |
| |
| cdcomppath = sstrdup(dir); |
| STARTSTACKSTR(new); |
| if (*dir != '/') { |
| if (curdir == nullstr) |
| return 0; |
| new = stputs(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; |
| } else if (p[1] == '\0') |
| break; |
| /* fall through */ |
| default: |
| new = stputs(p, new); |
| USTPUTC('/', new); |
| } |
| p = strtok(0, "/"); |
| } |
| if (new > lim) |
| STUNPUTC(new); |
| *new = 0; |
| return stackblock(); |
| } |
| |
| /* |
| * 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)); |
| |
| INTOFF; |
| if (!(flags & CD_PHYSICAL)) { |
| dir = updatepwd(dest); |
| if (dir) |
| dest = dir; |
| } |
| err = chdir(dest); |
| if (err) |
| goto out; |
| setpwd(dir, 1); |
| hashcd(); |
| out: |
| INTON; |
| return err; |
| } |
| |
| /* |
| * Find out what the current directory is. If we already know the current |
| * directory, this routine returns immediately. |
| */ |
| static inline char * |
| getpwd(void) |
| { |
| char *dir = getcwd(0, 0); |
| return dir ? dir : nullstr; |
| } |
| |
| 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; |
| } |
| |
| static void |
| setpwd(const char *val, int setold) |
| { |
| char *oldcur, *dir; |
| |
| oldcur = dir = curdir; |
| |
| if (setold) { |
| setvar("OLDPWD", oldcur, VEXPORT); |
| } |
| INTOFF; |
| 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 = savestr(val); |
| if (oldcur != dir && oldcur != nullstr) { |
| free(oldcur); |
| } |
| curdir = dir; |
| INTON; |
| setvar("PWD", dir, VEXPORT); |
| } |
| |
| /* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */ |
| |
| /* |
| * Errors and exceptions. |
| */ |
| |
| /* |
| * Code to handle exceptions in C. |
| */ |
| |
| |
| |
| static void exverror(int, const char *, va_list) |
| __attribute__((__noreturn__)); |
| |
| /* |
| * 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 |
| exraise(int e) |
| { |
| #ifdef DEBUG |
| if (handler == NULL) |
| abort(); |
| #endif |
| INTOFF; |
| |
| exception = e; |
| longjmp(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 INTOFF macro. (The test for iflag is just |
| * defensive programming.) |
| */ |
| |
| static void |
| onint(void) { |
| int i; |
| |
| intpending = 0; |
| sigsetmask(0); |
| i = EXSIG; |
| if (gotsig[SIGINT - 1] && !trap[SIGINT]) { |
| if (!(rootshell && iflag)) { |
| signal(SIGINT, SIG_DFL); |
| raise(SIGINT); |
| } |
| i = EXINT; |
| } |
| exraise(i); |
| /* NOTREACHED */ |
| } |
| |
| static void |
| exvwarning(const char *msg, va_list ap) |
| { |
| FILE *errs; |
| const char *name; |
| const char *fmt; |
| |
| errs = stderr; |
| name = arg0; |
| fmt = "%s: "; |
| if (commandname) { |
| name = commandname; |
| fmt = "%s: %d: "; |
| } |
| fprintf(errs, fmt, name, startlinno); |
| vfprintf(errs, msg, ap); |
| outcslow('\n', errs); |
| } |
| |
| /* |
| * 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 |
| exverror(int cond, const char *msg, va_list ap) |
| { |
| #ifdef DEBUG |
| if (msg) { |
| TRACE(("exverror(%d, \"", cond)); |
| TRACEV((msg, ap)); |
| TRACE(("\") pid=%d\n", getpid())); |
| } else |
| TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); |
| if (msg) |
| #endif |
| exvwarning(msg, ap); |
| |
| flushall(); |
| exraise(cond); |
| /* NOTREACHED */ |
| } |
| |
| |
| static void |
| error(const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| exverror(EXERROR, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| |
| static void |
| exerror(int cond, const char *msg, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, msg); |
| exverror(cond, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| /* |
| * error/warning routines for external builtins |
| */ |
| |
| static void |
| sh_warnx(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| exvwarning(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); |
| } |
| |
| |
| /* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */ |
| |
| /* |
| * Evaluate a command. |
| */ |
| |
| /* flags in argument to evaltree */ |
| #define EV_EXIT 01 /* exit after evaluating tree */ |
| #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
| #define EV_BACKCMD 04 /* command executing within back quotes */ |
| |
| |
| static void evalloop(union node *, int); |
| static void evalfor(union node *, int); |
| static void evalcase(union node *, int); |
| static void evalsubshell(union node *, int); |
| static void expredir(union node *); |
| static void evalpipe(union node *, int); |
| static void evalcommand(union node *, int); |
| static int evalbltin(const struct builtincmd *, int, char **); |
| static int evalfun(struct funcnode *, int, char **, int); |
| static void prehash(union node *); |
| static int bltincmd(int, char **); |
| |
| |
| static const struct builtincmd bltin = { |
| "\0\0", bltincmd |
| }; |
| |
| |
| /* |
| * Called to reset things after an exception. |
| */ |
| |
| /* |
| * The eval commmand. |
| */ |
| |
| static int |
| evalcmd(int argc, char **argv) |
| { |
| char *p; |
| char *concat; |
| char **ap; |
| |
| if (argc > 1) { |
| p = argv[1]; |
| if (argc > 2) { |
| STARTSTACKSTR(concat); |
| ap = argv + 2; |
| for (;;) { |
| concat = stputs(p, concat); |
| if ((p = *ap++) == NULL) |
| break; |
| STPUTC(' ', concat); |
| } |
| STPUTC('\0', concat); |
| p = grabstackstr(concat); |
| } |
| evalstring(p); |
| } |
| return exitstatus; |
| } |
| |
| |
| /* |
| * Execute a command or commands contained in a string. |
| */ |
| |
| static void |
| evalstring(char *s) |
| { |
| union node *n; |
| struct stackmark smark; |
| |
| setstackmark(&smark); |
| setinputstring(s); |
| |
| while ((n = parsecmd(0)) != NEOF) { |
| evaltree(n, 0); |
| popstackmark(&smark); |
| if (evalskip) |
| break; |
| } |
| popfile(); |
| popstackmark(&smark); |
| } |
| |
| |
| |
| /* |
| * Evaluate a parse tree. The value is left in the global variable |
| * exitstatus. |
| */ |
| |
| static void |
| evaltree(union node *n, int flags) |
| { |
| int checkexit = 0; |
| void (*evalfn)(union node *, int); |
| unsigned isor; |
| int status; |
| if (n == NULL) { |
| TRACE(("evaltree(NULL) called\n")); |
| goto out; |
| } |
| TRACE(("pid %d, evaltree(%p: %d, %d) called\n", |
| getpid(), n, n->type, flags)); |
| switch (n->type) { |
| default: |
| #ifdef DEBUG |
| out1fmt("Node type = %d\n", n->type); |
| fflush(stdout); |
| break; |
| #endif |
| case NNOT: |
| evaltree(n->nnot.com, EV_TESTED); |
| status = !exitstatus; |
| goto setstatus; |
| case NREDIR: |
| expredir(n->nredir.redirect); |
| status = redirectsafe(n->nredir.redirect, REDIR_PUSH); |
| if (!status) { |
| evaltree(n->nredir.n, flags & EV_TESTED); |
| status = exitstatus; |
| } |
| popredir(0); |
| goto setstatus; |
| case NCMD: |
| evalfn = evalcommand; |
| checkexit: |
| if (eflag && !(flags & EV_TESTED)) |
| checkexit = ~0; |
| goto calleval; |
| case NFOR: |
| evalfn = evalfor; |
| goto calleval; |
| case NWHILE: |
| case NUNTIL: |
| evalfn = evalloop; |
| goto calleval; |
| case NSUBSHELL: |
| case NBACKGND: |
| evalfn = evalsubshell; |
| goto calleval; |
| case NPIPE: |
| evalfn = evalpipe; |
| goto checkexit; |
| case NCASE: |
| evalfn = evalcase; |
| goto calleval; |
| case NAND: |
| case NOR: |
| case NSEMI: |
| #if NAND + 1 != NOR |
| #error NAND + 1 != NOR |
| #endif |
| #if NOR + 1 != NSEMI |
| #error NOR + 1 != NSEMI |
| #endif |
| isor = n->type - NAND; |
| evaltree( |
| n->nbinary.ch1, |
| (flags | ((isor >> 1) - 1)) & EV_TESTED |
| ); |
| if (!exitstatus == isor) |
| break; |
| if (!evalskip) { |
| n = n->nbinary.ch2; |
| evaln: |
| evalfn = evaltree; |
| calleval: |
| evalfn(n, flags); |
| break; |
| } |
| break; |
| case NIF: |
| evaltree(n->nif.test, EV_TESTED); |
| if (evalskip) |
| break; |
| if (exitstatus == 0) { |
| n = n->nif.ifpart; |
| goto evaln; |
| } else if (n->nif.elsepart) { |
| n = n->nif.elsepart; |
| goto evaln; |
| } |
| goto success; |
| case NDEFUN: |
| defun(n->narg.text, n->narg.next); |
| success: |
| status = 0; |
| setstatus: |
| exitstatus = status; |
| break; |
| } |
| out: |
| if (pendingsigs) |
| dotrap(); |
| if (flags & EV_EXIT || checkexit & exitstatus) |
| exraise(EXEXIT); |
| } |
| |
| |
| #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) |
| static |
| #endif |
| void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); |
| |
| |
| static void |
| evalloop(union node *n, int flags) |
| { |
| int status; |
| |
| loopnest++; |
| status = 0; |
| flags &= EV_TESTED; |
| for (;;) { |
| int i; |
| |
| evaltree(n->nbinary.ch1, EV_TESTED); |
| if (evalskip) { |
| skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { |
| evalskip = 0; |
| continue; |
| } |
| if (evalskip == SKIPBREAK && --skipcount <= 0) |
| evalskip = 0; |
| break; |
| } |
| i = exitstatus; |
| if (n->type != NWHILE) |
| i = !i; |
| if (i != 0) |
| break; |
| evaltree(n->nbinary.ch2, flags); |
| status = exitstatus; |
| if (evalskip) |
| goto skipping; |
| } |
| loopnest--; |
| exitstatus = status; |
| } |
| |
| |
| |
| static void |
| evalfor(union node *n, int flags) |
| { |
| struct arglist arglist; |
| union node *argp; |
| struct strlist *sp; |
| struct stackmark smark; |
| |
| setstackmark(&smark); |
| arglist.lastp = &arglist.list; |
| for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { |
| expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); |
| /* XXX */ |
| if (evalskip) |
| goto out; |
| } |
| *arglist.lastp = NULL; |
| |
| exitstatus = 0; |
| loopnest++; |
| flags &= EV_TESTED; |
| for (sp = arglist.list ; sp ; sp = sp->next) { |
| setvar(n->nfor.var, sp->text, 0); |
| evaltree(n->nfor.body, flags); |
| if (evalskip) { |
| if (evalskip == SKIPCONT && --skipcount <= 0) { |
| evalskip = 0; |
| continue; |
| } |
| if (evalskip == SKIPBREAK && --skipcount <= 0) |
| evalskip = 0; |
| break; |
| } |
| } |
| loopnest--; |
| out: |
| popstackmark(&smark); |
| } |
| |
| |
| |
| static void |
| evalcase(union node *n, int flags) |
| { |
| union node *cp; |
| union node *patp; |
| struct arglist arglist; |
| struct stackmark smark; |
| |
| setstackmark(&smark); |
| arglist.lastp = &arglist.list; |
| expandarg(n->ncase.expr, &arglist, EXP_TILDE); |
| exitstatus = 0; |
| for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { |
| for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { |
| if (casematch(patp, arglist.list->text)) { |
| if (evalskip == 0) { |
| evaltree(cp->nclist.body, flags); |
| } |
| goto out; |
| } |
| } |
| } |
| out: |
| popstackmark(&smark); |
| } |
| |
| |
| |
| /* |
| * Kick off a subshell to evaluate a tree. |
| */ |
| |
| static void |
| evalsubshell(union node *n, int flags) |
| { |
| struct job *jp; |
| int backgnd = (n->type == NBACKGND); |
| int status; |
| |
| expredir(n->nredir.redirect); |
| if (!backgnd && flags & EV_EXIT && !trap[0]) |
| goto nofork; |
| INTOFF; |
| jp = makejob(n, 1); |
| if (forkshell(jp, n, backgnd) == 0) { |
| INTON; |
| flags |= EV_EXIT; |
| if (backgnd) |
| flags &=~ EV_TESTED; |
| nofork: |
| redirect(n->nredir.redirect, 0); |
| evaltreenr(n->nredir.n, flags); |
| /* never returns */ |
| } |
| status = 0; |
| if (! backgnd) |
| status = waitforjob(jp); |
| exitstatus = status; |
| INTON; |
| } |
| |
| |
| |
| /* |
| * Compute the names of the files in a redirection list. |
| */ |
| |
| static void |
| expredir(union node *n) |
| { |
| union node *redir; |
| |
| for (redir = n ; redir ; redir = redir->nfile.next) { |
| struct arglist fn; |
| fn.lastp = &fn.list; |
| switch (redir->type) { |
| case NFROMTO: |
| case NFROM: |
| case NTO: |
| case NCLOBBER: |
| case NAPPEND: |
| expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); |
| redir->nfile.expfname = fn.list->text; |
| break; |
| case NFROMFD: |
| case NTOFD: |
| if (redir->ndup.vname) { |
| expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); |
| fixredir(redir, fn.list->text, 1); |
| } |
| break; |
| } |
| } |
| } |
| |
| |
| |
| /* |
| * Evaluate a pipeline. All the processes in the pipeline are children |
| * of the process creating the pipeline. (This differs from some versions |
| * of the shell, which make the last process in a pipeline the parent |
| * of all the rest.) |
| */ |
| |
| static void |
| evalpipe(union node *n, int flags) |
| { |
| struct job *jp; |
| struct nodelist *lp; |
| int pipelen; |
| int prevfd; |
| int pip[2]; |
| |
| TRACE(("evalpipe(0x%lx) called\n", (long)n)); |
| pipelen = 0; |
| for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) |
| pipelen++; |
| flags |= EV_EXIT; |
| INTOFF; |
| jp = makejob(n, pipelen); |
| prevfd = -1; |
| for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { |
| prehash(lp->n); |
| pip[1] = -1; |
| if (lp->next) { |
| if (pipe(pip) < 0) { |
| close(prevfd); |
| error("Pipe call failed"); |
| } |
| } |
| if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { |
| INTON; |
| if (pip[1] >= 0) { |
| close(pip[0]); |
| } |
| if (prevfd > 0) { |
| dup2(prevfd, 0); |
| close(prevfd); |
| } |
| if (pip[1] > 1) { |
| dup2(pip[1], 1); |
| close(pip[1]); |
| } |
| evaltreenr(lp->n, flags); |
| /* never returns */ |
| } |
| if (prevfd >= 0) |
| close(prevfd); |
| prevfd = pip[0]; |
| close(pip[1]); |
| } |
| if (n->npipe.backgnd == 0) { |
| exitstatus = waitforjob(jp); |
| TRACE(("evalpipe: job done exit status %d\n", exitstatus)); |
| } |
| INTON; |
| } |
| |
| |
| |
| /* |
| * Execute a command inside back quotes. If it's a builtin command, we |
| * want to save its output in a block obtained from malloc. Otherwise |
| * we fork off a subprocess and get the output of the command via a pipe. |
| * Should be called with interrupts off. |
| */ |
| |
| static void |
| evalbackcmd(union node *n, struct backcmd *result) |
| { |
| int saveherefd; |
| |
| result->fd = -1; |
| result->buf = NULL; |
| result->nleft = 0; |
| result->jp = NULL; |
| if (n == NULL) { |
| goto out; |
| } |
| |
| saveherefd = herefd; |
| herefd = -1; |
| |
| { |
| int pip[2]; |
| struct job *jp; |
| |
| if (pipe(pip) < 0) |
| error("Pipe call failed"); |
| jp = makejob(n, 1); |
| if (forkshell(jp, n, FORK_NOJOB) == 0) { |
| FORCEINTON; |
| close(pip[0]); |
| if (pip[1] != 1) { |
| close(1); |
| copyfd(pip[1], 1); |
| close(pip[1]); |
| } |
| eflag = 0; |
| evaltreenr(n, EV_EXIT); |
| /* NOTREACHED */ |
| } |
| close(pip[1]); |
| result->fd = pip[0]; |
| result->jp = jp; |
| } |
| herefd = saveherefd; |
| out: |
| TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", |
| result->fd, result->buf, result->nleft, result->jp)); |
| } |
| |
| #ifdef CONFIG_ASH_CMDCMD |
| static inline char ** |
| parse_command_args(char **argv, const char **path) |
| { |
| char *cp, c; |
| |
| for (;;) { |
| cp = *++argv; |
| if (!cp) |
| return 0; |
| if (*cp++ != '-') |
| break; |
| if (!(c = *cp++)) |
| break; |
| if (c == '-' && !*cp) { |
| argv++; |
| break; |
| } |
| do { |
| switch (c) { |
| case 'p': |
| *path = defpath; |
| break; |
| |