1 /* vi: set sw=4 ts=4: */
3 * ash shell port for busybox
5 * Copyright (c) 1989, 1991, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
8 * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
9 * was re-ported from NetBSD and debianized.
12 * This code is derived from software contributed to Berkeley by
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * Original BSD copyright notice is retained at the end of this file.
33 * rewrite arith.y to micro stack based cryptic algorithm by
34 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
36 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2003 to be
37 * used in busybox and size optimizations,
38 * support locale, rewrited arith (see notes to this)
44 * The follow should be set to reflect the type of system you have:
45 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
46 * define SYSV if you are running under System V.
47 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
48 * define DEBUG=2 to compile in and turn on debugging.
50 * When debugging is on, debugging info will be written to ./trace and
51 * a quit signal will generate a core dump.
64 #include <sys/types.h>
65 #include <sys/cdefs.h>
66 #include <sys/ioctl.h>
67 #include <sys/param.h>
68 #include <sys/resource.h>
98 #ifdef CONFIG_ASH_JOB_CONTROL
112 static int *dash_errno;
114 #define errno (*dash_errno)
117 #if defined(__uClinux__)
118 #error "Do not even bother, ash will not run on uClinux"
122 #define _DIAGASSERT(assert_expr) assert(assert_expr)
124 #define _DIAGASSERT(assert_expr)
128 #ifdef CONFIG_ASH_ALIAS
129 /* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */
141 static struct alias *lookupalias(const char *, int);
142 static int aliascmd(int, char **);
143 static int unaliascmd(int, char **);
144 static void rmaliases(void);
145 static int unalias(const char *);
146 static void printalias(const struct alias *);
149 /* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */
152 static void setpwd(const char *, int);
154 /* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */
158 * Types of operations (passed to the errmsg routine).
162 static const char not_found_msg[] = "%s: not found";
165 #define E_OPEN "No such file" /* opening a file */
166 #define E_CREAT "Directory nonexistent" /* creating a file */
167 #define E_EXEC not_found_msg+4 /* executing a program */
170 * We enclose jmp_buf in a structure so that we can declare pointers to
171 * jump locations. The global variable handler contains the location to
172 * jump to when an exception occurs, and the global variable exception
173 * contains a code identifying the exeception. To implement nested
174 * exception handlers, the user should save the value of handler on entry
175 * to an inner scope, set handler to point to a jmploc structure for the
176 * inner scope, and restore handler on exit from the scope.
183 static struct jmploc *handler;
184 static int exception;
185 static volatile int suppressint;
186 static volatile sig_atomic_t intpending;
188 static int exerrno; /* Last exec error, error for EXEXEC */
191 #define EXINT 0 /* SIGINT received */
192 #define EXERROR 1 /* a generic error */
193 #define EXSHELLPROC 2 /* execute a shell procedure */
194 #define EXEXEC 3 /* command execution failed */
195 #define EXEXIT 4 /* exit the shell */
196 #define EXSIG 5 /* trapped signal in wait(1) */
199 /* do we generate EXSIG events */
201 /* last pending signal */
202 static volatile sig_atomic_t pendingsigs;
205 * These macros allow the user to suspend the handling of interrupt signals
206 * over a period of time. This is similar to SIGHOLD to or sigblock, but
207 * much more efficient and portable. (But hacking the kernel is so much
208 * more fun than worrying about efficiency and portability. :-))
211 #define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
218 #define SAVEINT(v) ((v) = suppressint)
219 #define RESTOREINT(v) \
222 if ((suppressint = (v)) == 0 && intpending) onint(); \
233 /* EXSIG is turned off by evalbltin(). */
236 static void exraise(int) __attribute__((__noreturn__));
237 static void onint(void) __attribute__((__noreturn__));
239 static void error(const char *, ...) __attribute__((__noreturn__));
240 static void exerror(int, const char *, ...) __attribute__((__noreturn__));
242 static void sh_warnx(const char *, ...);
244 #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
247 if (--suppressint == 0 && intpending) {
251 #define INTON inton()
252 static void forceinton(void)
258 #define FORCEINTON forceinton()
263 if (--suppressint == 0 && intpending) onint(); \
270 if (intpending) onint(); \
273 #endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
276 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
277 * so we use _setjmp instead.
280 #if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
281 #define setjmp(jmploc) _setjmp(jmploc)
282 #define longjmp(jmploc, val) _longjmp(jmploc, val)
285 /* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */
288 struct strlist *next;
294 struct strlist *list;
295 struct strlist **lastp;
301 #define EXP_FULL 0x1 /* perform word splitting & file globbing */
302 #define EXP_TILDE 0x2 /* do normal tilde expansion */
303 #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
304 #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
305 #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
306 #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
307 #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
308 #define EXP_WORD 0x80 /* expand word in parameter expansion */
309 #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
313 static void expandarg(union node *, struct arglist *, int);
314 #define rmescapes(p) _rmescapes((p), 0)
315 static char *_rmescapes(char *, int);
316 static int casematch(union node *, char *);
318 #ifdef CONFIG_ASH_MATH_SUPPORT
319 static void expari(int);
322 /* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */
324 static char *commandname; /* currently executing command */
325 static struct strlist *cmdenviron; /* environment for builtin command */
326 static int exitstatus; /* exit status of last command */
327 static int back_exitstatus; /* exit status of backquoted command */
330 struct backcmd { /* result of evalbackcmd */
331 int fd; /* file descriptor to read from */
332 char *buf; /* buffer */
333 int nleft; /* number of chars in buffer */
334 struct job *jp; /* job structure for command */
338 * This file was generated by the mknodes program.
374 union node *redirect;
381 struct nodelist *cmdlist;
388 union node *redirect;
403 union node *elsepart;
434 struct nodelist *backquote;
474 struct nredir nredir;
475 struct nbinary nbinary;
479 struct nclist nclist;
489 struct nodelist *next;
500 static void freefunc(struct funcnode *);
501 /* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */
503 /* control characters in argument strings */
504 #define CTL_FIRST '\201' /* first 'special' character */
505 #define CTLESC '\201' /* escape next character */
506 #define CTLVAR '\202' /* variable defn */
507 #define CTLENDVAR '\203'
508 #define CTLBACKQ '\204'
509 #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
510 /* CTLBACKQ | CTLQUOTE == '\205' */
511 #define CTLARI '\206' /* arithmetic expression */
512 #define CTLENDARI '\207'
513 #define CTLQUOTEMARK '\210'
514 #define CTL_LAST '\210' /* last 'special' character */
516 /* variable substitution byte (follows CTLVAR) */
517 #define VSTYPE 0x0f /* type of variable substitution */
518 #define VSNUL 0x10 /* colon--treat the empty string as unset */
519 #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
521 /* values of VSTYPE field */
522 #define VSNORMAL 0x1 /* normal variable: $var or ${var} */
523 #define VSMINUS 0x2 /* ${var-text} */
524 #define VSPLUS 0x3 /* ${var+text} */
525 #define VSQUESTION 0x4 /* ${var?message} */
526 #define VSASSIGN 0x5 /* ${var=text} */
527 #define VSTRIMRIGHT 0x6 /* ${var%pattern} */
528 #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
529 #define VSTRIMLEFT 0x8 /* ${var#pattern} */
530 #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
531 #define VSLENGTH 0xa /* ${#var} */
533 /* values of checkkwd variable */
538 #define IBUFSIZ (BUFSIZ + 1)
541 * NEOF is returned by parsecmd when it encounters an end of file. It
542 * must be distinct from NULL, so we use the address of a variable that
543 * happens to be handy.
545 static int plinno = 1; /* input line number */
547 /* number of characters left in input buffer */
548 static int parsenleft; /* copy of parsefile->nleft */
549 static int parselleft; /* copy of parsefile->lleft */
551 /* next character in input buffer */
552 static char *parsenextc; /* copy of parsefile->nextc */
553 static struct parsefile basepf; /* top level input file */
554 static char basebuf[IBUFSIZ]; /* buffer for top level input file */
555 static struct parsefile *parsefile = &basepf; /* current input file */
558 static int tokpushback; /* last token pushed back */
559 #define NEOF ((union node *)&tokpushback)
560 static int parsebackquote; /* nonzero if we are inside backquotes */
561 static int doprompt; /* if set, prompt the user */
562 static int needprompt; /* true if interactive and at start of line */
563 static int lasttoken; /* last token read */
564 static char *wordtext; /* text of last word returned by readtoken */
566 static struct nodelist *backquotelist;
567 static union node *redirnode;
568 static struct heredoc *heredoc;
569 static int quoteflag; /* set if (part of) last token was quoted */
570 static int startlinno; /* line # where last token started */
572 static union node *parsecmd(int);
573 static void fixredir(union node *, const char *, int);
574 static const char *const *findkwd(const char *);
575 static char *endofname(const char *);
577 /* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
579 typedef void *pointer;
581 static char nullstr[1]; /* zero length string */
582 static const char spcstr[] = " ";
583 static const char snlfmt[] = "%s\n";
584 static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
585 static const char illnum[] = "Illegal number: %s";
586 static const char homestr[] = "HOME";
589 #define TRACE(param) trace param
590 #define TRACEV(param) tracev param
593 #define TRACEV(param)
596 #if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
597 #define __builtin_expect(x, expected_value) (x)
600 #define likely(x) __builtin_expect((x),1)
615 #define TENDBQUOTE 12
633 /* first char is indicating which tokens mark the end of a list */
634 static const char *const tokname_array[] = {
649 /* the following are keywords */
668 static const char *tokname(int tok)
674 sprintf(buf + (tok >= TSEMI), "%s%c",
675 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
679 /* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */
682 * Most machines require the value returned from malloc to be aligned
683 * in some way. The following macro will get this right on many machines.
686 #define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
688 * It appears that grabstackstr() will barf with such alignments
689 * because stalloc() will return a string allocated in a new stackblock.
691 #define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
694 * This file was generated by the mksyntax program.
699 #define CWORD 0 /* character is nothing special */
700 #define CNL 1 /* newline character */
701 #define CBACK 2 /* a backslash character */
702 #define CSQUOTE 3 /* single quote */
703 #define CDQUOTE 4 /* double quote */
704 #define CENDQUOTE 5 /* a terminating quote */
705 #define CBQUOTE 6 /* backwards single quote */
706 #define CVAR 7 /* a dollar sign */
707 #define CENDVAR 8 /* a '}' character */
708 #define CLP 9 /* a left paren in arithmetic */
709 #define CRP 10 /* a right paren in arithmetic */
710 #define CENDFILE 11 /* end of file */
711 #define CCTL 12 /* like CWORD, except it must be escaped */
712 #define CSPCL 13 /* these terminate a word */
713 #define CIGN 14 /* character should be ignored */
715 #ifdef CONFIG_ASH_ALIAS
719 #define PEOA_OR_PEOF PEOA
723 #define PEOA_OR_PEOF PEOF
726 #define is_digit(c) ((unsigned)((c) - '0') <= 9)
727 #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
728 #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
731 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
732 * (assuming ascii char codes, as the original implementation did)
734 #define is_special(c) \
735 ( (((unsigned int)c) - 33 < 32) \
736 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
738 #define digit_val(c) ((c) - '0')
741 * This file was generated by the mksyntax program.
744 #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
745 #define USE_SIT_FUNCTION
748 /* number syntax index */
749 #define BASESYNTAX 0 /* not in quotes */
750 #define DQSYNTAX 1 /* in double quotes */
751 #define SQSYNTAX 2 /* in single quotes */
752 #define ARISYNTAX 3 /* in arithmetic */
754 #ifdef CONFIG_ASH_MATH_SUPPORT
755 static const char S_I_T[][4] = {
756 #ifdef CONFIG_ASH_ALIAS
757 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
759 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
760 {CNL, CNL, CNL, CNL}, /* 2, \n */
761 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
762 {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
763 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
764 {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
765 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
766 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
767 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
768 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
769 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
770 #ifndef USE_SIT_FUNCTION
771 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
772 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
773 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
777 static const char S_I_T[][3] = {
778 #ifdef CONFIG_ASH_ALIAS
779 {CSPCL, CIGN, CIGN}, /* 0, PEOA */
781 {CSPCL, CWORD, CWORD}, /* 1, ' ' */
782 {CNL, CNL, CNL}, /* 2, \n */
783 {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
784 {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
785 {CVAR, CVAR, CWORD}, /* 5, $ */
786 {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
787 {CSPCL, CWORD, CWORD}, /* 7, ( */
788 {CSPCL, CWORD, CWORD}, /* 8, ) */
789 {CBACK, CBACK, CCTL}, /* 9, \ */
790 {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
791 {CENDVAR, CENDVAR, CWORD}, /* 11, } */
792 #ifndef USE_SIT_FUNCTION
793 {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
794 {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
795 {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
798 #endif /* CONFIG_ASH_MATH_SUPPORT */
800 #ifdef USE_SIT_FUNCTION
802 #define U_C(c) ((unsigned char)(c))
804 static int SIT(int c, int syntax)
806 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
807 #ifdef CONFIG_ASH_ALIAS
808 static const char syntax_index_table[] = {
809 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
810 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
811 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
815 static const char syntax_index_table[] = {
816 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
817 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
818 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
825 if (c == PEOF) /* 2^8+2 */
827 #ifdef CONFIG_ASH_ALIAS
828 if (c == PEOA) /* 2^8+1 */
832 if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
835 s = strchr(spec_symbls, c);
836 if (s == 0 || *s == 0)
838 indx = syntax_index_table[(s - spec_symbls)];
840 return S_I_T[indx][syntax];
843 #else /* USE_SIT_FUNCTION */
845 #define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
847 #ifdef CONFIG_ASH_ALIAS
848 #define CSPCL_CIGN_CIGN_CIGN 0
849 #define CSPCL_CWORD_CWORD_CWORD 1
850 #define CNL_CNL_CNL_CNL 2
851 #define CWORD_CCTL_CCTL_CWORD 3
852 #define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
853 #define CVAR_CVAR_CWORD_CVAR 5
854 #define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
855 #define CSPCL_CWORD_CWORD_CLP 7
856 #define CSPCL_CWORD_CWORD_CRP 8
857 #define CBACK_CBACK_CCTL_CBACK 9
858 #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
859 #define CENDVAR_CENDVAR_CWORD_CENDVAR 11
860 #define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
861 #define CWORD_CWORD_CWORD_CWORD 13
862 #define CCTL_CCTL_CCTL_CCTL 14
864 #define CSPCL_CWORD_CWORD_CWORD 0
865 #define CNL_CNL_CNL_CNL 1
866 #define CWORD_CCTL_CCTL_CWORD 2
867 #define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
868 #define CVAR_CVAR_CWORD_CVAR 4
869 #define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
870 #define CSPCL_CWORD_CWORD_CLP 6
871 #define CSPCL_CWORD_CWORD_CRP 7
872 #define CBACK_CBACK_CCTL_CBACK 8
873 #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
874 #define CENDVAR_CENDVAR_CWORD_CENDVAR 10
875 #define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
876 #define CWORD_CWORD_CWORD_CWORD 12
877 #define CCTL_CCTL_CCTL_CCTL 13
880 static const char syntax_index_table[258] = {
881 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
882 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
883 #ifdef CONFIG_ASH_ALIAS
884 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
886 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
887 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
888 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
889 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
890 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
891 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
892 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
893 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
894 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
895 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
896 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
897 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
898 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
899 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
900 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
901 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
902 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
903 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
904 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
905 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
906 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
907 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
908 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
909 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
910 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
911 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
912 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
913 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
914 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
915 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
916 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
917 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
918 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
919 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
920 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
921 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
922 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
923 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
924 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
925 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
926 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
927 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
928 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
929 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
930 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
931 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
932 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
933 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
934 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
935 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
936 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
937 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
938 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
939 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
940 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
941 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
942 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
943 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
944 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
945 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
946 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
947 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
948 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
949 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
950 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
951 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
952 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
953 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
954 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
955 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
956 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
957 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
958 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
959 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
960 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
961 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
962 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
963 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
964 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
965 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
966 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
967 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
968 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
969 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
970 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
971 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
972 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
973 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
974 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
975 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
976 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
977 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
978 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
979 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
980 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
981 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
982 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
983 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
984 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
985 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
986 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
987 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
988 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
989 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
990 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
991 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
992 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
993 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
994 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
995 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
996 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
997 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
998 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
999 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
1000 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
1001 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
1002 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
1003 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
1004 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
1005 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
1006 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
1007 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
1008 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
1009 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
1010 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
1011 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
1012 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
1013 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
1014 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
1015 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
1016 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
1017 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
1018 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
1019 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
1020 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
1021 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
1022 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
1023 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
1024 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
1025 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
1026 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
1027 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
1028 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
1029 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
1030 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
1031 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
1032 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
1033 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
1034 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
1035 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
1036 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
1037 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
1038 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
1039 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
1040 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
1041 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
1042 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
1043 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
1044 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
1045 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
1046 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
1047 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
1048 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
1049 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
1050 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
1051 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
1052 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
1053 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
1054 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
1055 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
1056 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
1057 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
1058 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
1059 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
1060 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
1061 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
1062 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
1063 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
1064 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
1065 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
1066 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
1067 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
1068 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
1069 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
1070 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
1071 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
1072 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
1073 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
1074 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
1075 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
1076 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
1077 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
1078 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
1079 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
1080 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
1081 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
1082 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
1083 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
1084 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
1085 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
1086 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
1087 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
1088 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
1089 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
1090 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
1091 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
1092 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
1093 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
1094 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
1095 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
1096 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
1097 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
1098 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
1099 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
1100 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
1101 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
1102 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
1103 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
1104 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
1105 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
1106 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
1107 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
1108 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
1109 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
1110 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
1111 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
1112 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
1113 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
1114 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
1115 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
1116 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
1117 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
1118 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
1119 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
1120 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
1121 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
1122 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
1123 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
1124 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
1125 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
1126 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
1127 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
1128 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
1129 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
1130 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
1131 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
1132 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
1133 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
1134 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
1135 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
1136 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
1137 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
1138 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
1139 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
1140 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
1141 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
1144 #endif /* USE_SIT_FUNCTION */
1146 /* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */
1151 static int funcblocksize; /* size of structures in function */
1152 static int funcstringsize; /* size of strings in node */
1153 static pointer funcblock; /* block to allocate function from */
1154 static char *funcstring; /* block to allocate strings from */
1156 static const short nodesize[26] = {
1157 SHELL_ALIGN(sizeof (struct ncmd)),
1158 SHELL_ALIGN(sizeof (struct npipe)),
1159 SHELL_ALIGN(sizeof (struct nredir)),
1160 SHELL_ALIGN(sizeof (struct nredir)),
1161 SHELL_ALIGN(sizeof (struct nredir)),
1162 SHELL_ALIGN(sizeof (struct nbinary)),
1163 SHELL_ALIGN(sizeof (struct nbinary)),
1164 SHELL_ALIGN(sizeof (struct nbinary)),
1165 SHELL_ALIGN(sizeof (struct nif)),
1166 SHELL_ALIGN(sizeof (struct nbinary)),
1167 SHELL_ALIGN(sizeof (struct nbinary)),
1168 SHELL_ALIGN(sizeof (struct nfor)),
1169 SHELL_ALIGN(sizeof (struct ncase)),
1170 SHELL_ALIGN(sizeof (struct nclist)),
1171 SHELL_ALIGN(sizeof (struct narg)),
1172 SHELL_ALIGN(sizeof (struct narg)),
1173 SHELL_ALIGN(sizeof (struct nfile)),
1174 SHELL_ALIGN(sizeof (struct nfile)),
1175 SHELL_ALIGN(sizeof (struct nfile)),
1176 SHELL_ALIGN(sizeof (struct nfile)),
1177 SHELL_ALIGN(sizeof (struct nfile)),
1178 SHELL_ALIGN(sizeof (struct ndup)),
1179 SHELL_ALIGN(sizeof (struct ndup)),
1180 SHELL_ALIGN(sizeof (struct nhere)),
1181 SHELL_ALIGN(sizeof (struct nhere)),
1182 SHELL_ALIGN(sizeof (struct nnot)),
1186 static void calcsize(union node *);
1187 static void sizenodelist(struct nodelist *);
1188 static union node *copynode(union node *);
1189 static struct nodelist *copynodelist(struct nodelist *);
1190 static char *nodesavestr(char *);
1194 static void evalstring(char *, int);
1195 union node; /* BLETCH for ansi C */
1196 static void evaltree(union node *, int);
1197 static void evalbackcmd(union node *, struct backcmd *);
1199 /* in_function returns nonzero if we are currently evaluating a function */
1200 #define in_function() funcnest
1201 static int evalskip; /* set if we are skipping commands */
1202 static int skipcount; /* number of levels to skip */
1203 static int funcnest; /* depth of function calls */
1205 /* reasons for skipping commands (see comment on breakcmd routine) */
1212 * This file was generated by the mkbuiltins program.
1216 static int bgcmd(int, char **);
1218 static int breakcmd(int, char **);
1219 static int cdcmd(int, char **);
1220 #ifdef CONFIG_ASH_CMDCMD
1221 static int commandcmd(int, char **);
1223 static int dotcmd(int, char **);
1224 static int evalcmd(int, char **);
1225 static int execcmd(int, char **);
1226 static int exitcmd(int, char **);
1227 static int exportcmd(int, char **);
1228 static int falsecmd(int, char **);
1230 static int fgcmd(int, char **);
1232 #ifdef CONFIG_ASH_GETOPTS
1233 static int getoptscmd(int, char **);
1235 static int hashcmd(int, char **);
1236 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1237 static int helpcmd(int argc, char **argv);
1240 static int jobscmd(int, char **);
1242 #ifdef CONFIG_ASH_MATH_SUPPORT
1243 static int letcmd(int, char **);
1245 static int localcmd(int, char **);
1246 static int pwdcmd(int, char **);
1247 static int readcmd(int, char **);
1248 static int returncmd(int, char **);
1249 static int setcmd(int, char **);
1250 static int shiftcmd(int, char **);
1251 static int timescmd(int, char **);
1252 static int trapcmd(int, char **);
1253 static int truecmd(int, char **);
1254 static int typecmd(int, char **);
1255 static int umaskcmd(int, char **);
1256 static int unsetcmd(int, char **);
1257 static int waitcmd(int, char **);
1258 static int ulimitcmd(int, char **);
1260 static int killcmd(int, char **);
1263 /* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1265 #ifdef CONFIG_ASH_MAIL
1266 static void chkmail(void);
1267 static void changemail(const char *);
1270 /* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */
1272 /* values of cmdtype */
1273 #define CMDUNKNOWN -1 /* no entry in table for command */
1274 #define CMDNORMAL 0 /* command is an executable program */
1275 #define CMDFUNCTION 1 /* command is a shell function */
1276 #define CMDBUILTIN 2 /* command is a shell builtin */
1280 int (*builtin)(int, char **);
1281 /* unsigned flags; */
1284 #ifdef CONFIG_ASH_CMDCMD
1286 # ifdef CONFIG_ASH_ALIAS
1287 # define COMMANDCMD (builtincmd + 7)
1288 # define EXECCMD (builtincmd + 10)
1290 # define COMMANDCMD (builtincmd + 6)
1291 # define EXECCMD (builtincmd + 9)
1294 # ifdef CONFIG_ASH_ALIAS
1295 # define COMMANDCMD (builtincmd + 6)
1296 # define EXECCMD (builtincmd + 9)
1298 # define COMMANDCMD (builtincmd + 5)
1299 # define EXECCMD (builtincmd + 8)
1302 #else /* ! CONFIG_ASH_CMDCMD */
1304 # ifdef CONFIG_ASH_ALIAS
1305 # define EXECCMD (builtincmd + 9)
1307 # define EXECCMD (builtincmd + 8)
1310 # ifdef CONFIG_ASH_ALIAS
1311 # define EXECCMD (builtincmd + 8)
1313 # define EXECCMD (builtincmd + 7)
1316 #endif /* CONFIG_ASH_CMDCMD */
1318 #define BUILTIN_NOSPEC "0"
1319 #define BUILTIN_SPECIAL "1"
1320 #define BUILTIN_REGULAR "2"
1321 #define BUILTIN_SPEC_REG "3"
1322 #define BUILTIN_ASSIGN "4"
1323 #define BUILTIN_SPEC_ASSG "5"
1324 #define BUILTIN_REG_ASSG "6"
1325 #define BUILTIN_SPEC_REG_ASSG "7"
1327 #define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1328 #define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1330 static const struct builtincmd builtincmd[] = {
1331 { BUILTIN_SPEC_REG ".", dotcmd },
1332 { BUILTIN_SPEC_REG ":", truecmd },
1333 #ifdef CONFIG_ASH_ALIAS
1334 { BUILTIN_REG_ASSG "alias", aliascmd },
1337 { BUILTIN_REGULAR "bg", bgcmd },
1339 { BUILTIN_SPEC_REG "break", breakcmd },
1340 { BUILTIN_REGULAR "cd", cdcmd },
1341 { BUILTIN_NOSPEC "chdir", cdcmd },
1342 #ifdef CONFIG_ASH_CMDCMD
1343 { BUILTIN_REGULAR "command", commandcmd },
1345 { BUILTIN_SPEC_REG "continue", breakcmd },
1346 { BUILTIN_SPEC_REG "eval", evalcmd },
1347 { BUILTIN_SPEC_REG "exec", execcmd },
1348 { BUILTIN_SPEC_REG "exit", exitcmd },
1349 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
1350 { BUILTIN_REGULAR "false", falsecmd },
1352 { BUILTIN_REGULAR "fg", fgcmd },
1354 #ifdef CONFIG_ASH_GETOPTS
1355 { BUILTIN_REGULAR "getopts", getoptscmd },
1357 { BUILTIN_NOSPEC "hash", hashcmd },
1358 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1359 { BUILTIN_NOSPEC "help", helpcmd },
1362 { BUILTIN_REGULAR "jobs", jobscmd },
1363 { BUILTIN_REGULAR "kill", killcmd },
1365 #ifdef CONFIG_ASH_MATH_SUPPORT
1366 { BUILTIN_NOSPEC "let", letcmd },
1368 { BUILTIN_ASSIGN "local", localcmd },
1369 { BUILTIN_NOSPEC "pwd", pwdcmd },
1370 { BUILTIN_REGULAR "read", readcmd },
1371 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
1372 { BUILTIN_SPEC_REG "return", returncmd },
1373 { BUILTIN_SPEC_REG "set", setcmd },
1374 { BUILTIN_SPEC_REG "shift", shiftcmd },
1375 { BUILTIN_SPEC_REG "times", timescmd },
1376 { BUILTIN_SPEC_REG "trap", trapcmd },
1377 { BUILTIN_REGULAR "true", truecmd },
1378 { BUILTIN_NOSPEC "type", typecmd },
1379 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
1380 { BUILTIN_REGULAR "umask", umaskcmd },
1381 #ifdef CONFIG_ASH_ALIAS
1382 { BUILTIN_REGULAR "unalias", unaliascmd },
1384 { BUILTIN_SPEC_REG "unset", unsetcmd },
1385 { BUILTIN_REGULAR "wait", waitcmd },
1388 #define NUMBUILTINS (sizeof (builtincmd) / sizeof (struct builtincmd) )
1396 const struct builtincmd *cmd;
1397 struct funcnode *func;
1402 /* action to find_command() */
1403 #define DO_ERR 0x01 /* prints errors */
1404 #define DO_ABS 0x02 /* checks absolute paths */
1405 #define DO_NOFUNC 0x04 /* don't return shell functions, for command */
1406 #define DO_ALTPATH 0x08 /* using alternate path */
1407 #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
1409 static const char *pathopt; /* set by padvance */
1411 static void shellexec(char **, const char *, int)
1412 __attribute__((__noreturn__));
1413 static char *padvance(const char **, const char *);
1414 static void find_command(char *, struct cmdentry *, int, const char *);
1415 static struct builtincmd *find_builtin(const char *);
1416 static void hashcd(void);
1417 static void changepath(const char *);
1418 static void defun(char *, union node *);
1419 static void unsetfunc(const char *);
1421 #ifdef CONFIG_ASH_MATH_SUPPORT
1422 static int dash_arith(const char *);
1425 /* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1427 static void reset(void);
1429 /* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */
1436 #define VEXPORT 0x01 /* variable is exported */
1437 #define VREADONLY 0x02 /* variable cannot be modified */
1438 #define VSTRFIXED 0x04 /* variable struct is statically allocated */
1439 #define VTEXTFIXED 0x08 /* text is statically allocated */
1440 #define VSTACK 0x10 /* text is allocated on the stack */
1441 #define VUNSET 0x20 /* the variable is not set */
1442 #define VNOFUNC 0x40 /* don't call the callback function */
1443 #define VNOSET 0x80 /* do not set variable - just readonly test */
1444 #define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1448 struct var *next; /* next entry in hash list */
1449 int flags; /* flags are defined above */
1450 const char *text; /* name=value */
1451 void (*func)(const char *);
1452 /* function to be called when */
1453 /* the variable gets set/unset */
1457 struct localvar *next; /* next local variable in list */
1458 struct var *vp; /* the variable that was made local */
1459 int flags; /* saved flags */
1460 const char *text; /* saved text */
1464 static struct localvar *localvars;
1470 #ifdef CONFIG_ASH_GETOPTS
1471 static void getoptsreset(const char *);
1474 #ifdef CONFIG_LOCALE_SUPPORT
1476 static void change_lc_all(const char *value);
1477 static void change_lc_ctype(const char *value);
1482 static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1484 static const char defifsvar[] = "IFS= \t\n";
1485 #define defifs (defifsvar + 4)
1487 static const char defifs[] = " \t\n";
1491 static struct var varinit[] = {
1493 { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
1495 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
1498 #ifdef CONFIG_ASH_MAIL
1499 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1500 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
1503 { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1504 { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
1505 { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
1506 { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
1507 #ifdef CONFIG_ASH_GETOPTS
1508 { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
1510 #ifdef CONFIG_LOCALE_SUPPORT
1511 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=", change_lc_all},
1512 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=", change_lc_ctype},
1514 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1515 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=", NULL},
1519 #define vifs varinit[0]
1520 #ifdef CONFIG_ASH_MAIL
1521 #define vmail (&vifs)[1]
1522 #define vmpath (&vmail)[1]
1526 #define vpath (&vmpath)[1]
1527 #define vps1 (&vpath)[1]
1528 #define vps2 (&vps1)[1]
1529 #define vps4 (&vps2)[1]
1530 #define voptind (&vps4)[1]
1532 #define defpath (defpathvar + 5)
1535 * The following macros access the values of the above variables.
1536 * They have to skip over the name. They return the null string
1537 * for unset variables.
1540 #define ifsval() (vifs.text + 4)
1541 #define ifsset() ((vifs.flags & VUNSET) == 0)
1542 #define mailval() (vmail.text + 5)
1543 #define mpathval() (vmpath.text + 9)
1544 #define pathval() (vpath.text + 5)
1545 #define ps1val() (vps1.text + 4)
1546 #define ps2val() (vps2.text + 4)
1547 #define ps4val() (vps4.text + 4)
1548 #define optindval() (voptind.text + 7)
1550 #define mpathset() ((vmpath.flags & VUNSET) == 0)
1552 static void setvar(const char *, const char *, int);
1553 static void setvareq(char *, int);
1554 static void listsetvar(struct strlist *, int);
1555 static char *lookupvar(const char *);
1556 static char *bltinlookup(const char *);
1557 static char **listvars(int, int, char ***);
1558 #define environment() listvars(VEXPORT, VUNSET, 0)
1559 static int showvars(const char *, int, int);
1560 static void poplocalvars(void);
1561 static int unsetvar(const char *);
1562 #ifdef CONFIG_ASH_GETOPTS
1563 static int setvarsafe(const char *, const char *, int);
1565 static int varcmp(const char *, const char *);
1566 static struct var **hashvar(const char *);
1569 static inline int varequal(const char *a, const char *b) {
1570 return !varcmp(a, b);
1574 static int loopnest; /* current loop nesting level */
1577 struct strpush *prev; /* preceding string on stack */
1580 #ifdef CONFIG_ASH_ALIAS
1581 struct alias *ap; /* if push was associated with an alias */
1583 char *string; /* remember the string since it may change */
1587 struct parsefile *prev; /* preceding file on stack */
1588 int linno; /* current line */
1589 int fd; /* file descriptor (or -1 if string) */
1590 int nleft; /* number of chars left in this line */
1591 int lleft; /* number of chars left in this buffer */
1592 char *nextc; /* next char in buffer */
1593 char *buf; /* input buffer */
1594 struct strpush *strpush; /* for pushing strings at this level */
1595 struct strpush basestrpush; /* so pushing one is fast */
1599 * The parsefile structure pointed to by the global variable parsefile
1600 * contains information about the current file being read.
1605 struct redirtab *next;
1610 static struct redirtab *redirlist;
1611 static int nullredirs;
1613 extern char **environ;
1615 /* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */
1618 static void outstr(const char *, FILE *);
1619 static void outcslow(int, FILE *);
1620 static void flushall(void);
1621 static void flushout(FILE *);
1622 static int out1fmt(const char *, ...)
1623 __attribute__((__format__(__printf__,1,2)));
1624 static int fmtstr(char *, size_t, const char *, ...)
1625 __attribute__((__format__(__printf__,3,4)));
1626 static void xwrite(int, const void *, size_t);
1629 #define outerr(f) ferror(f)
1630 #define out2c(c) outcslow((c), stderr)
1632 static void out1str(const char *p)
1637 static void out2str(const char *p)
1642 static void out1c(char c)
1652 * Initialization code.
1656 * This routine initializes the builtin variables.
1667 * PS1 depends on uid
1669 #if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1670 vps1.text = "PS1=\\w \\$ ";
1673 vps1.text = "PS1=# ";
1676 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1678 vpp = hashvar(vp->text);
1681 } while (++vp < end);
1690 basepf.nextc = basepf.buf = basebuf;
1695 signal(SIGCHLD, SIG_DFL);
1704 for (envp = environ ; *envp ; envp++) {
1705 if (strchr(*envp, '=')) {
1706 setvareq(*envp, VEXPORT|VTEXTFIXED);
1710 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
1711 setvar("PPID", ppid, 0);
1716 /* PEOF (the end of file marker) */
1719 * The input line number. Input.c just defines this variable, and saves
1720 * and restores it when files are pushed and popped. The user of this
1721 * package must set its value.
1724 static int pgetc(void);
1725 static int pgetc2(void);
1726 static int preadbuffer(void);
1727 static void pungetc(void);
1728 static void pushstring(char *, void *);
1729 static void popstring(void);
1730 static void setinputfile(const char *, int);
1731 static void setinputfd(int, int);
1732 static void setinputstring(char *);
1733 static void popfile(void);
1734 static void popallfiles(void);
1735 static void closescript(void);
1738 /* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */
1741 /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
1744 #define FORK_NOJOB 2
1746 /* mode flags for showjob(s) */
1747 #define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
1748 #define SHOW_PID 0x04 /* include process pid */
1749 #define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
1753 * A job structure contains information about a job. A job is either a
1754 * single process or a set of processes contained in a pipeline. In the
1755 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1760 pid_t pid; /* process id */
1761 int status; /* last process status from wait() */
1762 char *cmd; /* text of command being run */
1766 struct procstat ps0; /* status of process */
1767 struct procstat *ps; /* status or processes when more than one */
1769 int stopstatus; /* status of a stopped job */
1772 nprocs: 16, /* number of processes */
1774 #define JOBRUNNING 0 /* at least one proc running */
1775 #define JOBSTOPPED 1 /* all procs are stopped */
1776 #define JOBDONE 2 /* all procs are completed */
1778 sigint: 1, /* job was killed by SIGINT */
1779 jobctl: 1, /* job running under job control */
1781 waited: 1, /* true if this entry has been waited for */
1782 used: 1, /* true if this entry is in used */
1783 changed: 1; /* true if status has changed */
1784 struct job *prev_job; /* previous job */
1787 static pid_t backgndpid; /* pid of last background process */
1788 static int job_warning; /* user was warned about stopped jobs */
1790 static int jobctl; /* true if doing job control */
1793 static struct job *makejob(union node *, int);
1794 static int forkshell(struct job *, union node *, int);
1795 static int waitforjob(struct job *);
1796 static int stoppedjobs(void);
1799 #define setjobctl(on) /* do nothing */
1801 static void setjobctl(int);
1802 static void showjobs(FILE *, int);
1805 /* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */
1808 /* pid of main shell */
1810 /* true if we aren't a child of the main shell */
1811 static int rootshell;
1813 static void readcmdfile(char *);
1814 static void cmdloop(int);
1816 /* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */
1820 struct stack_block *stackp;
1823 struct stackmark *marknext;
1826 /* minimum size of a block */
1827 #define MINSIZE SHELL_ALIGN(504)
1829 struct stack_block {
1830 struct stack_block *prev;
1831 char space[MINSIZE];
1834 static struct stack_block stackbase;
1835 static struct stack_block *stackp = &stackbase;
1836 static struct stackmark *markp;
1837 static char *stacknxt = stackbase.space;
1838 static size_t stacknleft = MINSIZE;
1839 static char *sstrend = stackbase.space + MINSIZE;
1840 static int herefd = -1;
1843 static pointer ckmalloc(size_t);
1844 static pointer ckrealloc(pointer, size_t);
1845 static char *savestr(const char *);
1846 static pointer stalloc(size_t);
1847 static void stunalloc(pointer);
1848 static void setstackmark(struct stackmark *);
1849 static void popstackmark(struct stackmark *);
1850 static void growstackblock(void);
1851 static void *growstackstr(void);
1852 static char *makestrspace(size_t, char *);
1853 static char *stnputs(const char *, size_t, char *);
1854 static char *stputs(const char *, char *);
1857 static inline char *_STPUTC(char c, char *p) {
1864 #define stackblock() ((void *)stacknxt)
1865 #define stackblocksize() stacknleft
1866 #define STARTSTACKSTR(p) ((p) = stackblock())
1867 #define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
1868 #define CHECKSTRSPACE(n, p) \
1872 size_t m = sstrend - q; \
1874 (p) = makestrspace(l, q); \
1877 #define USTPUTC(c, p) (*p++ = (c))
1878 #define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
1879 #define STUNPUTC(p) (--p)
1880 #define STTOPC(p) p[-1]
1881 #define STADJUST(amount, p) (p += (amount))
1883 #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1884 #define ungrabstackstr(s, p) stunalloc((s))
1885 #define stackstrend() ((void *)sstrend)
1887 #define ckfree(p) free((pointer)(p))
1889 /* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */
1892 #define DOLATSTRLEN 4
1894 static char *prefix(const char *, const char *);
1895 static int number(const char *);
1896 static int is_number(const char *);
1897 static char *single_quote(const char *);
1898 static char *sstrdup(const char *);
1900 #define equal(s1, s2) (strcmp(s1, s2) == 0)
1901 #define scopy(s1, s2) ((void)strcpy(s2, s1))
1903 /* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
1906 int nparam; /* # of positional parameters (without $0) */
1907 unsigned char malloc; /* if parameter list dynamically allocated */
1908 char **p; /* parameter list */
1909 #ifdef CONFIG_ASH_GETOPTS
1910 int optind; /* next parameter to be processed by getopts */
1911 int optoff; /* used by getopts */
1916 #define eflag optlist[0]
1917 #define fflag optlist[1]
1918 #define Iflag optlist[2]
1919 #define iflag optlist[3]
1920 #define mflag optlist[4]
1921 #define nflag optlist[5]
1922 #define sflag optlist[6]
1923 #define xflag optlist[7]
1924 #define vflag optlist[8]
1925 #define Cflag optlist[9]
1926 #define aflag optlist[10]
1927 #define bflag optlist[11]
1928 #define uflag optlist[12]
1929 #define qflag optlist[13]
1932 #define nolog optlist[14]
1933 #define debug optlist[15]
1939 /* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
1942 static const char *const optletters_optnames[NOPTS] = {
1963 #define optletters(n) optletters_optnames[(n)][0]
1964 #define optnames(n) (&optletters_optnames[(n)][1])
1967 static char optlist[NOPTS];
1970 static char *arg0; /* value of $0 */
1971 static struct shparam shellparam; /* $@ current positional parameters */
1972 static char **argptr; /* argument list for builtin commands */
1973 static char *optionarg; /* set by nextopt (like getopt) */
1974 static char *optptr; /* used by nextopt */
1976 static char *minusc; /* argument to -c option */
1979 static void procargs(int, char **);
1980 static void optschanged(void);
1981 static void setparam(char **);
1982 static void freeparam(volatile struct shparam *);
1983 static int shiftcmd(int, char **);
1984 static int setcmd(int, char **);
1985 static int nextopt(const char *);
1987 /* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */
1989 /* flags passed to redirect */
1990 #define REDIR_PUSH 01 /* save previous values of file descriptors */
1993 static void redirect(union node *, int);
1994 static void popredir(int);
1995 static void clearredir(int);
1996 static int copyfd(int, int);
1997 static int redirectsafe(union node *, int);
1999 /* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */
2003 static void showtree(union node *);
2004 static void trace(const char *, ...);
2005 static void tracev(const char *, va_list);
2006 static void trargs(char **);
2007 static void trputc(int);
2008 static void trputs(const char *);
2009 static void opentrace(void);
2012 /* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */
2015 /* trap handler commands */
2016 static char *trap[NSIG];
2017 /* current value of signal */
2018 static char sigmode[NSIG - 1];
2019 /* indicates specified signal received */
2020 static char gotsig[NSIG - 1];
2022 static void clear_traps(void);
2023 static void setsignal(int);
2024 static void ignoresig(int);
2025 static void onsig(int);
2026 static void dotrap(void);
2027 static void setinteractive(int);
2028 static void exitshell(void) __attribute__((__noreturn__));
2029 static int decode_signal(const char *, int);
2032 * This routine is called when an error or an interrupt occurs in an
2033 * interactive shell and control is returned to the main command loop.
2048 parselleft = parsenleft = 0; /* clear input buffer */
2052 /* from parser.c: */
2065 #ifdef CONFIG_ASH_ALIAS
2066 static struct alias *atab[ATABSIZE];
2068 static void setalias(const char *, const char *);
2069 static struct alias *freealias(struct alias *);
2070 static struct alias **__lookupalias(const char *);
2073 setalias(const char *name, const char *val)
2075 struct alias *ap, **app;
2077 app = __lookupalias(name);
2081 if (!(ap->flag & ALIASINUSE)) {
2084 ap->val = savestr(val);
2085 ap->flag &= ~ALIASDEAD;
2088 ap = ckmalloc(sizeof (struct alias));
2089 ap->name = savestr(name);
2090 ap->val = savestr(val);
2099 unalias(const char *name)
2103 app = __lookupalias(name);
2107 *app = freealias(*app);
2118 struct alias *ap, **app;
2122 for (i = 0; i < ATABSIZE; i++) {
2124 for (ap = *app; ap; ap = *app) {
2125 *app = freealias(*app);
2134 static struct alias *
2135 lookupalias(const char *name, int check)
2137 struct alias *ap = *__lookupalias(name);
2139 if (check && ap && (ap->flag & ALIASINUSE))
2145 * TODO - sort output
2148 aliascmd(int argc, char **argv)
2157 for (i = 0; i < ATABSIZE; i++)
2158 for (ap = atab[i]; ap; ap = ap->next) {
2163 while ((n = *++argv) != NULL) {
2164 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
2165 if ((ap = *__lookupalias(n)) == NULL) {
2166 fprintf(stderr, "%s: %s not found\n", "alias", n);
2180 unaliascmd(int argc, char **argv)
2184 while ((i = nextopt("a")) != '\0') {
2190 for (i = 0; *argptr; argptr++) {
2191 if (unalias(*argptr)) {
2192 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
2200 static struct alias *
2201 freealias(struct alias *ap) {
2204 if (ap->flag & ALIASINUSE) {
2205 ap->flag |= ALIASDEAD;
2217 printalias(const struct alias *ap) {
2218 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
2221 static struct alias **
2222 __lookupalias(const char *name) {
2223 unsigned int hashval;
2230 ch = (unsigned char)*p;
2234 ch = (unsigned char)*++p;
2236 app = &atab[hashval % ATABSIZE];
2238 for (; *app; app = &(*app)->next) {
2239 if (equal(name, (*app)->name)) {
2246 #endif /* CONFIG_ASH_ALIAS */
2249 /* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2252 * The cd and pwd commands.
2255 #define CD_PHYSICAL 1
2258 static int docd(const char *, int);
2259 static int cdopt(void);
2261 static char *curdir = nullstr; /* current working directory */
2262 static char *physdir = nullstr; /* physical working directory */
2271 while ((i = nextopt("LP"))) {
2273 flags ^= CD_PHYSICAL;
2282 cdcmd(int argc, char **argv)
2294 dest = bltinlookup(homestr);
2295 else if (dest[0] == '-' && dest[1] == '\0') {
2296 dest = bltinlookup("OLDPWD");
2319 if (!(path = bltinlookup("CDPATH"))) {
2327 p = padvance(&path, dest);
2328 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2332 if (!docd(p, flags))
2337 error("can't cd to %s", dest);
2340 if (flags & CD_PRINT)
2341 out1fmt(snlfmt, curdir);
2347 * Update curdir (the name of the current directory) in response to a
2351 static inline const char *
2352 updatepwd(const char *dir)
2359 cdcomppath = sstrdup(dir);
2362 if (curdir == nullstr)
2364 new = stputs(curdir, new);
2366 new = makestrspace(strlen(dir) + 2, new);
2367 lim = stackblock() + 1;
2371 if (new > lim && *lim == '/')
2376 if (dir[1] == '/' && dir[2] != '/') {
2382 p = strtok(cdcomppath, "/");
2386 if (p[1] == '.' && p[2] == '\0') {
2393 } else if (p[1] == '\0')
2397 new = stputs(p, new);
2405 return stackblock();
2409 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2410 * know that the current directory has changed.
2414 docd(const char *dest, int flags)
2416 const char *dir = 0;
2419 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2422 if (!(flags & CD_PHYSICAL)) {
2423 dir = updatepwd(dest);
2438 * Find out what the current directory is. If we already know the current
2439 * directory, this routine returns immediately.
2441 static inline char *
2444 char *dir = getcwd(0, 0);
2445 return dir ? dir : nullstr;
2449 pwdcmd(int argc, char **argv)
2452 const char *dir = curdir;
2456 if (physdir == nullstr)
2460 out1fmt(snlfmt, dir);
2465 setpwd(const char *val, int setold)
2469 oldcur = dir = curdir;
2472 setvar("OLDPWD", oldcur, VEXPORT);
2475 if (physdir != nullstr) {
2476 if (physdir != oldcur)
2480 if (oldcur == val || !val) {
2487 if (oldcur != dir && oldcur != nullstr) {
2492 setvar("PWD", dir, VEXPORT);
2495 /* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2498 * Errors and exceptions.
2502 * Code to handle exceptions in C.
2507 static void exverror(int, const char *, va_list)
2508 __attribute__((__noreturn__));
2511 * Called to raise an exception. Since C doesn't include exceptions, we
2512 * just do a longjmp to the exception handler. The type of exception is
2513 * stored in the global variable "exception".
2520 if (handler == NULL)
2526 longjmp(handler->loc, 1);
2531 * Called from trap.c when a SIGINT is received. (If the user specifies
2532 * that SIGINT is to be trapped or ignored using the trap builtin, then
2533 * this routine is not called.) Suppressint is nonzero when interrupts
2534 * are held using the INTOFF macro. (The test for iflag is just
2535 * defensive programming.)
2545 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
2546 if (!(rootshell && iflag)) {
2547 signal(SIGINT, SIG_DFL);
2557 exvwarning(const char *msg, va_list ap)
2570 fprintf(errs, fmt, name, startlinno);
2571 vfprintf(errs, msg, ap);
2572 outcslow('\n', errs);
2576 * Exverror is called to raise the error exception. If the second argument
2577 * is not NULL then error prints an error message using printf style
2578 * formatting. It then raises the error exception.
2581 exverror(int cond, const char *msg, va_list ap)
2585 TRACE(("exverror(%d, \"", cond));
2587 TRACE(("\") pid=%d\n", getpid()));
2589 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
2592 exvwarning(msg, ap);
2601 error(const char *msg, ...)
2606 exverror(EXERROR, msg, ap);
2613 exerror(int cond, const char *msg, ...)
2618 exverror(cond, msg, ap);
2624 * error/warning routines for external builtins
2628 sh_warnx(const char *fmt, ...)
2633 exvwarning(fmt, ap);
2639 * Return a string describing an error. The returned string may be a
2640 * pointer to a static buffer that will be overwritten on the next call.
2641 * Action describes the operation that got the error.
2645 errmsg(int e, const char *em)
2647 if(e == ENOENT || e == ENOTDIR) {
2655 /* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */
2658 * Evaluate a command.
2661 /* flags in argument to evaltree */
2662 #define EV_EXIT 01 /* exit after evaluating tree */
2663 #define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2664 #define EV_BACKCMD 04 /* command executing within back quotes */
2667 static void evalloop(union node *, int);
2668 static void evalfor(union node *, int);
2669 static void evalcase(union node *, int);
2670 static void evalsubshell(union node *, int);
2671 static void expredir(union node *);
2672 static void evalpipe(union node *, int);
2673 static void evalcommand(union node *, int);
2674 static int evalbltin(const struct builtincmd *, int, char **);
2675 static int evalfun(struct funcnode *, int, char **, int);
2676 static void prehash(union node *);
2677 static int eprintlist(struct strlist *, int);
2678 static int bltincmd(int, char **);
2681 static const struct builtincmd bltin = {
2687 * Called to reset things after an exception.
2691 * The eval commmand.
2695 evalcmd(int argc, char **argv)
2704 STARTSTACKSTR(concat);
2707 concat = stputs(p, concat);
2708 if ((p = *ap++) == NULL)
2710 STPUTC(' ', concat);
2712 STPUTC('\0', concat);
2713 p = grabstackstr(concat);
2715 evalstring(p, EV_TESTED);
2722 * Execute a command or commands contained in a string.
2726 evalstring(char *s, int flag)
2729 struct stackmark smark;
2731 setstackmark(&smark);
2734 while ((n = parsecmd(0)) != NEOF) {
2736 popstackmark(&smark);
2741 popstackmark(&smark);
2747 * Evaluate a parse tree. The value is left in the global variable
2752 evaltree(union node *n, int flags)
2755 void (*evalfn)(union node *, int);
2759 TRACE(("evaltree(NULL) called\n"));
2762 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
2763 getpid(), n, n->type, flags));
2767 out1fmt("Node type = %d\n", n->type);
2772 evaltree(n->nnot.com, EV_TESTED);
2773 status = !exitstatus;
2776 expredir(n->nredir.redirect);
2777 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
2779 evaltree(n->nredir.n, flags & EV_TESTED);
2780 status = exitstatus;
2785 evalfn = evalcommand;
2787 if (eflag && !(flags & EV_TESTED))
2799 evalfn = evalsubshell;
2811 #error NAND + 1 != NOR
2813 #if NOR + 1 != NSEMI
2814 #error NOR + 1 != NSEMI
2816 isor = n->type - NAND;
2819 (flags | ((isor >> 1) - 1)) & EV_TESTED
2821 if (!exitstatus == isor)
2833 evaltree(n->nif.test, EV_TESTED);
2836 if (exitstatus == 0) {
2839 } else if (n->nif.elsepart) {
2840 n = n->nif.elsepart;
2845 defun(n->narg.text, n->narg.next);
2849 exitstatus = status;
2855 if (flags & EV_EXIT || checkexit & exitstatus)
2860 #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
2863 void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
2867 evalloop(union node *n, int flags)
2877 evaltree(n->nbinary.ch1, EV_TESTED);
2879 skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
2883 if (evalskip == SKIPBREAK && --skipcount <= 0)
2888 if (n->type != NWHILE)
2892 evaltree(n->nbinary.ch2, flags);
2893 status = exitstatus;
2898 exitstatus = status;
2904 evalfor(union node *n, int flags)
2906 struct arglist arglist;
2909 struct stackmark smark;
2911 setstackmark(&smark);
2912 arglist.lastp = &arglist.list;
2913 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
2914 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2919 *arglist.lastp = NULL;
2924 for (sp = arglist.list ; sp ; sp = sp->next) {
2925 setvar(n->nfor.var, sp->text, 0);
2926 evaltree(n->nfor.body, flags);
2928 if (evalskip == SKIPCONT && --skipcount <= 0) {
2932 if (evalskip == SKIPBREAK && --skipcount <= 0)
2939 popstackmark(&smark);
2945 evalcase(union node *n, int flags)
2949 struct arglist arglist;
2950 struct stackmark smark;
2952 setstackmark(&smark);
2953 arglist.lastp = &arglist.list;
2954 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
2956 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
2957 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
2958 if (casematch(patp, arglist.list->text)) {
2959 if (evalskip == 0) {
2960 evaltree(cp->nclist.body, flags);
2967 popstackmark(&smark);
2973 * Kick off a subshell to evaluate a tree.
2977 evalsubshell(union node *n, int flags)
2980 int backgnd = (n->type == NBACKGND);
2983 expredir(n->nredir.redirect);
2984 if (!backgnd && flags & EV_EXIT && !trap[0])
2988 if (forkshell(jp, n, backgnd) == 0) {
2992 flags &=~ EV_TESTED;
2994 redirect(n->nredir.redirect, 0);
2995 evaltreenr(n->nredir.n, flags);
3000 status = waitforjob(jp);
3001 exitstatus = status;
3008 * Compute the names of the files in a redirection list.
3012 expredir(union node *n)
3016 for (redir = n ; redir ; redir = redir->nfile.next) {
3018 fn.lastp = &fn.list;
3019 switch (redir->type) {
3025 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3026 redir->nfile.expfname = fn.list->text;
3030 if (redir->ndup.vname) {
3031 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
3032 fixredir(redir, fn.list->text, 1);
3042 * Evaluate a pipeline. All the processes in the pipeline are children
3043 * of the process creating the pipeline. (This differs from some versions
3044 * of the shell, which make the last process in a pipeline the parent
3049 evalpipe(union node *n, int flags)
3052 struct nodelist *lp;
3057 TRACE(("evalpipe(0x%lx) called\n", (long)n));
3059 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
3063 jp = makejob(n, pipelen);
3065 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
3069 if (pipe(pip) < 0) {
3071 error("Pipe call failed");
3074 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
3087 evaltreenr(lp->n, flags);
3095 if (n->npipe.backgnd == 0) {
3096 exitstatus = waitforjob(jp);
3097 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
3105 * Execute a command inside back quotes. If it's a builtin command, we
3106 * want to save its output in a block obtained from malloc. Otherwise
3107 * we fork off a subprocess and get the output of the command via a pipe.
3108 * Should be called with interrupts off.
3112 evalbackcmd(union node *n, struct backcmd *result)
3124 saveherefd = herefd;
3132 error("Pipe call failed");
3134 if (forkshell(jp, n, FORK_NOJOB) == 0) {
3143 evaltreenr(n, EV_EXIT);
3147 result->fd = pip[0];
3150 herefd = saveherefd;
3152 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
3153 result->fd, result->buf, result->nleft, result->jp));
3156 #ifdef CONFIG_ASH_CMDCMD
3157 static inline char **
3158 parse_command_args(char **argv, const char **path)
3170 if (c == '-' && !*cp) {
3180 /* run 'typecmd' for other options */
3183 } while ((c = *cp++));
3192 * Execute a simple command.
3196 evalcommand(union node *cmd, int flags)
3198 struct stackmark smark;
3200 struct arglist arglist;
3201 struct arglist varlist;
3205 struct cmdentry cmdentry;
3214 /* First expand the arguments. */
3215 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
3216 setstackmark(&smark);
3217 back_exitstatus = 0;
3219 cmdentry.cmdtype = CMDBUILTIN;
3220 cmdentry.u.cmd = &bltin;
3221 varlist.lastp = &varlist.list;
3222 *varlist.lastp = NULL;
3223 arglist.lastp = &arglist.list;
3224 *arglist.lastp = NULL;
3227 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
3228 struct strlist **spp;
3230 spp = arglist.lastp;
3231 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3232 for (sp = *spp; sp; sp = sp->next)
3236 argv = nargv = stalloc(sizeof (char *) * (argc + 1));
3237 for (sp = arglist.list ; sp ; sp = sp->next) {
3238 TRACE(("evalcommand arg: %s\n", sp->text));
3239 *nargv++ = sp->text;
3244 if (iflag && funcnest == 0 && argc > 0)
3245 lastarg = nargv[-1];
3247 expredir(cmd->ncmd.redirect);
3248 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH);
3251 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
3252 struct strlist **spp;
3255 spp = varlist.lastp;
3256 expandarg(argp, &varlist, EXP_VARTILDE);
3259 * Modify the command lookup path, if a PATH= assignment
3263 if (varequal(p, path))
3267 /* Print the command if xflag is set. */
3273 sep = eprintlist(varlist.list, sep);
3274 eprintlist(arglist.list, sep);
3282 /* Now locate the command. */
3284 const char *oldpath;
3285 int cmd_flag = DO_ERR;
3290 find_command(argv[0], &cmdentry, cmd_flag, path);
3291 if (cmdentry.cmdtype == CMDUNKNOWN) {
3297 /* implement bltin and command here */
3298 if (cmdentry.cmdtype != CMDBUILTIN)
3301 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
3302 if (cmdentry.u.cmd == EXECCMD)
3304 #ifdef CONFIG_ASH_CMDCMD
3305 if (cmdentry.u.cmd == COMMANDCMD) {
3308 nargv = parse_command_args(argv, &path);
3311 argc -= nargv - argv;
3313 cmd_flag |= DO_NOFUNC;
3321 /* We have a redirection error. */
3325 exitstatus = status;
3329 /* Execute the command. */
3330 switch (cmdentry.cmdtype) {
3332 /* Fork off a child process if necessary. */
3333 if (!(flags & EV_EXIT) || trap[0]) {
3335 jp = makejob(cmd, 1);
3336 if (forkshell(jp, cmd, FORK_FG) != 0) {
3337 exitstatus = waitforjob(jp);
3343 listsetvar(varlist.list, VEXPORT|VSTACK);
3344 shellexec(argv, path, cmdentry.u.index);
3348 cmdenviron = varlist.list;
3350 struct strlist *list = cmdenviron;
3352 if (spclbltin > 0 || argc == 0) {
3354 if (cmd_is_exec && argc > 1)
3357 listsetvar(list, i);
3359 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
3374 exit_status = j + 128;
3375 exitstatus = exit_status;
3377 if (i == EXINT || spclbltin > 0) {
3379 longjmp(handler->loc, 1);
3386 listsetvar(varlist.list, 0);
3387 if (evalfun(cmdentry.u.func, argc, argv, flags))
3393 popredir(cmd_is_exec);
3395 /* dsl: I think this is intended to be used to support
3396 * '_' in 'vi' command mode during line editing...
3397 * However I implemented that within libedit itself.
3399 setvar("_", lastarg, 0);
3400 popstackmark(&smark);
3404 evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
3405 char *volatile savecmdname;
3406 struct jmploc *volatile savehandler;
3407 struct jmploc jmploc;
3410 savecmdname = commandname;
3411 if ((i = setjmp(jmploc.loc)))
3413 savehandler = handler;
3415 commandname = argv[0];
3417 optptr = NULL; /* initialize nextopt */
3418 exitstatus = (*cmd->builtin)(argc, argv);
3421 exitstatus |= outerr(stdout);
3422 commandname = savecmdname;
3424 handler = savehandler;
3430 evalfun(struct funcnode *func, int argc, char **argv, int flags)
3432 volatile struct shparam saveparam;
3433 struct localvar *volatile savelocalvars;
3434 struct jmploc *volatile savehandler;
3435 struct jmploc jmploc;
3438 saveparam = shellparam;
3439 savelocalvars = localvars;
3440 if ((e = setjmp(jmploc.loc))) {
3444 savehandler = handler;
3447 shellparam.malloc = 0;
3450 shellparam.nparam = argc - 1;
3451 shellparam.p = argv + 1;
3452 #ifdef CONFIG_ASH_GETOPTS
3453 shellparam.optind = 1;
3454 shellparam.optoff = -1;
3457 evaltree(&func->n, flags & EV_TESTED);
3463 localvars = savelocalvars;
3464 freeparam(&shellparam);
3465 shellparam = saveparam;
3466 handler = savehandler;
3468 if (evalskip == SKIPFUNC) {
3477 * Search for a command. This is called before we fork so that the
3478 * location of the command will be available in the parent as well as
3483 prehash(union node *n)
3485 struct cmdentry entry;
3487 if (n->type == NCMD && n->ncmd.args)
3488 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
3494 * Builtin commands. Builtin commands whose functions are closely
3495 * tied to evaluation are implemented here.
3503 bltincmd(int argc, char **argv)
3506 * Preserve exitstatus of a previous possible redirection
3509 return back_exitstatus;
3514 * Handle break and continue commands. Break, continue, and return are
3515 * all handled by setting the evalskip flag. The evaluation routines
3516 * above all check this flag, and if it is set they start skipping
3517 * commands rather than executing them. The variable skipcount is
3518 * the number of loops to break/continue, or the number of function
3519 * levels to return. (The latter is always 1.) It should probably
3520 * be an error to break out of more loops than exist, but it isn't
3521 * in the standard shell so we don't make it one here.
3525 breakcmd(int argc, char **argv)
3527 int n = argc > 1 ? number(argv[1]) : 1;
3530 error(illnum, argv[1]);
3534 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
3542 * The return command.
3546 returncmd(int argc, char **argv)
3548 int ret = argc > 1 ? number(argv[1]) : exitstatus;
3551 evalskip = SKIPFUNC;
3556 /* Do what ksh does; skip the rest of the file */
3557 evalskip = SKIPFILE;
3565 falsecmd(int argc, char **argv)
3572 truecmd(int argc, char **argv)
3579 execcmd(int argc, char **argv)
3582 iflag = 0; /* exit on error */
3585 shellexec(argv + 1, pathval(), 0);
3592 eprintlist(struct strlist *sp, int sep)
3597 p = " %s" + (1 - sep);
3599 fprintf(stderr, p, sp->text);
3605 /* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */
3608 * When commands are first encountered, they are entered in a hash table.
3609 * This ensures that a full path search will not have to be done for them
3610 * on each invocation.
3612 * We should investigate converting to a linear search, even though that
3613 * would make the command name "hash" a misnomer.
3616 #define CMDTABLESIZE 31 /* should be prime */
3617 #define ARB 1 /* actual size determined at run time */
3622 struct tblentry *next; /* next entry in hash chain */
3623 union param param; /* definition of builtin function */
3624 short cmdtype; /* index identifying command */
3625 char rehash; /* if set, cd done since entry created */
3626 char cmdname[ARB]; /* name of command */
3630 static struct tblentry *cmdtable[CMDTABLESIZE];
3631 static int builtinloc = -1; /* index in path of %builtin, or -1 */
3634 static void tryexec(char *, char **, char **);
3635 static void printentry(struct tblentry *);
3636 static void clearcmdentry(int);
3637 static struct tblentry *cmdlookup(const char *, int);
3638 static void delete_cmd_entry(void);
3642 * Exec a program. Never returns. If you change this routine, you may
3643 * have to change the find_command routine as well.
3647 shellexec(char **argv, const char *path, int idx)
3654 envp = environment();
3655 if (strchr(argv[0], '/') != NULL
3656 #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3657 || find_applet_by_name(argv[0])
3660 tryexec(argv[0], argv, envp);
3664 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3665 if (--idx < 0 && pathopt == NULL) {
3666 tryexec(cmdname, argv, envp);
3667 if (errno != ENOENT && errno != ENOTDIR)
3674 /* Map to POSIX errors */
3686 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
3687 argv[0], e, suppressint ));
3688 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3694 tryexec(char *cmd, char **argv, char **envp)
3697 #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3701 #ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
3702 name = bb_get_last_path_component(name);
3703 if(find_applet_by_name(name) != NULL)
3706 if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
3715 if(strcmp(name, "busybox")) {
3716 for (ap = argv; *ap; ap++);
3717 ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
3718 *ap++ = cmd = "/bin/busybox";
3719 while ((*ap++ = *argv++));
3723 cmd = "/bin/busybox";
3731 execve(cmd, argv, envp);
3732 } while (errno == EINTR);
3734 execve(cmd, argv, envp);
3738 } else if (errno == ENOEXEC) {
3742 for (ap = argv; *ap; ap++)
3744 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
3745 *ap++ = cmd = "/bin/sh";
3746 while ((*ap++ = *argv++))
3756 * Do a path search. The variable path (passed by reference) should be
3757 * set to the start of the path before the first call; padvance will update
3758 * this value as it proceeds. Successive calls to padvance will return
3759 * the possible path expansions in sequence. If an option (indicated by
3760 * a percent sign) appears in the path entry then the global variable
3761 * pathopt will be set to point to it; otherwise pathopt will be set to
3766 padvance(const char **path, const char *name)
3776 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
3777 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
3778 while (stackblocksize() < len)
3782 memcpy(q, start, p - start);
3790 while (*p && *p != ':') p++;
3796 return stalloc(len);
3801 /*** Command hashing code ***/
3805 hashcmd(int argc, char **argv)
3807 struct tblentry **pp;
3808 struct tblentry *cmdp;
3810 struct cmdentry entry;
3813 while ((c = nextopt("r")) != '\0') {
3817 if (*argptr == NULL) {
3818 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3819 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3820 if (cmdp->cmdtype == CMDNORMAL)
3827 while ((name = *argptr) != NULL) {
3828 if ((cmdp = cmdlookup(name, 0)) != NULL
3829 && (cmdp->cmdtype == CMDNORMAL
3830 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
3832 find_command(name, &entry, DO_ERR, pathval());
3833 if (entry.cmdtype == CMDUNKNOWN)
3842 printentry(struct tblentry *cmdp)
3848 idx = cmdp->param.index;
3851 name = padvance(&path, cmdp->cmdname);
3853 } while (--idx >= 0);
3855 out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
3861 * Resolve a command name. If you change this routine, you may have to
3862 * change the shellexec routine as well.
3866 find_command(char *name, struct cmdentry *entry, int act, const char *path)
3868 struct tblentry *cmdp;
3875 struct builtincmd *bcmd;
3877 /* If name contains a slash, don't use PATH or hash table */
3878 if (strchr(name, '/') != NULL) {
3879 entry->u.index = -1;
3881 while (stat(name, &statb) < 0) {
3886 entry->cmdtype = CMDUNKNOWN;
3890 entry->cmdtype = CMDNORMAL;
3894 #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3895 if (find_applet_by_name(name)) {
3896 entry->cmdtype = CMDNORMAL;
3897 entry->u.index = -1;
3902 updatetbl = (path == pathval());
3905 if (strstr(path, "%builtin") != NULL)
3909 /* If name is in the table, check answer will be ok */
3910 if ((cmdp = cmdlookup(name, 0)) != NULL) {
3913 switch (cmdp->cmdtype) {
3931 } else if (cmdp->rehash == 0)
3932 /* if not invalidated by cd, we're done */
3936 /* If %builtin not in path, check for builtin next */
3937 bcmd = find_builtin(name);
3938 if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
3939 act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
3941 goto builtin_success;
3943 /* We have to search path. */
3944 prev = -1; /* where to start */
3945 if (cmdp && cmdp->rehash) { /* doing a rehash */
3946 if (cmdp->cmdtype == CMDBUILTIN)
3949 prev = cmdp->param.index;
3955 while ((fullname = padvance(&path, name)) != NULL) {
3956 stunalloc(fullname);
3959 if (prefix(pathopt, "builtin")) {
3961 goto builtin_success;
3963 } else if (!(act & DO_NOFUNC) &&
3964 prefix(pathopt, "func")) {
3967 /* ignore unimplemented options */
3971 /* if rehash, don't redo absolute path names */
3972 if (fullname[0] == '/' && idx <= prev) {
3975 TRACE(("searchexec \"%s\": no change\n", name));
3978 while (stat(fullname, &statb) < 0) {
3983 if (errno != ENOENT && errno != ENOTDIR)
3987 e = EACCES; /* if we fail, this will be the error */
3988 if (!S_ISREG(statb.st_mode))
3990 if (pathopt) { /* this is a %func directory */
3991 stalloc(strlen(fullname) + 1);
3992 readcmdfile(fullname);
3993 if ((cmdp = cmdlookup(name, 0)) == NULL ||
3994 cmdp->cmdtype != CMDFUNCTION)
3995 error("%s not defined in %s", name, fullname);
3996 stunalloc(fullname);
3999 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
4001 entry->cmdtype = CMDNORMAL;
4002 entry->u.index = idx;
4006 cmdp = cmdlookup(name, 1);
4007 cmdp->cmdtype = CMDNORMAL;
4008 cmdp->param.index = idx;
4013 /* We failed. If there was an entry for this command, delete it */
4014 if (cmdp && updatetbl)
4017 sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
4018 entry->cmdtype = CMDUNKNOWN;
4023 entry->cmdtype = CMDBUILTIN;
4024 entry->u.cmd = bcmd;
4028 cmdp = cmdlookup(name, 1);
4029 cmdp->cmdtype = CMDBUILTIN;
4030 cmdp->param.cmd = bcmd;
4034 entry->cmdtype = cmdp->cmdtype;
4035 entry->u = cmdp->param;
4040 * Wrapper around strcmp for qsort/bsearch/...
4042 static int pstrcmp(const void *a, const void *b)
4044 return strcmp((const char *) a, (*(const char *const *) b) + 1);
4048 * Search the table of builtin commands.
4051 static struct builtincmd *
4052 find_builtin(const char *name)
4054 struct builtincmd *bp;
4057 name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
4066 * Called when a cd is done. Marks all commands so the next time they
4067 * are executed they will be rehashed.
4073 struct tblentry **pp;
4074 struct tblentry *cmdp;
4076 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
4077 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4078 if (cmdp->cmdtype == CMDNORMAL || (
4079 cmdp->cmdtype == CMDBUILTIN &&
4080 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
4091 * Fix command hash table when PATH changed.
4092 * Called before PATH is changed. The argument is the new value of PATH;
4093 * pathval() still returns the old value at this point.
4094 * Called with interrupts off.
4098 changepath(const char *newval)
4100 const char *old, *new;
4107 firstchange = 9999; /* assume no change */
4113 if ((*old == '\0' && *new == ':')
4114 || (*old == ':' && *new == '\0'))
4116 old = new; /* ignore subsequent differences */
4120 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
4127 if (builtinloc < 0 && idx_bltin >= 0)
4128 builtinloc = idx_bltin; /* zap builtins */
4129 if (builtinloc >= 0 && idx_bltin < 0)
4131 clearcmdentry(firstchange);
4132 builtinloc = idx_bltin;
4137 * Clear out command entries. The argument specifies the first entry in
4138 * PATH which has changed.
4142 clearcmdentry(int firstchange)
4144 struct tblentry **tblp;
4145 struct tblentry **pp;
4146 struct tblentry *cmdp;
4149 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4151 while ((cmdp = *pp) != NULL) {
4152 if ((cmdp->cmdtype == CMDNORMAL &&
4153 cmdp->param.index >= firstchange)
4154 || (cmdp->cmdtype == CMDBUILTIN &&
4155 builtinloc >= firstchange)) {
4169 * Locate a command in the command hash table. If "add" is nonzero,
4170 * add the command to the table if it is not already present. The
4171 * variable "lastcmdentry" is set to point to the address of the link
4172 * pointing to the entry, so that delete_cmd_entry can delete the
4175 * Interrupts must be off if called with add != 0.
4178 static struct tblentry **lastcmdentry;
4181 static struct tblentry *
4182 cmdlookup(const char *name, int add)
4184 unsigned int hashval;
4186 struct tblentry *cmdp;
4187 struct tblentry **pp;
4190 hashval = (unsigned char)*p << 4;
4192 hashval += (unsigned char)*p++;
4194 pp = &cmdtable[hashval % CMDTABLESIZE];
4195 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4196 if (equal(cmdp->cmdname, name))
4200 if (add && cmdp == NULL) {
4201 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
4202 + strlen(name) + 1);
4204 cmdp->cmdtype = CMDUNKNOWN;
4205 strcpy(cmdp->cmdname, name);
4212 * Delete the command entry returned on the last lookup.
4216 delete_cmd_entry(void)
4218 struct tblentry *cmdp;
4221 cmdp = *lastcmdentry;
4222 *lastcmdentry = cmdp->next;
4223 if (cmdp->cmdtype == CMDFUNCTION)
4224 freefunc(cmdp->param.func);
4231 * Add a new command entry, replacing any existing command entry for
4232 * the same name - except special builtins.
4236 addcmdentry(char *name, struct cmdentry *entry)
4238 struct tblentry *cmdp;
4240 cmdp = cmdlookup(name, 1);
4241 if (cmdp->cmdtype == CMDFUNCTION) {
4242 freefunc(cmdp->param.func);
4244 cmdp->cmdtype = entry->cmdtype;
4245 cmdp->param = entry->u;
4250 * Make a copy of a parse tree.
4253 static inline struct funcnode *
4254 copyfunc(union node *n)
4259 funcblocksize = offsetof(struct funcnode, n);
4262 blocksize = funcblocksize;
4263 f = ckmalloc(blocksize + funcstringsize);
4264 funcblock = (char *) f + offsetof(struct funcnode, n);
4265 funcstring = (char *) f + blocksize;
4272 * Define a shell function.
4276 defun(char *name, union node *func)
4278 struct cmdentry entry;
4281 entry.cmdtype = CMDFUNCTION;
4282 entry.u.func = copyfunc(func);
4283 addcmdentry(name, &entry);
4289 * Delete a function if it exists.
4293 unsetfunc(const char *name)
4295 struct tblentry *cmdp;
4297 if ((cmdp = cmdlookup(name, 0)) != NULL &&
4298 cmdp->cmdtype == CMDFUNCTION)
4303 * Locate and print what a word is...
4307 #ifdef CONFIG_ASH_CMDCMD
4309 describe_command(char *command, int describe_command_verbose)
4311 #define describe_command_verbose 1
4313 describe_command(char *command)
4316 struct cmdentry entry;
4317 struct tblentry *cmdp;
4318 #ifdef CONFIG_ASH_ALIAS
4319 const struct alias *ap;
4321 const char *path = pathval();
4323 if (describe_command_verbose) {
4327 /* First look at the keywords */
4328 if (findkwd(command)) {
4329 out1str(describe_command_verbose ? " is a shell keyword" : command);
4333 #ifdef CONFIG_ASH_ALIAS
4334 /* Then look at the aliases */
4335 if ((ap = lookupalias(command, 0)) != NULL) {
4336 if (describe_command_verbose) {
4337 out1fmt(" is an alias for %s", ap->val);
4346 /* Then check if it is a tracked alias */
4347 if ((cmdp = cmdlookup(command, 0)) != NULL) {
4348 entry.cmdtype = cmdp->cmdtype;
4349 entry.u = cmdp->param;
4351 /* Finally use brute force */
4352 find_command(command, &entry, DO_ABS, path);
4355 switch (entry.cmdtype) {
4357 int j = entry.u.index;
4363 p = padvance(&path, command);
4367 if (describe_command_verbose) {
4369 (cmdp ? " a tracked alias for" : nullstr), p
4378 if (describe_command_verbose) {
4379 out1str(" is a shell function");
4386 if (describe_command_verbose) {
4387 out1fmt(" is a %sshell builtin",
4388 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
4389 "special " : nullstr
4397 if (describe_command_verbose) {
4398 out1str(": not found\n");
4409 typecmd(int argc, char **argv)
4414 for (i = 1; i < argc; i++) {
4415 #ifdef CONFIG_ASH_CMDCMD
4416 err |= describe_command(argv[i], 1);
4418 err |= describe_command(argv[i]);
4424 #ifdef CONFIG_ASH_CMDCMD
4426 commandcmd(int argc, char **argv)
4429 int default_path = 0;
4430 int verify_only = 0;
4431 int verbose_verify_only = 0;
4433 while ((c = nextopt("pvV")) != '\0')
4438 "command: nextopt returned character code 0%o\n", c);
4448 verbose_verify_only = 1;
4452 if (default_path + verify_only + verbose_verify_only > 1 ||
4455 "command [-p] command [arg ...]\n"
4456 "command {-v|-V} command\n");
4460 if (verify_only || verbose_verify_only) {
4461 return describe_command(*argptr, verbose_verify_only);
4468 /* $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $ */
4471 * Routines to expand arguments to commands. We have to deal with
4472 * backquotes, shell variables, and file metacharacters.
4478 #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4479 #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4480 #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4481 #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4482 #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4485 * Structure specifying which parts of the string should be searched
4486 * for IFS characters.
4490 struct ifsregion *next; /* next region in list */
4491 int begoff; /* offset of start of region */
4492 int endoff; /* offset of end of region */
4493 int nulonly; /* search for nul bytes only */
4496 /* output of current string */
4497 static char *expdest;
4498 /* list of back quote expressions */
4499 static struct nodelist *argbackq;
4500 /* first struct in list of ifs regions */
4501 static struct ifsregion ifsfirst;
4502 /* last struct in list */
4503 static struct ifsregion *ifslastp;
4504 /* holds expanded arg list */
4505 static struct arglist exparg;
4507 static void argstr(char *, int);
4508 static char *exptilde(char *, char *, int);
4509 static void expbackq(union node *, int, int);
4510 static const char *subevalvar(char *, char *, int, int, int, int, int);
4511 static char *evalvar(char *, int);
4512 static int varisset(char *, int);
4513 static void strtodest(const char *, int, int);
4514 static void memtodest(const char *p, size_t len, int syntax, int quotes);
4515 static void varvalue(char *, int, int);
4516 static void recordregion(int, int, int);
4517 static void removerecordregions(int);
4518 static void ifsbreakup(char *, struct arglist *);
4519 static void ifsfree(void);
4520 static void expandmeta(struct strlist *, int);
4521 static int patmatch(char *, const char *);
4523 static int cvtnum(long);
4524 static size_t esclen(const char *, const char *);
4525 static char *scanleft(char *, char *, char *, char *, int, int);
4526 static char *scanright(char *, char *, char *, char *, int, int);
4527 static void varunset(const char *, const char *, const char *, int)
4528 __attribute__((__noreturn__));
4531 #define pmatch(a, b) !fnmatch((a), (b), 0)
4533 * Prepare a pattern for a expmeta (internal glob(3)) call.
4535 * Returns an stalloced string.
4538 static inline char *
4539 preglob(const char *pattern, int quoted, int flag) {
4540 flag |= RMESCAPE_GLOB;
4542 flag |= RMESCAPE_QUOTED;
4544 return _rmescapes((char *)pattern, flag);
4549 esclen(const char *start, const char *p) {
4552 while (p > start && *--p == CTLESC) {
4560 * Expand shell variables and backquotes inside a here document.
4564 expandhere(union node *arg, int fd)
4567 expandarg(arg, (struct arglist *)NULL, 0);
4568 xwrite(fd, stackblock(), expdest - (char *)stackblock());
4573 * Perform variable substitution and command substitution on an argument,
4574 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4575 * perform splitting and file name expansion. When arglist is NULL, perform
4576 * here document expansion.
4580 expandarg(union node *arg, struct arglist *arglist, int flag)
4585 argbackq = arg->narg.backquote;
4586 STARTSTACKSTR(expdest);
4587 ifsfirst.next = NULL;
4589 argstr(arg->narg.text, flag);
4590 if (arglist == NULL) {
4591 return; /* here document expanded */
4593 STPUTC('\0', expdest);
4594 p = grabstackstr(expdest);
4595 exparg.lastp = &exparg.list;
4599 if (flag & EXP_FULL) {
4600 ifsbreakup(p, &exparg);
4601 *exparg.lastp = NULL;
4602 exparg.lastp = &exparg.list;
4603 expandmeta(exparg.list, flag);
4605 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
4607 sp = (struct strlist *)stalloc(sizeof (struct strlist));
4610 exparg.lastp = &sp->next;
4614 *exparg.lastp = NULL;
4616 *arglist->lastp = exparg.list;
4617 arglist->lastp = exparg.lastp;
4623 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4624 * characters to allow for further processing. Otherwise treat
4625 * $@ like $* since no splitting will be performed.
4629 argstr(char *p, int flag)
4631 static const char spclchars[] = {
4639 CTLBACKQ | CTLQUOTE,
4640 #ifdef CONFIG_ASH_MATH_SUPPORT
4645 const char *reject = spclchars;
4647 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
4648 int breakall = flag & EXP_WORD;
4653 if (!(flag & EXP_VARTILDE)) {
4655 } else if (flag & EXP_VARTILDE2) {
4660 if (flag & EXP_TILDE) {
4666 if (*q == CTLESC && (flag & EXP_QWORD))
4669 p = exptilde(p, q, flag);
4672 startloc = expdest - (char *)stackblock();
4674 length += strcspn(p + length, reject);
4676 if (c && (!(c & 0x80)
4677 #ifdef CONFIG_ASH_MATH_SUPPORT
4681 /* c == '=' || c == ':' || c == CTLENDARI */
4686 expdest = stnputs(p, length, expdest);
4687 newloc = expdest - (char *)stackblock();
4688 if (breakall && !inquotes && newloc > startloc) {
4689 recordregion(startloc, newloc, 0);
4700 if (flag & EXP_VARTILDE2) {
4704 flag |= EXP_VARTILDE2;
4709 * sort of a hack - expand tildes in variable
4710 * assignments (after the first '=' and after ':'s).
4719 case CTLENDVAR: /* ??? */
4722 /* "$@" syntax adherence hack */
4725 !memcmp(p, dolatstr, DOLATSTRLEN) &&
4726 (p[4] == CTLQUOTEMARK || (
4727 p[4] == CTLENDVAR &&
4728 p[5] == CTLQUOTEMARK
4731 p = evalvar(p + 1, flag) + 1;
4734 inquotes = !inquotes;
4747 p = evalvar(p, flag);
4751 case CTLBACKQ|CTLQUOTE:
4752 expbackq(argbackq->n, c, quotes);
4753 argbackq = argbackq->next;
4755 #ifdef CONFIG_ASH_MATH_SUPPORT
4768 exptilde(char *startp, char *p, int flag)
4774 int quotes = flag & (EXP_FULL | EXP_CASE);
4779 while ((c = *++p) != '\0') {
4786 if (flag & EXP_VARTILDE)
4796 if (*name == '\0') {
4797 if ((home = lookupvar(homestr)) == NULL)
4800 if ((pw = getpwnam(name)) == NULL)
4807 startloc = expdest - (char *)stackblock();
4808 strtodest(home, SQSYNTAX, quotes);
4809 recordregion(startloc, expdest - (char *)stackblock(), 0);
4818 removerecordregions(int endoff)
4820 if (ifslastp == NULL)
4823 if (ifsfirst.endoff > endoff) {
4824 while (ifsfirst.next != NULL) {
4825 struct ifsregion *ifsp;
4827 ifsp = ifsfirst.next->next;
4828 ckfree(ifsfirst.next);
4829 ifsfirst.next = ifsp;
4832 if (ifsfirst.begoff > endoff)
4835 ifslastp = &ifsfirst;
4836 ifsfirst.endoff = endoff;
4841 ifslastp = &ifsfirst;
4842 while (ifslastp->next && ifslastp->next->begoff < endoff)
4843 ifslastp=ifslastp->next;
4844 while (ifslastp->next != NULL) {
4845 struct ifsregion *ifsp;
4847 ifsp = ifslastp->next->next;
4848 ckfree(ifslastp->next);
4849 ifslastp->next = ifsp;
4852 if (ifslastp->endoff > endoff)
4853 ifslastp->endoff = endoff;
4857 #ifdef CONFIG_ASH_MATH_SUPPORT
4859 * Expand arithmetic expression. Backup to start of expression,
4860 * evaluate, place result in (backed up) result, adjust string position.
4873 * This routine is slightly over-complicated for
4874 * efficiency. Next we scan backwards looking for the
4875 * start of arithmetic.
4877 start = stackblock();
4884 while (*p != CTLARI) {
4888 error("missing CTLARI (shouldn't happen)");
4893 esc = esclen(start, p);
4903 removerecordregions(begoff);
4912 len = cvtnum(dash_arith(p + 2));
4915 recordregion(begoff, begoff + len, 0);
4920 * Expand stuff in backwards quotes.
4924 expbackq(union node *cmd, int quoted, int quotes)
4932 int syntax = quoted? DQSYNTAX : BASESYNTAX;
4933 struct stackmark smark;
4936 setstackmark(&smark);
4938 startloc = dest - (char *)stackblock();
4940 evalbackcmd(cmd, (struct backcmd *) &in);
4941 popstackmark(&smark);
4948 memtodest(p, i, syntax, quotes);
4952 i = safe_read(in.fd, buf, sizeof buf);
4953 TRACE(("expbackq: read returns %d\n", i));
4963 back_exitstatus = waitforjob(in.jp);
4967 /* Eat all trailing newlines */
4969 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
4974 recordregion(startloc, dest - (char *)stackblock(), 0);
4975 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4976 (dest - (char *)stackblock()) - startloc,
4977 (dest - (char *)stackblock()) - startloc,
4978 stackblock() + startloc));
4983 scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
4994 const char *s = loc2;
5000 match = pmatch(str, s);
5004 if (quotes && *loc == CTLESC)
5014 scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5021 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5024 const char *s = loc2;
5029 match = pmatch(str, s);
5036 esc = esclen(startp, loc);
5048 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5052 int saveherefd = herefd;
5053 struct nodelist *saveargbackq = argbackq;
5055 char *rmesc, *rmescend;
5057 char *(*scan)(char *, char *, char *, char *, int , int);
5060 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5061 STPUTC('\0', expdest);
5062 herefd = saveherefd;
5063 argbackq = saveargbackq;
5064 startp = stackblock() + startloc;
5068 setvar(str, startp, 0);
5069 amount = startp - expdest;
5070 STADJUST(amount, expdest);
5074 varunset(p, str, startp, varflags);
5078 subtype -= VSTRIMRIGHT;
5080 if (subtype < 0 || subtype > 3)
5085 rmescend = stackblock() + strloc;
5087 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5088 if (rmesc != startp) {
5090 startp = stackblock() + startloc;
5094 str = stackblock() + strloc;
5095 preglob(str, varflags & VSQUOTE, 0);
5097 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5098 zero = subtype >> 1;
5099 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5100 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5102 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5105 memmove(startp, loc, str - loc);
5106 loc = startp + (str - loc) - 1;
5109 amount = loc - expdest;
5110 STADJUST(amount, expdest);
5117 * Expand a variable, and return a pointer to the next character in the
5121 evalvar(char *p, int flag)
5135 quotes = flag & (EXP_FULL | EXP_CASE);
5137 subtype = varflags & VSTYPE;
5138 quoted = varflags & VSQUOTE;
5140 easy = (!quoted || (*var == '@' && shellparam.nparam));
5142 startloc = expdest - (char *)stackblock();
5143 p = strchr(p, '=') + 1;
5145 if (!is_name(*var)) {
5146 set = varisset(var, varflags & VSNUL);
5148 if (subtype == VSPLUS)
5151 varvalue(var, quoted, flag);
5152 if (subtype == VSLENGTH) {
5154 expdest - (char *)stackblock() -
5156 STADJUST(-varlen, expdest);
5163 /* jump here after setting a variable with ${var=text} */
5164 val = lookupvar(var);
5165 set = !val || ((varflags & VSNUL) && !*val);
5166 if (subtype == VSPLUS)
5169 varlen = strlen(val);
5170 if (subtype == VSLENGTH)
5173 val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
5180 if (subtype == VSMINUS) {
5184 p, flag | EXP_TILDE |
5185 (quoted ? EXP_QWORD : EXP_WORD)
5194 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5196 if (subevalvar(p, var, 0, subtype, startloc,
5200 * Remove any recorded regions beyond
5203 removerecordregions(startloc);
5214 varunset(p, var, 0, 0);
5216 if (subtype == VSLENGTH) {
5222 if (subtype == VSNORMAL) {
5226 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5235 case VSTRIMRIGHTMAX:
5244 * Terminate the string and start recording the pattern
5247 STPUTC('\0', expdest);
5248 patloc = expdest - (char *)stackblock();
5249 if (subevalvar(p, NULL, patloc, subtype,
5250 startloc, varflags, quotes) == 0) {
5251 int amount = expdest - (
5252 (char *)stackblock() + patloc - 1
5254 STADJUST(-amount, expdest);
5256 /* Remove any recorded regions beyond start of variable */
5257 removerecordregions(startloc);
5262 if (subtype != VSNORMAL) { /* skip to end of alternative */
5265 if ((c = *p++) == CTLESC)
5267 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5269 argbackq = argbackq->next;
5270 } else if (c == CTLVAR) {
5271 if ((*p++ & VSTYPE) != VSNORMAL)
5273 } else if (c == CTLENDVAR) {
5285 * Test whether a specialized variable is set.
5289 varisset(char *name, int nulok)
5292 return backgndpid != 0;
5293 else if (*name == '@' || *name == '*') {
5294 if (*shellparam.p == NULL)
5300 for (av = shellparam.p; *av; av++)
5305 } else if (is_digit(*name)) {
5307 int num = atoi(name);
5309 if (num > shellparam.nparam)
5315 ap = shellparam.p[num - 1];
5317 if (nulok && (ap == NULL || *ap == '\0'))
5325 * Put a string on the stack.
5329 memtodest(const char *p, size_t len, int syntax, int quotes) {
5332 q = makestrspace(len * 2, q);
5338 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5348 strtodest(const char *p, int syntax, int quotes)
5350 memtodest(p, strlen(p), syntax, quotes);
5355 * Add the value of a specialized variable to the stack string.
5359 varvalue(char *name, int quoted, int flags)
5368 int allow_split = flags & EXP_FULL;
5369 int quotes = flags & (EXP_FULL | EXP_CASE);
5371 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5380 num = shellparam.nparam;
5388 for (i = 0 ; i < NOPTS ; i++) {
5390 STPUTC(optletters(i), expdest);
5394 if (allow_split && quoted) {
5395 sep = 1 << CHAR_BIT;
5400 sep = ifsset() ? ifsval()[0] : ' ';
5402 sepq = (SIT(sep, syntax) == CCTL) || (SIT(sep, syntax) == CBACK);
5405 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
5406 strtodest(p, syntax, quotes);
5417 strtodest(arg0, syntax, quotes);
5421 if (num > 0 && num <= shellparam.nparam) {
5422 strtodest(shellparam.p[num - 1], syntax, quotes);
5430 * Record the fact that we have to scan this region of the
5431 * string for IFS characters.
5435 recordregion(int start, int end, int nulonly)
5437 struct ifsregion *ifsp;
5439 if (ifslastp == NULL) {
5443 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
5445 ifslastp->next = ifsp;
5449 ifslastp->begoff = start;
5450 ifslastp->endoff = end;
5451 ifslastp->nulonly = nulonly;
5456 * Break the argument string into pieces based upon IFS and add the
5457 * strings to the argument list. The regions of the string to be
5458 * searched for IFS characters have been stored by recordregion.
5461 ifsbreakup(char *string, struct arglist *arglist)
5463 struct ifsregion *ifsp;
5468 const char *ifs, *realifs;
5474 if (ifslastp != NULL) {
5477 realifs = ifsset() ? ifsval() : defifs;
5480 p = string + ifsp->begoff;
5481 nulonly = ifsp->nulonly;
5482 ifs = nulonly ? nullstr : realifs;
5484 while (p < string + ifsp->endoff) {
5488 if (strchr(ifs, *p)) {
5490 ifsspc = (strchr(defifs, *p) != NULL);
5491 /* Ignore IFS whitespace at start */
5492 if (q == start && ifsspc) {
5498 sp = (struct strlist *)stalloc(sizeof *sp);
5500 *arglist->lastp = sp;
5501 arglist->lastp = &sp->next;
5505 if (p >= string + ifsp->endoff) {
5511 if (strchr(ifs, *p) == NULL ) {
5514 } else if (strchr(defifs, *p) == NULL) {
5530 } while ((ifsp = ifsp->next) != NULL);
5539 sp = (struct strlist *)stalloc(sizeof *sp);
5541 *arglist->lastp = sp;
5542 arglist->lastp = &sp->next;
5548 struct ifsregion *p;
5553 struct ifsregion *ifsp;
5559 ifsfirst.next = NULL;
5563 static void expmeta(char *, char *);
5564 static struct strlist *expsort(struct strlist *);
5565 static struct strlist *msort(struct strlist *, int);
5567 static char *expdir;
5571 expandmeta(struct strlist *str, int flag)
5573 static const char metachars[] = {
5576 /* TODO - EXP_REDIR */
5579 struct strlist **savelastp;
5585 if (!strpbrk(str->text, metachars))
5587 savelastp = exparg.lastp;
5590 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
5592 int i = strlen(str->text);
5593 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
5601 if (exparg.lastp == savelastp) {
5606 *exparg.lastp = str;
5607 rmescapes(str->text);
5608 exparg.lastp = &str->next;
5610 *exparg.lastp = NULL;
5611 *savelastp = sp = expsort(*savelastp);
5612 while (sp->next != NULL)
5614 exparg.lastp = &sp->next;
5621 * Add a file name to the list.
5625 addfname(const char *name)
5629 sp = (struct strlist *)stalloc(sizeof *sp);
5630 sp->text = sstrdup(name);
5632 exparg.lastp = &sp->next;
5637 * Do metacharacter (i.e. *, ?, [...]) expansion.
5641 expmeta(char *enddir, char *name)
5648 struct stat64 statb;
5656 for (p = name; *p; p++) {
5657 if (*p == '*' || *p == '?')
5659 else if (*p == '[') {
5666 if (*q == '/' || *q == '\0')
5673 } else if (*p == '\\')
5675 else if (*p == '/') {
5682 if (metaflag == 0) { /* we've reached the end of the file name */
5683 if (enddir != expdir)
5691 if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
5702 } while (p < start);
5704 if (enddir == expdir) {
5706 } else if (enddir == expdir + 1 && *expdir == '/') {
5712 if ((dirp = opendir(cp)) == NULL)
5714 if (enddir != expdir)
5716 if (*endname == 0) {
5728 while (! intpending && (dp = readdir(dirp)) != NULL) {
5729 if (dp->d_name[0] == '.' && ! matchdot)
5731 if (pmatch(start, dp->d_name)) {
5733 scopy(dp->d_name, enddir);
5736 for (p = enddir, cp = dp->d_name;
5737 (*p++ = *cp++) != '\0';)
5740 expmeta(p, endname);
5750 * Sort the results of file name expansion. It calculates the number of
5751 * strings to sort and then calls msort (short for merge sort) to do the
5755 static struct strlist *
5756 expsort(struct strlist *str)
5762 for (sp = str ; sp ; sp = sp->next)
5764 return msort(str, len);
5768 static struct strlist *
5769 msort(struct strlist *list, int len)
5771 struct strlist *p, *q = NULL;
5772 struct strlist **lpp;
5780 for (n = half ; --n >= 0 ; ) {
5784 q->next = NULL; /* terminate first half of list */
5785 q = msort(list, half); /* sort first half of list */
5786 p = msort(p, len - half); /* sort second half */
5789 #ifdef CONFIG_LOCALE_SUPPORT
5790 if (strcoll(p->text, q->text) < 0)
5792 if (strcmp(p->text, q->text) < 0)
5797 if ((p = *lpp) == NULL) {
5804 if ((q = *lpp) == NULL) {
5815 * Returns true if the pattern matches the string.
5819 patmatch(char *pattern, const char *string)
5821 return pmatch(preglob(pattern, 0, 0), string);
5826 * Remove any CTLESC characters from a string.
5830 _rmescapes(char *str, int flag)
5833 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5838 p = strpbrk(str, qchars);
5844 if (flag & RMESCAPE_ALLOC) {
5845 size_t len = p - str;
5846 size_t fulllen = len + strlen(p) + 1;
5848 if (flag & RMESCAPE_GROW) {
5849 r = makestrspace(fulllen, expdest);
5850 } else if (flag & RMESCAPE_HEAP) {
5851 r = ckmalloc(fulllen);
5853 r = stalloc(fulllen);
5857 q = mempcpy(q, str, len);
5860 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5861 globbing = flag & RMESCAPE_GLOB;
5862 notescaped = globbing;
5864 if (*p == CTLQUOTEMARK) {
5865 inquotes = ~inquotes;
5867 notescaped = globbing;
5871 /* naked back slash */
5877 if (notescaped && inquotes && *p != '/') {
5881 notescaped = globbing;
5886 if (flag & RMESCAPE_GROW) {
5888 STADJUST(q - r + 1, expdest);
5895 * See if a pattern matches in a case statement.
5899 casematch(union node *pattern, char *val)
5901 struct stackmark smark;
5904 setstackmark(&smark);
5905 argbackq = pattern->narg.backquote;
5906 STARTSTACKSTR(expdest);
5908 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5909 STACKSTRNUL(expdest);
5910 result = patmatch(stackblock(), val);
5911 popstackmark(&smark);
5924 expdest = makestrspace(32, expdest);
5925 len = fmtstr(expdest, 32, "%ld", num);
5926 STADJUST(len, expdest);
5931 varunset(const char *end, const char *var, const char *umsg, int varflags)
5937 msg = "parameter not set";
5939 if (*end == CTLENDVAR) {
5940 if (varflags & VSNUL)
5945 error("%.*s: %s%s", end - var - 1, var, msg, tail);
5949 /* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
5952 * This implements the input routines used by the parser.
5955 #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
5956 #define IBUFSIZ (BUFSIZ + 1)
5958 static void pushfile(void);
5961 * Read a line from the script.
5964 static inline char *
5965 pfgets(char *line, int len)
5971 while (--nleft > 0) {
5988 * Read a character from the script, returning PEOF on end of file.
5989 * Nul characters in the input are silently discarded.
5992 #define pgetc_as_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
5994 #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
5995 #define pgetc_macro() pgetc()
5999 return pgetc_as_macro();
6002 #define pgetc_macro() pgetc_as_macro()
6006 return pgetc_macro();
6012 * Same as pgetc(), but ignores PEOA.
6014 #ifdef CONFIG_ASH_ALIAS
6015 static int pgetc2(void)
6021 } while (c == PEOA);
6025 static inline int pgetc2(void)
6027 return pgetc_macro();
6032 #ifdef CONFIG_FEATURE_COMMAND_EDITING
6033 static const char *cmdedit_prompt;
6034 static inline void putprompt(const char *s)
6039 static inline void putprompt(const char *s)
6049 char *buf = parsefile->buf;
6053 #ifdef CONFIG_FEATURE_COMMAND_EDITING
6054 if (!iflag || parsefile->fd)
6055 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6057 nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
6059 /* Ctrl+C presend */
6064 /* Ctrl+D presend */
6069 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6073 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6074 int flags = fcntl(0, F_GETFL, 0);
6075 if (flags >= 0 && flags & O_NONBLOCK) {
6076 flags &=~ O_NONBLOCK;
6077 if (fcntl(0, F_SETFL, flags) >= 0) {
6078 out2str("sh: turning off NDELAY mode\n");
6088 * Refill the input buffer and return the next input character:
6090 * 1) If a string was pushed back on the input, pop it;
6091 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6092 * from a string so we can't refill the buffer, return EOF.
6093 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6094 * 4) Process input up to the next newline, deleting nul characters.
6104 while (parsefile->strpush) {
6105 #ifdef CONFIG_ASH_ALIAS
6106 if (parsenleft == -1 && parsefile->strpush->ap &&
6107 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
6112 if (--parsenleft >= 0)
6113 return (*parsenextc++);
6115 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6120 if (parselleft <= 0) {
6121 if ((parselleft = preadfd()) <= 0) {
6122 parselleft = parsenleft = EOF_NLEFT;
6129 /* delete nul characters */
6130 for (more = 1; more;) {
6137 parsenleft = q - parsenextc;
6138 more = 0; /* Stop processing here */
6145 if (--parselleft <= 0 && more) {
6146 parsenleft = q - parsenextc - 1;
6157 out2str(parsenextc);
6163 return *parsenextc++;
6167 * Undo the last call to pgetc. Only one character may be pushed back.
6168 * PEOF may be pushed back.
6179 * Push a string back onto the input at this current parsefile level.
6180 * We handle aliases this way.
6183 pushstring(char *s, void *ap)
6190 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6191 if (parsefile->strpush) {
6192 sp = ckmalloc(sizeof (struct strpush));
6193 sp->prev = parsefile->strpush;
6194 parsefile->strpush = sp;
6196 sp = parsefile->strpush = &(parsefile->basestrpush);
6197 sp->prevstring = parsenextc;
6198 sp->prevnleft = parsenleft;
6199 #ifdef CONFIG_ASH_ALIAS
6200 sp->ap = (struct alias *)ap;
6202 ((struct alias *)ap)->flag |= ALIASINUSE;
6214 struct strpush *sp = parsefile->strpush;
6217 #ifdef CONFIG_ASH_ALIAS
6219 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6220 checkkwd |= CHKALIAS;
6222 if (sp->string != sp->ap->val) {
6225 sp->ap->flag &= ~ALIASINUSE;
6226 if (sp->ap->flag & ALIASDEAD) {
6227 unalias(sp->ap->name);
6231 parsenextc = sp->prevstring;
6232 parsenleft = sp->prevnleft;
6233 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6234 parsefile->strpush = sp->prev;
6235 if (sp != &(parsefile->basestrpush))
6241 * Set the input to take input from a file. If push is set, push the
6242 * old input onto the stack first.
6246 setinputfile(const char *fname, int push)
6252 if ((fd = open(fname, O_RDONLY)) < 0)
6253 error("Can't open %s", fname);
6255 fd2 = copyfd(fd, 10);
6258 error("Out of file descriptors");
6261 setinputfd(fd, push);
6267 * Like setinputfile, but takes an open file descriptor. Call this with
6272 setinputfd(int fd, int push)
6274 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
6280 if (parsefile->buf == NULL)
6281 parsefile->buf = ckmalloc(IBUFSIZ);
6282 parselleft = parsenleft = 0;
6288 * Like setinputfile, but takes input from a string.
6292 setinputstring(char *string)
6296 parsenextc = string;
6297 parsenleft = strlen(string);
6298 parsefile->buf = NULL;
6305 * To handle the "." command, a stack of input files is used. Pushfile
6306 * adds a new entry to the stack and popfile restores the previous level.
6312 struct parsefile *pf;
6314 parsefile->nleft = parsenleft;
6315 parsefile->lleft = parselleft;
6316 parsefile->nextc = parsenextc;
6317 parsefile->linno = plinno;
6318 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
6319 pf->prev = parsefile;
6322 pf->basestrpush.prev = NULL;
6330 struct parsefile *pf = parsefile;
6339 parsefile = pf->prev;
6341 parsenleft = parsefile->nleft;
6342 parselleft = parsefile->lleft;
6343 parsenextc = parsefile->nextc;
6344 plinno = parsefile->linno;
6350 * Return to top level.
6356 while (parsefile != &basepf)
6362 * Close the file(s) that the shell is reading commands from. Called
6363 * after a fork is done.
6370 if (parsefile->fd > 0) {
6371 close(parsefile->fd);
6376 /* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
6378 /* mode flags for set_curjob */
6379 #define CUR_DELETE 2
6380 #define CUR_RUNNING 1
6381 #define CUR_STOPPED 0
6383 /* mode flags for dowait */
6384 #define DOWAIT_NORMAL 0
6385 #define DOWAIT_BLOCK 1
6388 static struct job *jobtab;
6390 static unsigned njobs;
6392 /* pgrp of shell on invocation */
6393 static int initialpgrp;
6394 static int ttyfd = -1;
6397 static struct job *curjob;
6398 /* number of presumed living untracked jobs */
6401 static void set_curjob(struct job *, unsigned);
6403 static int restartjob(struct job *, int);
6404 static void xtcsetpgrp(int, pid_t);
6405 static char *commandtext(union node *);
6406 static void cmdlist(union node *, int);
6407 static void cmdtxt(union node *);
6408 static void cmdputs(const char *);
6409 static void showpipe(struct job *, FILE *);
6411 static int sprint_status(char *, int, int);
6412 static void freejob(struct job *);
6413 static struct job *getjob(const char *, int);
6414 static struct job *growjobtab(void);
6415 static void forkchild(struct job *, union node *, int);
6416 static void forkparent(struct job *, union node *, int, pid_t);
6417 static int dowait(int, struct job *);
6418 static int getstatus(struct job *);
6421 set_curjob(struct job *jp, unsigned mode)
6424 struct job **jpp, **curp;
6426 /* first remove from list */
6427 jpp = curp = &curjob;
6432 jpp = &jp1->prev_job;
6434 *jpp = jp1->prev_job;
6436 /* Then re-insert in correct position */
6444 /* job being deleted */
6447 /* newly created job or backgrounded job,
6448 put after all stopped jobs. */
6452 if (!jp1 || jp1->state != JOBSTOPPED)
6455 jpp = &jp1->prev_job;
6461 /* newly stopped job - becomes curjob */
6462 jp->prev_job = *jpp;
6470 * Turn job control on and off.
6472 * Note: This code assumes that the third arg to ioctl is a character
6473 * pointer, which is true on Berkeley systems but not System V. Since
6474 * System V doesn't have job control yet, this isn't a problem now.
6476 * Called with interrupts off.
6485 if (on == jobctl || rootshell == 0)
6489 ofd = fd = open(_PATH_TTY, O_RDWR);
6492 while (!isatty(fd) && --fd >= 0)
6495 fd = fcntl(fd, F_DUPFD, 10);
6499 fcntl(fd, F_SETFD, FD_CLOEXEC);
6500 do { /* while we are in the background */
6501 if ((pgrp = tcgetpgrp(fd)) < 0) {
6503 sh_warnx("can't access tty; job control turned off");
6507 if (pgrp == getpgrp())
6518 xtcsetpgrp(fd, pgrp);
6520 /* turning job control off */
6523 xtcsetpgrp(fd, pgrp);
6537 killcmd(int argc, char **argv)
6548 "Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6549 "kill -l [exitstatus]"
6553 if (**++argv == '-') {
6554 signo = decode_signal(*argv + 1, 1);
6558 while ((c = nextopt("ls:")) != '\0')
6568 signo = decode_signal(optionarg, 1);
6571 "invalid signal number or name: %s",
6582 if (!list && signo < 0)
6585 if ((signo < 0 || !*argv) ^ list) {
6593 for (i = 1; i < NSIG; i++) {
6594 name = u_signal_names(0, &i, 1);
6596 out1fmt(snlfmt, name);
6600 name = u_signal_names(*argptr, &signo, -1);
6602 out1fmt(snlfmt, name);
6604 error("invalid signal number or exit status: %s", *argptr);
6610 if (**argv == '%') {
6611 jp = getjob(*argv, 0);
6612 pid = -jp->ps[0].pid;
6614 pid = number(*argv);
6615 if (kill(pid, signo) != 0) {
6625 #if defined(JOBS) || defined(DEBUG)
6627 jobno(const struct job *jp)
6629 return jp - jobtab + 1;
6635 fgcmd(int argc, char **argv)
6642 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
6647 jp = getjob(*argv, 1);
6648 if (mode == FORK_BG) {
6649 set_curjob(jp, CUR_RUNNING);
6650 fprintf(out, "[%d] ", jobno(jp));
6652 outstr(jp->ps->cmd, out);
6654 retval = restartjob(jp, mode);
6655 } while (*argv && *++argv);
6659 static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
6663 restartjob(struct job *jp, int mode)
6665 struct procstat *ps;
6671 if (jp->state == JOBDONE)
6673 jp->state = JOBRUNNING;
6675 if (mode == FORK_FG)
6676 xtcsetpgrp(ttyfd, pgid);
6677 killpg(pgid, SIGCONT);
6681 if (WIFSTOPPED(ps->status)) {
6684 } while (ps++, --i);
6686 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
6693 sprint_status(char *s, int status, int sigonly)
6699 st = WEXITSTATUS(status);
6700 if (!WIFEXITED(status)) {
6701 st = WSTOPSIG(status);
6703 if (!WIFSTOPPED(status))
6704 st = WTERMSIG(status);
6707 if (st == SIGINT || st == SIGPIPE)
6709 if (WIFSTOPPED(status))
6713 col = fmtstr(s, 32, u_signal_names(NULL, &st, 0));
6714 if (WCOREDUMP(status)) {
6715 col += fmtstr(s + col, 16, " (core dumped)");
6717 } else if (!sigonly) {
6719 col = fmtstr(s, 16, "Done(%d)", st);
6721 col = fmtstr(s, 16, "Done");
6730 showjob(FILE *out, struct job *jp, int mode)
6732 struct procstat *ps;
6733 struct procstat *psend;
6740 if (mode & SHOW_PGID) {
6741 /* just output process (group) id of pipeline */
6742 fprintf(out, "%d\n", ps->pid);
6746 col = fmtstr(s, 16, "[%d] ", jobno(jp));
6751 else if (curjob && jp == curjob->prev_job)
6754 if (mode & SHOW_PID)
6755 col += fmtstr(s + col, 16, "%d ", ps->pid);
6757 psend = ps + jp->nprocs;
6759 if (jp->state == JOBRUNNING) {
6760 scopy("Running", s + col);
6761 col += strlen("Running");
6763 int status = psend[-1].status;
6765 if (jp->state == JOBSTOPPED)
6766 status = jp->stopstatus;
6768 col += sprint_status(s + col, status, 0);
6774 /* for each process */
6775 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
6778 fprintf(out, "%s%*c%s",
6779 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
6781 if (!(mode & SHOW_PID)) {
6785 if (++ps == psend) {
6786 outcslow('\n', out);
6793 if (jp->state == JOBDONE) {
6794 TRACE(("showjob: freeing job %d\n", jobno(jp)));
6801 jobscmd(int argc, char **argv)
6807 while ((m = nextopt("lp")))
6817 showjob(out, getjob(*argv,0), mode);
6820 showjobs(out, mode);
6827 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6828 * statuses have changed since the last call to showjobs.
6832 showjobs(FILE *out, int mode)
6836 TRACE(("showjobs(%x) called\n", mode));
6838 /* If not even one one job changed, there is nothing to do */
6839 while (dowait(DOWAIT_NORMAL, NULL) > 0)
6842 for (jp = curjob; jp; jp = jp->prev_job) {
6843 if (!(mode & SHOW_CHANGED) || jp->changed)
6844 showjob(out, jp, mode);
6850 * Mark a job structure as unused.
6854 freejob(struct job *jp)
6856 struct procstat *ps;
6860 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
6861 if (ps->cmd != nullstr)
6864 if (jp->ps != &jp->ps0)
6867 set_curjob(jp, CUR_DELETE);
6873 waitcmd(int argc, char **argv)
6886 /* wait for all jobs */
6891 /* no running procs */
6894 if (jp->state == JOBRUNNING)
6899 dowait(DOWAIT_BLOCK, 0);
6905 if (**argv != '%') {
6906 pid_t pid = number(*argv);
6910 if (job->ps[job->nprocs - 1].pid == pid)
6912 job = job->prev_job;
6918 job = getjob(*argv, 0);
6919 /* loop until process terminated or stopped */
6920 while (job->state == JOBRUNNING)
6921 dowait(DOWAIT_BLOCK, 0);
6923 retval = getstatus(job);
6934 * Convert a job name to a job structure.
6938 getjob(const char *name, int getctl)
6942 const char *err_msg = "No such job: %s";
6946 char *(*match)(const char *, const char *);
6961 if (c == '+' || c == '%') {
6963 err_msg = "No current job";
6965 } else if (c == '-') {
6968 err_msg = "No previous job";
6979 jp = jobtab + num - 1;
6996 if (match(jp->ps[0].cmd, p)) {
7000 err_msg = "%s: ambiguous";
7007 err_msg = "job %s not created under job control";
7008 if (getctl && jp->jobctl == 0)
7013 error(err_msg, name);
7018 * Return a new job structure.
7019 * Called with interrupts off.
7023 makejob(union node *node, int nprocs)
7028 for (i = njobs, jp = jobtab ; ; jp++) {
7035 if (jp->state != JOBDONE || !jp->waited)
7044 memset(jp, 0, sizeof(*jp));
7049 jp->prev_job = curjob;
7054 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
7056 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
7066 struct job *jp, *jq;
7068 len = njobs * sizeof(*jp);
7070 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
7072 offset = (char *)jp - (char *)jq;
7074 /* Relocate pointers */
7077 jq = (struct job *)((char *)jq + l);
7081 #define joff(p) ((struct job *)((char *)(p) + l))
7082 #define jmove(p) (p) = (void *)((char *)(p) + offset)
7083 if (likely(joff(jp)->ps == &jq->ps0))
7084 jmove(joff(jp)->ps);
7085 if (joff(jp)->prev_job)
7086 jmove(joff(jp)->prev_job);
7096 jp = (struct job *)((char *)jp + len);
7100 } while (--jq >= jp);
7106 * Fork off a subshell. If we are doing job control, give the subshell its
7107 * own process group. Jp is a job structure that the job is to be added to.
7108 * N is the command that will be evaluated by the child. Both jp and n may
7109 * be NULL. The mode parameter can be one of the following:
7110 * FORK_FG - Fork off a foreground process.
7111 * FORK_BG - Fork off a background process.
7112 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
7113 * process group even if job control is on.
7115 * When job control is turned off, background processes have their standard
7116 * input redirected to /dev/null (except for the second and later processes
7119 * Called with interrupts off.
7123 forkchild(struct job *jp, union node *n, int mode)
7127 TRACE(("Child shell %d\n", getpid()));
7128 wasroot = rootshell;
7134 /* do job control only in root shell */
7136 if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
7139 if (jp->nprocs == 0)
7142 pgrp = jp->ps[0].pid;
7143 /* This can fail because we are doing it in the parent also */
7144 (void)setpgid(0, pgrp);
7145 if (mode == FORK_FG)
7146 xtcsetpgrp(ttyfd, pgrp);
7151 if (mode == FORK_BG) {
7154 if (jp->nprocs == 0) {
7156 if (open(_PATH_DEVNULL, O_RDONLY) != 0)
7157 error("Can't open %s", _PATH_DEVNULL);
7160 if (wasroot && iflag) {
7165 for (jp = curjob; jp; jp = jp->prev_job)
7171 forkparent(struct job *jp, union node *n, int mode, pid_t pid)
7173 TRACE(("In parent shell: child = %d\n", pid));
7175 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
7180 if (mode != FORK_NOJOB && jp->jobctl) {
7183 if (jp->nprocs == 0)
7186 pgrp = jp->ps[0].pid;
7187 /* This can fail because we are doing it in the child also */
7188 (void)setpgid(pid, pgrp);
7191 if (mode == FORK_BG) {
7192 backgndpid = pid; /* set $! */
7193 set_curjob(jp, CUR_RUNNING);
7196 struct procstat *ps = &jp->ps[jp->nprocs++];
7202 ps->cmd = commandtext(n);
7208 forkshell(struct job *jp, union node *n, int mode)
7212 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
7215 TRACE(("Fork failed, errno=%d", errno));
7218 error("Cannot fork");
7221 forkchild(jp, n, mode);
7223 forkparent(jp, n, mode, pid);
7228 * Wait for job to finish.
7230 * Under job control we have the problem that while a child process is
7231 * running interrupts generated by the user are sent to the child but not
7232 * to the shell. This means that an infinite loop started by an inter-
7233 * active user may be hard to kill. With job control turned off, an
7234 * interactive user may place an interactive program inside a loop. If
7235 * the interactive program catches interrupts, the user doesn't want
7236 * these interrupts to also abort the loop. The approach we take here
7237 * is to have the shell ignore interrupt signals while waiting for a
7238 * forground process to terminate, and then send itself an interrupt
7239 * signal if the child process was terminated by an interrupt signal.
7240 * Unfortunately, some programs want to do a bit of cleanup and then
7241 * exit on interrupt; unless these processes terminate themselves by
7242 * sending a signal to themselves (instead of calling exit) they will
7243 * confuse this approach.
7245 * Called with interrupts off.
7249 waitforjob(struct job *jp)
7253 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
7254 while (jp->state == JOBRUNNING) {
7255 dowait(DOWAIT_BLOCK, jp);
7260 xtcsetpgrp(ttyfd, rootpid);
7262 * This is truly gross.
7263 * If we're doing job control, then we did a TIOCSPGRP which
7264 * caused us (the shell) to no longer be in the controlling
7265 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
7266 * intuit from the subprocess exit status whether a SIGINT
7267 * occurred, and if so interrupt ourselves. Yuck. - mycroft
7272 if (jp->state == JOBDONE)
7280 * Do a wait system call. If job control is compiled in, we accept
7281 * stopped processes. If block is zero, we return a value of zero
7282 * rather than blocking.
7284 * System V doesn't have a non-blocking wait system call. It does
7285 * have a SIGCLD signal that is sent to a process when one of it's
7286 * children dies. The obvious way to use SIGCLD would be to install
7287 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7288 * was received, and have waitproc bump another counter when it got
7289 * the status of a process. Waitproc would then know that a wait
7290 * system call would not block if the two counters were different.
7291 * This approach doesn't work because if a process has children that
7292 * have not been waited for, System V will send it a SIGCLD when it
7293 * installs a signal handler for SIGCLD. What this means is that when
7294 * a child exits, the shell will be sent SIGCLD signals continuously
7295 * until is runs out of stack space, unless it does a wait call before
7296 * restoring the signal handler. The code below takes advantage of
7297 * this (mis)feature by installing a signal handler for SIGCLD and
7298 * then checking to see whether it was called. If there are any
7299 * children to be waited for, it will be.
7301 * If neither SYSV nor BSD is defined, we don't implement nonblocking
7302 * waits at all. In this case, the user will not be informed when
7303 * a background process until the next time she runs a real program
7304 * (as opposed to running a builtin command or just typing return),
7305 * and the jobs command may give out of date information.
7309 waitproc(int block, int *status)
7319 return wait3(status, flags, (struct rusage *)NULL);
7323 * Wait for a process to terminate.
7327 dowait(int block, struct job *job)
7332 struct job *thisjob;
7335 TRACE(("dowait(%d) called\n", block));
7336 pid = waitproc(block, &status);
7337 TRACE(("wait returns pid %d, status=%d\n", pid, status));
7342 for (jp = curjob; jp; jp = jp->prev_job) {
7343 struct procstat *sp;
7344 struct procstat *spend;
7345 if (jp->state == JOBDONE)
7348 spend = jp->ps + jp->nprocs;
7351 if (sp->pid == pid) {
7352 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
7353 sp->status = status;
7356 if (sp->status == -1)
7359 if (state == JOBRUNNING)
7361 if (WIFSTOPPED(sp->status)) {
7362 jp->stopstatus = sp->status;
7366 } while (++sp < spend);
7371 if (!WIFSTOPPED(status))
7378 if (state != JOBRUNNING) {
7379 thisjob->changed = 1;
7381 if (thisjob->state != state) {
7382 TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
7383 thisjob->state = state;
7385 if (state == JOBSTOPPED) {
7386 set_curjob(thisjob, CUR_STOPPED);
7395 if (thisjob && thisjob == job) {
7399 len = sprint_status(s, status, 1);
7411 * return 1 if there are stopped jobs, otherwise 0
7424 if (jp && jp->state == JOBSTOPPED) {
7425 out2str("You have stopped jobs.\n");
7435 * Return a string identifying a command (to be printed by the
7440 static char *cmdnextc;
7443 commandtext(union node *n)
7447 STARTSTACKSTR(cmdnextc);
7449 name = stackblock();
7450 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
7451 name, cmdnextc, cmdnextc));
7452 return savestr(name);
7456 cmdtxt(union node *n)
7459 struct nodelist *lp;
7469 lp = n->npipe.cmdlist;
7487 cmdtxt(n->nbinary.ch1);
7503 cmdtxt(n->nif.test);
7506 if (n->nif.elsepart) {
7509 n = n->nif.elsepart;
7525 cmdtxt(n->nbinary.ch1);
7535 cmdputs(n->nfor.var);
7537 cmdlist(n->nfor.args, 1);
7542 cmdputs(n->narg.text);
7546 cmdlist(n->ncmd.args, 1);
7547 cmdlist(n->ncmd.redirect, 0);
7560 cmdputs(n->ncase.expr->narg.text);
7562 for (np = n->ncase.cases; np; np = np->nclist.next) {
7563 cmdtxt(np->nclist.pattern);
7565 cmdtxt(np->nclist.body);
7591 s[0] = n->nfile.fd + '0';
7595 if (n->type == NTOFD || n->type == NFROMFD) {
7596 s[0] = n->ndup.dupfd + '0';
7607 cmdlist(union node *np, int sep)
7609 for (; np; np = np->narg.next) {
7613 if (sep && np->narg.next)
7619 cmdputs(const char *s)
7621 const char *p, *str;
7622 char c, cc[2] = " ";
7626 static const char *const vstype[16] = {
7627 nullstr, "}", "-", "+", "?", "=",
7628 "#", "##", "%", "%%"
7631 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
7633 while ((c = *p++) != 0) {
7641 if ((subtype & VSTYPE) == VSLENGTH)
7645 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
7663 case CTLBACKQ+CTLQUOTE:
7666 #ifdef CONFIG_ASH_MATH_SUPPORT
7681 str = vstype[subtype & VSTYPE];
7682 if (subtype & VSNUL)
7693 /* These can only happen inside quotes */
7705 while ((c = *str++)) {
7710 USTPUTC('"', nextc);
7718 showpipe(struct job *jp, FILE *out)
7720 struct procstat *sp;
7721 struct procstat *spend;
7723 spend = jp->ps + jp->nprocs;
7724 for (sp = jp->ps + 1; sp < spend; sp++)
7725 fprintf(out, " | %s", sp->cmd);
7726 outcslow('\n', out);
7731 xtcsetpgrp(int fd, pid_t pgrp)
7733 if (tcsetpgrp(fd, pgrp))
7734 error("Cannot set tty process group (%m)");
7739 getstatus(struct job *job) {
7743 status = job->ps[job->nprocs - 1].status;
7744 retval = WEXITSTATUS(status);
7745 if (!WIFEXITED(status)) {
7747 retval = WSTOPSIG(status);
7748 if (!WIFSTOPPED(status))
7751 /* XXX: limits number of signals */
7752 retval = WTERMSIG(status);
7754 if (retval == SIGINT)
7760 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
7761 jobno(job), job->nprocs, status, retval));
7765 #ifdef CONFIG_ASH_MAIL
7766 /* $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $ */
7769 * Routines to check for mail. (Perhaps make part of main.c?)
7772 #define MAXMBOXES 10
7774 /* times of mailboxes */
7775 static time_t mailtime[MAXMBOXES];
7776 /* Set if MAIL or MAILPATH is changed. */
7777 static int mail_var_path_changed;
7782 * Print appropriate message(s) if mail has arrived.
7783 * If mail_var_path_changed is set,
7784 * then the value of MAIL has mail_var_path_changed,
7785 * so we just update the values.
7795 struct stackmark smark;
7798 setstackmark(&smark);
7799 mpath = mpathset() ? mpathval() : mailval();
7800 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
7801 p = padvance(&mpath, nullstr);
7806 for (q = p ; *q ; q++);
7811 q[-1] = '\0'; /* delete trailing '/' */
7812 if (stat(p, &statb) < 0) {
7816 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
7819 pathopt ? pathopt : "you have mail"
7822 *mtp = statb.st_mtime;
7824 mail_var_path_changed = 0;
7825 popstackmark(&smark);
7830 changemail(const char *val)
7832 mail_var_path_changed++;
7835 #endif /* CONFIG_ASH_MAIL */
7837 /* $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $ */
7841 static short profile_buf[16384];
7845 static int isloginsh;
7847 static void read_profile(const char *);
7850 * Main routine. We initialize things, parse the arguments, execute
7851 * profiles if we're a login shell, and then call cmdloop to execute
7852 * commands. The setjmp call sets up the location to jump to when an
7853 * exception occurs. When an exception occurs the variable "state"
7854 * is used to figure out how far we had gotten.
7858 ash_main(int argc, char **argv)
7862 struct jmploc jmploc;
7863 struct stackmark smark;
7866 dash_errno = __errno_location();
7870 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7873 if (setjmp(jmploc.loc)) {
7880 switch (exception) {
7890 status = exitstatus;
7893 exitstatus = status;
7895 if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
7899 outcslow('\n', stderr);
7901 popstackmark(&smark);
7902 FORCEINTON; /* enable interrupts */
7905 else if (state == 2)
7907 else if (state == 3)
7915 trputs("Shell args: "); trargs(argv);
7920 setstackmark(&smark);
7921 procargs(argc, argv);
7922 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7924 const char *hp = lookupvar("HISTFILE");
7927 hp = lookupvar("HOME");
7929 char *defhp = concat_path_file(hp, ".ash_history");
7930 setvar("HISTFILE", defhp, 0);
7936 if (argv[0] && argv[0][0] == '-')
7940 read_profile("/etc/profile");
7943 read_profile(".profile");
7949 getuid() == geteuid() && getgid() == getegid() &&
7953 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7954 read_profile(shinit);
7960 evalstring(minusc, 0);
7962 if (sflag || minusc == NULL) {
7963 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7965 const char *hp = lookupvar("HISTFILE");
7968 load_history ( hp );
7971 state4: /* XXX ??? - why isn't this before the "if" statement */
7979 extern void _mcleanup(void);
7989 * Read and execute commands. "Top" is nonzero for the top level command
7990 * loop; it turns on prompting if the shell is interactive.
7997 struct stackmark smark;
8001 TRACE(("cmdloop(%d) called\n", top));
8002 setstackmark(&smark);
8008 showjobs(stderr, SHOW_CHANGED);
8013 #ifdef CONFIG_ASH_MAIL
8017 n = parsecmd(inter);
8018 /* showtree(n); DEBUG */
8020 if (!top || numeof >= 50)
8022 if (!stoppedjobs()) {
8025 out2str("\nUse \"exit\" to leave shell.\n");
8028 } else if (n != NULL && nflag == 0) {
8029 job_warning = (job_warning == 2) ? 1 : 0;
8033 popstackmark(&smark);
8034 setstackmark(&smark);
8035 if (evalskip == SKIPFILE) {
8040 popstackmark(&smark);
8045 * Read /etc/profile or .profile. Return on error.
8049 read_profile(const char *name)
8056 if ((fd = open(name, O_RDONLY)) >= 0)
8061 /* -q turns off -x and -v just when executing init files */
8064 xflag = 0, xflag_set = 1;
8066 vflag = 0, vflag_set = 1;
8080 * Read a file containing shell functions.
8084 readcmdfile(char *name)
8089 if ((fd = open(name, O_RDONLY)) >= 0)
8092 error("Can't open %s", name);
8100 * Take commands from a file. To be compatible we should do a path
8101 * search for the file, which is necessary to find sub-commands.
8104 static inline char *
8105 find_dot_file(char *name)
8108 const char *path = pathval();
8111 /* don't try this for absolute or relative paths */
8112 if (strchr(name, '/'))
8115 while ((fullname = padvance(&path, name)) != NULL) {
8116 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
8118 * Don't bother freeing here, since it will
8119 * be freed by the caller.
8123 stunalloc(fullname);
8126 /* not found in the PATH */
8127 error(not_found_msg, name);
8132 dotcmd(int argc, char **argv)
8136 if (argc >= 2) { /* That's what SVR2 does */
8138 struct stackmark smark;
8140 setstackmark(&smark);
8141 fullname = find_dot_file(argv[1]);
8142 setinputfile(fullname, 1);
8143 commandname = fullname;
8146 popstackmark(&smark);
8153 exitcmd(int argc, char **argv)
8158 exitstatus = number(argv[1]);
8163 /* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
8166 * Same for malloc, realloc, but returns an error when out of space.
8170 ckrealloc(pointer p, size_t nbytes)
8172 p = realloc(p, nbytes);
8174 error(bb_msg_memory_exhausted);
8179 ckmalloc(size_t nbytes)
8181 return ckrealloc(NULL, nbytes);
8185 * Make a copy of a string in safe storage.
8189 savestr(const char *s)
8191 char *p = strdup(s);
8193 error(bb_msg_memory_exhausted);
8199 * Parse trees for commands are allocated in lifo order, so we use a stack
8200 * to make this more efficient, and also to avoid all sorts of exception
8201 * handling code to handle interrupts in the middle of a parse.
8203 * The size 504 was chosen because the Ultrix malloc handles that size
8209 stalloc(size_t nbytes)
8214 aligned = SHELL_ALIGN(nbytes);
8215 if (aligned > stacknleft) {
8218 struct stack_block *sp;
8220 blocksize = aligned;
8221 if (blocksize < MINSIZE)
8222 blocksize = MINSIZE;
8223 len = sizeof(struct stack_block) - MINSIZE + blocksize;
8224 if (len < blocksize)
8225 error(bb_msg_memory_exhausted);
8229 stacknxt = sp->space;
8230 stacknleft = blocksize;
8231 sstrend = stacknxt + blocksize;
8236 stacknxt += aligned;
8237 stacknleft -= aligned;
8243 stunalloc(pointer p)
8246 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
8247 write(2, "stunalloc\n", 10);
8251 stacknleft += stacknxt - (char *)p;
8257 setstackmark(struct stackmark *mark)
8259 mark->stackp = stackp;
8260 mark->stacknxt = stacknxt;
8261 mark->stacknleft = stacknleft;
8262 mark->marknext = markp;
8268 popstackmark(struct stackmark *mark)
8270 struct stack_block *sp;
8273 markp = mark->marknext;
8274 while (stackp != mark->stackp) {
8279 stacknxt = mark->stacknxt;
8280 stacknleft = mark->stacknleft;
8281 sstrend = mark->stacknxt + mark->stacknleft;
8287 * When the parser reads in a string, it wants to stick the string on the
8288 * stack and only adjust the stack pointer when it knows how big the
8289 * string is. Stackblock (defined in stack.h) returns a pointer to a block
8290 * of space on top of the stack and stackblocklen returns the length of
8291 * this block. Growstackblock will grow this space by at least one byte,
8292 * possibly moving it (like realloc). Grabstackblock actually allocates the
8293 * part of the block that has been used.
8297 growstackblock(void)
8301 newlen = stacknleft * 2;
8302 if (newlen < stacknleft)
8303 error(bb_msg_memory_exhausted);
8307 if (stacknxt == stackp->space && stackp != &stackbase) {
8308 struct stack_block *oldstackp;
8309 struct stackmark *xmark;
8310 struct stack_block *sp;
8311 struct stack_block *prevstackp;
8317 prevstackp = sp->prev;
8318 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
8319 sp = ckrealloc((pointer)sp, grosslen);
8320 sp->prev = prevstackp;
8322 stacknxt = sp->space;
8323 stacknleft = newlen;
8324 sstrend = sp->space + newlen;
8327 * Stack marks pointing to the start of the old block
8328 * must be relocated to point to the new block
8331 while (xmark != NULL && xmark->stackp == oldstackp) {
8332 xmark->stackp = stackp;
8333 xmark->stacknxt = stacknxt;
8334 xmark->stacknleft = stacknleft;
8335 xmark = xmark->marknext;
8339 char *oldspace = stacknxt;
8340 int oldlen = stacknleft;
8341 char *p = stalloc(newlen);
8343 /* free the space we just allocated */
8344 stacknxt = memcpy(p, oldspace, oldlen);
8345 stacknleft += newlen;
8350 grabstackblock(size_t len)
8352 len = SHELL_ALIGN(len);
8358 * The following routines are somewhat easier to use than the above.
8359 * The user declares a variable of type STACKSTR, which may be declared
8360 * to be a register. The macro STARTSTACKSTR initializes things. Then
8361 * the user uses the macro STPUTC to add characters to the string. In
8362 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
8363 * grown as necessary. When the user is done, she can just leave the
8364 * string there and refer to it using stackblock(). Or she can allocate
8365 * the space for it using grabstackstr(). If it is necessary to allow
8366 * someone else to use the stack temporarily and then continue to grow
8367 * the string, the user should use grabstack to allocate the space, and
8368 * then call ungrabstr(p) to return to the previous mode of operation.
8370 * USTPUTC is like STPUTC except that it doesn't check for overflow.
8371 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
8372 * is space for at least one character.
8378 size_t len = stackblocksize();
8379 if (herefd >= 0 && len >= 1024) {
8380 xwrite(herefd, stackblock(), len);
8381 return stackblock();
8384 return stackblock() + len;
8388 * Called from CHECKSTRSPACE.
8392 makestrspace(size_t newlen, char *p)
8394 size_t len = p - stacknxt;
8395 size_t size = stackblocksize();
8400 size = stackblocksize();
8402 if (nleft >= newlen)
8406 return stackblock() + len;
8410 stnputs(const char *s, size_t n, char *p)
8412 p = makestrspace(n, p);
8413 p = mempcpy(p, s, n);
8418 stputs(const char *s, char *p)
8420 return stnputs(s, strlen(s), p);
8423 /* $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $ */
8428 * number(s) Convert a string of digits to an integer.
8429 * is_number(s) Return true if s is a string of digits.
8433 * prefix -- see if pfx is a prefix of string.
8437 prefix(const char *string, const char *pfx)
8440 if (*pfx++ != *string++)
8443 return (char *) string;
8448 * Convert a string of digits to an integer, printing an error message on
8453 number(const char *s)
8463 * Check for a valid number. This should be elsewhere.
8467 is_number(const char *p)
8472 } while (*++p != '\0');
8478 * Produce a possibly single quoted string suitable as input to the shell.
8479 * The return string is allocated on the stack.
8483 single_quote(const char *s) {
8492 len = strchrnul(s, '\'') - s;
8494 q = p = makestrspace(len + 3, p);
8497 q = mempcpy(q, s, len);
8503 len = strspn(s, "'");
8507 q = p = makestrspace(len + 3, p);
8510 q = mempcpy(q, s, len);
8519 return stackblock();
8523 * Like strdup but works with the ash stack.
8527 sstrdup(const char *p)
8529 size_t len = strlen(p) + 1;
8530 return memcpy(stalloc(len), p, len);
8535 calcsize(union node *n)
8539 funcblocksize += nodesize[n->type];
8542 calcsize(n->ncmd.redirect);
8543 calcsize(n->ncmd.args);
8544 calcsize(n->ncmd.assign);
8547 sizenodelist(n->npipe.cmdlist);
8552 calcsize(n->nredir.redirect);
8553 calcsize(n->nredir.n);
8560 calcsize(n->nbinary.ch2);
8561 calcsize(n->nbinary.ch1);
8564 calcsize(n->nif.elsepart);
8565 calcsize(n->nif.ifpart);
8566 calcsize(n->nif.test);
8569 funcstringsize += strlen(n->nfor.var) + 1;
8570 calcsize(n->nfor.body);
8571 calcsize(n->nfor.args);
8574 calcsize(n->ncase.cases);
8575 calcsize(n->ncase.expr);
8578 calcsize(n->nclist.body);
8579 calcsize(n->nclist.pattern);
8580 calcsize(n->nclist.next);
8584 sizenodelist(n->narg.backquote);
8585 funcstringsize += strlen(n->narg.text) + 1;
8586 calcsize(n->narg.next);
8593 calcsize(n->nfile.fname);
8594 calcsize(n->nfile.next);
8598 calcsize(n->ndup.vname);
8599 calcsize(n->ndup.next);
8603 calcsize(n->nhere.doc);
8604 calcsize(n->nhere.next);
8607 calcsize(n->nnot.com);
8614 sizenodelist(struct nodelist *lp)
8617 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8625 copynode(union node *n)
8632 funcblock = (char *) funcblock + nodesize[n->type];
8635 new->ncmd.redirect = copynode(n->ncmd.redirect);
8636 new->ncmd.args = copynode(n->ncmd.args);
8637 new->ncmd.assign = copynode(n->ncmd.assign);
8640 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8641 new->npipe.backgnd = n->npipe.backgnd;
8646 new->nredir.redirect = copynode(n->nredir.redirect);
8647 new->nredir.n = copynode(n->nredir.n);
8654 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8655 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8658 new->nif.elsepart = copynode(n->nif.elsepart);
8659 new->nif.ifpart = copynode(n->nif.ifpart);
8660 new->nif.test = copynode(n->nif.test);
8663 new->nfor.var = nodesavestr(n->nfor.var);
8664 new->nfor.body = copynode(n->nfor.body);
8665 new->nfor.args = copynode(n->nfor.args);
8668 new->ncase.cases = copynode(n->ncase.cases);
8669 new->ncase.expr = copynode(n->ncase.expr);
8672 new->nclist.body = copynode(n->nclist.body);
8673 new->nclist.pattern = copynode(n->nclist.pattern);
8674 new->nclist.next = copynode(n->nclist.next);
8678 new->narg.backquote = copynodelist(n->narg.backquote);
8679 new->narg.text = nodesavestr(n->narg.text);
8680 new->narg.next = copynode(n->narg.next);
8687 new->nfile.fname = copynode(n->nfile.fname);
8688 new->nfile.fd = n->nfile.fd;
8689 new->nfile.next = copynode(n->nfile.next);
8693 new->ndup.vname = copynode(n->ndup.vname);
8694 new->ndup.dupfd = n->ndup.dupfd;
8695 new->ndup.fd = n->ndup.fd;
8696 new->ndup.next = copynode(n->ndup.next);
8700 new->nhere.doc = copynode(n->nhere.doc);
8701 new->nhere.fd = n->nhere.fd;
8702 new->nhere.next = copynode(n->nhere.next);
8705 new->nnot.com = copynode(n->nnot.com);
8708 new->type = n->type;
8713 static struct nodelist *
8714 copynodelist(struct nodelist *lp)
8716 struct nodelist *start;
8717 struct nodelist **lpp;
8722 funcblock = (char *) funcblock +
8723 SHELL_ALIGN(sizeof(struct nodelist));
8724 (*lpp)->n = copynode(lp->n);
8726 lpp = &(*lpp)->next;
8734 nodesavestr(char *s)
8736 char *rtn = funcstring;
8738 funcstring = stpcpy(funcstring, s) + 1;
8744 * Free a parse tree.
8748 freefunc(struct funcnode *f)
8750 if (f && --f->count < 0)
8755 static void options(int);
8756 static void setoption(int, int);
8760 * Process the shell command line arguments.
8764 procargs(int argc, char **argv)
8767 const char *xminusc;
8774 for (i = 0; i < NOPTS; i++)
8780 if (*xargv == NULL) {
8782 error("-c requires an argument");
8785 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8789 for (i = 0; i < NOPTS; i++)
8790 if (optlist[i] == 2)
8795 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8800 } else if (!sflag) {
8801 setinputfile(*xargv, 0);
8807 shellparam.p = xargv;
8808 #ifdef CONFIG_ASH_GETOPTS
8809 shellparam.optind = 1;
8810 shellparam.optoff = -1;
8812 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8814 shellparam.nparam++;
8827 setinteractive(iflag);
8832 minus_o(char *name, int val)
8837 out1str("Current option settings\n");
8838 for (i = 0; i < NOPTS; i++)
8839 out1fmt("%-16s%s\n", optnames(i),
8840 optlist[i] ? "on" : "off");
8842 for (i = 0; i < NOPTS; i++)
8843 if (equal(name, optnames(i))) {
8847 error("Illegal option -o %s", name);
8852 * Process shell options. The global variable argptr contains a pointer
8853 * to the argument list; we advance it past the options.
8857 options(int cmdline)
8865 while ((p = *argptr) != NULL) {
8867 if ((c = *p++) == '-') {
8869 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8871 /* "-" means turn off -x and -v */
8874 /* "--" means reset params */
8875 else if (*argptr == NULL)
8878 break; /* "-" or "--" terminates options */
8880 } else if (c == '+') {
8886 while ((c = *p++) != '\0') {
8887 if (c == 'c' && cmdline) {
8888 minusc = p; /* command is after shell args*/
8889 } else if (c == 'o') {
8890 minus_o(*argptr, val);
8893 } else if (cmdline && (c == '-')) { // long options
8894 if (strcmp(p, "login") == 0)
8906 setoption(int flag, int val)
8910 for (i = 0; i < NOPTS; i++)
8911 if (optletters(i) == flag) {
8915 error("Illegal option -%c", flag);
8922 * Set the shell parameters.
8926 setparam(char **argv)
8932 for (nparam = 0 ; argv[nparam] ; nparam++);
8933 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
8935 *ap++ = savestr(*argv++);
8938 freeparam(&shellparam);
8939 shellparam.malloc = 1;
8940 shellparam.nparam = nparam;
8941 shellparam.p = newparam;
8942 #ifdef CONFIG_ASH_GETOPTS
8943 shellparam.optind = 1;
8944 shellparam.optoff = -1;
8950 * Free the list of positional parameters.
8954 freeparam(volatile struct shparam *param)
8958 if (param->malloc) {
8959 for (ap = param->p ; *ap ; ap++)
8968 * The shift builtin command.
8972 shiftcmd(int argc, char **argv)
8979 n = number(argv[1]);
8980 if (n > shellparam.nparam)
8981 error("can't shift that many");
8983 shellparam.nparam -= n;
8984 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
8985 if (shellparam.malloc)
8989 while ((*ap2++ = *ap1++) != NULL);
8990 #ifdef CONFIG_ASH_GETOPTS
8991 shellparam.optind = 1;
8992 shellparam.optoff = -1;
9001 * The set command builtin.
9005 setcmd(int argc, char **argv)
9008 return showvars(nullstr, 0, VUNSET);
9012 if (*argptr != NULL) {
9020 #ifdef CONFIG_ASH_GETOPTS
9025 shellparam.optind = number(value);
9026 shellparam.optoff = -1;
9030 #ifdef CONFIG_LOCALE_SUPPORT
9031 static void change_lc_all(const char *value)
9033 if (value != 0 && *value != 0)
9034 setlocale(LC_ALL, value);
9037 static void change_lc_ctype(const char *value)
9039 if (value != 0 && *value != 0)
9040 setlocale(LC_CTYPE, value);
9045 #ifdef CONFIG_ASH_GETOPTS
9047 getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
9054 char **optnext = optfirst + *param_optind - 1;
9056 if (*param_optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
9057 strlen(*(optnext - 1)) < *optoff)
9060 p = *(optnext - 1) + *optoff;
9061 if (p == NULL || *p == '\0') {
9062 /* Current word is done, advance */
9063 if (optnext == NULL)
9066 if (p == NULL || *p != '-' || *++p == '\0') {
9073 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9078 for (q = optstr; *q != c; ) {
9080 if (optstr[0] == ':') {
9083 err |= setvarsafe("OPTARG", s, 0);
9085 fprintf(stderr, "Illegal option -%c\n", c);
9086 (void) unsetvar("OPTARG");
9096 if (*p == '\0' && (p = *optnext) == NULL) {
9097 if (optstr[0] == ':') {
9100 err |= setvarsafe("OPTARG", s, 0);
9103 fprintf(stderr, "No arg for -%c option\n", c);
9104 (void) unsetvar("OPTARG");
9112 err |= setvarsafe("OPTARG", p, 0);
9115 err |= setvarsafe("OPTARG", nullstr, 0);
9118 *optoff = p ? p - *(optnext - 1) : -1;
9119 *param_optind = optnext - optfirst + 1;
9120 fmtstr(s, sizeof(s), "%d", *param_optind);
9121 err |= setvarsafe("OPTIND", s, VNOFUNC);
9124 err |= setvarsafe(optvar, s, 0);
9135 * The getopts builtin. Shellparam.optnext points to the next argument
9136 * to be processed. Shellparam.optptr points to the next character to
9137 * be processed in the current argument. If shellparam.optnext is NULL,
9138 * then it's the first time getopts has been called.
9142 getoptscmd(int argc, char **argv)
9147 error("Usage: getopts optstring var [arg]");
9148 else if (argc == 3) {
9149 optbase = shellparam.p;
9150 if (shellparam.optind > shellparam.nparam + 1) {
9151 shellparam.optind = 1;
9152 shellparam.optoff = -1;
9157 if (shellparam.optind > argc - 2) {
9158 shellparam.optind = 1;
9159 shellparam.optoff = -1;
9163 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
9164 &shellparam.optoff);
9166 #endif /* CONFIG_ASH_GETOPTS */
9169 * XXX - should get rid of. have all builtins use getopt(3). the
9170 * library getopt must have the BSD extension static variable "optreset"
9171 * otherwise it can't be used within the shell safely.
9173 * Standard option processing (a la getopt) for builtin routines. The
9174 * only argument that is passed to nextopt is the option string; the
9175 * other arguments are unnecessary. It return the character, or '\0' on
9180 nextopt(const char *optstring)
9186 if ((p = optptr) == NULL || *p == '\0') {
9188 if (p == NULL || *p != '-' || *++p == '\0')
9191 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9195 for (q = optstring ; *q != c ; ) {
9197 error("Illegal option -%c", c);
9202 if (*p == '\0' && (p = *argptr++) == NULL)
9203 error("No arg for -%c option", c);
9211 /* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
9216 outstr(const char *p, FILE *file)
9234 flushout(FILE *dest)
9242 outcslow(int c, FILE *dest)
9252 out1fmt(const char *fmt, ...)
9259 r = vprintf(fmt, ap);
9267 fmtstr(char *outbuf, size_t length, const char *fmt, ...)
9274 ret = vsnprintf(outbuf, length, fmt, ap);
9282 * Version of write which resumes after a signal is caught.
9286 xwrite(int fd, const void *p, size_t n)
9291 i = bb_full_write(fd, p, n);
9292 } while (i < 0 && errno == EINTR);
9296 /* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */
9300 * Shell command parser.
9303 #define EOFMARKLEN 79
9307 struct heredoc *next; /* next here document in list */
9308 union node *here; /* redirection node */
9309 char *eofmark; /* string indicating end of input */
9310 int striptabs; /* if set, strip leading tabs */
9315 static struct heredoc *heredoclist; /* list of here documents to read */
9318 static union node *list(int);
9319 static union node *andor(void);
9320 static union node *pipeline(void);
9321 static union node *command(void);
9322 static union node *simplecmd(void);
9323 static union node *makename(void);
9324 static void parsefname(void);
9325 static void parseheredoc(void);
9326 static char peektoken(void);
9327 static int readtoken(void);
9328 static int xxreadtoken(void);
9329 static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
9330 static int noexpand(char *);
9331 static void synexpect(int) __attribute__((__noreturn__));
9332 static void synerror(const char *) __attribute__((__noreturn__));
9333 static void setprompt(int);
9337 goodname(const char *p)
9339 return !*endofname(p);
9343 isassignment(const char *p)
9345 const char *q = endofname(p);
9353 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9354 * valid parse tree indicating a blank line.)
9358 parsecmd(int interact)
9363 doprompt = interact;
9365 setprompt(doprompt);
9380 union node *n1, *n2, *n3;
9383 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9384 if (nlflag == 2 && peektoken())
9390 if (tok == TBACKGND) {
9391 if (n2->type == NPIPE) {
9392 n2->npipe.backgnd = 1;
9394 if (n2->type != NREDIR) {
9395 n3 = stalloc(sizeof(struct nredir));
9397 n3->nredir.redirect = NULL;
9400 n2->type = NBACKGND;
9407 n3 = (union node *)stalloc(sizeof (struct nbinary));
9409 n3->nbinary.ch1 = n1;
9410 n3->nbinary.ch2 = n2;
9426 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9434 pungetc(); /* push back EOF on input */
9450 union node *n1, *n2, *n3;
9455 if ((t = readtoken()) == TAND) {
9457 } else if (t == TOR) {
9463 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9465 n3 = (union node *)stalloc(sizeof (struct nbinary));
9467 n3->nbinary.ch1 = n1;
9468 n3->nbinary.ch2 = n2;
9478 union node *n1, *n2, *pipenode;
9479 struct nodelist *lp, *prev;
9483 TRACE(("pipeline: entered\n"));
9484 if (readtoken() == TNOT) {
9486 checkkwd = CHKKWD | CHKALIAS;
9490 if (readtoken() == TPIPE) {
9491 pipenode = (union node *)stalloc(sizeof (struct npipe));
9492 pipenode->type = NPIPE;
9493 pipenode->npipe.backgnd = 0;
9494 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9495 pipenode->npipe.cmdlist = lp;
9499 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9500 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9503 } while (readtoken() == TPIPE);
9509 n2 = (union node *)stalloc(sizeof (struct nnot));
9522 union node *n1, *n2;
9523 union node *ap, **app;
9524 union node *cp, **cpp;
9525 union node *redir, **rpp;
9532 switch (readtoken()) {
9537 n1 = (union node *)stalloc(sizeof (struct nif));
9539 n1->nif.test = list(0);
9540 if (readtoken() != TTHEN)
9542 n1->nif.ifpart = list(0);
9544 while (readtoken() == TELIF) {
9545 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9546 n2 = n2->nif.elsepart;
9548 n2->nif.test = list(0);
9549 if (readtoken() != TTHEN)
9551 n2->nif.ifpart = list(0);
9553 if (lasttoken == TELSE)
9554 n2->nif.elsepart = list(0);
9556 n2->nif.elsepart = NULL;
9564 n1 = (union node *)stalloc(sizeof (struct nbinary));
9565 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9566 n1->nbinary.ch1 = list(0);
9567 if ((got=readtoken()) != TDO) {
9568 TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
9571 n1->nbinary.ch2 = list(0);
9576 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9577 synerror("Bad for loop variable");
9578 n1 = (union node *)stalloc(sizeof (struct nfor));
9580 n1->nfor.var = wordtext;
9581 checkkwd = CHKKWD | CHKALIAS;
9582 if (readtoken() == TIN) {
9584 while (readtoken() == TWORD) {
9585 n2 = (union node *)stalloc(sizeof (struct narg));
9587 n2->narg.text = wordtext;
9588 n2->narg.backquote = backquotelist;
9590 app = &n2->narg.next;
9594 if (lasttoken != TNL && lasttoken != TSEMI)
9597 n2 = (union node *)stalloc(sizeof (struct narg));
9599 n2->narg.text = (char *)dolatstr;
9600 n2->narg.backquote = NULL;
9601 n2->narg.next = NULL;
9604 * Newline or semicolon here is optional (but note
9605 * that the original Bourne shell only allowed NL).
9607 if (lasttoken != TNL && lasttoken != TSEMI)
9610 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9611 if (readtoken() != TDO)
9613 n1->nfor.body = list(0);
9617 n1 = (union node *)stalloc(sizeof (struct ncase));
9619 if (readtoken() != TWORD)
9621 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9623 n2->narg.text = wordtext;
9624 n2->narg.backquote = backquotelist;
9625 n2->narg.next = NULL;
9627 checkkwd = CHKKWD | CHKALIAS;
9628 } while (readtoken() == TNL);
9629 if (lasttoken != TIN)
9631 cpp = &n1->ncase.cases;
9633 checkkwd = CHKNL | CHKKWD;
9636 if (lasttoken == TLP)
9638 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9640 app = &cp->nclist.pattern;
9642 *app = ap = (union node *)stalloc(sizeof (struct narg));
9644 ap->narg.text = wordtext;
9645 ap->narg.backquote = backquotelist;
9646 if (readtoken() != TPIPE)
9648 app = &ap->narg.next;
9651 ap->narg.next = NULL;
9652 if (lasttoken != TRP)
9654 cp->nclist.body = list(2);
9656 cpp = &cp->nclist.next;
9658 checkkwd = CHKNL | CHKKWD;
9659 if ((t = readtoken()) != TESAC) {
9661 synexpect(TENDCASE);
9669 n1 = (union node *)stalloc(sizeof (struct nredir));
9670 n1->type = NSUBSHELL;
9671 n1->nredir.n = list(0);
9672 n1->nredir.redirect = NULL;
9685 if (readtoken() != t)
9689 /* Now check for redirection which may follow command */
9690 checkkwd = CHKKWD | CHKALIAS;
9692 while (readtoken() == TREDIR) {
9693 *rpp = n2 = redirnode;
9694 rpp = &n2->nfile.next;
9700 if (n1->type != NSUBSHELL) {
9701 n2 = (union node *)stalloc(sizeof (struct nredir));
9706 n1->nredir.redirect = redir;
9715 union node *args, **app;
9716 union node *n = NULL;
9717 union node *vars, **vpp;
9718 union node **rpp, *redir;
9728 savecheckkwd = CHKALIAS;
9730 checkkwd = savecheckkwd;
9731 switch (readtoken()) {
9733 n = (union node *)stalloc(sizeof (struct narg));
9735 n->narg.text = wordtext;
9736 n->narg.backquote = backquotelist;
9737 if (savecheckkwd && isassignment(wordtext)) {
9739 vpp = &n->narg.next;
9742 app = &n->narg.next;
9747 *rpp = n = redirnode;
9748 rpp = &n->nfile.next;
9749 parsefname(); /* read name of redirection file */
9753 args && app == &args->narg.next &&
9756 struct builtincmd *bcmd;
9759 /* We have a function */
9760 if (readtoken() != TRP)
9762 name = n->narg.text;
9764 !goodname(name) || (
9765 (bcmd = find_builtin(name)) &&
9766 IS_BUILTIN_SPECIAL(bcmd)
9769 synerror("Bad function name");
9771 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9772 n->narg.next = command();
9785 n = (union node *)stalloc(sizeof (struct ncmd));
9787 n->ncmd.args = args;
9788 n->ncmd.assign = vars;
9789 n->ncmd.redirect = redir;
9798 n = (union node *)stalloc(sizeof (struct narg));
9800 n->narg.next = NULL;
9801 n->narg.text = wordtext;
9802 n->narg.backquote = backquotelist;
9806 void fixredir(union node *n, const char *text, int err)
9808 TRACE(("Fix redir %s %d\n", text, err));
9810 n->ndup.vname = NULL;
9812 if (is_digit(text[0]) && text[1] == '\0')
9813 n->ndup.dupfd = digit_val(text[0]);
9814 else if (text[0] == '-' && text[1] == '\0')
9819 synerror("Bad fd number");
9821 n->ndup.vname = makename();
9829 union node *n = redirnode;
9831 if (readtoken() != TWORD)
9833 if (n->type == NHERE) {
9834 struct heredoc *here = heredoc;
9840 TRACE(("Here document %d\n", n->type));
9841 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9842 synerror("Illegal eof marker for << redirection");
9843 rmescapes(wordtext);
9844 here->eofmark = wordtext;
9846 if (heredoclist == NULL)
9849 for (p = heredoclist ; p->next ; p = p->next);
9852 } else if (n->type == NTOFD || n->type == NFROMFD) {
9853 fixredir(n, wordtext, 0);
9855 n->nfile.fname = makename();
9861 * Input any here documents.
9867 struct heredoc *here;
9878 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
9879 here->eofmark, here->striptabs);
9880 n = (union node *)stalloc(sizeof (struct narg));
9881 n->narg.type = NARG;
9882 n->narg.next = NULL;
9883 n->narg.text = wordtext;
9884 n->narg.backquote = backquotelist;
9885 here->here->nhere.doc = n;
9890 static char peektoken(void)
9896 return tokname_array[t][0];
9904 int alreadyseen = tokpushback;
9907 #ifdef CONFIG_ASH_ALIAS
9916 if (checkkwd & CHKNL) {
9923 if (t != TWORD || quoteflag) {
9928 * check for keywords
9930 if (checkkwd & CHKKWD) {
9931 const char *const *pp;
9933 if ((pp = findkwd(wordtext))) {
9934 lasttoken = t = pp - tokname_array;
9935 TRACE(("keyword %s recognized\n", tokname(t)));
9940 if (checkkwd & CHKALIAS) {
9941 #ifdef CONFIG_ASH_ALIAS
9943 if ((ap = lookupalias(wordtext, 1)) != NULL) {
9945 pushstring(ap->val, ap);
9955 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9957 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9964 * Read the next input token.
9965 * If the token is a word, we set backquotelist to the list of cmds in
9966 * backquotes. We set quoteflag to true if any part of the word was
9968 * If the token is TREDIR, then we set redirnode to a structure containing
9970 * In all cases, the variable startlinno is set to the number of the line
9971 * on which the token starts.
9973 * [Change comment: here documents and internal procedures]
9974 * [Readtoken shouldn't have any arguments. Perhaps we should make the
9975 * word parsing code into a separate routine. In this case, readtoken
9976 * doesn't need to have any internal procedures, but parseword does.
9977 * We could also make parseoperator in essence the main routine, and
9978 * have parseword (readtoken1?) handle both words and redirection.]
9981 #define NEW_xxreadtoken
9982 #ifdef NEW_xxreadtoken
9984 /* singles must be first! */
9985 static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
9987 static const char xxreadtoken_tokens[] = {
9988 TNL, TLP, TRP, /* only single occurrence allowed */
9989 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
9990 TEOF, /* corresponds to trailing nul */
9991 TAND, TOR, TENDCASE, /* if double occurrence */
9994 #define xxreadtoken_doubles \
9995 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
9996 #define xxreadtoken_singles \
9997 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
9999 static int xxreadtoken()
10011 startlinno = plinno;
10012 for (;;) { /* until token or start of word found */
10015 if ((c != ' ') && (c != '\t')
10016 #ifdef CONFIG_ASH_ALIAS
10021 while ((c = pgetc()) != '\n' && c != PEOF);
10023 } else if (c == '\\') {
10024 if (pgetc() != '\n') {
10028 startlinno = ++plinno;
10033 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10038 needprompt = doprompt;
10041 p = strchr(xxreadtoken_chars, c);
10044 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10047 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10048 if (pgetc() == *p) { /* double occurrence? */
10049 p += xxreadtoken_doubles + 1;
10056 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10064 #define RETURN(token) return lasttoken = token
10079 startlinno = plinno;
10080 for (;;) { /* until token or start of word found */
10083 case ' ': case '\t':
10084 #ifdef CONFIG_ASH_ALIAS
10089 while ((c = pgetc()) != '\n' && c != PEOF);
10093 if (pgetc() == '\n') {
10094 startlinno = ++plinno;
10103 needprompt = doprompt;
10108 if (pgetc() == '&')
10113 if (pgetc() == '|')
10118 if (pgetc() == ';')
10131 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10134 #endif /* NEW_xxreadtoken */
10138 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10139 * is not NULL, read a here document. In the latter case, eofmark is the
10140 * word which marks the end of the document and striptabs is true if
10141 * leading tabs should be stripped from the document. The argument firstc
10142 * is the first character of the input token or document.
10144 * Because C does not have internal subroutines, I have simulated them
10145 * using goto's to implement the subroutine linkage. The following macros
10146 * will run code that appears at the end of readtoken1.
10149 #define CHECKEND() {goto checkend; checkend_return:;}
10150 #define PARSEREDIR() {goto parseredir; parseredir_return:;}
10151 #define PARSESUB() {goto parsesub; parsesub_return:;}
10152 #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10153 #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10154 #define PARSEARITH() {goto parsearith; parsearith_return:;}
10157 readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
10162 char line[EOFMARKLEN + 1];
10163 struct nodelist *bqlist;
10166 int varnest; /* levels of variables expansion */
10167 int arinest; /* levels of arithmetic expansion */
10168 int parenlevel; /* levels of parens in arithmetic */
10169 int dqvarnest; /* levels of variables expansion within double quotes */
10171 int prevsyntax; /* syntax before arithmetic */
10173 /* Avoid longjmp clobbering */
10179 (void) &parenlevel;
10182 (void) &prevsyntax;
10186 startlinno = plinno;
10188 if (syntax == DQSYNTAX)
10197 STARTSTACKSTR(out);
10198 loop: { /* for each line, until end of word */
10199 CHECKEND(); /* set c to PEOF if at end of here document */
10200 for (;;) { /* until end of line or end of word */
10201 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
10202 switch(SIT(c, syntax)) {
10203 case CNL: /* '\n' */
10204 if (syntax == BASESYNTAX)
10205 goto endword; /* exit outer loop */
10211 goto loop; /* continue outer loop */
10216 if (eofmark == NULL || dblquote)
10217 USTPUTC(CTLESC, out);
10220 case CBACK: /* backslash */
10223 USTPUTC(CTLESC, out);
10224 USTPUTC('\\', out);
10226 } else if (c == '\n') {
10232 c != '\\' && c != '`' &&
10238 USTPUTC(CTLESC, out);
10239 USTPUTC('\\', out);
10241 if (SIT(c, SQSYNTAX) == CCTL)
10242 USTPUTC(CTLESC, out);
10250 if (eofmark == NULL) {
10251 USTPUTC(CTLQUOTEMARK, out);
10259 if (eofmark != NULL && arinest == 0 &&
10263 if (dqvarnest == 0) {
10264 syntax = BASESYNTAX;
10271 case CVAR: /* '$' */
10272 PARSESUB(); /* parse substitution */
10274 case CENDVAR: /* '}' */
10277 if (dqvarnest > 0) {
10280 USTPUTC(CTLENDVAR, out);
10285 #ifdef CONFIG_ASH_MATH_SUPPORT
10286 case CLP: /* '(' in arithmetic */
10290 case CRP: /* ')' in arithmetic */
10291 if (parenlevel > 0) {
10295 if (pgetc() == ')') {
10296 if (--arinest == 0) {
10297 USTPUTC(CTLENDARI, out);
10298 syntax = prevsyntax;
10299 if (syntax == DQSYNTAX)
10307 * unbalanced parens
10308 * (don't 2nd guess - no error)
10316 case CBQUOTE: /* '`' */
10320 goto endword; /* exit outer loop */
10325 goto endword; /* exit outer loop */
10326 #ifdef CONFIG_ASH_ALIAS
10336 #ifdef CONFIG_ASH_MATH_SUPPORT
10337 if (syntax == ARISYNTAX)
10338 synerror("Missing '))'");
10340 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10341 synerror("Unterminated quoted string");
10342 if (varnest != 0) {
10343 startlinno = plinno;
10345 synerror("Missing '}'");
10347 USTPUTC('\0', out);
10348 len = out - (char *)stackblock();
10349 out = stackblock();
10350 if (eofmark == NULL) {
10351 if ((c == '>' || c == '<')
10354 && (*out == '\0' || is_digit(*out))) {
10356 return lasttoken = TREDIR;
10361 quoteflag = quotef;
10362 backquotelist = bqlist;
10363 grabstackblock(len);
10365 return lasttoken = TWORD;
10366 /* end of readtoken routine */
10371 * Check to see whether we are at the end of the here document. When this
10372 * is called, c is set to the first character of the next input line. If
10373 * we are at the end of the here document, this routine sets the c to PEOF.
10378 #ifdef CONFIG_ASH_ALIAS
10384 while (c == '\t') {
10388 if (c == *eofmark) {
10389 if (pfgets(line, sizeof line) != NULL) {
10393 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10394 if (*p == '\n' && *q == '\0') {
10397 needprompt = doprompt;
10399 pushstring(line, NULL);
10404 goto checkend_return;
10409 * Parse a redirection operator. The variable "out" points to a string
10410 * specifying the fd to be redirected. The variable "c" contains the
10411 * first character of the redirection operator.
10418 np = (union node *)stalloc(sizeof (struct nfile));
10423 np->type = NAPPEND;
10425 np->type = NCLOBBER;
10432 } else { /* c == '<' */
10434 switch (c = pgetc()) {
10436 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10437 np = (union node *)stalloc(sizeof (struct nhere));
10441 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10442 heredoc->here = np;
10443 if ((c = pgetc()) == '-') {
10444 heredoc->striptabs = 1;
10446 heredoc->striptabs = 0;
10452 np->type = NFROMFD;
10456 np->type = NFROMTO;
10466 np->nfile.fd = digit_val(fd);
10468 goto parseredir_return;
10473 * Parse a substitution. At this point, we have read the dollar sign
10474 * and nothing else.
10482 static const char types[] = "}-+?=";
10486 c <= PEOA_OR_PEOF ||
10487 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10491 } else if (c == '(') { /* $(command) or $((arith)) */
10492 if (pgetc() == '(') {
10493 #ifdef CONFIG_ASH_MATH_SUPPORT
10496 synerror("We unsupport $((arith))");
10503 USTPUTC(CTLVAR, out);
10504 typeloc = out - (char *)stackblock();
10505 USTPUTC(VSNORMAL, out);
10506 subtype = VSNORMAL;
10510 if ((c = pgetc()) == '}')
10513 subtype = VSLENGTH;
10518 if (c > PEOA_OR_PEOF && is_name(c)) {
10522 } while (c > PEOA_OR_PEOF && is_in_name(c));
10523 } else if (is_digit(c)) {
10527 } while (is_digit(c));
10529 else if (is_special(c)) {
10534 badsub: synerror("Bad substitution");
10538 if (subtype == 0) {
10545 p = strchr(types, c);
10548 subtype = p - types + VSNORMAL;
10554 subtype = c == '#' ? VSTRIMLEFT :
10567 if (dblquote || arinest)
10569 *((char *)stackblock() + typeloc) = subtype | flags;
10570 if (subtype != VSNORMAL) {
10572 if (dblquote || arinest) {
10577 goto parsesub_return;
10582 * Called to parse command substitutions. Newstyle is set if the command
10583 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10584 * list of commands (passed by reference), and savelen is the number of
10585 * characters on the top of the stack which must be preserved.
10589 struct nodelist **nlpp;
10592 char *volatile str;
10593 struct jmploc jmploc;
10594 struct jmploc *volatile savehandler;
10598 (void) &saveprompt;
10601 savepbq = parsebackquote;
10602 if (setjmp(jmploc.loc)) {
10605 parsebackquote = 0;
10606 handler = savehandler;
10607 longjmp(handler->loc, 1);
10611 savelen = out - (char *)stackblock();
10613 str = ckmalloc(savelen);
10614 memcpy(str, stackblock(), savelen);
10616 savehandler = handler;
10620 /* We must read until the closing backquote, giving special
10621 treatment to some slashes, and then push the string and
10622 reread it as input, interpreting it normally. */
10629 STARTSTACKSTR(pout);
10635 switch (pc = pgetc()) {
10640 if ((pc = pgetc()) == '\n') {
10645 * If eating a newline, avoid putting
10646 * the newline into the new character
10647 * stream (via the STPUTC after the
10652 if (pc != '\\' && pc != '`' && pc != '$'
10653 && (!dblquote || pc != '"'))
10654 STPUTC('\\', pout);
10655 if (pc > PEOA_OR_PEOF) {
10661 #ifdef CONFIG_ASH_ALIAS
10664 startlinno = plinno;
10665 synerror("EOF in backquote substitution");
10669 needprompt = doprompt;
10678 STPUTC('\0', pout);
10679 psavelen = pout - (char *)stackblock();
10680 if (psavelen > 0) {
10681 pstr = grabstackstr(pout);
10682 setinputstring(pstr);
10687 nlpp = &(*nlpp)->next;
10688 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
10689 (*nlpp)->next = NULL;
10690 parsebackquote = oldstyle;
10693 saveprompt = doprompt;
10700 doprompt = saveprompt;
10702 if (readtoken() != TRP)
10709 * Start reading from old file again, ignoring any pushed back
10710 * tokens left from the backquote parsing
10715 while (stackblocksize() <= savelen)
10717 STARTSTACKSTR(out);
10719 memcpy(out, str, savelen);
10720 STADJUST(savelen, out);
10726 parsebackquote = savepbq;
10727 handler = savehandler;
10728 if (arinest || dblquote)
10729 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10731 USTPUTC(CTLBACKQ, out);
10733 goto parsebackq_oldreturn;
10735 goto parsebackq_newreturn;
10738 #ifdef CONFIG_ASH_MATH_SUPPORT
10740 * Parse an arithmetic expansion (indicate start of one and set state)
10744 if (++arinest == 1) {
10745 prevsyntax = syntax;
10746 syntax = ARISYNTAX;
10747 USTPUTC(CTLARI, out);
10754 * we collapse embedded arithmetic expansion to
10755 * parenthesis, which should be equivalent
10759 goto parsearith_return;
10763 } /* end of readtoken */
10768 * Returns true if the text contains nothing to expand (no dollar signs
10773 noexpand(char *text)
10779 while ((c = *p++) != '\0') {
10780 if (c == CTLQUOTEMARK)
10784 else if (SIT(c, BASESYNTAX) == CCTL)
10792 * Return of a legal variable name (a letter or underscore followed by zero or
10793 * more letters, underscores, and digits).
10797 endofname(const char *name)
10805 if (! is_in_name(*p))
10813 * Called when an unexpected token is read during the parse. The argument
10814 * is the token that is expected, or -1 if more than one type of token can
10815 * occur at this point.
10818 static void synexpect(int token)
10823 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10825 sprintf(msg + l, " (expecting %s)", tokname(token));
10831 synerror(const char *msg)
10833 error("Syntax error: %s", msg);
10839 * called by editline -- any expansions to the prompt
10840 * should be added here.
10843 static void setprompt(int whichprompt)
10845 const char *prompt;
10847 switch (whichprompt) {
10861 static const char *const *findkwd(const char *s)
10863 return bsearch(s, tokname_array + KWDOFFSET,
10864 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
10865 sizeof(const char *), pstrcmp);
10868 /* $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
10871 * Code for dealing with input/output redirection.
10874 #define EMPTY -2 /* marks an unused slot in redirtab */
10876 # define PIPESIZE 4096 /* amount of buffering in a pipe */
10878 # define PIPESIZE PIPE_BUF
10882 * Open a file in noclobber mode.
10883 * The code was copied from bash.
10886 noclobberopen(const char *fname)
10889 struct stat finfo, finfo2;
10892 * If the file exists and is a regular file, return an error
10895 r = stat(fname, &finfo);
10896 if (r == 0 && S_ISREG(finfo.st_mode)) {
10902 * If the file was not present (r != 0), make sure we open it
10903 * exclusively so that if it is created before we open it, our open
10904 * will fail. Make sure that we do not truncate an existing file.
10905 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10906 * file was not a regular file, we leave O_EXCL off.
10909 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10910 fd = open(fname, O_WRONLY|O_CREAT, 0666);
10912 /* If the open failed, return the file descriptor right away. */
10917 * OK, the open succeeded, but the file may have been changed from a
10918 * non-regular file to a regular file between the stat and the open.
10919 * We are assuming that the O_EXCL open handles the case where FILENAME
10920 * did not exist and is symlinked to an existing file between the stat
10925 * If we can open it and fstat the file descriptor, and neither check
10926 * revealed that it was a regular file, and the file has not been
10927 * replaced, return the file descriptor.
10929 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
10930 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
10933 /* The file has been replaced. badness. */
10940 * Handle here documents. Normally we fork off a process to write the
10941 * data to a pipe. If the document is short, we can stuff the data in
10942 * the pipe without forking.
10946 openhere(union node *redir)
10952 error("Pipe call failed");
10953 if (redir->type == NHERE) {
10954 len = strlen(redir->nhere.doc->narg.text);
10955 if (len <= PIPESIZE) {
10956 xwrite(pip[1], redir->nhere.doc->narg.text, len);
10960 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
10962 signal(SIGINT, SIG_IGN);
10963 signal(SIGQUIT, SIG_IGN);
10964 signal(SIGHUP, SIG_IGN);
10966 signal(SIGTSTP, SIG_IGN);
10968 signal(SIGPIPE, SIG_DFL);
10969 if (redir->type == NHERE)
10970 xwrite(pip[1], redir->nhere.doc->narg.text, len);
10972 expandhere(redir->nhere.doc, pip[1]);
10981 openredirect(union node *redir)
10986 switch (redir->nfile.type) {
10988 fname = redir->nfile.expfname;
10989 if ((f = open(fname, O_RDONLY)) < 0)
10993 fname = redir->nfile.expfname;
10994 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
10998 /* Take care of noclobber mode. */
11000 fname = redir->nfile.expfname;
11001 if ((f = noclobberopen(fname)) < 0)
11007 fname = redir->nfile.expfname;
11008 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
11012 fname = redir->nfile.expfname;
11013 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
11020 /* Fall through to eliminate warning. */
11027 f = openhere(redir);
11033 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11035 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11039 dupredirect(union node *redir, int f)
11041 int fd = redir->nfile.fd;
11043 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
11044 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
11045 copyfd(redir->ndup.dupfd, fd);
11058 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11059 * old file descriptors are stashed away so that the redirection can be
11060 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11061 * standard output, and the standard error if it becomes a duplicate of
11062 * stdout, is saved in memory.
11066 redirect(union node *redir, int flags)
11069 struct redirtab *sv;
11080 if (flags & REDIR_PUSH) {
11081 struct redirtab *q;
11082 q = ckmalloc(sizeof (struct redirtab));
11083 q->next = redirlist;
11085 q->nullredirs = nullredirs - 1;
11086 for (i = 0 ; i < 10 ; i++)
11087 q->renamed[i] = EMPTY;
11094 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
11095 n->ndup.dupfd == fd)
11096 continue; /* redirect from/to same file descriptor */
11098 newfd = openredirect(n);
11101 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
11102 i = fcntl(fd, F_DUPFD, 10);
11109 error("%d: %m", fd);
11119 dupredirect(n, newfd);
11120 } while ((n = n->nfile.next));
11126 * Undo the effects of the last redirection.
11132 struct redirtab *rp;
11135 if (--nullredirs >= 0)
11139 for (i = 0 ; i < 10 ; i++) {
11140 if (rp->renamed[i] != EMPTY) {
11143 copyfd(rp->renamed[i], i);
11145 close(rp->renamed[i]);
11148 redirlist = rp->next;
11149 nullredirs = rp->nullredirs;
11155 * Undo all redirections. Called on error or interrupt.
11159 * Discard all saved file descriptors.
11163 clearredir(int drop)
11175 * Copy a file descriptor to be >= to. Returns -1
11176 * if the source file descriptor is closed, EMPTY if there are no unused
11177 * file descriptors left.
11181 copyfd(int from, int to)
11185 newfd = fcntl(from, F_DUPFD, to);
11187 if (errno == EMFILE)
11190 error("%d: %m", from);
11197 redirectsafe(union node *redir, int flags)
11200 volatile int saveint;
11201 struct jmploc *volatile savehandler = handler;
11202 struct jmploc jmploc;
11205 if (!(err = setjmp(jmploc.loc) * 2)) {
11207 redirect(redir, flags);
11209 handler = savehandler;
11210 if (err && exception != EXERROR)
11211 longjmp(handler->loc, 1);
11212 RESTOREINT(saveint);
11216 /* $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $ */
11219 static void shtree(union node *, int, char *, FILE*);
11220 static void shcmd(union node *, FILE *);
11221 static void sharg(union node *, FILE *);
11222 static void indent(int, char *, FILE *);
11223 static void trstring(char *);
11227 showtree(union node *n)
11229 trputs("showtree called\n");
11230 shtree(n, 1, NULL, stdout);
11235 shtree(union node *n, int ind, char *pfx, FILE *fp)
11237 struct nodelist *lp;
11243 indent(ind, pfx, fp);
11254 shtree(n->nbinary.ch1, ind, NULL, fp);
11257 shtree(n->nbinary.ch2, ind, NULL, fp);
11265 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11270 if (n->npipe.backgnd)
11276 fprintf(fp, "<node type %d>", n->type);
11285 shcmd(union node *cmd, FILE *fp)
11293 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11299 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11302 switch (np->nfile.type) {
11303 case NTO: s = ">"; dftfd = 1; break;
11304 case NCLOBBER: s = ">|"; dftfd = 1; break;
11305 case NAPPEND: s = ">>"; dftfd = 1; break;
11306 case NTOFD: s = ">&"; dftfd = 1; break;
11307 case NFROM: s = "<"; dftfd = 0; break;
11308 case NFROMFD: s = "<&"; dftfd = 0; break;
11309 case NFROMTO: s = "<>"; dftfd = 0; break;
11310 default: s = "*error*"; dftfd = 0; break;
11312 if (np->nfile.fd != dftfd)
11313 fprintf(fp, "%d", np->nfile.fd);
11315 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11316 fprintf(fp, "%d", np->ndup.dupfd);
11318 sharg(np->nfile.fname, fp);
11327 sharg(union node *arg, FILE *fp)
11330 struct nodelist *bqlist;
11333 if (arg->type != NARG) {
11334 out1fmt("<node type %d>\n", arg->type);
11337 bqlist = arg->narg.backquote;
11338 for (p = arg->narg.text ; *p ; p++) {
11347 if (subtype == VSLENGTH)
11353 if (subtype & VSNUL)
11356 switch (subtype & VSTYPE) {
11375 case VSTRIMLEFTMAX:
11382 case VSTRIMRIGHTMAX:
11389 out1fmt("<subtype %d>", subtype);
11396 case CTLBACKQ|CTLQUOTE:
11399 shtree(bqlist->n, -1, NULL, fp);
11411 indent(int amount, char *pfx, FILE *fp)
11415 for (i = 0 ; i < amount ; i++) {
11416 if (pfx && i == amount - 1)
11437 putc(c, tracefile);
11441 trace(const char *fmt, ...)
11448 (void) vfprintf(tracefile, fmt, va);
11453 tracev(const char *fmt, va_list va)
11457 (void) vfprintf(tracefile, fmt, va);
11462 trputs(const char *s)
11466 fputs(s, tracefile);
11478 putc('"', tracefile);
11479 for (p = s ; *p ; p++) {
11481 case '\n': c = 'n'; goto backslash;
11482 case '\t': c = 't'; goto backslash;
11483 case '\r': c = 'r'; goto backslash;
11484 case '"': c = '"'; goto backslash;
11485 case '\\': c = '\\'; goto backslash;
11486 case CTLESC: c = 'e'; goto backslash;
11487 case CTLVAR: c = 'v'; goto backslash;
11488 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11489 case CTLBACKQ: c = 'q'; goto backslash;
11490 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
11491 backslash: putc('\\', tracefile);
11492 putc(c, tracefile);
11495 if (*p >= ' ' && *p <= '~')
11496 putc(*p, tracefile);
11498 putc('\\', tracefile);
11499 putc(*p >> 6 & 03, tracefile);
11500 putc(*p >> 3 & 07, tracefile);
11501 putc(*p & 07, tracefile);
11506 putc('"', tracefile);
11518 putc(' ', tracefile);
11520 putc('\n', tracefile);
11536 /* leave open because libedit might be using it */
11539 scopy("./trace", s);
11541 if (!freopen(s, "a", tracefile)) {
11542 fprintf(stderr, "Can't re-open %s\n", s);
11547 if ((tracefile = fopen(s, "a")) == NULL) {
11548 fprintf(stderr, "Can't open %s\n", s);
11554 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11555 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11557 setlinebuf(tracefile);
11558 fputs("\nTracing started.\n", tracefile);
11563 /* $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $ */
11566 * Sigmode records the current value of the signal handlers for the various
11567 * modes. A value of zero means that the current handler is not known.
11568 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
11571 #define S_DFL 1 /* default signal handling (SIG_DFL) */
11572 #define S_CATCH 2 /* signal is caught */
11573 #define S_IGN 3 /* signal is ignored (SIG_IGN) */
11574 #define S_HARD_IGN 4 /* signal is ignored permenantly */
11575 #define S_RESET 5 /* temporary - to reset a hard ignored sig */
11580 * The trap builtin.
11584 trapcmd(int argc, char **argv)
11593 for (signo = 0 ; signo < NSIG ; signo++) {
11594 if (trap[signo] != NULL) {
11597 sn = u_signal_names(0, &signo, 0);
11600 out1fmt("trap -- %s %s\n",
11601 single_quote(trap[signo]), sn);
11611 if ((signo = decode_signal(*ap, 0)) < 0)
11612 error("%s: bad trap", *ap);
11615 if (action[0] == '-' && action[1] == '\0')
11618 action = savestr(action);
11621 ckfree(trap[signo]);
11622 trap[signo] = action;
11633 * Clear traps on a fork.
11641 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
11642 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
11646 if (tp != &trap[0])
11647 setsignal(tp - trap);
11655 * Set the signal handler for the specified signal. The routine figures
11656 * out what it should be set to.
11660 setsignal(int signo)
11664 struct sigaction act;
11666 if ((t = trap[signo]) == NULL)
11668 else if (*t != '\0')
11672 if (rootshell && action == S_DFL) {
11675 if (iflag || minusc || sflag == 0)
11698 t = &sigmode[signo - 1];
11702 * current setting unknown
11704 if (sigaction(signo, 0, &act) == -1) {
11706 * Pretend it worked; maybe we should give a warning
11707 * here, but other shells don't. We don't alter
11708 * sigmode, so that we retry every time.
11712 if (act.sa_handler == SIG_IGN) {
11713 if (mflag && (signo == SIGTSTP ||
11714 signo == SIGTTIN || signo == SIGTTOU)) {
11715 tsig = S_IGN; /* don't hard ignore these */
11719 tsig = S_RESET; /* force to be set */
11722 if (tsig == S_HARD_IGN || tsig == action)
11726 act.sa_handler = onsig;
11729 act.sa_handler = SIG_IGN;
11732 act.sa_handler = SIG_DFL;
11736 sigfillset(&act.sa_mask);
11737 sigaction(signo, &act, 0);
11745 ignoresig(int signo)
11747 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11748 signal(signo, SIG_IGN);
11750 sigmode[signo - 1] = S_HARD_IGN;
11761 gotsig[signo - 1] = 1;
11762 pendingsigs = signo;
11764 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
11773 * Called to execute a trap. Perhaps we should avoid entering new trap
11774 * handlers while we are executing a trap handler.
11784 savestatus = exitstatus;
11786 while (pendingsigs = 0, barrier(), (p = memchr(q, 1, NSIG - 1))) {
11788 p = trap[p - q + 1];
11792 exitstatus = savestatus;
11798 * Controls whether the shell is interactive or not.
11802 setinteractive(int on)
11804 static int is_interactive;
11806 if (++on == is_interactive)
11808 is_interactive = on;
11810 setsignal(SIGQUIT);
11811 setsignal(SIGTERM);
11812 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11813 if(is_interactive > 1) {
11814 /* Looks like they want an interactive shell */
11815 static int do_banner;
11819 "\n\n" BB_BANNER " Built-in shell (ash)\n"
11820 "Enter 'help' for a list of built-in commands.\n\n");
11828 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11829 /*** List the available builtins ***/
11831 static int helpcmd(int argc, char **argv)
11835 out1fmt("\nBuilt-in commands:\n-------------------\n");
11836 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11837 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
11838 builtincmd[i].name + 1);
11844 #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
11846 extern const struct BB_applet applets[];
11847 extern const size_t NUM_APPLETS;
11849 for (i = 0; i < NUM_APPLETS; i++) {
11851 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11860 return EXIT_SUCCESS;
11862 #endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */
11865 * Called to exit the shell.
11875 status = exitstatus;
11876 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
11877 if (setjmp(loc.loc)) {
11881 if ((p = trap[0]) != NULL && *p != '\0') {
11886 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
11887 if (iflag && rootshell) {
11888 const char *hp = lookupvar("HISTFILE");
11891 save_history ( hp );
11899 static int decode_signal(const char *string, int minsig)
11902 const char *name = u_signal_names(string, &signo, minsig);
11904 return name ? signo : -1;
11907 /* $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $ */
11909 static struct var *vartab[VTABSIZE];
11911 static int vpcmp(const void *, const void *);
11912 static struct var **findvar(struct var **, const char *);
11915 * Initialize the varable symbol tables and import the environment
11919 #ifdef CONFIG_ASH_GETOPTS
11921 * Safe version of setvar, returns 1 on success 0 on failure.
11925 setvarsafe(const char *name, const char *val, int flags)
11928 volatile int saveint;
11929 struct jmploc *volatile savehandler = handler;
11930 struct jmploc jmploc;
11933 if (setjmp(jmploc.loc))
11937 setvar(name, val, flags);
11940 handler = savehandler;
11941 RESTOREINT(saveint);
11947 * Set the value of a variable. The flags argument is ored with the
11948 * flags of the variable. If val is NULL, the variable is unset.
11952 setvar(const char *name, const char *val, int flags)
11959 q = endofname(name);
11960 p = strchrnul(q, '=');
11961 namelen = p - name;
11962 if (!namelen || p != q)
11963 error("%.*s: bad variable name", namelen, name);
11968 vallen = strlen(val);
11971 p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
11975 p = mempcpy(p, val, vallen);
11978 setvareq(nameeq, flags | VNOSAVE);
11984 * Same as setvar except that the variable and value are passed in
11985 * the first argument as name=value. Since the first argument will
11986 * be actually stored in the table, it should not be a string that
11988 * Called with interrupts off.
11992 setvareq(char *s, int flags)
11994 struct var *vp, **vpp;
11997 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
11998 vp = *findvar(vpp, s);
12000 if (vp->flags & VREADONLY) {
12001 if (flags & VNOSAVE)
12003 error("%.*s: is read only", strchrnul(s, '=') - s, s);
12006 if (flags & VNOSET)
12009 if (vp->func && (flags & VNOFUNC) == 0)
12010 (*vp->func)(strchrnul(s, '=') + 1);
12012 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12015 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
12017 if (flags & VNOSET)
12020 vp = ckmalloc(sizeof (*vp));
12025 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
12033 * Process a linked list of variable assignments.
12037 listsetvar(struct strlist *list_set_var, int flags)
12039 struct strlist *lp = list_set_var;
12045 setvareq(lp->text, flags);
12046 } while ((lp = lp->next));
12052 * Find the value of a variable. Returns NULL if not set.
12056 lookupvar(const char *name)
12060 if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
12061 return strchrnul(v->text, '=') + 1;
12068 * Search the environment of a builtin command.
12072 bltinlookup(const char *name)
12074 struct strlist *sp;
12076 for (sp = cmdenviron ; sp ; sp = sp->next) {
12077 if (varequal(sp->text, name))
12078 return strchrnul(sp->text, '=') + 1;
12080 return lookupvar(name);
12085 * Generate a list of variables satisfying the given conditions.
12089 listvars(int on, int off, char ***end)
12100 for (vp = *vpp ; vp ; vp = vp->next)
12101 if ((vp->flags & mask) == on) {
12102 if (ep == stackstrend())
12103 ep = growstackstr();
12104 *ep++ = (char *) vp->text;
12106 } while (++vpp < vartab + VTABSIZE);
12107 if (ep == stackstrend())
12108 ep = growstackstr();
12112 return grabstackstr(ep);
12117 * POSIX requires that 'set' (but not export or readonly) output the
12118 * variables in lexicographic order - by the locale's collating order (sigh).
12119 * Maybe we could keep them in an ordered balanced binary tree
12120 * instead of hashed lists.
12121 * For now just roll 'em through qsort for printing...
12125 showvars(const char *sep_prefix, int on, int off)
12128 char **ep, **epend;
12130 ep = listvars(on, off, &epend);
12131 qsort(ep, epend - ep, sizeof(char *), vpcmp);
12133 sep = *sep_prefix ? spcstr : sep_prefix;
12135 for (; ep < epend; ep++) {
12139 p = strchrnul(*ep, '=');
12142 q = single_quote(++p);
12144 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
12153 * The export and readonly commands.
12157 exportcmd(int argc, char **argv)
12163 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
12166 notp = nextopt("p") - 'p';
12167 if (notp && ((name = *(aptr = argptr)))) {
12169 if ((p = strchr(name, '=')) != NULL) {
12172 if ((vp = *findvar(hashvar(name), name))) {
12177 setvar(name, p, flag);
12178 } while ((name = *++aptr) != NULL);
12180 showvars(argv[0], flag, 0);
12187 * Make a variable a local variable. When a variable is made local, it's
12188 * value and flags are saved in a localvar structure. The saved values
12189 * will be restored when the shell function returns. We handle the name
12190 * "-" as a special case.
12194 mklocal(char *name)
12196 struct localvar *lvp;
12201 lvp = ckmalloc(sizeof (struct localvar));
12202 if (name[0] == '-' && name[1] == '\0') {
12204 p = ckmalloc(sizeof(optlist));
12205 lvp->text = memcpy(p, optlist, sizeof(optlist));
12210 vpp = hashvar(name);
12211 vp = *findvar(vpp, name);
12212 eq = strchr(name, '=');
12215 setvareq(name, VSTRFIXED);
12217 setvar(name, NULL, VSTRFIXED);
12218 vp = *vpp; /* the new variable */
12219 lvp->flags = VUNSET;
12221 lvp->text = vp->text;
12222 lvp->flags = vp->flags;
12223 vp->flags |= VSTRFIXED|VTEXTFIXED;
12229 lvp->next = localvars;
12235 * The "local" command.
12239 localcmd(int argc, char **argv)
12244 while ((name = *argv++) != NULL) {
12252 * Called after a function returns.
12253 * Interrupts must be off.
12259 struct localvar *lvp;
12262 while ((lvp = localvars) != NULL) {
12263 localvars = lvp->next;
12265 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
12266 if (vp == NULL) { /* $- saved */
12267 memcpy(optlist, lvp->text, sizeof(optlist));
12270 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
12271 unsetvar(vp->text);
12274 (*vp->func)(strchrnul(lvp->text, '=') + 1);
12275 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12277 vp->flags = lvp->flags;
12278 vp->text = lvp->text;
12286 * The unset builtin command. We unset the function before we unset the
12287 * variable to allow a function to be unset when there is a readonly variable
12288 * with the same name.
12292 unsetcmd(int argc, char **argv)
12299 while ((i = nextopt("vf")) != '\0') {
12303 for (ap = argptr; *ap ; ap++) {
12318 * Unset the specified variable.
12322 unsetvar(const char *s)
12328 vpp = findvar(hashvar(s), s);
12332 int flags = vp->flags;
12335 if (flags & VREADONLY)
12337 if (flags & VUNSET)
12339 if ((flags & VSTRFIXED) == 0) {
12341 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
12348 vp->flags &= ~VEXPORT;
12361 * Find the appropriate entry in the hash table from the name.
12364 static struct var **
12365 hashvar(const char *p)
12367 unsigned int hashval;
12369 hashval = ((unsigned char) *p) << 4;
12370 while (*p && *p != '=')
12371 hashval += (unsigned char) *p++;
12372 return &vartab[hashval % VTABSIZE];
12378 * Compares two strings up to the first = or '\0'. The first
12379 * string must be terminated by '='; the second may be terminated by
12380 * either '=' or '\0'.
12384 varcmp(const char *p, const char *q)
12388 while ((c = *p) == (d = *q)) {
12389 if (!c || c == '=')
12403 vpcmp(const void *a, const void *b)
12405 return varcmp(*(const char **)a, *(const char **)b);
12408 static struct var **
12409 findvar(struct var **vpp, const char *name)
12411 for (; *vpp; vpp = &(*vpp)->next) {
12412 if (varequal((*vpp)->text, name)) {
12418 /* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */
12420 #include <sys/times.h>
12422 static const unsigned char timescmd_str[] = {
12423 ' ', offsetof(struct tms, tms_utime),
12424 '\n', offsetof(struct tms, tms_stime),
12425 ' ', offsetof(struct tms, tms_cutime),
12426 '\n', offsetof(struct tms, tms_cstime),
12430 static int timescmd(int ac, char **av)
12432 long int clk_tck, s, t;
12433 const unsigned char *p;
12436 clk_tck = sysconf(_SC_CLK_TCK);
12441 t = *(clock_t *)(((char *) &buf) + p[1]);
12443 out1fmt("%ldm%ld.%.3lds%c",
12445 ((t - s * clk_tck) * 1000) / clk_tck,
12447 } while (*(p += 2));
12452 #ifdef CONFIG_ASH_MATH_SUPPORT
12454 dash_arith(const char *s)
12460 result = arith(s, &errcode);
12463 error("exponent less than 0");
12464 else if (errcode == -2)
12465 error("divide by zero");
12466 else if (errcode == -5)
12467 error("expression recursion loop detected");
12478 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12479 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12481 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12485 letcmd(int argc, char **argv)
12492 error("expression expected");
12493 for (ap = argv + 1; *ap; ap++) {
12494 i = dash_arith(*ap);
12499 #endif /* CONFIG_ASH_MATH_SUPPORT */
12501 /* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
12504 * Miscelaneous builtins.
12510 #if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
12511 typedef enum __rlimit_resource rlim_t;
12517 * The read builtin. The -e option causes backslashes to escape the
12518 * following character.
12520 * This uses unbuffered input, which may be avoidable in some cases.
12524 readcmd(int argc, char **argv)
12539 while ((i = nextopt("p:r")) != '\0') {
12541 prompt = optionarg;
12545 if (prompt && isatty(0)) {
12549 if (*(ap = argptr) == NULL)
12550 error("arg count");
12551 if ((ifs = bltinlookup("IFS")) == NULL)
12558 if (read(0, &c, 1) != 1) {
12570 if (!rflag && c == '\\') {
12576 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12580 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12582 setvar(*ap, stackblock(), 0);
12592 /* Remove trailing blanks */
12593 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12595 setvar(*ap, stackblock(), 0);
12596 while (*++ap != NULL)
12597 setvar(*ap, nullstr, 0);
12602 static int umaskcmd(int argc, char **argv)
12604 static const char permuser[3] = "ugo";
12605 static const char permmode[3] = "rwx";
12606 static const short int permmask[] = {
12607 S_IRUSR, S_IWUSR, S_IXUSR,
12608 S_IRGRP, S_IWGRP, S_IXGRP,
12609 S_IROTH, S_IWOTH, S_IXOTH
12615 int symbolic_mode = 0;
12617 while (nextopt("S") != '\0') {
12626 if ((ap = *argptr) == NULL) {
12627 if (symbolic_mode) {
12631 for (i = 0; i < 3; i++) {
12634 *p++ = permuser[i];
12636 for (j = 0; j < 3; j++) {
12637 if ((mask & permmask[3 * i + j]) == 0) {
12638 *p++ = permmode[j];
12646 out1fmt("%.4o\n", mask);
12649 if (is_digit((unsigned char) *ap)) {
12652 if (*ap >= '8' || *ap < '0')
12653 error(illnum, argv[1]);
12654 mask = (mask << 3) + (*ap - '0');
12655 } while (*++ap != '\0');
12658 mask = ~mask & 0777;
12659 if (!bb_parse_mode(ap, &mask)) {
12660 error("Illegal mode: %s", ap);
12662 umask(~mask & 0777);
12671 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12672 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12673 * ash by J.T. Conklin.
12681 int factor; /* multiply by to get rlim_{cur,max} values */
12685 static const struct limits limits[] = {
12687 { "time(seconds)", RLIMIT_CPU, 1, 't' },
12689 #ifdef RLIMIT_FSIZE
12690 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
12693 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
12695 #ifdef RLIMIT_STACK
12696 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
12699 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
12702 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
12704 #ifdef RLIMIT_MEMLOCK
12705 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
12707 #ifdef RLIMIT_NPROC
12708 { "process(processes)", RLIMIT_NPROC, 1, 'p' },
12710 #ifdef RLIMIT_NOFILE
12711 { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
12714 { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
12717 { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' },
12719 { (char *) 0, 0, 0, '\0' }
12723 ulimitcmd(int argc, char **argv)
12727 enum { SOFT = 0x1, HARD = 0x2 }
12729 const struct limits *l;
12732 struct rlimit limit;
12735 while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
12750 for (l = limits; l->name && l->option != what; l++)
12753 error("internal error (%c)", what);
12755 set = *argptr ? 1 : 0;
12759 if (all || argptr[1])
12760 error("too many arguments");
12761 if (strncmp(p, "unlimited\n", 9) == 0)
12762 val = RLIM_INFINITY;
12766 while ((c = *p++) >= '0' && c <= '9')
12768 val = (val * 10) + (long)(c - '0');
12769 if (val < (rlim_t) 0)
12773 error("bad number");
12778 for (l = limits; l->name; l++) {
12779 getrlimit(l->cmd, &limit);
12781 val = limit.rlim_cur;
12782 else if (how & HARD)
12783 val = limit.rlim_max;
12785 out1fmt("%-20s ", l->name);
12786 if (val == RLIM_INFINITY)
12787 out1fmt("unlimited\n");
12791 out1fmt("%lld\n", (long long) val);
12797 getrlimit(l->cmd, &limit);
12800 limit.rlim_max = val;
12802 limit.rlim_cur = val;
12803 if (setrlimit(l->cmd, &limit) < 0)
12804 error("error setting limit (%m)");
12807 val = limit.rlim_cur;
12808 else if (how & HARD)
12809 val = limit.rlim_max;
12811 if (val == RLIM_INFINITY)
12812 out1fmt("unlimited\n");
12816 out1fmt("%lld\n", (long long) val);
12823 #ifdef CONFIG_ASH_MATH_SUPPORT
12825 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12827 Permission is hereby granted, free of charge, to any person obtaining
12828 a copy of this software and associated documentation files (the
12829 "Software"), to deal in the Software without restriction, including
12830 without limitation the rights to use, copy, modify, merge, publish,
12831 distribute, sublicense, and/or sell copies of the Software, and to
12832 permit persons to whom the Software is furnished to do so, subject to
12833 the following conditions:
12835 The above copyright notice and this permission notice shall be
12836 included in all copies or substantial portions of the Software.
12838 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12839 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12840 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12841 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12842 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12843 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12844 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12847 /* This is my infix parser/evaluator. It is optimized for size, intended
12848 * as a replacement for yacc-based parsers. However, it may well be faster
12849 * than a comparable parser writen in yacc. The supported operators are
12850 * listed in #defines below. Parens, order of operations, and error handling
12851 * are supported. This code is threadsafe. The exact expression format should
12852 * be that which POSIX specifies for shells. */
12854 /* The code uses a simple two-stack algorithm. See
12855 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
12856 * for a detailed explaination of the infix-to-postfix algorithm on which
12857 * this is based (this code differs in that it applies operators immediately
12858 * to the stack instead of adding them to a queue to end up with an
12861 /* To use the routine, call it with an expression string and error return
12865 * Aug 24, 2001 Manuel Novoa III
12867 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12869 * 1) In arith_apply():
12870 * a) Cached values of *numptr and &(numptr[-1]).
12871 * b) Removed redundant test for zero denominator.
12874 * a) Eliminated redundant code for processing operator tokens by moving
12875 * to a table-based implementation. Also folded handling of parens
12877 * b) Combined all 3 loops which called arith_apply to reduce generated
12878 * code size at the cost of speed.
12880 * 3) The following expressions were treated as valid by the original code:
12881 * 1() , 0! , 1 ( *3 ) .
12882 * These bugs have been fixed by internally enclosing the expression in
12883 * parens and then checking that all binary ops and right parens are
12884 * preceded by a valid expression (NUM_TOKEN).
12886 * Note: It may be desireable to replace Aaron's test for whitespace with
12887 * ctype's isspace() if it is used by another busybox applet or if additional
12888 * whitespace chars should be considered. Look below the "#include"s for a
12889 * precompiler test.
12893 * Aug 26, 2001 Manuel Novoa III
12895 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12897 * Merge in Aaron's comments previously posted to the busybox list,
12898 * modified slightly to take account of my changes to the code.
12903 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12905 * - allow access to variable,
12906 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12907 * - realize assign syntax (VAR=expr, +=, *= etc)
12908 * - realize exponentiation (** operator)
12909 * - realize comma separated - expr, expr
12910 * - realise ++expr --expr expr++ expr--
12911 * - realise expr ? expr : expr (but, second expr calculate always)
12912 * - allow hexdecimal and octal numbers
12913 * - was restored loses XOR operator
12914 * - remove one goto label, added three ;-)
12915 * - protect $((num num)) as true zero expr (Manuel`s error)
12916 * - always use special isspace(), see comment from bash ;-)
12920 #define arith_isspace(arithval) \
12921 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12924 typedef unsigned char operator;
12926 /* An operator's token id is a bit of a bitfield. The lower 5 bits are the
12927 * precedence, and 3 high bits are an ID unique accross operators of that
12928 * precedence. The ID portion is so that multiple operators can have the
12929 * same precedence, ensuring that the leftmost one is evaluated first.
12930 * Consider * and /. */
12932 #define tok_decl(prec,id) (((id)<<5)|(prec))
12933 #define PREC(op) ((op) & 0x1F)
12935 #define TOK_LPAREN tok_decl(0,0)
12937 #define TOK_COMMA tok_decl(1,0)
12939 #define TOK_ASSIGN tok_decl(2,0)
12940 #define TOK_AND_ASSIGN tok_decl(2,1)
12941 #define TOK_OR_ASSIGN tok_decl(2,2)
12942 #define TOK_XOR_ASSIGN tok_decl(2,3)
12943 #define TOK_PLUS_ASSIGN tok_decl(2,4)
12944 #define TOK_MINUS_ASSIGN tok_decl(2,5)
12945 #define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12946 #define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12948 #define TOK_MUL_ASSIGN tok_decl(3,0)
12949 #define TOK_DIV_ASSIGN tok_decl(3,1)
12950 #define TOK_REM_ASSIGN tok_decl(3,2)
12952 /* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
12953 #define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
12955 /* conditional is right associativity too */
12956 #define TOK_CONDITIONAL tok_decl(4,0)
12957 #define TOK_CONDITIONAL_SEP tok_decl(4,1)
12959 #define TOK_OR tok_decl(5,0)
12961 #define TOK_AND tok_decl(6,0)
12963 #define TOK_BOR tok_decl(7,0)
12965 #define TOK_BXOR tok_decl(8,0)
12967 #define TOK_BAND tok_decl(9,0)
12969 #define TOK_EQ tok_decl(10,0)
12970 #define TOK_NE tok_decl(10,1)
12972 #define TOK_LT tok_decl(11,0)
12973 #define TOK_GT tok_decl(11,1)
12974 #define TOK_GE tok_decl(11,2)
12975 #define TOK_LE tok_decl(11,3)
12977 #define TOK_LSHIFT tok_decl(12,0)
12978 #define TOK_RSHIFT tok_decl(12,1)
12980 #define TOK_ADD tok_decl(13,0)
12981 #define TOK_SUB tok_decl(13,1)
12983 #define TOK_MUL tok_decl(14,0)
12984 #define TOK_DIV tok_decl(14,1)
12985 #define TOK_REM tok_decl(14,2)
12987 /* exponent is right associativity */
12988 #define TOK_EXPONENT tok_decl(15,1)
12990 /* For now unary operators. */
12991 #define UNARYPREC 16
12992 #define TOK_BNOT tok_decl(UNARYPREC,0)
12993 #define TOK_NOT tok_decl(UNARYPREC,1)
12995 #define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12996 #define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12998 #define PREC_PRE (UNARYPREC+2)
13000 #define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13001 #define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13003 #define PREC_POST (UNARYPREC+3)
13005 #define TOK_POST_INC tok_decl(PREC_POST, 0)
13006 #define TOK_POST_DEC tok_decl(PREC_POST, 1)
13008 #define SPEC_PREC (UNARYPREC+4)
13010 #define TOK_NUM tok_decl(SPEC_PREC, 0)
13011 #define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13013 #define NUMPTR (*numstackptr)
13015 static inline int tok_have_assign(operator op)
13017 operator prec = PREC(op);
13019 convert_prec_is_assing(prec);
13020 return (prec == PREC(TOK_ASSIGN) ||
13021 prec == PREC_PRE || prec == PREC_POST);
13024 static inline int is_right_associativity(operator prec)
13026 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
13027 prec == PREC(TOK_CONDITIONAL));
13031 typedef struct ARITCH_VAR_NUM {
13033 long contidional_second_val;
13034 char contidional_second_val_initialized;
13035 char *var; /* if NULL then is regular number,
13036 else is varable name */
13040 typedef struct CHK_VAR_RECURSIVE_LOOPED {
13042 struct CHK_VAR_RECURSIVE_LOOPED *next;
13043 } chk_var_recursive_looped_t;
13045 static chk_var_recursive_looped_t *prev_chk_var_recursive;
13048 static int arith_lookup_val(v_n_t *t)
13051 const char * p = lookupvar(t->var);
13056 /* recursive try as expression */
13057 chk_var_recursive_looped_t *cur;
13058 chk_var_recursive_looped_t cur_save;
13060 for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
13061 if(strcmp(cur->var, t->var) == 0) {
13062 /* expression recursion loop detected */
13066 /* save current lookuped var name */
13067 cur = prev_chk_var_recursive;
13068 cur_save.var = t->var;
13069 cur_save.next = cur;
13070 prev_chk_var_recursive = &cur_save;
13072 t->val = arith (p, &errcode);
13073 /* restore previous ptr after recursiving */
13074 prev_chk_var_recursive = cur;
13077 /* allow undefined var as 0 */
13084 /* "applying" a token means performing it on the top elements on the integer
13085 * stack. For a unary operator it will only change the top element, but a
13086 * binary operator will pop two arguments and push a result */
13088 arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
13093 int ret_arith_lookup_val;
13095 if (NUMPTR == numstack) goto err; /* There is no operator that can work
13096 without arguments */
13097 numptr_m1 = NUMPTR - 1;
13099 /* check operand is var with noninteger value */
13100 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13101 if(ret_arith_lookup_val)
13102 return ret_arith_lookup_val;
13104 rez = numptr_m1->val;
13105 if (op == TOK_UMINUS)
13107 else if (op == TOK_NOT)
13109 else if (op == TOK_BNOT)
13111 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13113 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13115 else if (op != TOK_UPLUS) {
13116 /* Binary operators */
13118 /* check and binary operators need two arguments */
13119 if (numptr_m1 == numstack) goto err;
13121 /* ... and they pop one */
13124 if (op == TOK_CONDITIONAL) {
13125 if(! numptr_m1->contidional_second_val_initialized) {
13126 /* protect $((expr1 ? expr2)) without ": expr" */
13129 rez = numptr_m1->contidional_second_val;
13130 } else if(numptr_m1->contidional_second_val_initialized) {
13131 /* protect $((expr1 : expr2)) without "expr ? " */
13134 numptr_m1 = NUMPTR - 1;
13135 if(op != TOK_ASSIGN) {
13136 /* check operand is var with noninteger value for not '=' */
13137 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13138 if(ret_arith_lookup_val)
13139 return ret_arith_lookup_val;
13141 if (op == TOK_CONDITIONAL) {
13142 numptr_m1->contidional_second_val = rez;
13144 rez = numptr_m1->val;
13145 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
13147 else if (op == TOK_OR)
13148 rez = numptr_val || rez;
13149 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
13151 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
13153 else if (op == TOK_AND)
13154 rez = rez && numptr_val;
13155 else if (op == TOK_EQ)
13156 rez = (rez == numptr_val);
13157 else if (op == TOK_NE)
13158 rez = (rez != numptr_val);
13159 else if (op == TOK_GE)
13160 rez = (rez >= numptr_val);
13161 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
13162 rez >>= numptr_val;
13163 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
13164 rez <<= numptr_val;
13165 else if (op == TOK_GT)
13166 rez = (rez > numptr_val);
13167 else if (op == TOK_LT)
13168 rez = (rez < numptr_val);
13169 else if (op == TOK_LE)
13170 rez = (rez <= numptr_val);
13171 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
13173 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
13175 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
13177 else if (op == TOK_ASSIGN || op == TOK_COMMA)
13179 else if (op == TOK_CONDITIONAL_SEP) {
13180 if (numptr_m1 == numstack) {
13181 /* protect $((expr : expr)) without "expr ? " */
13184 numptr_m1->contidional_second_val_initialized = op;
13185 numptr_m1->contidional_second_val = numptr_val;
13187 else if (op == TOK_CONDITIONAL) {
13189 numptr_val : numptr_m1->contidional_second_val;
13191 else if(op == TOK_EXPONENT) {
13193 return -3; /* exponent less than 0 */
13198 while(numptr_val--)
13203 else if(numptr_val==0) /* zero divisor check */
13205 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
13207 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
13210 if(tok_have_assign(op)) {
13213 if(numptr_m1->var == NULL) {
13217 /* save to shell variable */
13218 sprintf(buf, "%ld", rez);
13219 setvar(numptr_m1->var, buf, 0);
13220 /* after saving, make previous value for v++ or v-- */
13221 if(op == TOK_POST_INC)
13223 else if(op == TOK_POST_DEC)
13226 numptr_m1->val = rez;
13227 /* protect geting var value, is number now */
13228 numptr_m1->var = NULL;
13233 /* longest must first */
13234 static const char op_tokens[] = {
13235 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13236 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13237 '<','<', 0, TOK_LSHIFT,
13238 '>','>', 0, TOK_RSHIFT,
13239 '|','|', 0, TOK_OR,
13240 '&','&', 0, TOK_AND,
13241 '!','=', 0, TOK_NE,
13242 '<','=', 0, TOK_LE,
13243 '>','=', 0, TOK_GE,
13244 '=','=', 0, TOK_EQ,
13245 '|','=', 0, TOK_OR_ASSIGN,
13246 '&','=', 0, TOK_AND_ASSIGN,
13247 '*','=', 0, TOK_MUL_ASSIGN,
13248 '/','=', 0, TOK_DIV_ASSIGN,
13249 '%','=', 0, TOK_REM_ASSIGN,
13250 '+','=', 0, TOK_PLUS_ASSIGN,
13251 '-','=', 0, TOK_MINUS_ASSIGN,
13252 '-','-', 0, TOK_POST_DEC,
13253 '^','=', 0, TOK_XOR_ASSIGN,
13254 '+','+', 0, TOK_POST_INC,
13255 '*','*', 0, TOK_EXPONENT,
13259 '=', 0, TOK_ASSIGN,
13271 '?', 0, TOK_CONDITIONAL,
13272 ':', 0, TOK_CONDITIONAL_SEP,
13273 ')', 0, TOK_RPAREN,
13274 '(', 0, TOK_LPAREN,
13278 #define endexpression &op_tokens[sizeof(op_tokens)-7]
13281 extern long arith (const char *expr, int *perrcode)
13283 register char arithval; /* Current character under analysis */
13284 operator lasttok, op;
13287 const char *p = endexpression;
13290 size_t datasizes = strlen(expr) + 2;
13292 /* Stack of integers */
13293 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13294 * in any given correct or incorrect expression is left as an excersize to
13296 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
13297 *numstackptr = numstack;
13298 /* Stack of operator tokens */
13299 operator *stack = alloca((datasizes) * sizeof(operator)),
13302 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13303 *perrcode = errcode = 0;
13306 if ((arithval = *expr) == 0) {
13307 if (p == endexpression) {
13308 /* Null expression. */
13312 /* This is only reached after all tokens have been extracted from the
13313 * input stream. If there are still tokens on the operator stack, they
13314 * are to be applied in order. At the end, there should be a final
13315 * result on the integer stack */
13317 if (expr != endexpression + 1) {
13318 /* If we haven't done so already, */
13319 /* append a closing right paren */
13320 expr = endexpression;
13321 /* and let the loop process it. */
13324 /* At this point, we're done with the expression. */
13325 if (numstackptr != numstack+1) {
13326 /* ... but if there isn't, it's bad */
13328 return (*perrcode = -1);
13330 if(numstack->var) {
13331 /* expression is $((var)) only, lookup now */
13332 errcode = arith_lookup_val(numstack);
13335 *perrcode = errcode;
13336 return numstack->val;
13338 /* Continue processing the expression. */
13339 if (arith_isspace(arithval)) {
13340 /* Skip whitespace */
13343 if((p = endofname(expr)) != expr) {
13344 int var_name_size = (p-expr) + 1; /* trailing zero */
13346 numstackptr->var = alloca(var_name_size);
13347 safe_strncpy(numstackptr->var, expr, var_name_size);
13350 numstackptr->contidional_second_val_initialized = 0;
13354 } else if (is_digit(arithval)) {
13355 numstackptr->var = NULL;
13356 numstackptr->val = strtol(expr, (char **) &expr, 0);
13359 for(p = op_tokens; ; p++) {
13363 /* strange operator not found */
13366 for(o = expr; *p && *o == *p; p++)
13373 /* skip tail uncompared token */
13376 /* skip zero delim */
13381 /* post grammar: a++ reduce to num */
13382 if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13385 /* Plus and minus are binary (not unary) _only_ if the last
13386 * token was as number, or a right paren (which pretends to be
13387 * a number, since it evaluates to one). Think about it.
13388 * It makes sense. */
13389 if (lasttok != TOK_NUM) {
13405 /* We don't want a unary operator to cause recursive descent on the
13406 * stack, because there can be many in a row and it could cause an
13407 * operator to be evaluated before its argument is pushed onto the
13408 * integer stack. */
13409 /* But for binary operators, "apply" everything on the operator
13410 * stack until we find an operator with a lesser priority than the
13411 * one we have just extracted. */
13412 /* Left paren is given the lowest priority so it will never be
13413 * "applied" in this way.
13414 * if associativity is right and priority eq, applied also skip
13417 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13418 /* not left paren or unary */
13419 if (lasttok != TOK_NUM) {
13420 /* binary op must be preceded by a num */
13423 while (stackptr != stack) {
13424 if (op == TOK_RPAREN) {
13425 /* The algorithm employed here is simple: while we don't
13426 * hit an open paren nor the bottom of the stack, pop
13427 * tokens and apply them */
13428 if (stackptr[-1] == TOK_LPAREN) {
13430 /* Any operator directly after a */
13432 /* close paren should consider itself binary */
13436 operator prev_prec = PREC(stackptr[-1]);
13438 convert_prec_is_assing(prec);
13439 convert_prec_is_assing(prev_prec);
13440 if (prev_prec < prec)
13442 /* check right assoc */
13443 if(prev_prec == prec && is_right_associativity(prec))
13446 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13447 if(errcode) goto ret;
13449 if (op == TOK_RPAREN) {
13454 /* Push this operator to the stack and remember it. */
13455 *stackptr++ = lasttok = op;
13462 #endif /* CONFIG_ASH_MATH_SUPPORT */
13466 const char *bb_applet_name = "debug stuff usage";
13467 int main(int argc, char **argv)
13469 return ash_main(argc, argv);
13474 * Copyright (c) 1989, 1991, 1993, 1994
13475 * The Regents of the University of California. All rights reserved.
13477 * This code is derived from software contributed to Berkeley by
13478 * Kenneth Almquist.
13480 * Redistribution and use in source and binary forms, with or without
13481 * modification, are permitted provided that the following conditions
13483 * 1. Redistributions of source code must retain the above copyright
13484 * notice, this list of conditions and the following disclaimer.
13485 * 2. Redistributions in binary form must reproduce the above copyright
13486 * notice, this list of conditions and the following disclaimer in the
13487 * documentation and/or other materials provided with the distribution.
13489 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
13490 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
13492 * 4. Neither the name of the University nor the names of its contributors
13493 * may be used to endorse or promote products derived from this software
13494 * without specific prior written permission.
13496 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13497 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13498 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13499 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13500 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13501 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13502 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13503 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13504 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13505 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF