aboutsummaryrefslogtreecommitdiff
path: root/busybox/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'busybox/shell/ash.c')
-rw-r--r--busybox/shell/ash.c13586
1 files changed, 13586 insertions, 0 deletions
diff --git a/busybox/shell/ash.c b/busybox/shell/ash.c
new file mode 100644
index 000000000..d9ea2b0f3
--- /dev/null
+++ b/busybox/shell/ash.c
@@ -0,0 +1,13586 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
9 * was re-ported from NetBSD and debianized.
10 *
11 *
12 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
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.
19 *
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.
24 *
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
28 *
29 * Original BSD copyright notice is retained at the end of this file.
30 */
31
32/*
33 * rewrite arith.y to micro stack based cryptic algorithm by
34 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
35 *
36 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
37 * dynamic variables.
38 *
39 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
40 * used in busybox and size optimizations,
41 * rewrote arith (see notes to this), added locale support,
42 * rewrote dynamic variables.
43 *
44 */
45
46
47/*
48 * The follow should be set to reflect the type of system you have:
49 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
50 * define SYSV if you are running under System V.
51 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
52 * define DEBUG=2 to compile in and turn on debugging.
53 *
54 * When debugging is on, debugging info will be written to ./trace and
55 * a quit signal will generate a core dump.
56 */
57
58
59
60#define IFS_BROKEN
61
62#define PROFILE 0
63
64#ifdef DEBUG
65#define _GNU_SOURCE
66#endif
67
68#include <sys/types.h>
69#include <sys/cdefs.h>
70#include <sys/ioctl.h>
71#include <sys/param.h>
72#include <sys/resource.h>
73#include <sys/stat.h>
74#include <sys/time.h>
75#include <sys/wait.h>
76
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <unistd.h>
81
82#include <stdarg.h>
83#include <stddef.h>
84#include <assert.h>
85#include <ctype.h>
86#include <dirent.h>
87#include <errno.h>
88#include <fcntl.h>
89#include <limits.h>
90#include <paths.h>
91#include <setjmp.h>
92#include <signal.h>
93#include <stdint.h>
94#include <sysexits.h>
95#include <time.h>
96#include <fnmatch.h>
97
98
99#include "busybox.h"
100#include "pwd_.h"
101
102#ifdef CONFIG_ASH_JOB_CONTROL
103#define JOBS 1
104#else
105#undef JOBS
106#endif
107
108#if JOBS
109#include <termios.h>
110#endif
111
112#include "cmdedit.h"
113
114#ifdef __GLIBC__
115/* glibc sucks */
116static int *dash_errno;
117#undef errno
118#define errno (*dash_errno)
119#endif
120
121#if defined(__uClinux__)
122#error "Do not even bother, ash will not run on uClinux"
123#endif
124
125#ifdef DEBUG
126#define _DIAGASSERT(assert_expr) assert(assert_expr)
127#else
128#define _DIAGASSERT(assert_expr)
129#endif
130
131
132#ifdef CONFIG_ASH_ALIAS
133/* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */
134
135#define ALIASINUSE 1
136#define ALIASDEAD 2
137
138struct alias {
139 struct alias *next;
140 char *name;
141 char *val;
142 int flag;
143};
144
145static struct alias *lookupalias(const char *, int);
146static int aliascmd(int, char **);
147static int unaliascmd(int, char **);
148static void rmaliases(void);
149static int unalias(const char *);
150static void printalias(const struct alias *);
151#endif
152
153/* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */
154
155
156static void setpwd(const char *, int);
157
158/* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */
159
160
161/*
162 * Types of operations (passed to the errmsg routine).
163 */
164
165
166static const char not_found_msg[] = "%s: not found";
167
168
169#define E_OPEN "No such file" /* opening a file */
170#define E_CREAT "Directory nonexistent" /* creating a file */
171#define E_EXEC not_found_msg+4 /* executing a program */
172
173/*
174 * We enclose jmp_buf in a structure so that we can declare pointers to
175 * jump locations. The global variable handler contains the location to
176 * jump to when an exception occurs, and the global variable exception
177 * contains a code identifying the exception. To implement nested
178 * exception handlers, the user should save the value of handler on entry
179 * to an inner scope, set handler to point to a jmploc structure for the
180 * inner scope, and restore handler on exit from the scope.
181 */
182
183struct jmploc {
184 jmp_buf loc;
185};
186
187static struct jmploc *handler;
188static int exception;
189static volatile int suppressint;
190static volatile sig_atomic_t intpending;
191
192static int exerrno; /* Last exec error, error for EXEXEC */
193
194/* exceptions */
195#define EXINT 0 /* SIGINT received */
196#define EXERROR 1 /* a generic error */
197#define EXSHELLPROC 2 /* execute a shell procedure */
198#define EXEXEC 3 /* command execution failed */
199#define EXEXIT 4 /* exit the shell */
200#define EXSIG 5 /* trapped signal in wait(1) */
201
202
203/* do we generate EXSIG events */
204static int exsig;
205/* last pending signal */
206static volatile sig_atomic_t pendingsigs;
207
208/*
209 * These macros allow the user to suspend the handling of interrupt signals
210 * over a period of time. This is similar to SIGHOLD to or sigblock, but
211 * much more efficient and portable. (But hacking the kernel is so much
212 * more fun than worrying about efficiency and portability. :-))
213 */
214
215#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
216#define INTOFF \
217 ({ \
218 suppressint++; \
219 xbarrier(); \
220 0; \
221 })
222#define SAVEINT(v) ((v) = suppressint)
223#define RESTOREINT(v) \
224 ({ \
225 xbarrier(); \
226 if ((suppressint = (v)) == 0 && intpending) onint(); \
227 0; \
228 })
229#define EXSIGON() \
230 ({ \
231 exsig++; \
232 xbarrier(); \
233 if (pendingsigs) \
234 exraise(EXSIG); \
235 0; \
236 })
237/* EXSIG is turned off by evalbltin(). */
238
239
240static void exraise(int) __attribute__((__noreturn__));
241static void onint(void) __attribute__((__noreturn__));
242
243static void error(const char *, ...) __attribute__((__noreturn__));
244static void exerror(int, const char *, ...) __attribute__((__noreturn__));
245
246static void sh_warnx(const char *, ...);
247
248#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
249static void
250inton(void) {
251 if (--suppressint == 0 && intpending) {
252 onint();
253 }
254}
255#define INTON inton()
256static void forceinton(void)
257{
258 suppressint = 0;
259 if (intpending)
260 onint();
261}
262#define FORCEINTON forceinton()
263#else
264#define INTON \
265 ({ \
266 xbarrier(); \
267 if (--suppressint == 0 && intpending) onint(); \
268 0; \
269 })
270#define FORCEINTON \
271 ({ \
272 xbarrier(); \
273 suppressint = 0; \
274 if (intpending) onint(); \
275 0; \
276 })
277#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
278
279/*
280 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
281 * so we use _setjmp instead.
282 */
283
284#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
285#define setjmp(jmploc) _setjmp(jmploc)
286#define longjmp(jmploc, val) _longjmp(jmploc, val)
287#endif
288
289/* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */
290
291struct strlist {
292 struct strlist *next;
293 char *text;
294};
295
296
297struct arglist {
298 struct strlist *list;
299 struct strlist **lastp;
300};
301
302/*
303 * expandarg() flags
304 */
305#define EXP_FULL 0x1 /* perform word splitting & file globbing */
306#define EXP_TILDE 0x2 /* do normal tilde expansion */
307#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
308#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
309#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
310#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
311#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
312#define EXP_WORD 0x80 /* expand word in parameter expansion */
313#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
314
315
316union node;
317static void expandarg(union node *, struct arglist *, int);
318#define rmescapes(p) _rmescapes((p), 0)
319static char *_rmescapes(char *, int);
320static int casematch(union node *, char *);
321
322#ifdef CONFIG_ASH_MATH_SUPPORT
323static void expari(int);
324#endif
325
326/* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */
327
328static char *commandname; /* currently executing command */
329static struct strlist *cmdenviron; /* environment for builtin command */
330static int exitstatus; /* exit status of last command */
331static int back_exitstatus; /* exit status of backquoted command */
332
333
334struct backcmd { /* result of evalbackcmd */
335 int fd; /* file descriptor to read from */
336 char *buf; /* buffer */
337 int nleft; /* number of chars in buffer */
338 struct job *jp; /* job structure for command */
339};
340
341/*
342 * This file was generated by the mknodes program.
343 */
344
345#define NCMD 0
346#define NPIPE 1
347#define NREDIR 2
348#define NBACKGND 3
349#define NSUBSHELL 4
350#define NAND 5
351#define NOR 6
352#define NSEMI 7
353#define NIF 8
354#define NWHILE 9
355#define NUNTIL 10
356#define NFOR 11
357#define NCASE 12
358#define NCLIST 13
359#define NDEFUN 14
360#define NARG 15
361#define NTO 16
362#define NCLOBBER 17
363#define NFROM 18
364#define NFROMTO 19
365#define NAPPEND 20
366#define NTOFD 21
367#define NFROMFD 22
368#define NHERE 23
369#define NXHERE 24
370#define NNOT 25
371
372
373
374struct ncmd {
375 int type;
376 union node *assign;
377 union node *args;
378 union node *redirect;
379};
380
381
382struct npipe {
383 int type;
384 int backgnd;
385 struct nodelist *cmdlist;
386};
387
388
389struct nredir {
390 int type;
391 union node *n;
392 union node *redirect;
393};
394
395
396struct nbinary {
397 int type;
398 union node *ch1;
399 union node *ch2;
400};
401
402
403struct nif {
404 int type;
405 union node *test;
406 union node *ifpart;
407 union node *elsepart;
408};
409
410
411struct nfor {
412 int type;
413 union node *args;
414 union node *body;
415 char *var;
416};
417
418
419struct ncase {
420 int type;
421 union node *expr;
422 union node *cases;
423};
424
425
426struct nclist {
427 int type;
428 union node *next;
429 union node *pattern;
430 union node *body;
431};
432
433
434struct narg {
435 int type;
436 union node *next;
437 char *text;
438 struct nodelist *backquote;
439};
440
441
442struct nfile {
443 int type;
444 union node *next;
445 int fd;
446 union node *fname;
447 char *expfname;
448};
449
450
451struct ndup {
452 int type;
453 union node *next;
454 int fd;
455 int dupfd;
456 union node *vname;
457};
458
459
460struct nhere {
461 int type;
462 union node *next;
463 int fd;
464 union node *doc;
465};
466
467
468struct nnot {
469 int type;
470 union node *com;
471};
472
473
474union node {
475 int type;
476 struct ncmd ncmd;
477 struct npipe npipe;
478 struct nredir nredir;
479 struct nbinary nbinary;
480 struct nif nif;
481 struct nfor nfor;
482 struct ncase ncase;
483 struct nclist nclist;
484 struct narg narg;
485 struct nfile nfile;
486 struct ndup ndup;
487 struct nhere nhere;
488 struct nnot nnot;
489};
490
491
492struct nodelist {
493 struct nodelist *next;
494 union node *n;
495};
496
497
498struct funcnode {
499 int count;
500 union node n;
501};
502
503
504static void freefunc(struct funcnode *);
505/* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */
506
507/* control characters in argument strings */
508#define CTL_FIRST '\201' /* first 'special' character */
509#define CTLESC '\201' /* escape next character */
510#define CTLVAR '\202' /* variable defn */
511#define CTLENDVAR '\203'
512#define CTLBACKQ '\204'
513#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
514/* CTLBACKQ | CTLQUOTE == '\205' */
515#define CTLARI '\206' /* arithmetic expression */
516#define CTLENDARI '\207'
517#define CTLQUOTEMARK '\210'
518#define CTL_LAST '\210' /* last 'special' character */
519
520/* variable substitution byte (follows CTLVAR) */
521#define VSTYPE 0x0f /* type of variable substitution */
522#define VSNUL 0x10 /* colon--treat the empty string as unset */
523#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
524
525/* values of VSTYPE field */
526#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
527#define VSMINUS 0x2 /* ${var-text} */
528#define VSPLUS 0x3 /* ${var+text} */
529#define VSQUESTION 0x4 /* ${var?message} */
530#define VSASSIGN 0x5 /* ${var=text} */
531#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
532#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
533#define VSTRIMLEFT 0x8 /* ${var#pattern} */
534#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
535#define VSLENGTH 0xa /* ${#var} */
536
537/* values of checkkwd variable */
538#define CHKALIAS 0x1
539#define CHKKWD 0x2
540#define CHKNL 0x4
541
542#define IBUFSIZ (BUFSIZ + 1)
543
544/*
545 * NEOF is returned by parsecmd when it encounters an end of file. It
546 * must be distinct from NULL, so we use the address of a variable that
547 * happens to be handy.
548 */
549static int plinno = 1; /* input line number */
550
551/* number of characters left in input buffer */
552static int parsenleft; /* copy of parsefile->nleft */
553static int parselleft; /* copy of parsefile->lleft */
554
555/* next character in input buffer */
556static char *parsenextc; /* copy of parsefile->nextc */
557
558struct strpush {
559 struct strpush *prev; /* preceding string on stack */
560 char *prevstring;
561 int prevnleft;
562#ifdef CONFIG_ASH_ALIAS
563 struct alias *ap; /* if push was associated with an alias */
564#endif
565 char *string; /* remember the string since it may change */
566};
567
568struct parsefile {
569 struct parsefile *prev; /* preceding file on stack */
570 int linno; /* current line */
571 int fd; /* file descriptor (or -1 if string) */
572 int nleft; /* number of chars left in this line */
573 int lleft; /* number of chars left in this buffer */
574 char *nextc; /* next char in buffer */
575 char *buf; /* input buffer */
576 struct strpush *strpush; /* for pushing strings at this level */
577 struct strpush basestrpush; /* so pushing one is fast */
578};
579
580static struct parsefile basepf; /* top level input file */
581static char basebuf[IBUFSIZ]; /* buffer for top level input file */
582static struct parsefile *parsefile = &basepf; /* current input file */
583
584
585static int tokpushback; /* last token pushed back */
586#define NEOF ((union node *)&tokpushback)
587static int parsebackquote; /* nonzero if we are inside backquotes */
588static int doprompt; /* if set, prompt the user */
589static int needprompt; /* true if interactive and at start of line */
590static int lasttoken; /* last token read */
591static char *wordtext; /* text of last word returned by readtoken */
592static int checkkwd;
593static struct nodelist *backquotelist;
594static union node *redirnode;
595static struct heredoc *heredoc;
596static int quoteflag; /* set if (part of) last token was quoted */
597static int startlinno; /* line # where last token started */
598
599static union node *parsecmd(int);
600static void fixredir(union node *, const char *, int);
601static const char *const *findkwd(const char *);
602static char *endofname(const char *);
603
604/* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
605
606typedef void *pointer;
607
608static char nullstr[1]; /* zero length string */
609static const char spcstr[] = " ";
610static const char snlfmt[] = "%s\n";
611static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
612static const char illnum[] = "Illegal number: %s";
613static const char homestr[] = "HOME";
614
615#ifdef DEBUG
616#define TRACE(param) trace param
617#define TRACEV(param) tracev param
618#else
619#define TRACE(param)
620#define TRACEV(param)
621#endif
622
623#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
624#define __builtin_expect(x, expected_value) (x)
625#endif
626
627#define xlikely(x) __builtin_expect((x),1)
628
629
630#define TEOF 0
631#define TNL 1
632#define TREDIR 2
633#define TWORD 3
634#define TSEMI 4
635#define TBACKGND 5
636#define TAND 6
637#define TOR 7
638#define TPIPE 8
639#define TLP 9
640#define TRP 10
641#define TENDCASE 11
642#define TENDBQUOTE 12
643#define TNOT 13
644#define TCASE 14
645#define TDO 15
646#define TDONE 16
647#define TELIF 17
648#define TELSE 18
649#define TESAC 19
650#define TFI 20
651#define TFOR 21
652#define TIF 22
653#define TIN 23
654#define TTHEN 24
655#define TUNTIL 25
656#define TWHILE 26
657#define TBEGIN 27
658#define TEND 28
659
660/* first char is indicating which tokens mark the end of a list */
661static const char *const tokname_array[] = {
662 "\1end of file",
663 "\0newline",
664 "\0redirection",
665 "\0word",
666 "\0;",
667 "\0&",
668 "\0&&",
669 "\0||",
670 "\0|",
671 "\0(",
672 "\1)",
673 "\1;;",
674 "\1`",
675#define KWDOFFSET 13
676 /* the following are keywords */
677 "\0!",
678 "\0case",
679 "\1do",
680 "\1done",
681 "\1elif",
682 "\1else",
683 "\1esac",
684 "\1fi",
685 "\0for",
686 "\0if",
687 "\0in",
688 "\1then",
689 "\0until",
690 "\0while",
691 "\0{",
692 "\1}",
693};
694
695static const char *tokname(int tok)
696{
697 static char buf[16];
698
699 if (tok >= TSEMI)
700 buf[0] = '"';
701 sprintf(buf + (tok >= TSEMI), "%s%c",
702 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
703 return buf;
704}
705
706/* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */
707
708/*
709 * Most machines require the value returned from malloc to be aligned
710 * in some way. The following macro will get this right on many machines.
711 */
712
713#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
714/*
715 * It appears that grabstackstr() will barf with such alignments
716 * because stalloc() will return a string allocated in a new stackblock.
717 */
718#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
719
720/*
721 * This file was generated by the mksyntax program.
722 */
723
724
725/* Syntax classes */
726#define CWORD 0 /* character is nothing special */
727#define CNL 1 /* newline character */
728#define CBACK 2 /* a backslash character */
729#define CSQUOTE 3 /* single quote */
730#define CDQUOTE 4 /* double quote */
731#define CENDQUOTE 5 /* a terminating quote */
732#define CBQUOTE 6 /* backwards single quote */
733#define CVAR 7 /* a dollar sign */
734#define CENDVAR 8 /* a '}' character */
735#define CLP 9 /* a left paren in arithmetic */
736#define CRP 10 /* a right paren in arithmetic */
737#define CENDFILE 11 /* end of file */
738#define CCTL 12 /* like CWORD, except it must be escaped */
739#define CSPCL 13 /* these terminate a word */
740#define CIGN 14 /* character should be ignored */
741
742#ifdef CONFIG_ASH_ALIAS
743#define SYNBASE 130
744#define PEOF -130
745#define PEOA -129
746#define PEOA_OR_PEOF PEOA
747#else
748#define SYNBASE 129
749#define PEOF -129
750#define PEOA_OR_PEOF PEOF
751#endif
752
753#define is_digit(c) ((unsigned)((c) - '0') <= 9)
754#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
755#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
756
757/*
758 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
759 * (assuming ascii char codes, as the original implementation did)
760 */
761#define is_special(c) \
762 ( (((unsigned int)c) - 33 < 32) \
763 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
764
765#define digit_val(c) ((c) - '0')
766
767/*
768 * This file was generated by the mksyntax program.
769 */
770
771#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
772#define USE_SIT_FUNCTION
773#endif
774
775/* number syntax index */
776#define BASESYNTAX 0 /* not in quotes */
777#define DQSYNTAX 1 /* in double quotes */
778#define SQSYNTAX 2 /* in single quotes */
779#define ARISYNTAX 3 /* in arithmetic */
780
781#ifdef CONFIG_ASH_MATH_SUPPORT
782static const char S_I_T[][4] = {
783#ifdef CONFIG_ASH_ALIAS
784 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
785#endif
786 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
787 {CNL, CNL, CNL, CNL}, /* 2, \n */
788 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
789 {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
790 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
791 {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
792 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
793 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
794 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
795 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
796 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
797#ifndef USE_SIT_FUNCTION
798 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
799 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
800 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
801#endif
802};
803#else
804static const char S_I_T[][3] = {
805#ifdef CONFIG_ASH_ALIAS
806 {CSPCL, CIGN, CIGN}, /* 0, PEOA */
807#endif
808 {CSPCL, CWORD, CWORD}, /* 1, ' ' */
809 {CNL, CNL, CNL}, /* 2, \n */
810 {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
811 {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
812 {CVAR, CVAR, CWORD}, /* 5, $ */
813 {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
814 {CSPCL, CWORD, CWORD}, /* 7, ( */
815 {CSPCL, CWORD, CWORD}, /* 8, ) */
816 {CBACK, CBACK, CCTL}, /* 9, \ */
817 {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
818 {CENDVAR, CENDVAR, CWORD}, /* 11, } */
819#ifndef USE_SIT_FUNCTION
820 {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
821 {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
822 {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
823#endif
824};
825#endif /* CONFIG_ASH_MATH_SUPPORT */
826
827#ifdef USE_SIT_FUNCTION
828
829#define U_C(c) ((unsigned char)(c))
830
831static int SIT(int c, int syntax)
832{
833 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
834#ifdef CONFIG_ASH_ALIAS
835 static const char syntax_index_table[] = {
836 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
837 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
838 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
839 11, 3 /* "}~" */
840 };
841#else
842 static const char syntax_index_table[] = {
843 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
844 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
845 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
846 10, 2 /* "}~" */
847 };
848#endif
849 const char *s;
850 int indx;
851
852 if (c == PEOF) /* 2^8+2 */
853 return CENDFILE;
854#ifdef CONFIG_ASH_ALIAS
855 if (c == PEOA) /* 2^8+1 */
856 indx = 0;
857 else
858#endif
859 if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
860 return CCTL;
861 else {
862 s = strchr(spec_symbls, c);
863 if (s == 0 || *s == 0)
864 return CWORD;
865 indx = syntax_index_table[(s - spec_symbls)];
866 }
867 return S_I_T[indx][syntax];
868}
869
870#else /* USE_SIT_FUNCTION */
871
872#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
873
874#ifdef CONFIG_ASH_ALIAS
875#define CSPCL_CIGN_CIGN_CIGN 0
876#define CSPCL_CWORD_CWORD_CWORD 1
877#define CNL_CNL_CNL_CNL 2
878#define CWORD_CCTL_CCTL_CWORD 3
879#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
880#define CVAR_CVAR_CWORD_CVAR 5
881#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
882#define CSPCL_CWORD_CWORD_CLP 7
883#define CSPCL_CWORD_CWORD_CRP 8
884#define CBACK_CBACK_CCTL_CBACK 9
885#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
886#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
887#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
888#define CWORD_CWORD_CWORD_CWORD 13
889#define CCTL_CCTL_CCTL_CCTL 14
890#else
891#define CSPCL_CWORD_CWORD_CWORD 0
892#define CNL_CNL_CNL_CNL 1
893#define CWORD_CCTL_CCTL_CWORD 2
894#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
895#define CVAR_CVAR_CWORD_CVAR 4
896#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
897#define CSPCL_CWORD_CWORD_CLP 6
898#define CSPCL_CWORD_CWORD_CRP 7
899#define CBACK_CBACK_CCTL_CBACK 8
900#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
901#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
902#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
903#define CWORD_CWORD_CWORD_CWORD 12
904#define CCTL_CCTL_CCTL_CCTL 13
905#endif
906
907static const char syntax_index_table[258] = {
908 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
909 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
910#ifdef CONFIG_ASH_ALIAS
911 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
912#endif
913 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
914 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
915 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
916 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
917 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
918 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
919 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
920 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
921 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
922 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
923 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
924 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
925 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
926 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
927 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
928 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
929 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
930 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
931 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
932 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
933 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
934 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
935 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
936 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
937 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
938 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
939 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
940 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
941 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
942 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
943 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
944 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
945 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
946 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
947 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
948 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
949 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
950 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
951 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
952 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
953 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
954 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
955 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
956 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
957 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
958 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
959 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
960 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
961 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
962 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
963 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
964 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
965 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
966 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
967 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
968 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
969 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
970 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
971 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
972 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
973 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
974 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
975 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
976 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
977 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
978 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
979 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
980 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
981 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
982 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
983 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
984 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
985 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
986 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
987 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
988 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
989 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
990 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
991 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
992 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
993 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
994 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
995 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
996 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
997 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
998 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
999 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
1000 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
1001 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
1002 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
1003 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
1004 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
1005 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
1006 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
1007 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
1008 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
1009 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
1010 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
1011 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
1012 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
1013 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
1014 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
1015 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
1016 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
1017 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
1018 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
1019 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
1020 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
1021 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
1022 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
1023 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
1024 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
1025 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
1026 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
1027 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
1028 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
1029 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
1030 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
1031 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
1032 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
1033 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
1034 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
1035 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
1036 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
1037 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
1038 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
1039 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
1040 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
1041 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
1042 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
1043 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
1044 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
1045 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
1046 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
1047 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
1048 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
1049 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
1050 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
1051 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
1052 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
1053 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
1054 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
1055 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
1056 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
1057 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
1058 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
1059 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
1060 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
1061 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
1062 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
1063 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
1064 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
1065 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
1066 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
1067 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
1068 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
1069 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
1070 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
1071 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
1072 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
1073 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
1074 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
1075 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
1076 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
1077 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
1078 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
1079 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
1080 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
1081 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
1082 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
1083 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
1084 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
1085 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
1086 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
1087 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
1088 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
1089 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
1090 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
1091 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
1092 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
1093 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
1094 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
1095 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
1096 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
1097 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
1098 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
1099 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
1100 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
1101 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
1102 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
1103 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
1104 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
1105 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
1106 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
1107 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
1108 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
1109 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
1110 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
1111 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
1112 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
1113 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
1114 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
1115 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
1116 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
1117 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
1118 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
1119 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
1120 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
1121 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
1122 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
1123 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
1124 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
1125 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
1126 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
1127 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
1128 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
1129 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
1130 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
1131 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
1132 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
1133 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
1134 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
1135 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
1136 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
1137 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
1138 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
1139 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
1140 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
1141 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
1142 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
1143 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
1144 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
1145 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
1146 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
1147 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
1148 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
1149 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
1150 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
1151 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
1152 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
1153 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
1154 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
1155 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
1156 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
1157 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
1158 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
1159 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
1160 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
1161 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
1162 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
1163 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
1164 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
1165 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
1166 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
1167 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
1168 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
1169};
1170
1171#endif /* USE_SIT_FUNCTION */
1172
1173/* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */
1174
1175
1176#define ATABSIZE 39
1177
1178static int funcblocksize; /* size of structures in function */
1179static int funcstringsize; /* size of strings in node */
1180static pointer funcblock; /* block to allocate function from */
1181static char *funcstring; /* block to allocate strings from */
1182
1183static const short nodesize[26] = {
1184 SHELL_ALIGN(sizeof (struct ncmd)),
1185 SHELL_ALIGN(sizeof (struct npipe)),
1186 SHELL_ALIGN(sizeof (struct nredir)),
1187 SHELL_ALIGN(sizeof (struct nredir)),
1188 SHELL_ALIGN(sizeof (struct nredir)),
1189 SHELL_ALIGN(sizeof (struct nbinary)),
1190 SHELL_ALIGN(sizeof (struct nbinary)),
1191 SHELL_ALIGN(sizeof (struct nbinary)),
1192 SHELL_ALIGN(sizeof (struct nif)),
1193 SHELL_ALIGN(sizeof (struct nbinary)),
1194 SHELL_ALIGN(sizeof (struct nbinary)),
1195 SHELL_ALIGN(sizeof (struct nfor)),
1196 SHELL_ALIGN(sizeof (struct ncase)),
1197 SHELL_ALIGN(sizeof (struct nclist)),
1198 SHELL_ALIGN(sizeof (struct narg)),
1199 SHELL_ALIGN(sizeof (struct narg)),
1200 SHELL_ALIGN(sizeof (struct nfile)),
1201 SHELL_ALIGN(sizeof (struct nfile)),
1202 SHELL_ALIGN(sizeof (struct nfile)),
1203 SHELL_ALIGN(sizeof (struct nfile)),
1204 SHELL_ALIGN(sizeof (struct nfile)),
1205 SHELL_ALIGN(sizeof (struct ndup)),
1206 SHELL_ALIGN(sizeof (struct ndup)),
1207 SHELL_ALIGN(sizeof (struct nhere)),
1208 SHELL_ALIGN(sizeof (struct nhere)),
1209 SHELL_ALIGN(sizeof (struct nnot)),
1210};
1211
1212
1213static void calcsize(union node *);
1214static void sizenodelist(struct nodelist *);
1215static union node *copynode(union node *);
1216static struct nodelist *copynodelist(struct nodelist *);
1217static char *nodesavestr(char *);
1218
1219
1220
1221static void evalstring(char *);
1222union node; /* BLETCH for ansi C */
1223static void evaltree(union node *, int);
1224static void evalbackcmd(union node *, struct backcmd *);
1225
1226/* in_function returns nonzero if we are currently evaluating a function */
1227#define in_function() funcnest
1228static int evalskip; /* set if we are skipping commands */
1229static int skipcount; /* number of levels to skip */
1230static int funcnest; /* depth of function calls */
1231
1232/* reasons for skipping commands (see comment on breakcmd routine) */
1233#define SKIPBREAK 1
1234#define SKIPCONT 2
1235#define SKIPFUNC 3
1236#define SKIPFILE 4
1237
1238/*
1239 * This file was generated by the mkbuiltins program.
1240 */
1241
1242#ifdef JOBS
1243static int bgcmd(int, char **);
1244#endif
1245static int breakcmd(int, char **);
1246static int cdcmd(int, char **);
1247#ifdef CONFIG_ASH_CMDCMD
1248static int commandcmd(int, char **);
1249#endif
1250static int dotcmd(int, char **);
1251static int evalcmd(int, char **);
1252static int execcmd(int, char **);
1253static int exitcmd(int, char **);
1254static int exportcmd(int, char **);
1255static int falsecmd(int, char **);
1256#ifdef JOBS
1257static int fgcmd(int, char **);
1258#endif
1259#ifdef CONFIG_ASH_GETOPTS
1260static int getoptscmd(int, char **);
1261#endif
1262static int hashcmd(int, char **);
1263#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1264static int helpcmd(int argc, char **argv);
1265#endif
1266#ifdef JOBS
1267static int jobscmd(int, char **);
1268#endif
1269#ifdef CONFIG_ASH_MATH_SUPPORT
1270static int letcmd(int, char **);
1271#endif
1272static int localcmd(int, char **);
1273static int pwdcmd(int, char **);
1274static int readcmd(int, char **);
1275static int returncmd(int, char **);
1276static int setcmd(int, char **);
1277static int shiftcmd(int, char **);
1278static int timescmd(int, char **);
1279static int trapcmd(int, char **);
1280static int truecmd(int, char **);
1281static int typecmd(int, char **);
1282static int umaskcmd(int, char **);
1283static int unsetcmd(int, char **);
1284static int waitcmd(int, char **);
1285static int ulimitcmd(int, char **);
1286#ifdef JOBS
1287static int killcmd(int, char **);
1288#endif
1289
1290/* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1291
1292#ifdef CONFIG_ASH_MAIL
1293static void chkmail(void);
1294static void changemail(const char *);
1295#endif
1296
1297/* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */
1298
1299/* values of cmdtype */
1300#define CMDUNKNOWN -1 /* no entry in table for command */
1301#define CMDNORMAL 0 /* command is an executable program */
1302#define CMDFUNCTION 1 /* command is a shell function */
1303#define CMDBUILTIN 2 /* command is a shell builtin */
1304
1305struct builtincmd {
1306 const char *name;
1307 int (*builtin)(int, char **);
1308 /* unsigned flags; */
1309};
1310
1311#ifdef CONFIG_ASH_CMDCMD
1312# ifdef JOBS
1313# ifdef CONFIG_ASH_ALIAS
1314# define COMMANDCMD (builtincmd + 7)
1315# define EXECCMD (builtincmd + 10)
1316# else
1317# define COMMANDCMD (builtincmd + 6)
1318# define EXECCMD (builtincmd + 9)
1319# endif
1320# else /* ! JOBS */
1321# ifdef CONFIG_ASH_ALIAS
1322# define COMMANDCMD (builtincmd + 6)
1323# define EXECCMD (builtincmd + 9)
1324# else
1325# define COMMANDCMD (builtincmd + 5)
1326# define EXECCMD (builtincmd + 8)
1327# endif
1328# endif /* JOBS */
1329#else /* ! CONFIG_ASH_CMDCMD */
1330# ifdef JOBS
1331# ifdef CONFIG_ASH_ALIAS
1332# define EXECCMD (builtincmd + 9)
1333# else
1334# define EXECCMD (builtincmd + 8)
1335# endif
1336# else /* ! JOBS */
1337# ifdef CONFIG_ASH_ALIAS
1338# define EXECCMD (builtincmd + 8)
1339# else
1340# define EXECCMD (builtincmd + 7)
1341# endif
1342# endif /* JOBS */
1343#endif /* CONFIG_ASH_CMDCMD */
1344
1345#define BUILTIN_NOSPEC "0"
1346#define BUILTIN_SPECIAL "1"
1347#define BUILTIN_REGULAR "2"
1348#define BUILTIN_SPEC_REG "3"
1349#define BUILTIN_ASSIGN "4"
1350#define BUILTIN_SPEC_ASSG "5"
1351#define BUILTIN_REG_ASSG "6"
1352#define BUILTIN_SPEC_REG_ASSG "7"
1353
1354#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1355#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1356
1357static const struct builtincmd builtincmd[] = {
1358 { BUILTIN_SPEC_REG ".", dotcmd },
1359 { BUILTIN_SPEC_REG ":", truecmd },
1360#ifdef CONFIG_ASH_ALIAS
1361 { BUILTIN_REG_ASSG "alias", aliascmd },
1362#endif
1363#ifdef JOBS
1364 { BUILTIN_REGULAR "bg", bgcmd },
1365#endif
1366 { BUILTIN_SPEC_REG "break", breakcmd },
1367 { BUILTIN_REGULAR "cd", cdcmd },
1368 { BUILTIN_NOSPEC "chdir", cdcmd },
1369#ifdef CONFIG_ASH_CMDCMD
1370 { BUILTIN_REGULAR "command", commandcmd },
1371#endif
1372 { BUILTIN_SPEC_REG "continue", breakcmd },
1373 { BUILTIN_SPEC_REG "eval", evalcmd },
1374 { BUILTIN_SPEC_REG "exec", execcmd },
1375 { BUILTIN_SPEC_REG "exit", exitcmd },
1376 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
1377 { BUILTIN_REGULAR "false", falsecmd },
1378#ifdef JOBS
1379 { BUILTIN_REGULAR "fg", fgcmd },
1380#endif
1381#ifdef CONFIG_ASH_GETOPTS
1382 { BUILTIN_REGULAR "getopts", getoptscmd },
1383#endif
1384 { BUILTIN_NOSPEC "hash", hashcmd },
1385#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1386 { BUILTIN_NOSPEC "help", helpcmd },
1387#endif
1388#ifdef JOBS
1389 { BUILTIN_REGULAR "jobs", jobscmd },
1390 { BUILTIN_REGULAR "kill", killcmd },
1391#endif
1392#ifdef CONFIG_ASH_MATH_SUPPORT
1393 { BUILTIN_NOSPEC "let", letcmd },
1394#endif
1395 { BUILTIN_ASSIGN "local", localcmd },
1396 { BUILTIN_NOSPEC "pwd", pwdcmd },
1397 { BUILTIN_REGULAR "read", readcmd },
1398 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
1399 { BUILTIN_SPEC_REG "return", returncmd },
1400 { BUILTIN_SPEC_REG "set", setcmd },
1401 { BUILTIN_SPEC_REG "shift", shiftcmd },
1402 { BUILTIN_SPEC_REG "times", timescmd },
1403 { BUILTIN_SPEC_REG "trap", trapcmd },
1404 { BUILTIN_REGULAR "true", truecmd },
1405 { BUILTIN_NOSPEC "type", typecmd },
1406 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
1407 { BUILTIN_REGULAR "umask", umaskcmd },
1408#ifdef CONFIG_ASH_ALIAS
1409 { BUILTIN_REGULAR "unalias", unaliascmd },
1410#endif
1411 { BUILTIN_SPEC_REG "unset", unsetcmd },
1412 { BUILTIN_REGULAR "wait", waitcmd },
1413};
1414
1415#define NUMBUILTINS (sizeof (builtincmd) / sizeof (struct builtincmd) )
1416
1417
1418
1419struct cmdentry {
1420 int cmdtype;
1421 union param {
1422 int index;
1423 const struct builtincmd *cmd;
1424 struct funcnode *func;
1425 } u;
1426};
1427
1428
1429/* action to find_command() */
1430#define DO_ERR 0x01 /* prints errors */
1431#define DO_ABS 0x02 /* checks absolute paths */
1432#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
1433#define DO_ALTPATH 0x08 /* using alternate path */
1434#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
1435
1436static const char *pathopt; /* set by padvance */
1437
1438static void shellexec(char **, const char *, int)
1439 __attribute__((__noreturn__));
1440static char *padvance(const char **, const char *);
1441static void find_command(char *, struct cmdentry *, int, const char *);
1442static struct builtincmd *find_builtin(const char *);
1443static void hashcd(void);
1444static void changepath(const char *);
1445static void defun(char *, union node *);
1446static void unsetfunc(const char *);
1447
1448#ifdef CONFIG_ASH_MATH_SUPPORT_64
1449typedef int64_t arith_t;
1450#else
1451typedef long arith_t;
1452#endif
1453
1454#ifdef CONFIG_ASH_MATH_SUPPORT
1455static arith_t dash_arith(const char *);
1456static arith_t arith(const char *expr, int *perrcode);
1457#endif
1458
1459#ifdef CONFIG_ASH_RANDOM_SUPPORT
1460static unsigned long rseed;
1461static void change_random(const char *);
1462# ifndef DYNAMIC_VAR
1463# define DYNAMIC_VAR
1464# endif
1465#endif
1466
1467/* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1468
1469static void reset(void);
1470
1471/* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */
1472
1473/*
1474 * Shell variables.
1475 */
1476
1477/* flags */
1478#define VEXPORT 0x01 /* variable is exported */
1479#define VREADONLY 0x02 /* variable cannot be modified */
1480#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1481#define VTEXTFIXED 0x08 /* text is statically allocated */
1482#define VSTACK 0x10 /* text is allocated on the stack */
1483#define VUNSET 0x20 /* the variable is not set */
1484#define VNOFUNC 0x40 /* don't call the callback function */
1485#define VNOSET 0x80 /* do not set variable - just readonly test */
1486#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1487#ifdef DYNAMIC_VAR
1488# define VDYNAMIC 0x200 /* dynamic variable */
1489# else
1490# define VDYNAMIC 0
1491#endif
1492
1493struct var {
1494 struct var *next; /* next entry in hash list */
1495 int flags; /* flags are defined above */
1496 const char *text; /* name=value */
1497 void (*func)(const char *); /* function to be called when */
1498 /* the variable gets set/unset */
1499};
1500
1501struct localvar {
1502 struct localvar *next; /* next local variable in list */
1503 struct var *vp; /* the variable that was made local */
1504 int flags; /* saved flags */
1505 const char *text; /* saved text */
1506};
1507
1508
1509static struct localvar *localvars;
1510
1511/*
1512 * Shell variables.
1513 */
1514
1515#ifdef CONFIG_ASH_GETOPTS
1516static void getoptsreset(const char *);
1517#endif
1518
1519#ifdef CONFIG_LOCALE_SUPPORT
1520#include <locale.h>
1521static void change_lc_all(const char *value);
1522static void change_lc_ctype(const char *value);
1523#endif
1524
1525
1526#define VTABSIZE 39
1527
1528static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1529#ifdef IFS_BROKEN
1530static const char defifsvar[] = "IFS= \t\n";
1531#define defifs (defifsvar + 4)
1532#else
1533static const char defifs[] = " \t\n";
1534#endif
1535
1536
1537static struct var varinit[] = {
1538#ifdef IFS_BROKEN
1539 { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
1540#else
1541 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
1542#endif
1543
1544#ifdef CONFIG_ASH_MAIL
1545 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1546 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
1547#endif
1548
1549 { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1550 { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
1551 { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
1552 { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
1553#ifdef CONFIG_ASH_GETOPTS
1554 { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
1555#endif
1556#ifdef CONFIG_ASH_RANDOM_SUPPORT
1557 {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
1558#endif
1559#ifdef CONFIG_LOCALE_SUPPORT
1560 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1561 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
1562#endif
1563#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1564 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
1565#endif
1566};
1567
1568#define vifs varinit[0]
1569#ifdef CONFIG_ASH_MAIL
1570#define vmail (&vifs)[1]
1571#define vmpath (&vmail)[1]
1572#else
1573#define vmpath vifs
1574#endif
1575#define vpath (&vmpath)[1]
1576#define vps1 (&vpath)[1]
1577#define vps2 (&vps1)[1]
1578#define vps4 (&vps2)[1]
1579#define voptind (&vps4)[1]
1580#ifdef CONFIG_ASH_GETOPTS
1581#define vrandom (&voptind)[1]
1582#else
1583#define vrandom (&vps4)[1]
1584#endif
1585#define defpath (defpathvar + 5)
1586
1587/*
1588 * The following macros access the values of the above variables.
1589 * They have to skip over the name. They return the null string
1590 * for unset variables.
1591 */
1592
1593#define ifsval() (vifs.text + 4)
1594#define ifsset() ((vifs.flags & VUNSET) == 0)
1595#define mailval() (vmail.text + 5)
1596#define mpathval() (vmpath.text + 9)
1597#define pathval() (vpath.text + 5)
1598#define ps1val() (vps1.text + 4)
1599#define ps2val() (vps2.text + 4)
1600#define ps4val() (vps4.text + 4)
1601#define optindval() (voptind.text + 7)
1602
1603#define mpathset() ((vmpath.flags & VUNSET) == 0)
1604
1605static void setvar(const char *, const char *, int);
1606static void setvareq(char *, int);
1607static void listsetvar(struct strlist *, int);
1608static char *lookupvar(const char *);
1609static char *bltinlookup(const char *);
1610static char **listvars(int, int, char ***);
1611#define environment() listvars(VEXPORT, VUNSET, 0)
1612static int showvars(const char *, int, int);
1613static void poplocalvars(void);
1614static int unsetvar(const char *);
1615#ifdef CONFIG_ASH_GETOPTS
1616static int setvarsafe(const char *, const char *, int);
1617#endif
1618static int varcmp(const char *, const char *);
1619static struct var **hashvar(const char *);
1620
1621
1622static inline int varequal(const char *a, const char *b) {
1623 return !varcmp(a, b);
1624}
1625
1626
1627static int loopnest; /* current loop nesting level */
1628
1629/*
1630 * The parsefile structure pointed to by the global variable parsefile
1631 * contains information about the current file being read.
1632 */
1633
1634
1635struct redirtab {
1636 struct redirtab *next;
1637 int renamed[10];
1638 int nullredirs;
1639};
1640
1641static struct redirtab *redirlist;
1642static int nullredirs;
1643
1644extern char **environ;
1645
1646/* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */
1647
1648
1649static void outstr(const char *, FILE *);
1650static void outcslow(int, FILE *);
1651static void flushall(void);
1652static void flusherr(void);
1653static int out1fmt(const char *, ...)
1654 __attribute__((__format__(__printf__,1,2)));
1655static int fmtstr(char *, size_t, const char *, ...)
1656 __attribute__((__format__(__printf__,3,4)));
1657
1658static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1659
1660
1661static void out1str(const char *p)
1662{
1663 outstr(p, stdout);
1664}
1665
1666static void out2str(const char *p)
1667{
1668 outstr(p, stderr);
1669 flusherr();
1670}
1671
1672/*
1673 * Initialization code.
1674 */
1675
1676/*
1677 * This routine initializes the builtin variables.
1678 */
1679
1680static inline void
1681initvar(void)
1682{
1683 struct var *vp;
1684 struct var *end;
1685 struct var **vpp;
1686
1687 /*
1688 * PS1 depends on uid
1689 */
1690#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1691 vps1.text = "PS1=\\w \\$ ";
1692#else
1693 if (!geteuid())
1694 vps1.text = "PS1=# ";
1695#endif
1696 vp = varinit;
1697 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1698 do {
1699 vpp = hashvar(vp->text);
1700 vp->next = *vpp;
1701 *vpp = vp;
1702 } while (++vp < end);
1703}
1704
1705static inline void
1706init(void)
1707{
1708
1709 /* from input.c: */
1710 {
1711 basepf.nextc = basepf.buf = basebuf;
1712 }
1713
1714 /* from trap.c: */
1715 {
1716 signal(SIGCHLD, SIG_DFL);
1717 }
1718
1719 /* from var.c: */
1720 {
1721 char **envp;
1722 char ppid[32];
1723
1724 initvar();
1725 for (envp = environ ; *envp ; envp++) {
1726 if (strchr(*envp, '=')) {
1727 setvareq(*envp, VEXPORT|VTEXTFIXED);
1728 }
1729 }
1730
1731 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
1732 setvar("PPID", ppid, 0);
1733 setpwd(0, 0);
1734 }
1735}
1736
1737/* PEOF (the end of file marker) */
1738
1739/*
1740 * The input line number. Input.c just defines this variable, and saves
1741 * and restores it when files are pushed and popped. The user of this
1742 * package must set its value.
1743 */
1744
1745static int pgetc(void);
1746static int pgetc2(void);
1747static int preadbuffer(void);
1748static void pungetc(void);
1749static void pushstring(char *, void *);
1750static void popstring(void);
1751static void setinputfile(const char *, int);
1752static void setinputfd(int, int);
1753static void setinputstring(char *);
1754static void popfile(void);
1755static void popallfiles(void);
1756static void closescript(void);
1757
1758
1759/* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */
1760
1761
1762/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
1763#define FORK_FG 0
1764#define FORK_BG 1
1765#define FORK_NOJOB 2
1766
1767/* mode flags for showjob(s) */
1768#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
1769#define SHOW_PID 0x04 /* include process pid */
1770#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
1771
1772
1773/*
1774 * A job structure contains information about a job. A job is either a
1775 * single process or a set of processes contained in a pipeline. In the
1776 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1777 * array of pids.
1778 */
1779
1780struct procstat {
1781 pid_t pid; /* process id */
1782 int status; /* last process status from wait() */
1783 char *cmd; /* text of command being run */
1784};
1785
1786struct job {
1787 struct procstat ps0; /* status of process */
1788 struct procstat *ps; /* status or processes when more than one */
1789#if JOBS
1790 int stopstatus; /* status of a stopped job */
1791#endif
1792 uint32_t
1793 nprocs: 16, /* number of processes */
1794 state: 8,
1795#define JOBRUNNING 0 /* at least one proc running */
1796#define JOBSTOPPED 1 /* all procs are stopped */
1797#define JOBDONE 2 /* all procs are completed */
1798#if JOBS
1799 sigint: 1, /* job was killed by SIGINT */
1800 jobctl: 1, /* job running under job control */
1801#endif
1802 waited: 1, /* true if this entry has been waited for */
1803 used: 1, /* true if this entry is in used */
1804 changed: 1; /* true if status has changed */
1805 struct job *prev_job; /* previous job */
1806};
1807
1808static pid_t backgndpid; /* pid of last background process */
1809static int job_warning; /* user was warned about stopped jobs */
1810#if JOBS
1811static int jobctl; /* true if doing job control */
1812#endif
1813
1814static struct job *makejob(union node *, int);
1815static int forkshell(struct job *, union node *, int);
1816static int waitforjob(struct job *);
1817static int stoppedjobs(void);
1818
1819#if ! JOBS
1820#define setjobctl(on) /* do nothing */
1821#else
1822static void setjobctl(int);
1823static void showjobs(FILE *, int);
1824#endif
1825
1826/* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */
1827
1828
1829/* pid of main shell */
1830static int rootpid;
1831/* true if we aren't a child of the main shell */
1832static int rootshell;
1833
1834static void readcmdfile(char *);
1835static void cmdloop(int);
1836
1837/* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */
1838
1839
1840struct stackmark {
1841 struct stack_block *stackp;
1842 char *stacknxt;
1843 size_t stacknleft;
1844 struct stackmark *marknext;
1845};
1846
1847/* minimum size of a block */
1848#define MINSIZE SHELL_ALIGN(504)
1849
1850struct stack_block {
1851 struct stack_block *prev;
1852 char space[MINSIZE];
1853};
1854
1855static struct stack_block stackbase;
1856static struct stack_block *stackp = &stackbase;
1857static struct stackmark *markp;
1858static char *stacknxt = stackbase.space;
1859static size_t stacknleft = MINSIZE;
1860static char *sstrend = stackbase.space + MINSIZE;
1861static int herefd = -1;
1862
1863
1864static pointer ckmalloc(size_t);
1865static pointer ckrealloc(pointer, size_t);
1866static char *savestr(const char *);
1867static pointer stalloc(size_t);
1868static void stunalloc(pointer);
1869static void setstackmark(struct stackmark *);
1870static void popstackmark(struct stackmark *);
1871static void growstackblock(void);
1872static void *growstackstr(void);
1873static char *makestrspace(size_t, char *);
1874static char *stnputs(const char *, size_t, char *);
1875static char *stputs(const char *, char *);
1876
1877
1878static inline char *_STPUTC(char c, char *p) {
1879 if (p == sstrend)
1880 p = growstackstr();
1881 *p++ = c;
1882 return p;
1883}
1884
1885#define stackblock() ((void *)stacknxt)
1886#define stackblocksize() stacknleft
1887#define STARTSTACKSTR(p) ((p) = stackblock())
1888#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
1889#define CHECKSTRSPACE(n, p) \
1890 ({ \
1891 char *q = (p); \
1892 size_t l = (n); \
1893 size_t m = sstrend - q; \
1894 if (l > m) \
1895 (p) = makestrspace(l, q); \
1896 0; \
1897 })
1898#define USTPUTC(c, p) (*p++ = (c))
1899#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
1900#define STUNPUTC(p) (--p)
1901#define STTOPC(p) p[-1]
1902#define STADJUST(amount, p) (p += (amount))
1903
1904#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1905#define ungrabstackstr(s, p) stunalloc((s))
1906#define stackstrend() ((void *)sstrend)
1907
1908#define ckfree(p) free((pointer)(p))
1909
1910/* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */
1911
1912
1913#define DOLATSTRLEN 4
1914
1915static char *prefix(const char *, const char *);
1916static int number(const char *);
1917static int is_number(const char *);
1918static char *single_quote(const char *);
1919static char *sstrdup(const char *);
1920
1921#define equal(s1, s2) (strcmp(s1, s2) == 0)
1922#define scopy(s1, s2) ((void)strcpy(s2, s1))
1923
1924/* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
1925
1926struct shparam {
1927 int nparam; /* # of positional parameters (without $0) */
1928 unsigned char malloc; /* if parameter list dynamically allocated */
1929 char **p; /* parameter list */
1930#ifdef CONFIG_ASH_GETOPTS
1931 int optind; /* next parameter to be processed by getopts */
1932 int optoff; /* used by getopts */
1933#endif
1934};
1935
1936
1937#define eflag optlist[0]
1938#define fflag optlist[1]
1939#define Iflag optlist[2]
1940#define iflag optlist[3]
1941#define mflag optlist[4]
1942#define nflag optlist[5]
1943#define sflag optlist[6]
1944#define xflag optlist[7]
1945#define vflag optlist[8]
1946#define Cflag optlist[9]
1947#define aflag optlist[10]
1948#define bflag optlist[11]
1949#define uflag optlist[12]
1950#define qflag optlist[13]
1951
1952#ifdef DEBUG
1953#define nolog optlist[14]
1954#define debug optlist[15]
1955#define NOPTS 16
1956#else
1957#define NOPTS 14
1958#endif
1959
1960/* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
1961
1962
1963static const char *const optletters_optnames[NOPTS] = {
1964 "e" "errexit",
1965 "f" "noglob",
1966 "I" "ignoreeof",
1967 "i" "interactive",
1968 "m" "monitor",
1969 "n" "noexec",
1970 "s" "stdin",
1971 "x" "xtrace",
1972 "v" "verbose",
1973 "C" "noclobber",
1974 "a" "allexport",
1975 "b" "notify",
1976 "u" "nounset",
1977 "q" "quietprofile",
1978#ifdef DEBUG
1979 "\0" "nolog",
1980 "\0" "debug",
1981#endif
1982};
1983
1984#define optletters(n) optletters_optnames[(n)][0]
1985#define optnames(n) (&optletters_optnames[(n)][1])
1986
1987
1988static char optlist[NOPTS];
1989
1990
1991static char *arg0; /* value of $0 */
1992static struct shparam shellparam; /* $@ current positional parameters */
1993static char **argptr; /* argument list for builtin commands */
1994static char *optionarg; /* set by nextopt (like getopt) */
1995static char *optptr; /* used by nextopt */
1996
1997static char *minusc; /* argument to -c option */
1998
1999
2000static void procargs(int, char **);
2001static void optschanged(void);
2002static void setparam(char **);
2003static void freeparam(volatile struct shparam *);
2004static int shiftcmd(int, char **);
2005static int setcmd(int, char **);
2006static int nextopt(const char *);
2007
2008/* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */
2009
2010/* flags passed to redirect */
2011#define REDIR_PUSH 01 /* save previous values of file descriptors */
2012#define REDIR_SAVEFD2 03 /* set preverrout */
2013
2014union node;
2015static void redirect(union node *, int);
2016static void popredir(int);
2017static void clearredir(int);
2018static int copyfd(int, int);
2019static int redirectsafe(union node *, int);
2020
2021/* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */
2022
2023
2024#ifdef DEBUG
2025static void showtree(union node *);
2026static void trace(const char *, ...);
2027static void tracev(const char *, va_list);
2028static void trargs(char **);
2029static void trputc(int);
2030static void trputs(const char *);
2031static void opentrace(void);
2032#endif
2033
2034/* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */
2035
2036
2037/* trap handler commands */
2038static char *trap[NSIG];
2039/* current value of signal */
2040static char sigmode[NSIG - 1];
2041/* indicates specified signal received */
2042static char gotsig[NSIG - 1];
2043
2044static void clear_traps(void);
2045static void setsignal(int);
2046static void ignoresig(int);
2047static void onsig(int);
2048static void dotrap(void);
2049static void setinteractive(int);
2050static void exitshell(void) __attribute__((__noreturn__));
2051static int decode_signal(const char *, int);
2052
2053/*
2054 * This routine is called when an error or an interrupt occurs in an
2055 * interactive shell and control is returned to the main command loop.
2056 */
2057
2058static void
2059reset(void)
2060{
2061 /* from eval.c: */
2062 {
2063 evalskip = 0;
2064 loopnest = 0;
2065 funcnest = 0;
2066 }
2067
2068 /* from input.c: */
2069 {
2070 parselleft = parsenleft = 0; /* clear input buffer */
2071 popallfiles();
2072 }
2073
2074 /* from parser.c: */
2075 {
2076 tokpushback = 0;
2077 checkkwd = 0;
2078 }
2079
2080 /* from redir.c: */
2081 {
2082 clearredir(0);
2083 }
2084
2085}
2086
2087#ifdef CONFIG_ASH_ALIAS
2088static struct alias *atab[ATABSIZE];
2089
2090static void setalias(const char *, const char *);
2091static struct alias *freealias(struct alias *);
2092static struct alias **__lookupalias(const char *);
2093
2094static void
2095setalias(const char *name, const char *val)
2096{
2097 struct alias *ap, **app;
2098
2099 app = __lookupalias(name);
2100 ap = *app;
2101 INTOFF;
2102 if (ap) {
2103 if (!(ap->flag & ALIASINUSE)) {
2104 ckfree(ap->val);
2105 }
2106 ap->val = savestr(val);
2107 ap->flag &= ~ALIASDEAD;
2108 } else {
2109 /* not found */
2110 ap = ckmalloc(sizeof (struct alias));
2111 ap->name = savestr(name);
2112 ap->val = savestr(val);
2113 ap->flag = 0;
2114 ap->next = 0;
2115 *app = ap;
2116 }
2117 INTON;
2118}
2119
2120static int
2121unalias(const char *name)
2122{
2123 struct alias **app;
2124
2125 app = __lookupalias(name);
2126
2127 if (*app) {
2128 INTOFF;
2129 *app = freealias(*app);
2130 INTON;
2131 return (0);
2132 }
2133
2134 return (1);
2135}
2136
2137static void
2138rmaliases(void)
2139{
2140 struct alias *ap, **app;
2141 int i;
2142
2143 INTOFF;
2144 for (i = 0; i < ATABSIZE; i++) {
2145 app = &atab[i];
2146 for (ap = *app; ap; ap = *app) {
2147 *app = freealias(*app);
2148 if (ap == *app) {
2149 app = &ap->next;
2150 }
2151 }
2152 }
2153 INTON;
2154}
2155
2156static struct alias *
2157lookupalias(const char *name, int check)
2158{
2159 struct alias *ap = *__lookupalias(name);
2160
2161 if (check && ap && (ap->flag & ALIASINUSE))
2162 return (NULL);
2163 return (ap);
2164}
2165
2166/*
2167 * TODO - sort output
2168 */
2169static int
2170aliascmd(int argc, char **argv)
2171{
2172 char *n, *v;
2173 int ret = 0;
2174 struct alias *ap;
2175
2176 if (argc == 1) {
2177 int i;
2178
2179 for (i = 0; i < ATABSIZE; i++)
2180 for (ap = atab[i]; ap; ap = ap->next) {
2181 printalias(ap);
2182 }
2183 return (0);
2184 }
2185 while ((n = *++argv) != NULL) {
2186 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
2187 if ((ap = *__lookupalias(n)) == NULL) {
2188 fprintf(stderr, "%s: %s not found\n", "alias", n);
2189 ret = 1;
2190 } else
2191 printalias(ap);
2192 } else {
2193 *v++ = '\0';
2194 setalias(n, v);
2195 }
2196 }
2197
2198 return (ret);
2199}
2200
2201static int
2202unaliascmd(int argc, char **argv)
2203{
2204 int i;
2205
2206 while ((i = nextopt("a")) != '\0') {
2207 if (i == 'a') {
2208 rmaliases();
2209 return (0);
2210 }
2211 }
2212 for (i = 0; *argptr; argptr++) {
2213 if (unalias(*argptr)) {
2214 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
2215 i = 1;
2216 }
2217 }
2218
2219 return (i);
2220}
2221
2222static struct alias *
2223freealias(struct alias *ap) {
2224 struct alias *next;
2225
2226 if (ap->flag & ALIASINUSE) {
2227 ap->flag |= ALIASDEAD;
2228 return ap;
2229 }
2230
2231 next = ap->next;
2232 ckfree(ap->name);
2233 ckfree(ap->val);
2234 ckfree(ap);
2235 return next;
2236}
2237
2238static void
2239printalias(const struct alias *ap) {
2240 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
2241}
2242
2243static struct alias **
2244__lookupalias(const char *name) {
2245 unsigned int hashval;
2246 struct alias **app;
2247 const char *p;
2248 unsigned int ch;
2249
2250 p = name;
2251
2252 ch = (unsigned char)*p;
2253 hashval = ch << 4;
2254 while (ch) {
2255 hashval += ch;
2256 ch = (unsigned char)*++p;
2257 }
2258 app = &atab[hashval % ATABSIZE];
2259
2260 for (; *app; app = &(*app)->next) {
2261 if (equal(name, (*app)->name)) {
2262 break;
2263 }
2264 }
2265
2266 return app;
2267}
2268#endif /* CONFIG_ASH_ALIAS */
2269
2270
2271/* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2272
2273/*
2274 * The cd and pwd commands.
2275 */
2276
2277#define CD_PHYSICAL 1
2278#define CD_PRINT 2
2279
2280static int docd(const char *, int);
2281static int cdopt(void);
2282
2283static char *curdir = nullstr; /* current working directory */
2284static char *physdir = nullstr; /* physical working directory */
2285
2286static int
2287cdopt(void)
2288{
2289 int flags = 0;
2290 int i, j;
2291
2292 j = 'L';
2293 while ((i = nextopt("LP"))) {
2294 if (i != j) {
2295 flags ^= CD_PHYSICAL;
2296 j = i;
2297 }
2298 }
2299
2300 return flags;
2301}
2302
2303static int
2304cdcmd(int argc, char **argv)
2305{
2306 const char *dest;
2307 const char *path;
2308 const char *p;
2309 char c;
2310 struct stat statb;
2311 int flags;
2312
2313 flags = cdopt();
2314 dest = *argptr;
2315 if (!dest)
2316 dest = bltinlookup(homestr);
2317 else if (dest[0] == '-' && dest[1] == '\0') {
2318 dest = bltinlookup("OLDPWD");
2319 if ( !dest ) goto out;
2320 flags |= CD_PRINT;
2321 goto step7;
2322 }
2323 if (!dest)
2324 dest = nullstr;
2325 if (*dest == '/')
2326 goto step7;
2327 if (*dest == '.') {
2328 c = dest[1];
2329dotdot:
2330 switch (c) {
2331 case '\0':
2332 case '/':
2333 goto step6;
2334 case '.':
2335 c = dest[2];
2336 if (c != '.')
2337 goto dotdot;
2338 }
2339 }
2340 if (!*dest)
2341 dest = ".";
2342 if (!(path = bltinlookup("CDPATH"))) {
2343step6:
2344step7:
2345 p = dest;
2346 goto docd;
2347 }
2348 do {
2349 c = *path;
2350 p = padvance(&path, dest);
2351 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2352 if (c && c != ':')
2353 flags |= CD_PRINT;
2354docd:
2355 if (!docd(p, flags))
2356 goto out;
2357 break;
2358 }
2359 } while (path);
2360 error("can't cd to %s", dest);
2361 /* NOTREACHED */
2362out:
2363 if (flags & CD_PRINT)
2364 out1fmt(snlfmt, curdir);
2365 return 0;
2366}
2367
2368
2369/*
2370 * Update curdir (the name of the current directory) in response to a
2371 * cd command.
2372 */
2373
2374static inline const char *
2375updatepwd(const char *dir)
2376{
2377 char *new;
2378 char *p;
2379 char *cdcomppath;
2380 const char *lim;
2381
2382 cdcomppath = sstrdup(dir);
2383 STARTSTACKSTR(new);
2384 if (*dir != '/') {
2385 if (curdir == nullstr)
2386 return 0;
2387 new = stputs(curdir, new);
2388 }
2389 new = makestrspace(strlen(dir) + 2, new);
2390 lim = stackblock() + 1;
2391 if (*dir != '/') {
2392 if (new[-1] != '/')
2393 USTPUTC('/', new);
2394 if (new > lim && *lim == '/')
2395 lim++;
2396 } else {
2397 USTPUTC('/', new);
2398 cdcomppath++;
2399 if (dir[1] == '/' && dir[2] != '/') {
2400 USTPUTC('/', new);
2401 cdcomppath++;
2402 lim++;
2403 }
2404 }
2405 p = strtok(cdcomppath, "/");
2406 while (p) {
2407 switch(*p) {
2408 case '.':
2409 if (p[1] == '.' && p[2] == '\0') {
2410 while (new > lim) {
2411 STUNPUTC(new);
2412 if (new[-1] == '/')
2413 break;
2414 }
2415 break;
2416 } else if (p[1] == '\0')
2417 break;
2418 /* fall through */
2419 default:
2420 new = stputs(p, new);
2421 USTPUTC('/', new);
2422 }
2423 p = strtok(0, "/");
2424 }
2425 if (new > lim)
2426 STUNPUTC(new);
2427 *new = 0;
2428 return stackblock();
2429}
2430
2431/*
2432 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2433 * know that the current directory has changed.
2434 */
2435
2436static int
2437docd(const char *dest, int flags)
2438{
2439 const char *dir = 0;
2440 int err;
2441
2442 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2443
2444 INTOFF;
2445 if (!(flags & CD_PHYSICAL)) {
2446 dir = updatepwd(dest);
2447 if (dir)
2448 dest = dir;
2449 }
2450 err = chdir(dest);
2451 if (err)
2452 goto out;
2453 setpwd(dir, 1);
2454 hashcd();
2455out:
2456 INTON;
2457 return err;
2458}
2459
2460/*
2461 * Find out what the current directory is. If we already know the current
2462 * directory, this routine returns immediately.
2463 */
2464static inline char *
2465getpwd(void)
2466{
2467 char *dir = getcwd(0, 0);
2468 return dir ? dir : nullstr;
2469}
2470
2471static int
2472pwdcmd(int argc, char **argv)
2473{
2474 int flags;
2475 const char *dir = curdir;
2476
2477 flags = cdopt();
2478 if (flags) {
2479 if (physdir == nullstr)
2480 setpwd(dir, 0);
2481 dir = physdir;
2482 }
2483 out1fmt(snlfmt, dir);
2484 return 0;
2485}
2486
2487static void
2488setpwd(const char *val, int setold)
2489{
2490 char *oldcur, *dir;
2491
2492 oldcur = dir = curdir;
2493
2494 if (setold) {
2495 setvar("OLDPWD", oldcur, VEXPORT);
2496 }
2497 INTOFF;
2498 if (physdir != nullstr) {
2499 if (physdir != oldcur)
2500 free(physdir);
2501 physdir = nullstr;
2502 }
2503 if (oldcur == val || !val) {
2504 char *s = getpwd();
2505 physdir = s;
2506 if (!val)
2507 dir = s;
2508 } else
2509 dir = savestr(val);
2510 if (oldcur != dir && oldcur != nullstr) {
2511 free(oldcur);
2512 }
2513 curdir = dir;
2514 INTON;
2515 setvar("PWD", dir, VEXPORT);
2516}
2517
2518/* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2519
2520/*
2521 * Errors and exceptions.
2522 */
2523
2524/*
2525 * Code to handle exceptions in C.
2526 */
2527
2528
2529
2530static void exverror(int, const char *, va_list)
2531 __attribute__((__noreturn__));
2532
2533/*
2534 * Called to raise an exception. Since C doesn't include exceptions, we
2535 * just do a longjmp to the exception handler. The type of exception is
2536 * stored in the global variable "exception".
2537 */
2538
2539static void
2540exraise(int e)
2541{
2542#ifdef DEBUG
2543 if (handler == NULL)
2544 abort();
2545#endif
2546 INTOFF;
2547
2548 exception = e;
2549 longjmp(handler->loc, 1);
2550}
2551
2552
2553/*
2554 * Called from trap.c when a SIGINT is received. (If the user specifies
2555 * that SIGINT is to be trapped or ignored using the trap builtin, then
2556 * this routine is not called.) Suppressint is nonzero when interrupts
2557 * are held using the INTOFF macro. (The test for iflag is just
2558 * defensive programming.)
2559 */
2560
2561static void
2562onint(void) {
2563 int i;
2564
2565 intpending = 0;
2566 sigsetmask(0);
2567 i = EXSIG;
2568 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
2569 if (!(rootshell && iflag)) {
2570 signal(SIGINT, SIG_DFL);
2571 raise(SIGINT);
2572 }
2573 i = EXINT;
2574 }
2575 exraise(i);
2576 /* NOTREACHED */
2577}
2578
2579static void
2580exvwarning(const char *msg, va_list ap)
2581{
2582 FILE *errs;
2583 const char *name;
2584 const char *fmt;
2585
2586 errs = stderr;
2587 name = arg0;
2588 fmt = "%s: ";
2589 if (commandname) {
2590 name = commandname;
2591 fmt = "%s: %d: ";
2592 }
2593 fprintf(errs, fmt, name, startlinno);
2594 vfprintf(errs, msg, ap);
2595 outcslow('\n', errs);
2596}
2597
2598/*
2599 * Exverror is called to raise the error exception. If the second argument
2600 * is not NULL then error prints an error message using printf style
2601 * formatting. It then raises the error exception.
2602 */
2603static void
2604exverror(int cond, const char *msg, va_list ap)
2605{
2606#ifdef DEBUG
2607 if (msg) {
2608 TRACE(("exverror(%d, \"", cond));
2609 TRACEV((msg, ap));
2610 TRACE(("\") pid=%d\n", getpid()));
2611 } else
2612 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
2613 if (msg)
2614#endif
2615 exvwarning(msg, ap);
2616
2617 flushall();
2618 exraise(cond);
2619 /* NOTREACHED */
2620}
2621
2622
2623static void
2624error(const char *msg, ...)
2625{
2626 va_list ap;
2627
2628 va_start(ap, msg);
2629 exverror(EXERROR, msg, ap);
2630 /* NOTREACHED */
2631 va_end(ap);
2632}
2633
2634
2635static void
2636exerror(int cond, const char *msg, ...)
2637{
2638 va_list ap;
2639
2640 va_start(ap, msg);
2641 exverror(cond, msg, ap);
2642 /* NOTREACHED */
2643 va_end(ap);
2644}
2645
2646/*
2647 * error/warning routines for external builtins
2648 */
2649
2650static void
2651sh_warnx(const char *fmt, ...)
2652{
2653 va_list ap;
2654
2655 va_start(ap, fmt);
2656 exvwarning(fmt, ap);
2657 va_end(ap);
2658}
2659
2660
2661/*
2662 * Return a string describing an error. The returned string may be a
2663 * pointer to a static buffer that will be overwritten on the next call.
2664 * Action describes the operation that got the error.
2665 */
2666
2667static const char *
2668errmsg(int e, const char *em)
2669{
2670 if(e == ENOENT || e == ENOTDIR) {
2671
2672 return em;
2673 }
2674 return strerror(e);
2675}
2676
2677
2678/* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */
2679
2680/*
2681 * Evaluate a command.
2682 */
2683
2684/* flags in argument to evaltree */
2685#define EV_EXIT 01 /* exit after evaluating tree */
2686#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2687#define EV_BACKCMD 04 /* command executing within back quotes */
2688
2689
2690static void evalloop(union node *, int);
2691static void evalfor(union node *, int);
2692static void evalcase(union node *, int);
2693static void evalsubshell(union node *, int);
2694static void expredir(union node *);
2695static void evalpipe(union node *, int);
2696static void evalcommand(union node *, int);
2697static int evalbltin(const struct builtincmd *, int, char **);
2698static int evalfun(struct funcnode *, int, char **, int);
2699static void prehash(union node *);
2700static int bltincmd(int, char **);
2701
2702
2703static const struct builtincmd bltin = {
2704 "\0\0", bltincmd
2705};
2706
2707
2708/*
2709 * Called to reset things after an exception.
2710 */
2711
2712/*
2713 * The eval command.
2714 */
2715
2716static int
2717evalcmd(int argc, char **argv)
2718{
2719 char *p;
2720 char *concat;
2721 char **ap;
2722
2723 if (argc > 1) {
2724 p = argv[1];
2725 if (argc > 2) {
2726 STARTSTACKSTR(concat);
2727 ap = argv + 2;
2728 for (;;) {
2729 concat = stputs(p, concat);
2730 if ((p = *ap++) == NULL)
2731 break;
2732 STPUTC(' ', concat);
2733 }
2734 STPUTC('\0', concat);
2735 p = grabstackstr(concat);
2736 }
2737 evalstring(p);
2738 }
2739 return exitstatus;
2740}
2741
2742
2743/*
2744 * Execute a command or commands contained in a string.
2745 */
2746
2747static void
2748evalstring(char *s)
2749{
2750 union node *n;
2751 struct stackmark smark;
2752
2753 setstackmark(&smark);
2754 setinputstring(s);
2755
2756 while ((n = parsecmd(0)) != NEOF) {
2757 evaltree(n, 0);
2758 popstackmark(&smark);
2759 if (evalskip)
2760 break;
2761 }
2762 popfile();
2763 popstackmark(&smark);
2764}
2765
2766
2767
2768/*
2769 * Evaluate a parse tree. The value is left in the global variable
2770 * exitstatus.
2771 */
2772
2773static void
2774evaltree(union node *n, int flags)
2775{
2776 int checkexit = 0;
2777 void (*evalfn)(union node *, int);
2778 unsigned isor;
2779 int status;
2780 if (n == NULL) {
2781 TRACE(("evaltree(NULL) called\n"));
2782 goto out;
2783 }
2784 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
2785 getpid(), n, n->type, flags));
2786 switch (n->type) {
2787 default:
2788#ifdef DEBUG
2789 out1fmt("Node type = %d\n", n->type);
2790 fflush(stdout);
2791 break;
2792#endif
2793 case NNOT:
2794 evaltree(n->nnot.com, EV_TESTED);
2795 status = !exitstatus;
2796 goto setstatus;
2797 case NREDIR:
2798 expredir(n->nredir.redirect);
2799 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
2800 if (!status) {
2801 evaltree(n->nredir.n, flags & EV_TESTED);
2802 status = exitstatus;
2803 }
2804 popredir(0);
2805 goto setstatus;
2806 case NCMD:
2807 evalfn = evalcommand;
2808checkexit:
2809 if (eflag && !(flags & EV_TESTED))
2810 checkexit = ~0;
2811 goto calleval;
2812 case NFOR:
2813 evalfn = evalfor;
2814 goto calleval;
2815 case NWHILE:
2816 case NUNTIL:
2817 evalfn = evalloop;
2818 goto calleval;
2819 case NSUBSHELL:
2820 case NBACKGND:
2821 evalfn = evalsubshell;
2822 goto calleval;
2823 case NPIPE:
2824 evalfn = evalpipe;
2825 goto checkexit;
2826 case NCASE:
2827 evalfn = evalcase;
2828 goto calleval;
2829 case NAND:
2830 case NOR:
2831 case NSEMI:
2832#if NAND + 1 != NOR
2833#error NAND + 1 != NOR
2834#endif
2835#if NOR + 1 != NSEMI
2836#error NOR + 1 != NSEMI
2837#endif
2838 isor = n->type - NAND;
2839 evaltree(
2840 n->nbinary.ch1,
2841 (flags | ((isor >> 1) - 1)) & EV_TESTED
2842 );
2843 if (!exitstatus == isor)
2844 break;
2845 if (!evalskip) {
2846 n = n->nbinary.ch2;
2847evaln:
2848 evalfn = evaltree;
2849calleval:
2850 evalfn(n, flags);
2851 break;
2852 }
2853 break;
2854 case NIF:
2855 evaltree(n->nif.test, EV_TESTED);
2856 if (evalskip)
2857 break;
2858 if (exitstatus == 0) {
2859 n = n->nif.ifpart;
2860 goto evaln;
2861 } else if (n->nif.elsepart) {
2862 n = n->nif.elsepart;
2863 goto evaln;
2864 }
2865 goto success;
2866 case NDEFUN:
2867 defun(n->narg.text, n->narg.next);
2868success:
2869 status = 0;
2870setstatus:
2871 exitstatus = status;
2872 break;
2873 }
2874out:
2875 if (pendingsigs)
2876 dotrap();
2877 if (flags & EV_EXIT || checkexit & exitstatus)
2878 exraise(EXEXIT);
2879}
2880
2881
2882#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
2883static
2884#endif
2885void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
2886
2887
2888static void
2889evalloop(union node *n, int flags)
2890{
2891 int status;
2892
2893 loopnest++;
2894 status = 0;
2895 flags &= EV_TESTED;
2896 for (;;) {
2897 int i;
2898
2899 evaltree(n->nbinary.ch1, EV_TESTED);
2900 if (evalskip) {
2901skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
2902 evalskip = 0;
2903 continue;
2904 }
2905 if (evalskip == SKIPBREAK && --skipcount <= 0)
2906 evalskip = 0;
2907 break;
2908 }
2909 i = exitstatus;
2910 if (n->type != NWHILE)
2911 i = !i;
2912 if (i != 0)
2913 break;
2914 evaltree(n->nbinary.ch2, flags);
2915 status = exitstatus;
2916 if (evalskip)
2917 goto skipping;
2918 }
2919 loopnest--;
2920 exitstatus = status;
2921}
2922
2923
2924
2925static void
2926evalfor(union node *n, int flags)
2927{
2928 struct arglist arglist;
2929 union node *argp;
2930 struct strlist *sp;
2931 struct stackmark smark;
2932
2933 setstackmark(&smark);
2934 arglist.lastp = &arglist.list;
2935 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
2936 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2937 /* XXX */
2938 if (evalskip)
2939 goto out;
2940 }
2941 *arglist.lastp = NULL;
2942
2943 exitstatus = 0;
2944 loopnest++;
2945 flags &= EV_TESTED;
2946 for (sp = arglist.list ; sp ; sp = sp->next) {
2947 setvar(n->nfor.var, sp->text, 0);
2948 evaltree(n->nfor.body, flags);
2949 if (evalskip) {
2950 if (evalskip == SKIPCONT && --skipcount <= 0) {
2951 evalskip = 0;
2952 continue;
2953 }
2954 if (evalskip == SKIPBREAK && --skipcount <= 0)
2955 evalskip = 0;
2956 break;
2957 }
2958 }
2959 loopnest--;
2960out:
2961 popstackmark(&smark);
2962}
2963
2964
2965
2966static void
2967evalcase(union node *n, int flags)
2968{
2969 union node *cp;
2970 union node *patp;
2971 struct arglist arglist;
2972 struct stackmark smark;
2973
2974 setstackmark(&smark);
2975 arglist.lastp = &arglist.list;
2976 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
2977 exitstatus = 0;
2978 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
2979 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
2980 if (casematch(patp, arglist.list->text)) {
2981 if (evalskip == 0) {
2982 evaltree(cp->nclist.body, flags);
2983 }
2984 goto out;
2985 }
2986 }
2987 }
2988out:
2989 popstackmark(&smark);
2990}
2991
2992
2993
2994/*
2995 * Kick off a subshell to evaluate a tree.
2996 */
2997
2998static void
2999evalsubshell(union node *n, int flags)
3000{
3001 struct job *jp;
3002 int backgnd = (n->type == NBACKGND);
3003 int status;
3004
3005 expredir(n->nredir.redirect);
3006 if (!backgnd && flags & EV_EXIT && !trap[0])
3007 goto nofork;
3008 INTOFF;
3009 jp = makejob(n, 1);
3010 if (forkshell(jp, n, backgnd) == 0) {
3011 INTON;
3012 flags |= EV_EXIT;
3013 if (backgnd)
3014 flags &=~ EV_TESTED;
3015nofork:
3016 redirect(n->nredir.redirect, 0);
3017 evaltreenr(n->nredir.n, flags);
3018 /* never returns */
3019 }
3020 status = 0;
3021 if (! backgnd)
3022 status = waitforjob(jp);
3023 exitstatus = status;
3024 INTON;
3025}
3026
3027
3028
3029/*
3030 * Compute the names of the files in a redirection list.
3031 */
3032
3033static void
3034expredir(union node *n)
3035{
3036 union node *redir;
3037
3038 for (redir = n ; redir ; redir = redir->nfile.next) {
3039 struct arglist fn;
3040 fn.lastp = &fn.list;
3041 switch (redir->type) {
3042 case NFROMTO:
3043 case NFROM:
3044 case NTO:
3045 case NCLOBBER:
3046 case NAPPEND:
3047 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3048 redir->nfile.expfname = fn.list->text;
3049 break;
3050 case NFROMFD:
3051 case NTOFD:
3052 if (redir->ndup.vname) {
3053 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
3054 fixredir(redir, fn.list->text, 1);
3055 }
3056 break;
3057 }
3058 }
3059}
3060
3061
3062
3063/*
3064 * Evaluate a pipeline. All the processes in the pipeline are children
3065 * of the process creating the pipeline. (This differs from some versions
3066 * of the shell, which make the last process in a pipeline the parent
3067 * of all the rest.)
3068 */
3069
3070static void
3071evalpipe(union node *n, int flags)
3072{
3073 struct job *jp;
3074 struct nodelist *lp;
3075 int pipelen;
3076 int prevfd;
3077 int pip[2];
3078
3079 TRACE(("evalpipe(0x%lx) called\n", (long)n));
3080 pipelen = 0;
3081 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
3082 pipelen++;
3083 flags |= EV_EXIT;
3084 INTOFF;
3085 jp = makejob(n, pipelen);
3086 prevfd = -1;
3087 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
3088 prehash(lp->n);
3089 pip[1] = -1;
3090 if (lp->next) {
3091 if (pipe(pip) < 0) {
3092 close(prevfd);
3093 error("Pipe call failed");
3094 }
3095 }
3096 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
3097 INTON;
3098 if (pip[1] >= 0) {
3099 close(pip[0]);
3100 }
3101 if (prevfd > 0) {
3102 dup2(prevfd, 0);
3103 close(prevfd);
3104 }
3105 if (pip[1] > 1) {
3106 dup2(pip[1], 1);
3107 close(pip[1]);
3108 }
3109 evaltreenr(lp->n, flags);
3110 /* never returns */
3111 }
3112 if (prevfd >= 0)
3113 close(prevfd);
3114 prevfd = pip[0];
3115 close(pip[1]);
3116 }
3117 if (n->npipe.backgnd == 0) {
3118 exitstatus = waitforjob(jp);
3119 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
3120 }
3121 INTON;
3122}
3123
3124
3125
3126/*
3127 * Execute a command inside back quotes. If it's a builtin command, we
3128 * want to save its output in a block obtained from malloc. Otherwise
3129 * we fork off a subprocess and get the output of the command via a pipe.
3130 * Should be called with interrupts off.
3131 */
3132
3133static void
3134evalbackcmd(union node *n, struct backcmd *result)
3135{
3136 int saveherefd;
3137
3138 result->fd = -1;
3139 result->buf = NULL;
3140 result->nleft = 0;
3141 result->jp = NULL;
3142 if (n == NULL) {
3143 goto out;
3144 }
3145
3146 saveherefd = herefd;
3147 herefd = -1;
3148
3149 {
3150 int pip[2];
3151 struct job *jp;
3152
3153 if (pipe(pip) < 0)
3154 error("Pipe call failed");
3155 jp = makejob(n, 1);
3156 if (forkshell(jp, n, FORK_NOJOB) == 0) {
3157 FORCEINTON;
3158 close(pip[0]);
3159 if (pip[1] != 1) {
3160 close(1);
3161 copyfd(pip[1], 1);
3162 close(pip[1]);
3163 }
3164 eflag = 0;
3165 evaltreenr(n, EV_EXIT);
3166 /* NOTREACHED */
3167 }
3168 close(pip[1]);
3169 result->fd = pip[0];
3170 result->jp = jp;
3171 }
3172 herefd = saveherefd;
3173out:
3174 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
3175 result->fd, result->buf, result->nleft, result->jp));
3176}
3177
3178#ifdef CONFIG_ASH_CMDCMD
3179static inline char **
3180parse_command_args(char **argv, const char **path)
3181{
3182 char *cp, c;
3183
3184 for (;;) {
3185 cp = *++argv;
3186 if (!cp)
3187 return 0;
3188 if (*cp++ != '-')
3189 break;
3190 if (!(c = *cp++))
3191 break;
3192 if (c == '-' && !*cp) {
3193 argv++;
3194 break;
3195 }
3196 do {
3197 switch (c) {
3198 case 'p':
3199 *path = defpath;
3200 break;
3201 default:
3202 /* run 'typecmd' for other options */
3203 return 0;
3204 }
3205 } while ((c = *cp++));
3206 }
3207 return argv;
3208}
3209#endif
3210
3211
3212
3213/*
3214 * Execute a simple command.
3215 */
3216
3217static void
3218evalcommand(union node *cmd, int flags)
3219{
3220 struct stackmark smark;
3221 union node *argp;
3222 struct arglist arglist;
3223 struct arglist varlist;
3224 char **argv;
3225 int argc;
3226 const struct strlist *sp;
3227 struct cmdentry cmdentry;
3228 struct job *jp;
3229 char *lastarg;
3230 const char *path;
3231 int spclbltin;
3232 int cmd_is_exec;
3233 int status;
3234 char **nargv;
3235
3236 /* First expand the arguments. */
3237 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
3238 setstackmark(&smark);
3239 back_exitstatus = 0;
3240
3241 cmdentry.cmdtype = CMDBUILTIN;
3242 cmdentry.u.cmd = &bltin;
3243 varlist.lastp = &varlist.list;
3244 *varlist.lastp = NULL;
3245 arglist.lastp = &arglist.list;
3246 *arglist.lastp = NULL;
3247
3248 argc = 0;
3249 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
3250 struct strlist **spp;
3251
3252 spp = arglist.lastp;
3253 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3254 for (sp = *spp; sp; sp = sp->next)
3255 argc++;
3256 }
3257
3258 argv = nargv = stalloc(sizeof (char *) * (argc + 1));
3259 for (sp = arglist.list ; sp ; sp = sp->next) {
3260 TRACE(("evalcommand arg: %s\n", sp->text));
3261 *nargv++ = sp->text;
3262 }
3263 *nargv = NULL;
3264
3265 lastarg = NULL;
3266 if (iflag && funcnest == 0 && argc > 0)
3267 lastarg = nargv[-1];
3268
3269 preverrout_fd = 2;
3270 expredir(cmd->ncmd.redirect);
3271 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
3272
3273 path = vpath.text;
3274 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
3275 struct strlist **spp;
3276 char *p;
3277
3278 spp = varlist.lastp;
3279 expandarg(argp, &varlist, EXP_VARTILDE);
3280
3281 /*
3282 * Modify the command lookup path, if a PATH= assignment
3283 * is present
3284 */
3285 p = (*spp)->text;
3286 if (varequal(p, path))
3287 path = p;
3288 }
3289
3290 /* Print the command if xflag is set. */
3291 if (xflag) {
3292 int n;
3293 const char *p = " %s";
3294
3295 p++;
3296 dprintf(preverrout_fd, p, ps4val());
3297
3298 sp = varlist.list;
3299 for(n = 0; n < 2; n++) {
3300 while (sp) {
3301 dprintf(preverrout_fd, p, sp->text);
3302 sp = sp->next;
3303 if(*p == '%') {
3304 p--;
3305 }
3306 }
3307 sp = arglist.list;
3308 }
3309 bb_full_write(preverrout_fd, "\n", 1);
3310 }
3311
3312 cmd_is_exec = 0;
3313 spclbltin = -1;
3314
3315 /* Now locate the command. */
3316 if (argc) {
3317 const char *oldpath;
3318 int cmd_flag = DO_ERR;
3319
3320 path += 5;
3321 oldpath = path;
3322 for (;;) {
3323 find_command(argv[0], &cmdentry, cmd_flag, path);
3324 if (cmdentry.cmdtype == CMDUNKNOWN) {
3325 status = 127;
3326 flusherr();
3327 goto bail;
3328 }
3329
3330 /* implement bltin and command here */
3331 if (cmdentry.cmdtype != CMDBUILTIN)
3332 break;
3333 if (spclbltin < 0)
3334 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
3335 if (cmdentry.u.cmd == EXECCMD)
3336 cmd_is_exec++;
3337#ifdef CONFIG_ASH_CMDCMD
3338 if (cmdentry.u.cmd == COMMANDCMD) {
3339
3340 path = oldpath;
3341 nargv = parse_command_args(argv, &path);
3342 if (!nargv)
3343 break;
3344 argc -= nargv - argv;
3345 argv = nargv;
3346 cmd_flag |= DO_NOFUNC;
3347 } else
3348#endif
3349 break;
3350 }
3351 }
3352
3353 if (status) {
3354 /* We have a redirection error. */
3355 if (spclbltin > 0)
3356 exraise(EXERROR);
3357bail:
3358 exitstatus = status;
3359 goto out;
3360 }
3361
3362 /* Execute the command. */
3363 switch (cmdentry.cmdtype) {
3364 default:
3365 /* Fork off a child process if necessary. */
3366 if (!(flags & EV_EXIT) || trap[0]) {
3367 INTOFF;
3368 jp = makejob(cmd, 1);
3369 if (forkshell(jp, cmd, FORK_FG) != 0) {
3370 exitstatus = waitforjob(jp);
3371 INTON;
3372 break;
3373 }
3374 FORCEINTON;
3375 }
3376 listsetvar(varlist.list, VEXPORT|VSTACK);
3377 shellexec(argv, path, cmdentry.u.index);
3378 /* NOTREACHED */
3379
3380 case CMDBUILTIN:
3381 cmdenviron = varlist.list;
3382 if (cmdenviron) {
3383 struct strlist *list = cmdenviron;
3384 int i = VNOSET;
3385 if (spclbltin > 0 || argc == 0) {
3386 i = 0;
3387 if (cmd_is_exec && argc > 1)
3388 i = VEXPORT;
3389 }
3390 listsetvar(list, i);
3391 }
3392 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
3393 int exit_status;
3394 int i, j;
3395
3396 i = exception;
3397 if (i == EXEXIT)
3398 goto raise;
3399
3400 exit_status = 2;
3401 j = 0;
3402 if (i == EXINT)
3403 j = SIGINT;
3404 if (i == EXSIG)
3405 j = pendingsigs;
3406 if (j)
3407 exit_status = j + 128;
3408 exitstatus = exit_status;
3409
3410 if (i == EXINT || spclbltin > 0) {
3411raise:
3412 longjmp(handler->loc, 1);
3413 }
3414 FORCEINTON;
3415 }
3416 break;
3417
3418 case CMDFUNCTION:
3419 listsetvar(varlist.list, 0);
3420 if (evalfun(cmdentry.u.func, argc, argv, flags))
3421 goto raise;
3422 break;
3423 }
3424
3425out:
3426 popredir(cmd_is_exec);
3427 if (lastarg)
3428 /* dsl: I think this is intended to be used to support
3429 * '_' in 'vi' command mode during line editing...
3430 * However I implemented that within libedit itself.
3431 */
3432 setvar("_", lastarg, 0);
3433 popstackmark(&smark);
3434}
3435
3436static int
3437evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
3438 char *volatile savecmdname;
3439 struct jmploc *volatile savehandler;
3440 struct jmploc jmploc;
3441 int i;
3442
3443 savecmdname = commandname;
3444 if ((i = setjmp(jmploc.loc)))
3445 goto cmddone;
3446 savehandler = handler;
3447 handler = &jmploc;
3448 commandname = argv[0];
3449 argptr = argv + 1;
3450 optptr = NULL; /* initialize nextopt */
3451 exitstatus = (*cmd->builtin)(argc, argv);
3452 flushall();
3453cmddone:
3454 exitstatus |= ferror(stdout);
3455 commandname = savecmdname;
3456 exsig = 0;
3457 handler = savehandler;
3458
3459 return i;
3460}
3461
3462static int
3463evalfun(struct funcnode *func, int argc, char **argv, int flags)
3464{
3465 volatile struct shparam saveparam;
3466 struct localvar *volatile savelocalvars;
3467 struct jmploc *volatile savehandler;
3468 struct jmploc jmploc;
3469 int e;
3470
3471 saveparam = shellparam;
3472 savelocalvars = localvars;
3473 if ((e = setjmp(jmploc.loc))) {
3474 goto funcdone;
3475 }
3476 INTOFF;
3477 savehandler = handler;
3478 handler = &jmploc;
3479 localvars = NULL;
3480 shellparam.malloc = 0;
3481 func->count++;
3482 INTON;
3483 shellparam.nparam = argc - 1;
3484 shellparam.p = argv + 1;
3485#ifdef CONFIG_ASH_GETOPTS
3486 shellparam.optind = 1;
3487 shellparam.optoff = -1;
3488#endif
3489 funcnest++;
3490 evaltree(&func->n, flags & EV_TESTED);
3491 funcnest--;
3492funcdone:
3493 INTOFF;
3494 freefunc(func);
3495 poplocalvars();
3496 localvars = savelocalvars;
3497 freeparam(&shellparam);
3498 shellparam = saveparam;
3499 handler = savehandler;
3500 INTON;
3501 if (evalskip == SKIPFUNC) {
3502 evalskip = 0;
3503 skipcount = 0;
3504 }
3505 return e;
3506}
3507
3508
3509static inline int
3510goodname(const char *p)
3511{
3512 return !*endofname(p);
3513}
3514
3515/*
3516 * Search for a command. This is called before we fork so that the
3517 * location of the command will be available in the parent as well as
3518 * the child. The check for "goodname" is an overly conservative
3519 * check that the name will not be subject to expansion.
3520 */
3521
3522static void
3523prehash(union node *n)
3524{
3525 struct cmdentry entry;
3526
3527 if (n->type == NCMD && n->ncmd.args)
3528 if (goodname(n->ncmd.args->narg.text))
3529 find_command(n->ncmd.args->narg.text, &entry, 0,
3530 pathval());
3531}
3532
3533
3534
3535/*
3536 * Builtin commands. Builtin commands whose functions are closely
3537 * tied to evaluation are implemented here.
3538 */
3539
3540/*
3541 * No command given.
3542 */
3543
3544static int
3545bltincmd(int argc, char **argv)
3546{
3547 /*
3548 * Preserve exitstatus of a previous possible redirection
3549 * as POSIX mandates
3550 */
3551 return back_exitstatus;
3552}
3553
3554
3555/*
3556 * Handle break and continue commands. Break, continue, and return are
3557 * all handled by setting the evalskip flag. The evaluation routines
3558 * above all check this flag, and if it is set they start skipping
3559 * commands rather than executing them. The variable skipcount is
3560 * the number of loops to break/continue, or the number of function
3561 * levels to return. (The latter is always 1.) It should probably
3562 * be an error to break out of more loops than exist, but it isn't
3563 * in the standard shell so we don't make it one here.
3564 */
3565
3566static int
3567breakcmd(int argc, char **argv)
3568{
3569 int n = argc > 1 ? number(argv[1]) : 1;
3570
3571 if (n <= 0)
3572 error(illnum, argv[1]);
3573 if (n > loopnest)
3574 n = loopnest;
3575 if (n > 0) {
3576 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
3577 skipcount = n;
3578 }
3579 return 0;
3580}
3581
3582
3583/*
3584 * The return command.
3585 */
3586
3587static int
3588returncmd(int argc, char **argv)
3589{
3590 int ret = argc > 1 ? number(argv[1]) : exitstatus;
3591
3592 if (funcnest) {
3593 evalskip = SKIPFUNC;
3594 skipcount = 1;
3595 return ret;
3596 }
3597 else {
3598 /* Do what ksh does; skip the rest of the file */
3599 evalskip = SKIPFILE;
3600 skipcount = 1;
3601 return ret;
3602 }
3603}
3604
3605
3606static int
3607falsecmd(int argc, char **argv)
3608{
3609 return 1;
3610}
3611
3612
3613static int
3614truecmd(int argc, char **argv)
3615{
3616 return 0;
3617}
3618
3619
3620static int
3621execcmd(int argc, char **argv)
3622{
3623 if (argc > 1) {
3624 iflag = 0; /* exit on error */
3625 mflag = 0;
3626 optschanged();
3627 shellexec(argv + 1, pathval(), 0);
3628 }
3629 return 0;
3630}
3631
3632
3633/* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */
3634
3635/*
3636 * When commands are first encountered, they are entered in a hash table.
3637 * This ensures that a full path search will not have to be done for them
3638 * on each invocation.
3639 *
3640 * We should investigate converting to a linear search, even though that
3641 * would make the command name "hash" a misnomer.
3642 */
3643
3644#define CMDTABLESIZE 31 /* should be prime */
3645#define ARB 1 /* actual size determined at run time */
3646
3647
3648
3649struct tblentry {
3650 struct tblentry *next; /* next entry in hash chain */
3651 union param param; /* definition of builtin function */
3652 short cmdtype; /* index identifying command */
3653 char rehash; /* if set, cd done since entry created */
3654 char cmdname[ARB]; /* name of command */
3655};
3656
3657
3658static struct tblentry *cmdtable[CMDTABLESIZE];
3659static int builtinloc = -1; /* index in path of %builtin, or -1 */
3660
3661
3662static void tryexec(char *, char **, char **);
3663static void clearcmdentry(int);
3664static struct tblentry *cmdlookup(const char *, int);
3665static void delete_cmd_entry(void);
3666
3667
3668/*
3669 * Exec a program. Never returns. If you change this routine, you may
3670 * have to change the find_command routine as well.
3671 */
3672
3673static void
3674shellexec(char **argv, const char *path, int idx)
3675{
3676 char *cmdname;
3677 int e;
3678 char **envp;
3679
3680 clearredir(1);
3681 envp = environment();
3682 if (strchr(argv[0], '/') != NULL
3683#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3684 || find_applet_by_name(argv[0])
3685#endif
3686 ) {
3687 tryexec(argv[0], argv, envp);
3688 e = errno;
3689 } else {
3690 e = ENOENT;
3691 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3692 if (--idx < 0 && pathopt == NULL) {
3693 tryexec(cmdname, argv, envp);
3694 if (errno != ENOENT && errno != ENOTDIR)
3695 e = errno;
3696 }
3697 stunalloc(cmdname);
3698 }
3699 }
3700
3701 /* Map to POSIX errors */
3702 switch (e) {
3703 case EACCES:
3704 exerrno = 126;
3705 break;
3706 case ENOENT:
3707 exerrno = 127;
3708 break;
3709 default:
3710 exerrno = 2;
3711 break;
3712 }
3713 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
3714 argv[0], e, suppressint ));
3715 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3716 /* NOTREACHED */
3717}
3718
3719
3720static void
3721tryexec(char *cmd, char **argv, char **envp)
3722{
3723 int repeated = 0;
3724#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3725 int flg_bb = 0;
3726 char *name = cmd;
3727
3728 if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
3729 flg_bb = 1;
3730 }
3731 if(flg_bb) {
3732 char **ap;
3733 char **new;
3734
3735 *argv = name;
3736 if(strcmp(name, "busybox")) {
3737 for (ap = argv; *ap; ap++);
3738 ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
3739 *ap++ = cmd = "/bin/busybox";
3740 while ((*ap++ = *argv++));
3741 argv = new;
3742 repeated++;
3743 } else {
3744 cmd = "/bin/busybox";
3745 }
3746 }
3747#endif
3748
3749repeat:
3750#ifdef SYSV
3751 do {
3752 execve(cmd, argv, envp);
3753 } while (errno == EINTR);
3754#else
3755 execve(cmd, argv, envp);
3756#endif
3757 if (repeated++) {
3758 ckfree(argv);
3759 } else if (errno == ENOEXEC) {
3760 char **ap;
3761 char **new;
3762
3763 for (ap = argv; *ap; ap++)
3764 ;
3765 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
3766 ap[1] = cmd;
3767 *ap = cmd = (char *)DEFAULT_SHELL;
3768 ap += 2;
3769 argv++;
3770 while ((*ap++ = *argv++))
3771 ;
3772 argv = new;
3773 goto repeat;
3774 }
3775}
3776
3777
3778
3779/*
3780 * Do a path search. The variable path (passed by reference) should be
3781 * set to the start of the path before the first call; padvance will update
3782 * this value as it proceeds. Successive calls to padvance will return
3783 * the possible path expansions in sequence. If an option (indicated by
3784 * a percent sign) appears in the path entry then the global variable
3785 * pathopt will be set to point to it; otherwise pathopt will be set to
3786 * NULL.
3787 */
3788
3789static char *
3790padvance(const char **path, const char *name)
3791{
3792 const char *p;
3793 char *q;
3794 const char *start;
3795 size_t len;
3796
3797 if (*path == NULL)
3798 return NULL;
3799 start = *path;
3800 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
3801 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
3802 while (stackblocksize() < len)
3803 growstackblock();
3804 q = stackblock();
3805 if (p != start) {
3806 memcpy(q, start, p - start);
3807 q += p - start;
3808 *q++ = '/';
3809 }
3810 strcpy(q, name);
3811 pathopt = NULL;
3812 if (*p == '%') {
3813 pathopt = ++p;
3814 while (*p && *p != ':') p++;
3815 }
3816 if (*p == ':')
3817 *path = p + 1;
3818 else
3819 *path = NULL;
3820 return stalloc(len);
3821}
3822
3823
3824/*** Command hashing code ***/
3825
3826static void
3827printentry(struct tblentry *cmdp)
3828{
3829 int idx;
3830 const char *path;
3831 char *name;
3832
3833 idx = cmdp->param.index;
3834 path = pathval();
3835 do {
3836 name = padvance(&path, cmdp->cmdname);
3837 stunalloc(name);
3838 } while (--idx >= 0);
3839 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
3840}
3841
3842
3843static int
3844hashcmd(int argc, char **argv)
3845{
3846 struct tblentry **pp;
3847 struct tblentry *cmdp;
3848 int c;
3849 struct cmdentry entry;
3850 char *name;
3851
3852 while ((c = nextopt("r")) != '\0') {
3853 clearcmdentry(0);
3854 return 0;
3855 }
3856 if (*argptr == NULL) {
3857 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3858 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3859 if (cmdp->cmdtype == CMDNORMAL)
3860 printentry(cmdp);
3861 }
3862 }
3863 return 0;
3864 }
3865 c = 0;
3866 while ((name = *argptr) != NULL) {
3867 if ((cmdp = cmdlookup(name, 0)) != NULL
3868 && (cmdp->cmdtype == CMDNORMAL
3869 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
3870 delete_cmd_entry();
3871 find_command(name, &entry, DO_ERR, pathval());
3872 if (entry.cmdtype == CMDUNKNOWN)
3873 c = 1;
3874 argptr++;
3875 }
3876 return c;
3877}
3878
3879
3880/*
3881 * Resolve a command name. If you change this routine, you may have to
3882 * change the shellexec routine as well.
3883 */
3884
3885static void
3886find_command(char *name, struct cmdentry *entry, int act, const char *path)
3887{
3888 struct tblentry *cmdp;
3889 int idx;
3890 int prev;
3891 char *fullname;
3892 struct stat statb;
3893 int e;
3894 int updatetbl;
3895 struct builtincmd *bcmd;
3896
3897 /* If name contains a slash, don't use PATH or hash table */
3898 if (strchr(name, '/') != NULL) {
3899 entry->u.index = -1;
3900 if (act & DO_ABS) {
3901 while (stat(name, &statb) < 0) {
3902#ifdef SYSV
3903 if (errno == EINTR)
3904 continue;
3905#endif
3906 entry->cmdtype = CMDUNKNOWN;
3907 return;
3908 }
3909 }
3910 entry->cmdtype = CMDNORMAL;
3911 return;
3912 }
3913
3914#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3915 if (find_applet_by_name(name)) {
3916 entry->cmdtype = CMDNORMAL;
3917 entry->u.index = -1;
3918 return;
3919 }
3920#endif
3921
3922 updatetbl = (path == pathval());
3923 if (!updatetbl) {
3924 act |= DO_ALTPATH;
3925 if (strstr(path, "%builtin") != NULL)
3926 act |= DO_ALTBLTIN;
3927 }
3928
3929 /* If name is in the table, check answer will be ok */
3930 if ((cmdp = cmdlookup(name, 0)) != NULL) {
3931 int bit;
3932
3933 switch (cmdp->cmdtype) {
3934 default:
3935#if DEBUG
3936 abort();
3937#endif
3938 case CMDNORMAL:
3939 bit = DO_ALTPATH;
3940 break;
3941 case CMDFUNCTION:
3942 bit = DO_NOFUNC;
3943 break;
3944 case CMDBUILTIN:
3945 bit = DO_ALTBLTIN;
3946 break;
3947 }
3948 if (act & bit) {
3949 updatetbl = 0;
3950 cmdp = NULL;
3951 } else if (cmdp->rehash == 0)
3952 /* if not invalidated by cd, we're done */
3953 goto success;
3954 }
3955
3956 /* If %builtin not in path, check for builtin next */
3957 bcmd = find_builtin(name);
3958 if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
3959 act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
3960 )))
3961 goto builtin_success;
3962
3963 /* We have to search path. */
3964 prev = -1; /* where to start */
3965 if (cmdp && cmdp->rehash) { /* doing a rehash */
3966 if (cmdp->cmdtype == CMDBUILTIN)
3967 prev = builtinloc;
3968 else
3969 prev = cmdp->param.index;
3970 }
3971
3972 e = ENOENT;
3973 idx = -1;
3974loop:
3975 while ((fullname = padvance(&path, name)) != NULL) {
3976 stunalloc(fullname);
3977 idx++;
3978 if (pathopt) {
3979 if (prefix(pathopt, "builtin")) {
3980 if (bcmd)
3981 goto builtin_success;
3982 continue;
3983 } else if (!(act & DO_NOFUNC) &&
3984 prefix(pathopt, "func")) {
3985 /* handled below */
3986 } else {
3987 /* ignore unimplemented options */
3988 continue;
3989 }
3990 }
3991 /* if rehash, don't redo absolute path names */
3992 if (fullname[0] == '/' && idx <= prev) {
3993 if (idx < prev)
3994 continue;
3995 TRACE(("searchexec \"%s\": no change\n", name));
3996 goto success;
3997 }
3998 while (stat(fullname, &statb) < 0) {
3999#ifdef SYSV
4000 if (errno == EINTR)
4001 continue;
4002#endif
4003 if (errno != ENOENT && errno != ENOTDIR)
4004 e = errno;
4005 goto loop;
4006 }
4007 e = EACCES; /* if we fail, this will be the error */
4008 if (!S_ISREG(statb.st_mode))
4009 continue;
4010 if (pathopt) { /* this is a %func directory */
4011 stalloc(strlen(fullname) + 1);
4012 readcmdfile(fullname);
4013 if ((cmdp = cmdlookup(name, 0)) == NULL ||
4014 cmdp->cmdtype != CMDFUNCTION)
4015 error("%s not defined in %s", name, fullname);
4016 stunalloc(fullname);
4017 goto success;
4018 }
4019 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
4020 if (!updatetbl) {
4021 entry->cmdtype = CMDNORMAL;
4022 entry->u.index = idx;
4023 return;
4024 }
4025 INTOFF;
4026 cmdp = cmdlookup(name, 1);
4027 cmdp->cmdtype = CMDNORMAL;
4028 cmdp->param.index = idx;
4029 INTON;
4030 goto success;
4031 }
4032
4033 /* We failed. If there was an entry for this command, delete it */
4034 if (cmdp && updatetbl)
4035 delete_cmd_entry();
4036 if (act & DO_ERR)
4037 sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
4038 entry->cmdtype = CMDUNKNOWN;
4039 return;
4040
4041builtin_success:
4042 if (!updatetbl) {
4043 entry->cmdtype = CMDBUILTIN;
4044 entry->u.cmd = bcmd;
4045 return;
4046 }
4047 INTOFF;
4048 cmdp = cmdlookup(name, 1);
4049 cmdp->cmdtype = CMDBUILTIN;
4050 cmdp->param.cmd = bcmd;
4051 INTON;
4052success:
4053 cmdp->rehash = 0;
4054 entry->cmdtype = cmdp->cmdtype;
4055 entry->u = cmdp->param;
4056}
4057
4058
4059/*
4060 * Wrapper around strcmp for qsort/bsearch/...
4061 */
4062static int pstrcmp(const void *a, const void *b)
4063{
4064 return strcmp((const char *) a, (*(const char *const *) b) + 1);
4065}
4066
4067/*
4068 * Search the table of builtin commands.
4069 */
4070
4071static struct builtincmd *
4072find_builtin(const char *name)
4073{
4074 struct builtincmd *bp;
4075
4076 bp = bsearch(
4077 name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
4078 pstrcmp
4079 );
4080 return bp;
4081}
4082
4083
4084
4085/*
4086 * Called when a cd is done. Marks all commands so the next time they
4087 * are executed they will be rehashed.
4088 */
4089
4090static void
4091hashcd(void)
4092{
4093 struct tblentry **pp;
4094 struct tblentry *cmdp;
4095
4096 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
4097 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4098 if (cmdp->cmdtype == CMDNORMAL || (
4099 cmdp->cmdtype == CMDBUILTIN &&
4100 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
4101 builtinloc > 0
4102 ))
4103 cmdp->rehash = 1;
4104 }
4105 }
4106}
4107
4108
4109
4110/*
4111 * Fix command hash table when PATH changed.
4112 * Called before PATH is changed. The argument is the new value of PATH;
4113 * pathval() still returns the old value at this point.
4114 * Called with interrupts off.
4115 */
4116
4117static void
4118changepath(const char *newval)
4119{
4120 const char *old, *new;
4121 int idx;
4122 int firstchange;
4123 int idx_bltin;
4124
4125 old = pathval();
4126 new = newval;
4127 firstchange = 9999; /* assume no change */
4128 idx = 0;
4129 idx_bltin = -1;
4130 for (;;) {
4131 if (*old != *new) {
4132 firstchange = idx;
4133 if ((*old == '\0' && *new == ':')
4134 || (*old == ':' && *new == '\0'))
4135 firstchange++;
4136 old = new; /* ignore subsequent differences */
4137 }
4138 if (*new == '\0')
4139 break;
4140 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
4141 idx_bltin = idx;
4142 if (*new == ':') {
4143 idx++;
4144 }
4145 new++, old++;
4146 }
4147 if (builtinloc < 0 && idx_bltin >= 0)
4148 builtinloc = idx_bltin; /* zap builtins */
4149 if (builtinloc >= 0 && idx_bltin < 0)
4150 firstchange = 0;
4151 clearcmdentry(firstchange);
4152 builtinloc = idx_bltin;
4153}
4154
4155
4156/*
4157 * Clear out command entries. The argument specifies the first entry in
4158 * PATH which has changed.
4159 */
4160
4161static void
4162clearcmdentry(int firstchange)
4163{
4164 struct tblentry **tblp;
4165 struct tblentry **pp;
4166 struct tblentry *cmdp;
4167
4168 INTOFF;
4169 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4170 pp = tblp;
4171 while ((cmdp = *pp) != NULL) {
4172 if ((cmdp->cmdtype == CMDNORMAL &&
4173 cmdp->param.index >= firstchange)
4174 || (cmdp->cmdtype == CMDBUILTIN &&
4175 builtinloc >= firstchange)) {
4176 *pp = cmdp->next;
4177 ckfree(cmdp);
4178 } else {
4179 pp = &cmdp->next;
4180 }
4181 }
4182 }
4183 INTON;
4184}
4185
4186
4187
4188/*
4189 * Locate a command in the command hash table. If "add" is nonzero,
4190 * add the command to the table if it is not already present. The
4191 * variable "lastcmdentry" is set to point to the address of the link
4192 * pointing to the entry, so that delete_cmd_entry can delete the
4193 * entry.
4194 *
4195 * Interrupts must be off if called with add != 0.
4196 */
4197
4198static struct tblentry **lastcmdentry;
4199
4200
4201static struct tblentry *
4202cmdlookup(const char *name, int add)
4203{
4204 unsigned int hashval;
4205 const char *p;
4206 struct tblentry *cmdp;
4207 struct tblentry **pp;
4208
4209 p = name;
4210 hashval = (unsigned char)*p << 4;
4211 while (*p)
4212 hashval += (unsigned char)*p++;
4213 hashval &= 0x7FFF;
4214 pp = &cmdtable[hashval % CMDTABLESIZE];
4215 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4216 if (equal(cmdp->cmdname, name))
4217 break;
4218 pp = &cmdp->next;
4219 }
4220 if (add && cmdp == NULL) {
4221 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
4222 + strlen(name) + 1);
4223 cmdp->next = NULL;
4224 cmdp->cmdtype = CMDUNKNOWN;
4225 strcpy(cmdp->cmdname, name);
4226 }
4227 lastcmdentry = pp;
4228 return cmdp;
4229}
4230
4231/*
4232 * Delete the command entry returned on the last lookup.
4233 */
4234
4235static void
4236delete_cmd_entry(void)
4237{
4238 struct tblentry *cmdp;
4239
4240 INTOFF;
4241 cmdp = *lastcmdentry;
4242 *lastcmdentry = cmdp->next;
4243 if (cmdp->cmdtype == CMDFUNCTION)
4244 freefunc(cmdp->param.func);
4245 ckfree(cmdp);
4246 INTON;
4247}
4248
4249
4250/*
4251 * Add a new command entry, replacing any existing command entry for
4252 * the same name - except special builtins.
4253 */
4254
4255static inline void
4256addcmdentry(char *name, struct cmdentry *entry)
4257{
4258 struct tblentry *cmdp;
4259
4260 cmdp = cmdlookup(name, 1);
4261 if (cmdp->cmdtype == CMDFUNCTION) {
4262 freefunc(cmdp->param.func);
4263 }
4264 cmdp->cmdtype = entry->cmdtype;
4265 cmdp->param = entry->u;
4266 cmdp->rehash = 0;
4267}
4268
4269/*
4270 * Make a copy of a parse tree.
4271 */
4272
4273static inline struct funcnode *
4274copyfunc(union node *n)
4275{
4276 struct funcnode *f;
4277 size_t blocksize;
4278
4279 funcblocksize = offsetof(struct funcnode, n);
4280 funcstringsize = 0;
4281 calcsize(n);
4282 blocksize = funcblocksize;
4283 f = ckmalloc(blocksize + funcstringsize);
4284 funcblock = (char *) f + offsetof(struct funcnode, n);
4285 funcstring = (char *) f + blocksize;
4286 copynode(n);
4287 f->count = 0;
4288 return f;
4289}
4290
4291/*
4292 * Define a shell function.
4293 */
4294
4295static void
4296defun(char *name, union node *func)
4297{
4298 struct cmdentry entry;
4299
4300 INTOFF;
4301 entry.cmdtype = CMDFUNCTION;
4302 entry.u.func = copyfunc(func);
4303 addcmdentry(name, &entry);
4304 INTON;
4305}
4306
4307
4308/*
4309 * Delete a function if it exists.
4310 */
4311
4312static void
4313unsetfunc(const char *name)
4314{
4315 struct tblentry *cmdp;
4316
4317 if ((cmdp = cmdlookup(name, 0)) != NULL &&
4318 cmdp->cmdtype == CMDFUNCTION)
4319 delete_cmd_entry();
4320}
4321
4322/*
4323 * Locate and print what a word is...
4324 */
4325
4326
4327#ifdef CONFIG_ASH_CMDCMD
4328static int
4329describe_command(char *command, int describe_command_verbose)
4330#else
4331#define describe_command_verbose 1
4332static int
4333describe_command(char *command)
4334#endif
4335{
4336 struct cmdentry entry;
4337 struct tblentry *cmdp;
4338#ifdef CONFIG_ASH_ALIAS
4339 const struct alias *ap;
4340#endif
4341 const char *path = pathval();
4342
4343 if (describe_command_verbose) {
4344 out1str(command);
4345 }
4346
4347 /* First look at the keywords */
4348 if (findkwd(command)) {
4349 out1str(describe_command_verbose ? " is a shell keyword" : command);
4350 goto out;
4351 }
4352
4353#ifdef CONFIG_ASH_ALIAS
4354 /* Then look at the aliases */
4355 if ((ap = lookupalias(command, 0)) != NULL) {
4356 if (describe_command_verbose) {
4357 out1fmt(" is an alias for %s", ap->val);
4358 } else {
4359 out1str("alias ");
4360 printalias(ap);
4361 return 0;
4362 }
4363 goto out;
4364 }
4365#endif
4366 /* Then check if it is a tracked alias */
4367 if ((cmdp = cmdlookup(command, 0)) != NULL) {
4368 entry.cmdtype = cmdp->cmdtype;
4369 entry.u = cmdp->param;
4370 } else {
4371 /* Finally use brute force */
4372 find_command(command, &entry, DO_ABS, path);
4373 }
4374
4375 switch (entry.cmdtype) {
4376 case CMDNORMAL: {
4377 int j = entry.u.index;
4378 char *p;
4379 if (j == -1) {
4380 p = command;
4381 } else {
4382 do {
4383 p = padvance(&path, command);
4384 stunalloc(p);
4385 } while (--j >= 0);
4386 }
4387 if (describe_command_verbose) {
4388 out1fmt(" is%s %s",
4389 (cmdp ? " a tracked alias for" : nullstr), p
4390 );
4391 } else {
4392 out1str(p);
4393 }
4394 break;
4395 }
4396
4397 case CMDFUNCTION:
4398 if (describe_command_verbose) {
4399 out1str(" is a shell function");
4400 } else {
4401 out1str(command);
4402 }
4403 break;
4404
4405 case CMDBUILTIN:
4406 if (describe_command_verbose) {
4407 out1fmt(" is a %sshell builtin",
4408 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
4409 "special " : nullstr
4410 );
4411 } else {
4412 out1str(command);
4413 }
4414 break;
4415
4416 default:
4417 if (describe_command_verbose) {
4418 out1str(": not found\n");
4419 }
4420 return 127;
4421 }
4422
4423out:
4424 outstr("\n", stdout);
4425 return 0;
4426}
4427
4428static int
4429typecmd(int argc, char **argv)
4430{
4431 int i;
4432 int err = 0;
4433
4434 for (i = 1; i < argc; i++) {
4435#ifdef CONFIG_ASH_CMDCMD
4436 err |= describe_command(argv[i], 1);
4437#else
4438 err |= describe_command(argv[i]);
4439#endif
4440 }
4441 return err;
4442}
4443
4444#ifdef CONFIG_ASH_CMDCMD
4445static int
4446commandcmd(int argc, char **argv)
4447{
4448 int c;
4449 int default_path = 0;
4450 int verify_only = 0;
4451 int verbose_verify_only = 0;
4452
4453 while ((c = nextopt("pvV")) != '\0')
4454 switch (c) {
4455 default:
4456#ifdef DEBUG
4457 fprintf(stderr,
4458"command: nextopt returned character code 0%o\n", c);
4459 return EX_SOFTWARE;
4460#endif
4461 case 'p':
4462 default_path = 1;
4463 break;
4464 case 'v':
4465 verify_only = 1;
4466 break;
4467 case 'V':
4468 verbose_verify_only = 1;
4469 break;
4470 }
4471
4472 if (default_path + verify_only + verbose_verify_only > 1 ||
4473 !*argptr) {
4474 fprintf(stderr,
4475 "command [-p] command [arg ...]\n"
4476 "command {-v|-V} command\n");
4477 return EX_USAGE;
4478 }
4479
4480 if (verify_only || verbose_verify_only) {
4481 return describe_command(*argptr, verbose_verify_only);
4482 }
4483
4484 return 0;
4485}
4486#endif
4487
4488/* $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $ */
4489
4490/*
4491 * Routines to expand arguments to commands. We have to deal with
4492 * backquotes, shell variables, and file metacharacters.
4493 */
4494
4495/*
4496 * _rmescape() flags
4497 */
4498#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4499#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4500#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4501#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4502#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4503
4504/*
4505 * Structure specifying which parts of the string should be searched
4506 * for IFS characters.
4507 */
4508
4509struct ifsregion {
4510 struct ifsregion *next; /* next region in list */
4511 int begoff; /* offset of start of region */
4512 int endoff; /* offset of end of region */
4513 int nulonly; /* search for nul bytes only */
4514};
4515
4516/* output of current string */
4517static char *expdest;
4518/* list of back quote expressions */
4519static struct nodelist *argbackq;
4520/* first struct in list of ifs regions */
4521static struct ifsregion ifsfirst;
4522/* last struct in list */
4523static struct ifsregion *ifslastp;
4524/* holds expanded arg list */
4525static struct arglist exparg;
4526
4527static void argstr(char *, int);
4528static char *exptilde(char *, char *, int);
4529static void expbackq(union node *, int, int);
4530static const char *subevalvar(char *, char *, int, int, int, int, int);
4531static char *evalvar(char *, int);
4532static void strtodest(const char *, int, int);
4533static void memtodest(const char *p, size_t len, int syntax, int quotes);
4534static ssize_t varvalue(char *, int, int);
4535static void recordregion(int, int, int);
4536static void removerecordregions(int);
4537static void ifsbreakup(char *, struct arglist *);
4538static void ifsfree(void);
4539static void expandmeta(struct strlist *, int);
4540static int patmatch(char *, const char *);
4541
4542static int cvtnum(arith_t);
4543static size_t esclen(const char *, const char *);
4544static char *scanleft(char *, char *, char *, char *, int, int);
4545static char *scanright(char *, char *, char *, char *, int, int);
4546static void varunset(const char *, const char *, const char *, int)
4547 __attribute__((__noreturn__));
4548
4549
4550#define pmatch(a, b) !fnmatch((a), (b), 0)
4551/*
4552 * Prepare a pattern for a expmeta (internal glob(3)) call.
4553 *
4554 * Returns an stalloced string.
4555 */
4556
4557static inline char *
4558preglob(const char *pattern, int quoted, int flag) {
4559 flag |= RMESCAPE_GLOB;
4560 if (quoted) {
4561 flag |= RMESCAPE_QUOTED;
4562 }
4563 return _rmescapes((char *)pattern, flag);
4564}
4565
4566
4567static size_t
4568esclen(const char *start, const char *p) {
4569 size_t esc = 0;
4570
4571 while (p > start && *--p == CTLESC) {
4572 esc++;
4573 }
4574 return esc;
4575}
4576
4577
4578/*
4579 * Expand shell variables and backquotes inside a here document.
4580 */
4581
4582static inline void
4583expandhere(union node *arg, int fd)
4584{
4585 herefd = fd;
4586 expandarg(arg, (struct arglist *)NULL, 0);
4587 bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
4588}
4589
4590
4591/*
4592 * Perform variable substitution and command substitution on an argument,
4593 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4594 * perform splitting and file name expansion. When arglist is NULL, perform
4595 * here document expansion.
4596 */
4597
4598void
4599expandarg(union node *arg, struct arglist *arglist, int flag)
4600{
4601 struct strlist *sp;
4602 char *p;
4603
4604 argbackq = arg->narg.backquote;
4605 STARTSTACKSTR(expdest);
4606 ifsfirst.next = NULL;
4607 ifslastp = NULL;
4608 argstr(arg->narg.text, flag);
4609 if (arglist == NULL) {
4610 return; /* here document expanded */
4611 }
4612 STPUTC('\0', expdest);
4613 p = grabstackstr(expdest);
4614 exparg.lastp = &exparg.list;
4615 /*
4616 * TODO - EXP_REDIR
4617 */
4618 if (flag & EXP_FULL) {
4619 ifsbreakup(p, &exparg);
4620 *exparg.lastp = NULL;
4621 exparg.lastp = &exparg.list;
4622 expandmeta(exparg.list, flag);
4623 } else {
4624 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
4625 rmescapes(p);
4626 sp = (struct strlist *)stalloc(sizeof (struct strlist));
4627 sp->text = p;
4628 *exparg.lastp = sp;
4629 exparg.lastp = &sp->next;
4630 }
4631 if (ifsfirst.next)
4632 ifsfree();
4633 *exparg.lastp = NULL;
4634 if (exparg.list) {
4635 *arglist->lastp = exparg.list;
4636 arglist->lastp = exparg.lastp;
4637 }
4638}
4639
4640
4641/*
4642 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4643 * characters to allow for further processing. Otherwise treat
4644 * $@ like $* since no splitting will be performed.
4645 */
4646
4647static void
4648argstr(char *p, int flag)
4649{
4650 static const char spclchars[] = {
4651 '=',
4652 ':',
4653 CTLQUOTEMARK,
4654 CTLENDVAR,
4655 CTLESC,
4656 CTLVAR,
4657 CTLBACKQ,
4658 CTLBACKQ | CTLQUOTE,
4659#ifdef CONFIG_ASH_MATH_SUPPORT
4660 CTLENDARI,
4661#endif
4662 0
4663 };
4664 const char *reject = spclchars;
4665 int c;
4666 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
4667 int breakall = flag & EXP_WORD;
4668 int inquotes;
4669 size_t length;
4670 int startloc;
4671
4672 if (!(flag & EXP_VARTILDE)) {
4673 reject += 2;
4674 } else if (flag & EXP_VARTILDE2) {
4675 reject++;
4676 }
4677 inquotes = 0;
4678 length = 0;
4679 if (flag & EXP_TILDE) {
4680 char *q;
4681
4682 flag &= ~EXP_TILDE;
4683tilde:
4684 q = p;
4685 if (*q == CTLESC && (flag & EXP_QWORD))
4686 q++;
4687 if (*q == '~')
4688 p = exptilde(p, q, flag);
4689 }
4690start:
4691 startloc = expdest - (char *)stackblock();
4692 for (;;) {
4693 length += strcspn(p + length, reject);
4694 c = p[length];
4695 if (c && (!(c & 0x80)
4696#ifdef CONFIG_ASH_MATH_SUPPORT
4697 || c == CTLENDARI
4698#endif
4699 )) {
4700 /* c == '=' || c == ':' || c == CTLENDARI */
4701 length++;
4702 }
4703 if (length > 0) {
4704 int newloc;
4705 expdest = stnputs(p, length, expdest);
4706 newloc = expdest - (char *)stackblock();
4707 if (breakall && !inquotes && newloc > startloc) {
4708 recordregion(startloc, newloc, 0);
4709 }
4710 startloc = newloc;
4711 }
4712 p += length + 1;
4713 length = 0;
4714
4715 switch (c) {
4716 case '\0':
4717 goto breakloop;
4718 case '=':
4719 if (flag & EXP_VARTILDE2) {
4720 p--;
4721 continue;
4722 }
4723 flag |= EXP_VARTILDE2;
4724 reject++;
4725 /* fall through */
4726 case ':':
4727 /*
4728 * sort of a hack - expand tildes in variable
4729 * assignments (after the first '=' and after ':'s).
4730 */
4731 if (*--p == '~') {
4732 goto tilde;
4733 }
4734 continue;
4735 }
4736
4737 switch (c) {
4738 case CTLENDVAR: /* ??? */
4739 goto breakloop;
4740 case CTLQUOTEMARK:
4741 /* "$@" syntax adherence hack */
4742 if (
4743 !inquotes &&
4744 !memcmp(p, dolatstr, DOLATSTRLEN) &&
4745 (p[4] == CTLQUOTEMARK || (
4746 p[4] == CTLENDVAR &&
4747 p[5] == CTLQUOTEMARK
4748 ))
4749 ) {
4750 p = evalvar(p + 1, flag) + 1;
4751 goto start;
4752 }
4753 inquotes = !inquotes;
4754addquote:
4755 if (quotes) {
4756 p--;
4757 length++;
4758 startloc++;
4759 }
4760 break;
4761 case CTLESC:
4762 startloc++;
4763 length++;
4764 goto addquote;
4765 case CTLVAR:
4766 p = evalvar(p, flag);
4767 goto start;
4768 case CTLBACKQ:
4769 c = 0;
4770 case CTLBACKQ|CTLQUOTE:
4771 expbackq(argbackq->n, c, quotes);
4772 argbackq = argbackq->next;
4773 goto start;
4774#ifdef CONFIG_ASH_MATH_SUPPORT
4775 case CTLENDARI:
4776 p--;
4777 expari(quotes);
4778 goto start;
4779#endif
4780 }
4781 }
4782breakloop:
4783 ;
4784}
4785
4786static char *
4787exptilde(char *startp, char *p, int flag)
4788{
4789 char c;
4790 char *name;
4791 struct passwd *pw;
4792 const char *home;
4793 int quotes = flag & (EXP_FULL | EXP_CASE);
4794 int startloc;
4795
4796 name = p + 1;
4797
4798 while ((c = *++p) != '\0') {
4799 switch(c) {
4800 case CTLESC:
4801 return (startp);
4802 case CTLQUOTEMARK:
4803 return (startp);
4804 case ':':
4805 if (flag & EXP_VARTILDE)
4806 goto done;
4807 break;
4808 case '/':
4809 case CTLENDVAR:
4810 goto done;
4811 }
4812 }
4813done:
4814 *p = '\0';
4815 if (*name == '\0') {
4816 if ((home = lookupvar(homestr)) == NULL)
4817 goto lose;
4818 } else {
4819 if ((pw = getpwnam(name)) == NULL)
4820 goto lose;
4821 home = pw->pw_dir;
4822 }
4823 if (*home == '\0')
4824 goto lose;
4825 *p = c;
4826 startloc = expdest - (char *)stackblock();
4827 strtodest(home, SQSYNTAX, quotes);
4828 recordregion(startloc, expdest - (char *)stackblock(), 0);
4829 return (p);
4830lose:
4831 *p = c;
4832 return (startp);
4833}
4834
4835
4836static void
4837removerecordregions(int endoff)
4838{
4839 if (ifslastp == NULL)
4840 return;
4841
4842 if (ifsfirst.endoff > endoff) {
4843 while (ifsfirst.next != NULL) {
4844 struct ifsregion *ifsp;
4845 INTOFF;
4846 ifsp = ifsfirst.next->next;
4847 ckfree(ifsfirst.next);
4848 ifsfirst.next = ifsp;
4849 INTON;
4850 }
4851 if (ifsfirst.begoff > endoff)
4852 ifslastp = NULL;
4853 else {
4854 ifslastp = &ifsfirst;
4855 ifsfirst.endoff = endoff;
4856 }
4857 return;
4858 }
4859
4860 ifslastp = &ifsfirst;
4861 while (ifslastp->next && ifslastp->next->begoff < endoff)
4862 ifslastp=ifslastp->next;
4863 while (ifslastp->next != NULL) {
4864 struct ifsregion *ifsp;
4865 INTOFF;
4866 ifsp = ifslastp->next->next;
4867 ckfree(ifslastp->next);
4868 ifslastp->next = ifsp;
4869 INTON;
4870 }
4871 if (ifslastp->endoff > endoff)
4872 ifslastp->endoff = endoff;
4873}
4874
4875
4876#ifdef CONFIG_ASH_MATH_SUPPORT
4877/*
4878 * Expand arithmetic expression. Backup to start of expression,
4879 * evaluate, place result in (backed up) result, adjust string position.
4880 */
4881void
4882expari(int quotes)
4883{
4884 char *p, *start;
4885 int begoff;
4886 int flag;
4887 int len;
4888
4889 /* ifsfree(); */
4890
4891 /*
4892 * This routine is slightly over-complicated for
4893 * efficiency. Next we scan backwards looking for the
4894 * start of arithmetic.
4895 */
4896 start = stackblock();
4897 p = expdest - 1;
4898 *p = '\0';
4899 p--;
4900 do {
4901 int esc;
4902
4903 while (*p != CTLARI) {
4904 p--;
4905#ifdef DEBUG
4906 if (p < start) {
4907 error("missing CTLARI (shouldn't happen)");
4908 }
4909#endif
4910 }
4911
4912 esc = esclen(start, p);
4913 if (!(esc % 2)) {
4914 break;
4915 }
4916
4917 p -= esc + 1;
4918 } while (1);
4919
4920 begoff = p - start;
4921
4922 removerecordregions(begoff);
4923
4924 flag = p[1];
4925
4926 expdest = p;
4927
4928 if (quotes)
4929 rmescapes(p + 2);
4930
4931 len = cvtnum(dash_arith(p + 2));
4932
4933 if (flag != '"')
4934 recordregion(begoff, begoff + len, 0);
4935}
4936#endif
4937
4938/*
4939 * Expand stuff in backwards quotes.
4940 */
4941
4942static void
4943expbackq(union node *cmd, int quoted, int quotes)
4944{
4945 struct backcmd in;
4946 int i;
4947 char buf[128];
4948 char *p;
4949 char *dest;
4950 int startloc;
4951 int syntax = quoted? DQSYNTAX : BASESYNTAX;
4952 struct stackmark smark;
4953
4954 INTOFF;
4955 setstackmark(&smark);
4956 dest = expdest;
4957 startloc = dest - (char *)stackblock();
4958 grabstackstr(dest);
4959 evalbackcmd(cmd, (struct backcmd *) &in);
4960 popstackmark(&smark);
4961
4962 p = in.buf;
4963 i = in.nleft;
4964 if (i == 0)
4965 goto read;
4966 for (;;) {
4967 memtodest(p, i, syntax, quotes);
4968read:
4969 if (in.fd < 0)
4970 break;
4971 i = safe_read(in.fd, buf, sizeof buf);
4972 TRACE(("expbackq: read returns %d\n", i));
4973 if (i <= 0)
4974 break;
4975 p = buf;
4976 }
4977
4978 if (in.buf)
4979 ckfree(in.buf);
4980 if (in.fd >= 0) {
4981 close(in.fd);
4982 back_exitstatus = waitforjob(in.jp);
4983 }
4984 INTON;
4985
4986 /* Eat all trailing newlines */
4987 dest = expdest;
4988 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
4989 STUNPUTC(dest);
4990 expdest = dest;
4991
4992 if (quoted == 0)
4993 recordregion(startloc, dest - (char *)stackblock(), 0);
4994 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4995 (dest - (char *)stackblock()) - startloc,
4996 (dest - (char *)stackblock()) - startloc,
4997 stackblock() + startloc));
4998}
4999
5000
5001static char *
5002scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5003 int zero)
5004{
5005 char *loc;
5006 char *loc2;
5007 char c;
5008
5009 loc = startp;
5010 loc2 = rmesc;
5011 do {
5012 int match;
5013 const char *s = loc2;
5014 c = *loc2;
5015 if (zero) {
5016 *loc2 = '\0';
5017 s = rmesc;
5018 }
5019 match = pmatch(str, s);
5020 *loc2 = c;
5021 if (match)
5022 return loc;
5023 if (quotes && *loc == CTLESC)
5024 loc++;
5025 loc++;
5026 loc2++;
5027 } while (c);
5028 return 0;
5029}
5030
5031
5032static char *
5033scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5034 int zero)
5035{
5036 int esc = 0;
5037 char *loc;
5038 char *loc2;
5039
5040 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5041 int match;
5042 char c = *loc2;
5043 const char *s = loc2;
5044 if (zero) {
5045 *loc2 = '\0';
5046 s = rmesc;
5047 }
5048 match = pmatch(str, s);
5049 *loc2 = c;
5050 if (match)
5051 return loc;
5052 loc--;
5053 if (quotes) {
5054 if (--esc < 0) {
5055 esc = esclen(startp, loc);
5056 }
5057 if (esc % 2) {
5058 esc--;
5059 loc--;
5060 }
5061 }
5062 }
5063 return 0;
5064}
5065
5066static const char *
5067subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5068{
5069 char *startp;
5070 char *loc;
5071 int saveherefd = herefd;
5072 struct nodelist *saveargbackq = argbackq;
5073 int amount;
5074 char *rmesc, *rmescend;
5075 int zero;
5076 char *(*scan)(char *, char *, char *, char *, int , int);
5077
5078 herefd = -1;
5079 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5080 STPUTC('\0', expdest);
5081 herefd = saveherefd;
5082 argbackq = saveargbackq;
5083 startp = stackblock() + startloc;
5084
5085 switch (subtype) {
5086 case VSASSIGN:
5087 setvar(str, startp, 0);
5088 amount = startp - expdest;
5089 STADJUST(amount, expdest);
5090 return startp;
5091
5092 case VSQUESTION:
5093 varunset(p, str, startp, varflags);
5094 /* NOTREACHED */
5095 }
5096
5097 subtype -= VSTRIMRIGHT;
5098#ifdef DEBUG
5099 if (subtype < 0 || subtype > 3)
5100 abort();
5101#endif
5102
5103 rmesc = startp;
5104 rmescend = stackblock() + strloc;
5105 if (quotes) {
5106 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5107 if (rmesc != startp) {
5108 rmescend = expdest;
5109 startp = stackblock() + startloc;
5110 }
5111 }
5112 rmescend--;
5113 str = stackblock() + strloc;
5114 preglob(str, varflags & VSQUOTE, 0);
5115
5116 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5117 zero = subtype >> 1;
5118 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5119 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5120
5121 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5122 if (loc) {
5123 if (zero) {
5124 memmove(startp, loc, str - loc);
5125 loc = startp + (str - loc) - 1;
5126 }
5127 *loc = '\0';
5128 amount = loc - expdest;
5129 STADJUST(amount, expdest);
5130 }
5131 return loc;
5132}
5133
5134
5135/*
5136 * Expand a variable, and return a pointer to the next character in the
5137 * input string.
5138 */
5139static char *
5140evalvar(char *p, int flag)
5141{
5142 int subtype;
5143 int varflags;
5144 char *var;
5145 int patloc;
5146 int c;
5147 int startloc;
5148 ssize_t varlen;
5149 int easy;
5150 int quotes;
5151 int quoted;
5152
5153 quotes = flag & (EXP_FULL | EXP_CASE);
5154 varflags = *p++;
5155 subtype = varflags & VSTYPE;
5156 quoted = varflags & VSQUOTE;
5157 var = p;
5158 easy = (!quoted || (*var == '@' && shellparam.nparam));
5159 startloc = expdest - (char *)stackblock();
5160 p = strchr(p, '=') + 1;
5161
5162again:
5163 varlen = varvalue(var, varflags, flag);
5164 if (varflags & VSNUL)
5165 varlen--;
5166
5167 if (subtype == VSPLUS) {
5168 varlen = -1 - varlen;
5169 goto vsplus;
5170 }
5171
5172 if (subtype == VSMINUS) {
5173vsplus:
5174 if (varlen < 0) {
5175 argstr(
5176 p, flag | EXP_TILDE |
5177 (quoted ? EXP_QWORD : EXP_WORD)
5178 );
5179 goto end;
5180 }
5181 if (easy)
5182 goto record;
5183 goto end;
5184 }
5185
5186 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5187 if (varlen < 0) {
5188 if (subevalvar(p, var, 0, subtype, startloc,
5189 varflags, 0)) {
5190 varflags &= ~VSNUL;
5191 /*
5192 * Remove any recorded regions beyond
5193 * start of variable
5194 */
5195 removerecordregions(startloc);
5196 goto again;
5197 }
5198 goto end;
5199 }
5200 if (easy)
5201 goto record;
5202 goto end;
5203 }
5204
5205 if (varlen < 0 && uflag)
5206 varunset(p, var, 0, 0);
5207
5208 if (subtype == VSLENGTH) {
5209 cvtnum(varlen > 0 ? varlen : 0);
5210 goto record;
5211 }
5212
5213 if (subtype == VSNORMAL) {
5214 if (!easy)
5215 goto end;
5216record:
5217 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5218 goto end;
5219 }
5220
5221#ifdef DEBUG
5222 switch (subtype) {
5223 case VSTRIMLEFT:
5224 case VSTRIMLEFTMAX:
5225 case VSTRIMRIGHT:
5226 case VSTRIMRIGHTMAX:
5227 break;
5228 default:
5229 abort();
5230 }
5231#endif
5232
5233 if (varlen >= 0) {
5234 /*
5235 * Terminate the string and start recording the pattern
5236 * right after it
5237 */
5238 STPUTC('\0', expdest);
5239 patloc = expdest - (char *)stackblock();
5240 if (subevalvar(p, NULL, patloc, subtype,
5241 startloc, varflags, quotes) == 0) {
5242 int amount = expdest - (
5243 (char *)stackblock() + patloc - 1
5244 );
5245 STADJUST(-amount, expdest);
5246 }
5247 /* Remove any recorded regions beyond start of variable */
5248 removerecordregions(startloc);
5249 goto record;
5250 }
5251
5252end:
5253 if (subtype != VSNORMAL) { /* skip to end of alternative */
5254 int nesting = 1;
5255 for (;;) {
5256 if ((c = *p++) == CTLESC)
5257 p++;
5258 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5259 if (varlen >= 0)
5260 argbackq = argbackq->next;
5261 } else if (c == CTLVAR) {
5262 if ((*p++ & VSTYPE) != VSNORMAL)
5263 nesting++;
5264 } else if (c == CTLENDVAR) {
5265 if (--nesting == 0)
5266 break;
5267 }
5268 }
5269 }
5270 return p;
5271}
5272
5273
5274/*
5275 * Put a string on the stack.
5276 */
5277
5278static void
5279memtodest(const char *p, size_t len, int syntax, int quotes) {
5280 char *q = expdest;
5281
5282 q = makestrspace(len * 2, q);
5283
5284 while (len--) {
5285 int c = *p++;
5286 if (!c)
5287 continue;
5288 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5289 USTPUTC(CTLESC, q);
5290 USTPUTC(c, q);
5291 }
5292
5293 expdest = q;
5294}
5295
5296
5297static void
5298strtodest(const char *p, int syntax, int quotes)
5299{
5300 memtodest(p, strlen(p), syntax, quotes);
5301}
5302
5303
5304/*
5305 * Add the value of a specialized variable to the stack string.
5306 */
5307
5308static ssize_t
5309varvalue(char *name, int varflags, int flags)
5310{
5311 int num;
5312 char *p;
5313 int i;
5314 int sep = 0;
5315 int sepq = 0;
5316 ssize_t len = 0;
5317 char **ap;
5318 int syntax;
5319 int quoted = varflags & VSQUOTE;
5320 int subtype = varflags & VSTYPE;
5321 int quotes = flags & (EXP_FULL | EXP_CASE);
5322
5323 if (quoted && (flags & EXP_FULL))
5324 sep = 1 << CHAR_BIT;
5325
5326 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5327 switch (*name) {
5328 case '$':
5329 num = rootpid;
5330 goto numvar;
5331 case '?':
5332 num = exitstatus;
5333 goto numvar;
5334 case '#':
5335 num = shellparam.nparam;
5336 goto numvar;
5337 case '!':
5338 num = backgndpid;
5339 if (num == 0)
5340 return -1;
5341numvar:
5342 len = cvtnum(num);
5343 break;
5344 case '-':
5345 p = makestrspace(NOPTS, expdest);
5346 for (i = NOPTS - 1; i >= 0; i--) {
5347 if (optlist[i]) {
5348 USTPUTC(optletters(i), p);
5349 len++;
5350 }
5351 }
5352 expdest = p;
5353 break;
5354 case '@':
5355 if (sep)
5356 goto param;
5357 /* fall through */
5358 case '*':
5359 sep = ifsset() ? ifsval()[0] : ' ';
5360 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5361 sepq = 1;
5362param:
5363 if (!(ap = shellparam.p))
5364 return -1;
5365 while ((p = *ap++)) {
5366 size_t partlen;
5367
5368 partlen = strlen(p);
5369
5370 len += partlen;
5371 if (len > partlen && sep) {
5372 char *q;
5373
5374 len++;
5375 if (subtype == VSPLUS || subtype == VSLENGTH) {
5376 continue;
5377 }
5378 q = expdest;
5379 if (sepq)
5380 STPUTC(CTLESC, q);
5381 STPUTC(sep, q);
5382 expdest = q;
5383 }
5384
5385 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5386 memtodest(p, partlen, syntax, quotes);
5387 }
5388 return len;
5389 case '0':
5390 case '1':
5391 case '2':
5392 case '3':
5393 case '4':
5394 case '5':
5395 case '6':
5396 case '7':
5397 case '8':
5398 case '9':
5399 num = atoi(name);
5400 if (num < 0 || num > shellparam.nparam)
5401 return -1;
5402 p = num ? shellparam.p[num - 1] : arg0;
5403 goto value;
5404 default:
5405 p = lookupvar(name);
5406value:
5407 if (!p)
5408 return -1;
5409
5410 len = strlen(p);
5411 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5412 memtodest(p, len, syntax, quotes);
5413 return len;
5414 }
5415
5416 if (subtype == VSPLUS || subtype == VSLENGTH)
5417 STADJUST(-len, expdest);
5418 return len;
5419}
5420
5421
5422/*
5423 * Record the fact that we have to scan this region of the
5424 * string for IFS characters.
5425 */
5426
5427static void
5428recordregion(int start, int end, int nulonly)
5429{
5430 struct ifsregion *ifsp;
5431
5432 if (ifslastp == NULL) {
5433 ifsp = &ifsfirst;
5434 } else {
5435 INTOFF;
5436 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
5437 ifsp->next = NULL;
5438 ifslastp->next = ifsp;
5439 INTON;
5440 }
5441 ifslastp = ifsp;
5442 ifslastp->begoff = start;
5443 ifslastp->endoff = end;
5444 ifslastp->nulonly = nulonly;
5445}
5446
5447
5448/*
5449 * Break the argument string into pieces based upon IFS and add the
5450 * strings to the argument list. The regions of the string to be
5451 * searched for IFS characters have been stored by recordregion.
5452 */
5453static void
5454ifsbreakup(char *string, struct arglist *arglist)
5455{
5456 struct ifsregion *ifsp;
5457 struct strlist *sp;
5458 char *start;
5459 char *p;
5460 char *q;
5461 const char *ifs, *realifs;
5462 int ifsspc;
5463 int nulonly;
5464
5465
5466 start = string;
5467 if (ifslastp != NULL) {
5468 ifsspc = 0;
5469 nulonly = 0;
5470 realifs = ifsset() ? ifsval() : defifs;
5471 ifsp = &ifsfirst;
5472 do {
5473 p = string + ifsp->begoff;
5474 nulonly = ifsp->nulonly;
5475 ifs = nulonly ? nullstr : realifs;
5476 ifsspc = 0;
5477 while (p < string + ifsp->endoff) {
5478 q = p;
5479 if (*p == CTLESC)
5480 p++;
5481 if (strchr(ifs, *p)) {
5482 if (!nulonly)
5483 ifsspc = (strchr(defifs, *p) != NULL);
5484 /* Ignore IFS whitespace at start */
5485 if (q == start && ifsspc) {
5486 p++;
5487 start = p;
5488 continue;
5489 }
5490 *q = '\0';
5491 sp = (struct strlist *)stalloc(sizeof *sp);
5492 sp->text = start;
5493 *arglist->lastp = sp;
5494 arglist->lastp = &sp->next;
5495 p++;
5496 if (!nulonly) {
5497 for (;;) {
5498 if (p >= string + ifsp->endoff) {
5499 break;
5500 }
5501 q = p;
5502 if (*p == CTLESC)
5503 p++;
5504 if (strchr(ifs, *p) == NULL ) {
5505 p = q;
5506 break;
5507 } else if (strchr(defifs, *p) == NULL) {
5508 if (ifsspc) {
5509 p++;
5510 ifsspc = 0;
5511 } else {
5512 p = q;
5513 break;
5514 }
5515 } else
5516 p++;
5517 }
5518 }
5519 start = p;
5520 } else
5521 p++;
5522 }
5523 } while ((ifsp = ifsp->next) != NULL);
5524 if (nulonly)
5525 goto add;
5526 }
5527
5528 if (!*start)
5529 return;
5530
5531add:
5532 sp = (struct strlist *)stalloc(sizeof *sp);
5533 sp->text = start;
5534 *arglist->lastp = sp;
5535 arglist->lastp = &sp->next;
5536}
5537
5538static void
5539ifsfree(void)
5540{
5541 struct ifsregion *p;
5542
5543 INTOFF;
5544 p = ifsfirst.next;
5545 do {
5546 struct ifsregion *ifsp;
5547 ifsp = p->next;
5548 ckfree(p);
5549 p = ifsp;
5550 } while (p);
5551 ifslastp = NULL;
5552 ifsfirst.next = NULL;
5553 INTON;
5554}
5555
5556static void expmeta(char *, char *);
5557static struct strlist *expsort(struct strlist *);
5558static struct strlist *msort(struct strlist *, int);
5559
5560static char *expdir;
5561
5562
5563static void
5564expandmeta(struct strlist *str, int flag)
5565{
5566 static const char metachars[] = {
5567 '*', '?', '[', 0
5568 };
5569 /* TODO - EXP_REDIR */
5570
5571 while (str) {
5572 struct strlist **savelastp;
5573 struct strlist *sp;
5574 char *p;
5575
5576 if (fflag)
5577 goto nometa;
5578 if (!strpbrk(str->text, metachars))
5579 goto nometa;
5580 savelastp = exparg.lastp;
5581
5582 INTOFF;
5583 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
5584 {
5585 int i = strlen(str->text);
5586 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
5587 }
5588
5589 expmeta(expdir, p);
5590 ckfree(expdir);
5591 if (p != str->text)
5592 ckfree(p);
5593 INTON;
5594 if (exparg.lastp == savelastp) {
5595 /*
5596 * no matches
5597 */
5598nometa:
5599 *exparg.lastp = str;
5600 rmescapes(str->text);
5601 exparg.lastp = &str->next;
5602 } else {
5603 *exparg.lastp = NULL;
5604 *savelastp = sp = expsort(*savelastp);
5605 while (sp->next != NULL)
5606 sp = sp->next;
5607 exparg.lastp = &sp->next;
5608 }
5609 str = str->next;
5610 }
5611}
5612
5613/*
5614 * Add a file name to the list.
5615 */
5616
5617static void
5618addfname(const char *name)
5619{
5620 struct strlist *sp;
5621
5622 sp = (struct strlist *)stalloc(sizeof *sp);
5623 sp->text = sstrdup(name);
5624 *exparg.lastp = sp;
5625 exparg.lastp = &sp->next;
5626}
5627
5628
5629/*
5630 * Do metacharacter (i.e. *, ?, [...]) expansion.
5631 */
5632
5633static void
5634expmeta(char *enddir, char *name)
5635{
5636 char *p;
5637 const char *cp;
5638 char *start;
5639 char *endname;
5640 int metaflag;
5641 struct stat statb;
5642 DIR *dirp;
5643 struct dirent *dp;
5644 int atend;
5645 int matchdot;
5646
5647 metaflag = 0;
5648 start = name;
5649 for (p = name; *p; p++) {
5650 if (*p == '*' || *p == '?')
5651 metaflag = 1;
5652 else if (*p == '[') {
5653 char *q = p + 1;
5654 if (*q == '!')
5655 q++;
5656 for (;;) {
5657 if (*q == '\\')
5658 q++;
5659 if (*q == '/' || *q == '\0')
5660 break;
5661 if (*++q == ']') {
5662 metaflag = 1;
5663 break;
5664 }
5665 }
5666 } else if (*p == '\\')
5667 p++;
5668 else if (*p == '/') {
5669 if (metaflag)
5670 goto out;
5671 start = p + 1;
5672 }
5673 }
5674out:
5675 if (metaflag == 0) { /* we've reached the end of the file name */
5676 if (enddir != expdir)
5677 metaflag++;
5678 p = name;
5679 do {
5680 if (*p == '\\')
5681 p++;
5682 *enddir++ = *p;
5683 } while (*p++);
5684 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
5685 addfname(expdir);
5686 return;
5687 }
5688 endname = p;
5689 if (name < start) {
5690 p = name;
5691 do {
5692 if (*p == '\\')
5693 p++;
5694 *enddir++ = *p++;
5695 } while (p < start);
5696 }
5697 if (enddir == expdir) {
5698 cp = ".";
5699 } else if (enddir == expdir + 1 && *expdir == '/') {
5700 cp = "/";
5701 } else {
5702 cp = expdir;
5703 enddir[-1] = '\0';
5704 }
5705 if ((dirp = opendir(cp)) == NULL)
5706 return;
5707 if (enddir != expdir)
5708 enddir[-1] = '/';
5709 if (*endname == 0) {
5710 atend = 1;
5711 } else {
5712 atend = 0;
5713 *endname++ = '\0';
5714 }
5715 matchdot = 0;
5716 p = start;
5717 if (*p == '\\')
5718 p++;
5719 if (*p == '.')
5720 matchdot++;
5721 while (! intpending && (dp = readdir(dirp)) != NULL) {
5722 if (dp->d_name[0] == '.' && ! matchdot)
5723 continue;
5724 if (pmatch(start, dp->d_name)) {
5725 if (atend) {
5726 scopy(dp->d_name, enddir);
5727 addfname(expdir);
5728 } else {
5729 for (p = enddir, cp = dp->d_name;
5730 (*p++ = *cp++) != '\0';)
5731 continue;
5732 p[-1] = '/';
5733 expmeta(p, endname);
5734 }
5735 }
5736 }
5737 closedir(dirp);
5738 if (! atend)
5739 endname[-1] = '/';
5740}
5741
5742/*
5743 * Sort the results of file name expansion. It calculates the number of
5744 * strings to sort and then calls msort (short for merge sort) to do the
5745 * work.
5746 */
5747
5748static struct strlist *
5749expsort(struct strlist *str)
5750{
5751 int len;
5752 struct strlist *sp;
5753
5754 len = 0;
5755 for (sp = str ; sp ; sp = sp->next)
5756 len++;
5757 return msort(str, len);
5758}
5759
5760
5761static struct strlist *
5762msort(struct strlist *list, int len)
5763{
5764 struct strlist *p, *q = NULL;
5765 struct strlist **lpp;
5766 int half;
5767 int n;
5768
5769 if (len <= 1)
5770 return list;
5771 half = len >> 1;
5772 p = list;
5773 for (n = half ; --n >= 0 ; ) {
5774 q = p;
5775 p = p->next;
5776 }
5777 q->next = NULL; /* terminate first half of list */
5778 q = msort(list, half); /* sort first half of list */
5779 p = msort(p, len - half); /* sort second half */
5780 lpp = &list;
5781 for (;;) {
5782#ifdef CONFIG_LOCALE_SUPPORT
5783 if (strcoll(p->text, q->text) < 0)
5784#else
5785 if (strcmp(p->text, q->text) < 0)
5786#endif
5787 {
5788 *lpp = p;
5789 lpp = &p->next;
5790 if ((p = *lpp) == NULL) {
5791 *lpp = q;
5792 break;
5793 }
5794 } else {
5795 *lpp = q;
5796 lpp = &q->next;
5797 if ((q = *lpp) == NULL) {
5798 *lpp = p;
5799 break;
5800 }
5801 }
5802 }
5803 return list;
5804}
5805
5806
5807/*
5808 * Returns true if the pattern matches the string.
5809 */
5810
5811static inline int
5812patmatch(char *pattern, const char *string)
5813{
5814 return pmatch(preglob(pattern, 0, 0), string);
5815}
5816
5817
5818/*
5819 * Remove any CTLESC characters from a string.
5820 */
5821
5822static char *
5823_rmescapes(char *str, int flag)
5824{
5825 char *p, *q, *r;
5826 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5827 unsigned inquotes;
5828 int notescaped;
5829 int globbing;
5830
5831 p = strpbrk(str, qchars);
5832 if (!p) {
5833 return str;
5834 }
5835 q = p;
5836 r = str;
5837 if (flag & RMESCAPE_ALLOC) {
5838 size_t len = p - str;
5839 size_t fulllen = len + strlen(p) + 1;
5840
5841 if (flag & RMESCAPE_GROW) {
5842 r = makestrspace(fulllen, expdest);
5843 } else if (flag & RMESCAPE_HEAP) {
5844 r = ckmalloc(fulllen);
5845 } else {
5846 r = stalloc(fulllen);
5847 }
5848 q = r;
5849 if (len > 0) {
5850 q = mempcpy(q, str, len);
5851 }
5852 }
5853 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5854 globbing = flag & RMESCAPE_GLOB;
5855 notescaped = globbing;
5856 while (*p) {
5857 if (*p == CTLQUOTEMARK) {
5858 inquotes = ~inquotes;
5859 p++;
5860 notescaped = globbing;
5861 continue;
5862 }
5863 if (*p == '\\') {
5864 /* naked back slash */
5865 notescaped = 0;
5866 goto copy;
5867 }
5868 if (*p == CTLESC) {
5869 p++;
5870 if (notescaped && inquotes && *p != '/') {
5871 *q++ = '\\';
5872 }
5873 }
5874 notescaped = globbing;
5875copy:
5876 *q++ = *p++;
5877 }
5878 *q = '\0';
5879 if (flag & RMESCAPE_GROW) {
5880 expdest = r;
5881 STADJUST(q - r + 1, expdest);
5882 }
5883 return r;
5884}
5885
5886
5887/*
5888 * See if a pattern matches in a case statement.
5889 */
5890
5891int
5892casematch(union node *pattern, char *val)
5893{
5894 struct stackmark smark;
5895 int result;
5896
5897 setstackmark(&smark);
5898 argbackq = pattern->narg.backquote;
5899 STARTSTACKSTR(expdest);
5900 ifslastp = NULL;
5901 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5902 STACKSTRNUL(expdest);
5903 result = patmatch(stackblock(), val);
5904 popstackmark(&smark);
5905 return result;
5906}
5907
5908/*
5909 * Our own itoa().
5910 */
5911
5912static int
5913cvtnum(arith_t num)
5914{
5915 int len;
5916
5917 expdest = makestrspace(32, expdest);
5918#ifdef CONFIG_ASH_MATH_SUPPORT_64
5919 len = fmtstr(expdest, 32, "%lld", (long long) num);
5920#else
5921 len = fmtstr(expdest, 32, "%ld", num);
5922#endif
5923 STADJUST(len, expdest);
5924 return len;
5925}
5926
5927static void
5928varunset(const char *end, const char *var, const char *umsg, int varflags)
5929{
5930 const char *msg;
5931 const char *tail;
5932
5933 tail = nullstr;
5934 msg = "parameter not set";
5935 if (umsg) {
5936 if (*end == CTLENDVAR) {
5937 if (varflags & VSNUL)
5938 tail = " or null";
5939 } else
5940 msg = umsg;
5941 }
5942 error("%.*s: %s%s", end - var - 1, var, msg, tail);
5943}
5944
5945
5946/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
5947
5948/*
5949 * This implements the input routines used by the parser.
5950 */
5951
5952#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
5953#define IBUFSIZ (BUFSIZ + 1)
5954
5955static void pushfile(void);
5956
5957/*
5958 * Read a character from the script, returning PEOF on end of file.
5959 * Nul characters in the input are silently discarded.
5960 */
5961
5962#define pgetc_as_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
5963
5964#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
5965#define pgetc_macro() pgetc()
5966static int
5967pgetc(void)
5968{
5969 return pgetc_as_macro();
5970}
5971#else
5972#define pgetc_macro() pgetc_as_macro()
5973static int
5974pgetc(void)
5975{
5976 return pgetc_macro();
5977}
5978#endif
5979
5980
5981/*
5982 * Same as pgetc(), but ignores PEOA.
5983 */
5984#ifdef CONFIG_ASH_ALIAS
5985static int pgetc2(void)
5986{
5987 int c;
5988
5989 do {
5990 c = pgetc_macro();
5991 } while (c == PEOA);
5992 return c;
5993}
5994#else
5995static inline int pgetc2(void)
5996{
5997 return pgetc_macro();
5998}
5999#endif
6000
6001/*
6002 * Read a line from the script.
6003 */
6004
6005static inline char *
6006pfgets(char *line, int len)
6007{
6008 char *p = line;
6009 int nleft = len;
6010 int c;
6011
6012 while (--nleft > 0) {
6013 c = pgetc2();
6014 if (c == PEOF) {
6015 if (p == line)
6016 return NULL;
6017 break;
6018 }
6019 *p++ = c;
6020 if (c == '\n')
6021 break;
6022 }
6023 *p = '\0';
6024 return line;
6025}
6026
6027
6028
6029#ifdef CONFIG_FEATURE_COMMAND_EDITING
6030static const char *cmdedit_prompt;
6031static inline void putprompt(const char *s)
6032{
6033 cmdedit_prompt = s;
6034}
6035#else
6036static inline void putprompt(const char *s)
6037{
6038 out2str(s);
6039}
6040#endif
6041
6042static inline int
6043preadfd(void)
6044{
6045 int nr;
6046 char *buf = parsefile->buf;
6047 parsenextc = buf;
6048
6049retry:
6050#ifdef CONFIG_FEATURE_COMMAND_EDITING
6051 if (!iflag || parsefile->fd)
6052 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6053 else {
6054#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
6055 cmdedit_path_lookup = pathval();
6056#endif
6057 nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
6058 if(nr == 0) {
6059 /* Ctrl+C presend */
6060 if(trap[SIGINT]) {
6061 buf[0] = '\n';
6062 buf[1] = 0;
6063 raise(SIGINT);
6064 return 1;
6065 }
6066 goto retry;
6067 }
6068 if(nr < 0 && errno == 0) {
6069 /* Ctrl+D presend */
6070 nr = 0;
6071 }
6072 }
6073#else
6074 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6075#endif
6076
6077 if (nr < 0) {
6078 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6079 int flags = fcntl(0, F_GETFL, 0);
6080 if (flags >= 0 && flags & O_NONBLOCK) {
6081 flags &=~ O_NONBLOCK;
6082 if (fcntl(0, F_SETFL, flags) >= 0) {
6083 out2str("sh: turning off NDELAY mode\n");
6084 goto retry;
6085 }
6086 }
6087 }
6088 }
6089 return nr;
6090}
6091
6092/*
6093 * Refill the input buffer and return the next input character:
6094 *
6095 * 1) If a string was pushed back on the input, pop it;
6096 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6097 * from a string so we can't refill the buffer, return EOF.
6098 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6099 * 4) Process input up to the next newline, deleting nul characters.
6100 */
6101
6102int
6103preadbuffer(void)
6104{
6105 char *p, *q;
6106 int more;
6107 char savec;
6108
6109 while (parsefile->strpush) {
6110#ifdef CONFIG_ASH_ALIAS
6111 if (parsenleft == -1 && parsefile->strpush->ap &&
6112 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
6113 return PEOA;
6114 }
6115#endif
6116 popstring();
6117 if (--parsenleft >= 0)
6118 return (*parsenextc++);
6119 }
6120 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6121 return PEOF;
6122 flushall();
6123
6124again:
6125 if (parselleft <= 0) {
6126 if ((parselleft = preadfd()) <= 0) {
6127 parselleft = parsenleft = EOF_NLEFT;
6128 return PEOF;
6129 }
6130 }
6131
6132 q = p = parsenextc;
6133
6134 /* delete nul characters */
6135 for (more = 1; more;) {
6136 switch (*p) {
6137 case '\0':
6138 p++; /* Skip nul */
6139 goto check;
6140
6141 case '\n':
6142 parsenleft = q - parsenextc;
6143 more = 0; /* Stop processing here */
6144 break;
6145
6146 }
6147
6148 *q++ = *p++;
6149check:
6150 if (--parselleft <= 0 && more) {
6151 parsenleft = q - parsenextc - 1;
6152 if (parsenleft < 0)
6153 goto again;
6154 more = 0;
6155 }
6156 }
6157
6158 savec = *q;
6159 *q = '\0';
6160
6161 if (vflag) {
6162 out2str(parsenextc);
6163 }
6164
6165 *q = savec;
6166
6167 return *parsenextc++;
6168}
6169
6170/*
6171 * Undo the last call to pgetc. Only one character may be pushed back.
6172 * PEOF may be pushed back.
6173 */
6174
6175void
6176pungetc(void)
6177{
6178 parsenleft++;
6179 parsenextc--;
6180}
6181
6182/*
6183 * Push a string back onto the input at this current parsefile level.
6184 * We handle aliases this way.
6185 */
6186void
6187pushstring(char *s, void *ap)
6188{
6189 struct strpush *sp;
6190 size_t len;
6191
6192 len = strlen(s);
6193 INTOFF;
6194/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6195 if (parsefile->strpush) {
6196 sp = ckmalloc(sizeof (struct strpush));
6197 sp->prev = parsefile->strpush;
6198 parsefile->strpush = sp;
6199 } else
6200 sp = parsefile->strpush = &(parsefile->basestrpush);
6201 sp->prevstring = parsenextc;
6202 sp->prevnleft = parsenleft;
6203#ifdef CONFIG_ASH_ALIAS
6204 sp->ap = (struct alias *)ap;
6205 if (ap) {
6206 ((struct alias *)ap)->flag |= ALIASINUSE;
6207 sp->string = s;
6208 }
6209#endif
6210 parsenextc = s;
6211 parsenleft = len;
6212 INTON;
6213}
6214
6215void
6216popstring(void)
6217{
6218 struct strpush *sp = parsefile->strpush;
6219
6220 INTOFF;
6221#ifdef CONFIG_ASH_ALIAS
6222 if (sp->ap) {
6223 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6224 checkkwd |= CHKALIAS;
6225 }
6226 if (sp->string != sp->ap->val) {
6227 ckfree(sp->string);
6228 }
6229 sp->ap->flag &= ~ALIASINUSE;
6230 if (sp->ap->flag & ALIASDEAD) {
6231 unalias(sp->ap->name);
6232 }
6233 }
6234#endif
6235 parsenextc = sp->prevstring;
6236 parsenleft = sp->prevnleft;
6237/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6238 parsefile->strpush = sp->prev;
6239 if (sp != &(parsefile->basestrpush))
6240 ckfree(sp);
6241 INTON;
6242}
6243
6244/*
6245 * Set the input to take input from a file. If push is set, push the
6246 * old input onto the stack first.
6247 */
6248
6249void
6250setinputfile(const char *fname, int push)
6251{
6252 int fd;
6253 int fd2;
6254
6255 INTOFF;
6256 if ((fd = open(fname, O_RDONLY)) < 0)
6257 error("Can't open %s", fname);
6258 if (fd < 10) {
6259 fd2 = copyfd(fd, 10);
6260 close(fd);
6261 if (fd2 < 0)
6262 error("Out of file descriptors");
6263 fd = fd2;
6264 }
6265 setinputfd(fd, push);
6266 INTON;
6267}
6268
6269
6270/*
6271 * Like setinputfile, but takes an open file descriptor. Call this with
6272 * interrupts off.
6273 */
6274
6275static void
6276setinputfd(int fd, int push)
6277{
6278 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
6279 if (push) {
6280 pushfile();
6281 parsefile->buf = 0;
6282 }
6283 parsefile->fd = fd;
6284 if (parsefile->buf == NULL)
6285 parsefile->buf = ckmalloc(IBUFSIZ);
6286 parselleft = parsenleft = 0;
6287 plinno = 1;
6288}
6289
6290
6291/*
6292 * Like setinputfile, but takes input from a string.
6293 */
6294
6295static void
6296setinputstring(char *string)
6297{
6298 INTOFF;
6299 pushfile();
6300 parsenextc = string;
6301 parsenleft = strlen(string);
6302 parsefile->buf = NULL;
6303 plinno = 1;
6304 INTON;
6305}
6306
6307
6308/*
6309 * To handle the "." command, a stack of input files is used. Pushfile
6310 * adds a new entry to the stack and popfile restores the previous level.
6311 */
6312
6313static void
6314pushfile(void)
6315{
6316 struct parsefile *pf;
6317
6318 parsefile->nleft = parsenleft;
6319 parsefile->lleft = parselleft;
6320 parsefile->nextc = parsenextc;
6321 parsefile->linno = plinno;
6322 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
6323 pf->prev = parsefile;
6324 pf->fd = -1;
6325 pf->strpush = NULL;
6326 pf->basestrpush.prev = NULL;
6327 parsefile = pf;
6328}
6329
6330
6331static void
6332popfile(void)
6333{
6334 struct parsefile *pf = parsefile;
6335
6336 INTOFF;
6337 if (pf->fd >= 0)
6338 close(pf->fd);
6339 if (pf->buf)
6340 ckfree(pf->buf);
6341 while (pf->strpush)
6342 popstring();
6343 parsefile = pf->prev;
6344 ckfree(pf);
6345 parsenleft = parsefile->nleft;
6346 parselleft = parsefile->lleft;
6347 parsenextc = parsefile->nextc;
6348 plinno = parsefile->linno;
6349 INTON;
6350}
6351
6352
6353/*
6354 * Return to top level.
6355 */
6356
6357static void
6358popallfiles(void)
6359{
6360 while (parsefile != &basepf)
6361 popfile();
6362}
6363
6364
6365/*
6366 * Close the file(s) that the shell is reading commands from. Called
6367 * after a fork is done.
6368 */
6369
6370static void
6371closescript(void)
6372{
6373 popallfiles();
6374 if (parsefile->fd > 0) {
6375 close(parsefile->fd);
6376 parsefile->fd = 0;
6377 }
6378}
6379
6380/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
6381
6382/* mode flags for set_curjob */
6383#define CUR_DELETE 2
6384#define CUR_RUNNING 1
6385#define CUR_STOPPED 0
6386
6387/* mode flags for dowait */
6388#define DOWAIT_NORMAL 0
6389#define DOWAIT_BLOCK 1
6390
6391/* array of jobs */
6392static struct job *jobtab;
6393/* size of array */
6394static unsigned njobs;
6395#if JOBS
6396/* pgrp of shell on invocation */
6397static int initialpgrp;
6398static int ttyfd = -1;
6399#endif
6400/* current job */
6401static struct job *curjob;
6402/* number of presumed living untracked jobs */
6403static int jobless;
6404
6405static void set_curjob(struct job *, unsigned);
6406#if JOBS
6407static int restartjob(struct job *, int);
6408static void xtcsetpgrp(int, pid_t);
6409static char *commandtext(union node *);
6410static void cmdlist(union node *, int);
6411static void cmdtxt(union node *);
6412static void cmdputs(const char *);
6413static void showpipe(struct job *, FILE *);
6414#endif
6415static int sprint_status(char *, int, int);
6416static void freejob(struct job *);
6417static struct job *getjob(const char *, int);
6418static struct job *growjobtab(void);
6419static void forkchild(struct job *, union node *, int);
6420static void forkparent(struct job *, union node *, int, pid_t);
6421static int dowait(int, struct job *);
6422static int getstatus(struct job *);
6423
6424static void
6425set_curjob(struct job *jp, unsigned mode)
6426{
6427 struct job *jp1;
6428 struct job **jpp, **curp;
6429
6430 /* first remove from list */
6431 jpp = curp = &curjob;
6432 do {
6433 jp1 = *jpp;
6434 if (jp1 == jp)
6435 break;
6436 jpp = &jp1->prev_job;
6437 } while (1);
6438 *jpp = jp1->prev_job;
6439
6440 /* Then re-insert in correct position */
6441 jpp = curp;
6442 switch (mode) {
6443 default:
6444#ifdef DEBUG
6445 abort();
6446#endif
6447 case CUR_DELETE:
6448 /* job being deleted */
6449 break;
6450 case CUR_RUNNING:
6451 /* newly created job or backgrounded job,
6452 put after all stopped jobs. */
6453 do {
6454 jp1 = *jpp;
6455#ifdef JOBS
6456 if (!jp1 || jp1->state != JOBSTOPPED)
6457#endif
6458 break;
6459 jpp = &jp1->prev_job;
6460 } while (1);
6461 /* FALLTHROUGH */
6462#ifdef JOBS
6463 case CUR_STOPPED:
6464#endif
6465 /* newly stopped job - becomes curjob */
6466 jp->prev_job = *jpp;
6467 *jpp = jp;
6468 break;
6469 }
6470}
6471
6472#if JOBS
6473/*
6474 * Turn job control on and off.
6475 *
6476 * Note: This code assumes that the third arg to ioctl is a character
6477 * pointer, which is true on Berkeley systems but not System V. Since
6478 * System V doesn't have job control yet, this isn't a problem now.
6479 *
6480 * Called with interrupts off.
6481 */
6482
6483void
6484setjobctl(int on)
6485{
6486 int fd;
6487 int pgrp;
6488
6489 if (on == jobctl || rootshell == 0)
6490 return;
6491 if (on) {
6492 int ofd;
6493 ofd = fd = open(_PATH_TTY, O_RDWR);
6494 if (fd < 0) {
6495 fd += 3;
6496 while (!isatty(fd) && --fd >= 0)
6497 ;
6498 }
6499 fd = fcntl(fd, F_DUPFD, 10);
6500 close(ofd);
6501 if (fd < 0)
6502 goto out;
6503 fcntl(fd, F_SETFD, FD_CLOEXEC);
6504 do { /* while we are in the background */
6505 if ((pgrp = tcgetpgrp(fd)) < 0) {
6506out:
6507 sh_warnx("can't access tty; job control turned off");
6508 mflag = on = 0;
6509 goto close;
6510 }
6511 if (pgrp == getpgrp())
6512 break;
6513 killpg(0, SIGTTIN);
6514 } while (1);
6515 initialpgrp = pgrp;
6516
6517 setsignal(SIGTSTP);
6518 setsignal(SIGTTOU);
6519 setsignal(SIGTTIN);
6520 pgrp = rootpid;
6521 setpgid(0, pgrp);
6522 xtcsetpgrp(fd, pgrp);
6523 } else {
6524 /* turning job control off */
6525 fd = ttyfd;
6526 pgrp = initialpgrp;
6527 xtcsetpgrp(fd, pgrp);
6528 setpgid(0, pgrp);
6529 setsignal(SIGTSTP);
6530 setsignal(SIGTTOU);
6531 setsignal(SIGTTIN);
6532close:
6533 close(fd);
6534 fd = -1;
6535 }
6536 ttyfd = fd;
6537 jobctl = on;
6538}
6539
6540static int
6541killcmd(int argc, char **argv)
6542{
6543 int signo = -1;
6544 int list = 0;
6545 int i;
6546 pid_t pid;
6547 struct job *jp;
6548
6549 if (argc <= 1) {
6550usage:
6551 error(
6552"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6553"kill -l [exitstatus]"
6554 );
6555 }
6556
6557 if (**++argv == '-') {
6558 signo = decode_signal(*argv + 1, 1);
6559 if (signo < 0) {
6560 int c;
6561
6562 while ((c = nextopt("ls:")) != '\0')
6563 switch (c) {
6564 default:
6565#ifdef DEBUG
6566 abort();
6567#endif
6568 case 'l':
6569 list = 1;
6570 break;
6571 case 's':
6572 signo = decode_signal(optionarg, 1);
6573 if (signo < 0) {
6574 error(
6575 "invalid signal number or name: %s",
6576 optionarg
6577 );
6578 }
6579 break;
6580 }
6581 argv = argptr;
6582 } else
6583 argv++;
6584 }
6585
6586 if (!list && signo < 0)
6587 signo = SIGTERM;
6588
6589 if ((signo < 0 || !*argv) ^ list) {
6590 goto usage;
6591 }
6592
6593 if (list) {
6594 const char *name;
6595
6596 if (!*argv) {
6597 for (i = 1; i < NSIG; i++) {
6598 name = u_signal_names(0, &i, 1);
6599 if (name)
6600 out1fmt(snlfmt, name);
6601 }
6602 return 0;
6603 }
6604 name = u_signal_names(*argptr, &signo, -1);
6605 if (name)
6606 out1fmt(snlfmt, name);
6607 else
6608 error("invalid signal number or exit status: %s", *argptr);
6609 return 0;
6610 }
6611
6612 i = 0;
6613 do {
6614 if (**argv == '%') {
6615 jp = getjob(*argv, 0);
6616 pid = -jp->ps[0].pid;
6617 } else
6618 pid = number(*argv);
6619 if (kill(pid, signo) != 0) {
6620 sh_warnx("%m\n");
6621 i = 1;
6622 }
6623 } while (*++argv);
6624
6625 return i;
6626}
6627#endif /* JOBS */
6628
6629#if defined(JOBS) || defined(DEBUG)
6630static int
6631jobno(const struct job *jp)
6632{
6633 return jp - jobtab + 1;
6634}
6635#endif
6636
6637#ifdef JOBS
6638static int
6639fgcmd(int argc, char **argv)
6640{
6641 struct job *jp;
6642 FILE *out;
6643 int mode;
6644 int retval;
6645
6646 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
6647 nextopt(nullstr);
6648 argv = argptr;
6649 out = stdout;
6650 do {
6651 jp = getjob(*argv, 1);
6652 if (mode == FORK_BG) {
6653 set_curjob(jp, CUR_RUNNING);
6654 fprintf(out, "[%d] ", jobno(jp));
6655 }
6656 outstr(jp->ps->cmd, out);
6657 showpipe(jp, out);
6658 retval = restartjob(jp, mode);
6659 } while (*argv && *++argv);
6660 return retval;
6661}
6662
6663static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
6664
6665
6666static int
6667restartjob(struct job *jp, int mode)
6668{
6669 struct procstat *ps;
6670 int i;
6671 int status;
6672 pid_t pgid;
6673
6674 INTOFF;
6675 if (jp->state == JOBDONE)
6676 goto out;
6677 jp->state = JOBRUNNING;
6678 pgid = jp->ps->pid;
6679 if (mode == FORK_FG)
6680 xtcsetpgrp(ttyfd, pgid);
6681 killpg(pgid, SIGCONT);
6682 ps = jp->ps;
6683 i = jp->nprocs;
6684 do {
6685 if (WIFSTOPPED(ps->status)) {
6686 ps->status = -1;
6687 }
6688 } while (ps++, --i);
6689out:
6690 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
6691 INTON;
6692 return status;
6693}
6694#endif
6695
6696static int
6697sprint_status(char *s, int status, int sigonly)
6698{
6699 int col;
6700 int st;
6701
6702 col = 0;
6703 if (!WIFEXITED(status)) {
6704#if JOBS
6705 if (WIFSTOPPED(status))
6706 st = WSTOPSIG(status);
6707 else
6708#endif
6709 st = WTERMSIG(status);
6710 if (sigonly) {
6711 if (st == SIGINT || st == SIGPIPE)
6712 goto out;
6713#if JOBS
6714 if (WIFSTOPPED(status))
6715 goto out;
6716#endif
6717 }
6718 st &= 0x7f;
6719 col = fmtstr(s, 32, strsignal(st));
6720 if (WCOREDUMP(status)) {
6721 col += fmtstr(s + col, 16, " (core dumped)");
6722 }
6723 } else if (!sigonly) {
6724 st = WEXITSTATUS(status);
6725 if (st)
6726 col = fmtstr(s, 16, "Done(%d)", st);
6727 else
6728 col = fmtstr(s, 16, "Done");
6729 }
6730
6731out:
6732 return col;
6733}
6734
6735#if JOBS
6736static void
6737showjob(FILE *out, struct job *jp, int mode)
6738{
6739 struct procstat *ps;
6740 struct procstat *psend;
6741 int col;
6742 int indent;
6743 char s[80];
6744
6745 ps = jp->ps;
6746
6747 if (mode & SHOW_PGID) {
6748 /* just output process (group) id of pipeline */
6749 fprintf(out, "%d\n", ps->pid);
6750 return;
6751 }
6752
6753 col = fmtstr(s, 16, "[%d] ", jobno(jp));
6754 indent = col;
6755
6756 if (jp == curjob)
6757 s[col - 2] = '+';
6758 else if (curjob && jp == curjob->prev_job)
6759 s[col - 2] = '-';
6760
6761 if (mode & SHOW_PID)
6762 col += fmtstr(s + col, 16, "%d ", ps->pid);
6763
6764 psend = ps + jp->nprocs;
6765
6766 if (jp->state == JOBRUNNING) {
6767 scopy("Running", s + col);
6768 col += strlen("Running");
6769 } else {
6770 int status = psend[-1].status;
6771#if JOBS
6772 if (jp->state == JOBSTOPPED)
6773 status = jp->stopstatus;
6774#endif
6775 col += sprint_status(s + col, status, 0);
6776 }
6777
6778 goto start;
6779
6780 do {
6781 /* for each process */
6782 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
6783
6784start:
6785 fprintf(out, "%s%*c%s",
6786 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
6787 );
6788 if (!(mode & SHOW_PID)) {
6789 showpipe(jp, out);
6790 break;
6791 }
6792 if (++ps == psend) {
6793 outcslow('\n', out);
6794 break;
6795 }
6796 } while (1);
6797
6798 jp->changed = 0;
6799
6800 if (jp->state == JOBDONE) {
6801 TRACE(("showjob: freeing job %d\n", jobno(jp)));
6802 freejob(jp);
6803 }
6804}
6805
6806
6807static int
6808jobscmd(int argc, char **argv)
6809{
6810 int mode, m;
6811 FILE *out;
6812
6813 mode = 0;
6814 while ((m = nextopt("lp")))
6815 if (m == 'l')
6816 mode = SHOW_PID;
6817 else
6818 mode = SHOW_PGID;
6819
6820 out = stdout;
6821 argv = argptr;
6822 if (*argv)
6823 do
6824 showjob(out, getjob(*argv,0), mode);
6825 while (*++argv);
6826 else
6827 showjobs(out, mode);
6828
6829 return 0;
6830}
6831
6832
6833/*
6834 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6835 * statuses have changed since the last call to showjobs.
6836 */
6837
6838static void
6839showjobs(FILE *out, int mode)
6840{
6841 struct job *jp;
6842
6843 TRACE(("showjobs(%x) called\n", mode));
6844
6845 /* If not even one one job changed, there is nothing to do */
6846 while (dowait(DOWAIT_NORMAL, NULL) > 0)
6847 continue;
6848
6849 for (jp = curjob; jp; jp = jp->prev_job) {
6850 if (!(mode & SHOW_CHANGED) || jp->changed)
6851 showjob(out, jp, mode);
6852 }
6853}
6854#endif /* JOBS */
6855
6856/*
6857 * Mark a job structure as unused.
6858 */
6859
6860static void
6861freejob(struct job *jp)
6862{
6863 struct procstat *ps;
6864 int i;
6865
6866 INTOFF;
6867 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
6868 if (ps->cmd != nullstr)
6869 ckfree(ps->cmd);
6870 }
6871 if (jp->ps != &jp->ps0)
6872 ckfree(jp->ps);
6873 jp->used = 0;
6874 set_curjob(jp, CUR_DELETE);
6875 INTON;
6876}
6877
6878
6879static int
6880waitcmd(int argc, char **argv)
6881{
6882 struct job *job;
6883 int retval;
6884 struct job *jp;
6885
6886 EXSIGON();
6887
6888 nextopt(nullstr);
6889 retval = 0;
6890
6891 argv = argptr;
6892 if (!*argv) {
6893 /* wait for all jobs */
6894 for (;;) {
6895 jp = curjob;
6896 while (1) {
6897 if (!jp) {
6898 /* no running procs */
6899 goto out;
6900 }
6901 if (jp->state == JOBRUNNING)
6902 break;
6903 jp->waited = 1;
6904 jp = jp->prev_job;
6905 }
6906 dowait(DOWAIT_BLOCK, 0);
6907 }
6908 }
6909
6910 retval = 127;
6911 do {
6912 if (**argv != '%') {
6913 pid_t pid = number(*argv);
6914 job = curjob;
6915 goto start;
6916 do {
6917 if (job->ps[job->nprocs - 1].pid == pid)
6918 break;
6919 job = job->prev_job;
6920start:
6921 if (!job)
6922 goto repeat;
6923 } while (1);
6924 } else
6925 job = getjob(*argv, 0);
6926 /* loop until process terminated or stopped */
6927 while (job->state == JOBRUNNING)
6928 dowait(DOWAIT_BLOCK, 0);
6929 job->waited = 1;
6930 retval = getstatus(job);
6931repeat:
6932 ;
6933 } while (*++argv);
6934
6935out:
6936 return retval;
6937}
6938
6939
6940/*
6941 * Convert a job name to a job structure.
6942 */
6943
6944static struct job *
6945getjob(const char *name, int getctl)
6946{
6947 struct job *jp;
6948 struct job *found;
6949 const char *err_msg = "No such job: %s";
6950 unsigned num;
6951 int c;
6952 const char *p;
6953 char *(*match)(const char *, const char *);
6954
6955 jp = curjob;
6956 p = name;
6957 if (!p)
6958 goto currentjob;
6959
6960 if (*p != '%')
6961 goto err;
6962
6963 c = *++p;
6964 if (!c)
6965 goto currentjob;
6966
6967 if (!p[1]) {
6968 if (c == '+' || c == '%') {
6969currentjob:
6970 err_msg = "No current job";
6971 goto check;
6972 } else if (c == '-') {
6973 if (jp)
6974 jp = jp->prev_job;
6975 err_msg = "No previous job";
6976check:
6977 if (!jp)
6978 goto err;
6979 goto gotit;
6980 }
6981 }
6982
6983 if (is_number(p)) {
6984 num = atoi(p);
6985 if (num < njobs) {
6986 jp = jobtab + num - 1;
6987 if (jp->used)
6988 goto gotit;
6989 goto err;
6990 }
6991 }
6992
6993 match = prefix;
6994 if (*p == '?') {
6995 match = strstr;
6996 p++;
6997 }
6998
6999 found = 0;
7000 while (1) {
7001 if (!jp)
7002 goto err;
7003 if (match(jp->ps[0].cmd, p)) {
7004 if (found)
7005 goto err;
7006 found = jp;
7007 err_msg = "%s: ambiguous";
7008 }
7009 jp = jp->prev_job;
7010 }
7011
7012gotit:
7013#if JOBS
7014 err_msg = "job %s not created under job control";
7015 if (getctl && jp->jobctl == 0)
7016 goto err;
7017#endif
7018 return jp;
7019err:
7020 error(err_msg, name);
7021}
7022
7023
7024/*
7025 * Return a new job structure.
7026 * Called with interrupts off.
7027 */
7028
7029static struct job *
7030makejob(union node *node, int nprocs)
7031{
7032 int i;
7033 struct job *jp;
7034
7035 for (i = njobs, jp = jobtab ; ; jp++) {
7036 if (--i < 0) {
7037 jp = growjobtab();
7038 break;
7039 }
7040 if (jp->used == 0)
7041 break;
7042 if (jp->state != JOBDONE || !jp->waited)
7043 continue;
7044#if JOBS
7045 if (jobctl)
7046 continue;
7047#endif
7048 freejob(jp);
7049 break;
7050 }
7051 memset(jp, 0, sizeof(*jp));
7052#if JOBS
7053 if (jobctl)
7054 jp->jobctl = 1;
7055#endif
7056 jp->prev_job = curjob;
7057 curjob = jp;
7058 jp->used = 1;
7059 jp->ps = &jp->ps0;
7060 if (nprocs > 1) {
7061 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
7062 }
7063 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
7064 jobno(jp)));
7065 return jp;
7066}
7067
7068static struct job *
7069growjobtab(void)
7070{
7071 size_t len;
7072 ptrdiff_t offset;
7073 struct job *jp, *jq;
7074
7075 len = njobs * sizeof(*jp);
7076 jq = jobtab;
7077 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
7078
7079 offset = (char *)jp - (char *)jq;
7080 if (offset) {
7081 /* Relocate pointers */
7082 size_t l = len;
7083
7084 jq = (struct job *)((char *)jq + l);
7085 while (l) {
7086 l -= sizeof(*jp);
7087 jq--;
7088#define joff(p) ((struct job *)((char *)(p) + l))
7089#define jmove(p) (p) = (void *)((char *)(p) + offset)
7090 if (xlikely(joff(jp)->ps == &jq->ps0))
7091 jmove(joff(jp)->ps);
7092 if (joff(jp)->prev_job)
7093 jmove(joff(jp)->prev_job);
7094 }
7095 if (curjob)
7096 jmove(curjob);
7097#undef joff
7098#undef jmove
7099 }
7100
7101 njobs += 4;
7102 jobtab = jp;
7103 jp = (struct job *)((char *)jp + len);
7104 jq = jp + 3;
7105 do {
7106 jq->used = 0;
7107 } while (--jq >= jp);
7108 return jp;
7109}
7110
7111
7112/*
7113 * Fork off a subshell. If we are doing job control, give the subshell its
7114 * own process group. Jp is a job structure that the job is to be added to.
7115 * N is the command that will be evaluated by the child. Both jp and n may
7116 * be NULL. The mode parameter can be one of the following:
7117 * FORK_FG - Fork off a foreground process.
7118 * FORK_BG - Fork off a background process.
7119 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
7120 * process group even if job control is on.
7121 *
7122 * When job control is turned off, background processes have their standard
7123 * input redirected to /dev/null (except for the second and later processes
7124 * in a pipeline).
7125 *
7126 * Called with interrupts off.
7127 */
7128
7129static inline void
7130forkchild(struct job *jp, union node *n, int mode)
7131{
7132 int wasroot;
7133
7134 TRACE(("Child shell %d\n", getpid()));
7135 wasroot = rootshell;
7136 rootshell = 0;
7137
7138 closescript();
7139 clear_traps();
7140#if JOBS
7141 /* do job control only in root shell */
7142 jobctl = 0;
7143 if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
7144 pid_t pgrp;
7145
7146 if (jp->nprocs == 0)
7147 pgrp = getpid();
7148 else
7149 pgrp = jp->ps[0].pid;
7150 /* This can fail because we are doing it in the parent also */
7151 (void)setpgid(0, pgrp);
7152 if (mode == FORK_FG)
7153 xtcsetpgrp(ttyfd, pgrp);
7154 setsignal(SIGTSTP);
7155 setsignal(SIGTTOU);
7156 } else
7157#endif
7158 if (mode == FORK_BG) {
7159 ignoresig(SIGINT);
7160 ignoresig(SIGQUIT);
7161 if (jp->nprocs == 0) {
7162 close(0);
7163 if (open(_PATH_DEVNULL, O_RDONLY) != 0)
7164 error("Can't open %s", _PATH_DEVNULL);
7165 }
7166 }
7167 if (wasroot && iflag) {
7168 setsignal(SIGINT);
7169 setsignal(SIGQUIT);
7170 setsignal(SIGTERM);
7171 }
7172 for (jp = curjob; jp; jp = jp->prev_job)
7173 freejob(jp);
7174 jobless = 0;
7175}
7176
7177static inline void
7178forkparent(struct job *jp, union node *n, int mode, pid_t pid)
7179{
7180 TRACE(("In parent shell: child = %d\n", pid));
7181 if (!jp) {
7182 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
7183 jobless++;
7184 return;
7185 }
7186#if JOBS
7187 if (mode != FORK_NOJOB && jp->jobctl) {
7188 int pgrp;
7189
7190 if (jp->nprocs == 0)
7191 pgrp = pid;
7192 else
7193 pgrp = jp->ps[0].pid;
7194 /* This can fail because we are doing it in the child also */
7195 (void)setpgid(pid, pgrp);
7196 }
7197#endif
7198 if (mode == FORK_BG) {
7199 backgndpid = pid; /* set $! */
7200 set_curjob(jp, CUR_RUNNING);
7201 }
7202 if (jp) {
7203 struct procstat *ps = &jp->ps[jp->nprocs++];
7204 ps->pid = pid;
7205 ps->status = -1;
7206 ps->cmd = nullstr;
7207#if JOBS
7208 if (jobctl && n)
7209 ps->cmd = commandtext(n);
7210#endif
7211 }
7212}
7213
7214static int
7215forkshell(struct job *jp, union node *n, int mode)
7216{
7217 int pid;
7218
7219 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
7220 pid = fork();
7221 if (pid < 0) {
7222 TRACE(("Fork failed, errno=%d", errno));
7223 if (jp)
7224 freejob(jp);
7225 error("Cannot fork");
7226 }
7227 if (pid == 0)
7228 forkchild(jp, n, mode);
7229 else
7230 forkparent(jp, n, mode, pid);
7231 return pid;
7232}
7233
7234/*
7235 * Wait for job to finish.
7236 *
7237 * Under job control we have the problem that while a child process is
7238 * running interrupts generated by the user are sent to the child but not
7239 * to the shell. This means that an infinite loop started by an inter-
7240 * active user may be hard to kill. With job control turned off, an
7241 * interactive user may place an interactive program inside a loop. If
7242 * the interactive program catches interrupts, the user doesn't want
7243 * these interrupts to also abort the loop. The approach we take here
7244 * is to have the shell ignore interrupt signals while waiting for a
7245 * foreground process to terminate, and then send itself an interrupt
7246 * signal if the child process was terminated by an interrupt signal.
7247 * Unfortunately, some programs want to do a bit of cleanup and then
7248 * exit on interrupt; unless these processes terminate themselves by
7249 * sending a signal to themselves (instead of calling exit) they will
7250 * confuse this approach.
7251 *
7252 * Called with interrupts off.
7253 */
7254
7255int
7256waitforjob(struct job *jp)
7257{
7258 int st;
7259
7260 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
7261 while (jp->state == JOBRUNNING) {
7262 dowait(DOWAIT_BLOCK, jp);
7263 }
7264 st = getstatus(jp);
7265#if JOBS
7266 if (jp->jobctl) {
7267 xtcsetpgrp(ttyfd, rootpid);
7268 /*
7269 * This is truly gross.
7270 * If we're doing job control, then we did a TIOCSPGRP which
7271 * caused us (the shell) to no longer be in the controlling
7272 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
7273 * intuit from the subprocess exit status whether a SIGINT
7274 * occurred, and if so interrupt ourselves. Yuck. - mycroft
7275 */
7276 if (jp->sigint)
7277 raise(SIGINT);
7278 }
7279 if (jp->state == JOBDONE)
7280#endif
7281 freejob(jp);
7282 return st;
7283}
7284
7285
7286/*
7287 * Do a wait system call. If job control is compiled in, we accept
7288 * stopped processes. If block is zero, we return a value of zero
7289 * rather than blocking.
7290 *
7291 * System V doesn't have a non-blocking wait system call. It does
7292 * have a SIGCLD signal that is sent to a process when one of it's
7293 * children dies. The obvious way to use SIGCLD would be to install
7294 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7295 * was received, and have waitproc bump another counter when it got
7296 * the status of a process. Waitproc would then know that a wait
7297 * system call would not block if the two counters were different.
7298 * This approach doesn't work because if a process has children that
7299 * have not been waited for, System V will send it a SIGCLD when it
7300 * installs a signal handler for SIGCLD. What this means is that when
7301 * a child exits, the shell will be sent SIGCLD signals continuously
7302 * until is runs out of stack space, unless it does a wait call before
7303 * restoring the signal handler. The code below takes advantage of
7304 * this (mis)feature by installing a signal handler for SIGCLD and
7305 * then checking to see whether it was called. If there are any
7306 * children to be waited for, it will be.
7307 *
7308 * If neither SYSV nor BSD is defined, we don't implement nonblocking
7309 * waits at all. In this case, the user will not be informed when
7310 * a background process until the next time she runs a real program
7311 * (as opposed to running a builtin command or just typing return),
7312 * and the jobs command may give out of date information.
7313 */
7314
7315static inline int
7316waitproc(int block, int *status)
7317{
7318 int flags = 0;
7319
7320#if JOBS
7321 if (jobctl)
7322 flags |= WUNTRACED;
7323#endif
7324 if (block == 0)
7325 flags |= WNOHANG;
7326 return wait3(status, flags, (struct rusage *)NULL);
7327}
7328
7329/*
7330 * Wait for a process to terminate.
7331 */
7332
7333static int
7334dowait(int block, struct job *job)
7335{
7336 int pid;
7337 int status;
7338 struct job *jp;
7339 struct job *thisjob;
7340 int state;
7341
7342 TRACE(("dowait(%d) called\n", block));
7343 pid = waitproc(block, &status);
7344 TRACE(("wait returns pid %d, status=%d\n", pid, status));
7345 if (pid <= 0)
7346 return pid;
7347 INTOFF;
7348 thisjob = NULL;
7349 for (jp = curjob; jp; jp = jp->prev_job) {
7350 struct procstat *sp;
7351 struct procstat *spend;
7352 if (jp->state == JOBDONE)
7353 continue;
7354 state = JOBDONE;
7355 spend = jp->ps + jp->nprocs;
7356 sp = jp->ps;
7357 do {
7358 if (sp->pid == pid) {
7359 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
7360 sp->status = status;
7361 thisjob = jp;
7362 }
7363 if (sp->status == -1)
7364 state = JOBRUNNING;
7365#ifdef JOBS
7366 if (state == JOBRUNNING)
7367 continue;
7368 if (WIFSTOPPED(sp->status)) {
7369 jp->stopstatus = sp->status;
7370 state = JOBSTOPPED;
7371 }
7372#endif
7373 } while (++sp < spend);
7374 if (thisjob)
7375 goto gotjob;
7376 }
7377#ifdef JOBS
7378 if (!WIFSTOPPED(status))
7379#endif
7380
7381 jobless--;
7382 goto out;
7383
7384gotjob:
7385 if (state != JOBRUNNING) {
7386 thisjob->changed = 1;
7387
7388 if (thisjob->state != state) {
7389 TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
7390 thisjob->state = state;
7391#ifdef JOBS
7392 if (state == JOBSTOPPED) {
7393 set_curjob(thisjob, CUR_STOPPED);
7394 }
7395#endif
7396 }
7397 }
7398
7399out:
7400 INTON;
7401
7402 if (thisjob && thisjob == job) {
7403 char s[48 + 1];
7404 int len;
7405
7406 len = sprint_status(s, status, 1);
7407 if (len) {
7408 s[len] = '\n';
7409 s[len + 1] = 0;
7410 out2str(s);
7411 }
7412 }
7413 return pid;
7414}
7415
7416
7417/*
7418 * return 1 if there are stopped jobs, otherwise 0
7419 */
7420
7421int
7422stoppedjobs(void)
7423{
7424 struct job *jp;
7425 int retval;
7426
7427 retval = 0;
7428 if (job_warning)
7429 goto out;
7430 jp = curjob;
7431 if (jp && jp->state == JOBSTOPPED) {
7432 out2str("You have stopped jobs.\n");
7433 job_warning = 2;
7434 retval++;
7435 }
7436
7437out:
7438 return retval;
7439}
7440
7441/*
7442 * Return a string identifying a command (to be printed by the
7443 * jobs command).
7444 */
7445
7446#if JOBS
7447static char *cmdnextc;
7448
7449static char *
7450commandtext(union node *n)
7451{
7452 char *name;
7453
7454 STARTSTACKSTR(cmdnextc);
7455 cmdtxt(n);
7456 name = stackblock();
7457 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
7458 name, cmdnextc, cmdnextc));
7459 return savestr(name);
7460}
7461
7462static void
7463cmdtxt(union node *n)
7464{
7465 union node *np;
7466 struct nodelist *lp;
7467 const char *p;
7468 char s[2];
7469
7470 if (!n)
7471 return;
7472 switch (n->type) {
7473 default:
7474#if DEBUG
7475 abort();
7476#endif
7477 case NPIPE:
7478 lp = n->npipe.cmdlist;
7479 for (;;) {
7480 cmdtxt(lp->n);
7481 lp = lp->next;
7482 if (!lp)
7483 break;
7484 cmdputs(" | ");
7485 }
7486 break;
7487 case NSEMI:
7488 p = "; ";
7489 goto binop;
7490 case NAND:
7491 p = " && ";
7492 goto binop;
7493 case NOR:
7494 p = " || ";
7495binop:
7496 cmdtxt(n->nbinary.ch1);
7497 cmdputs(p);
7498 n = n->nbinary.ch2;
7499 goto donode;
7500 case NREDIR:
7501 case NBACKGND:
7502 n = n->nredir.n;
7503 goto donode;
7504 case NNOT:
7505 cmdputs("!");
7506 n = n->nnot.com;
7507donode:
7508 cmdtxt(n);
7509 break;
7510 case NIF:
7511 cmdputs("if ");
7512 cmdtxt(n->nif.test);
7513 cmdputs("; then ");
7514 n = n->nif.ifpart;
7515 if (n->nif.elsepart) {
7516 cmdtxt(n);
7517 cmdputs("; else ");
7518 n = n->nif.elsepart;
7519 }
7520 p = "; fi";
7521 goto dotail;
7522 case NSUBSHELL:
7523 cmdputs("(");
7524 n = n->nredir.n;
7525 p = ")";
7526 goto dotail;
7527 case NWHILE:
7528 p = "while ";
7529 goto until;
7530 case NUNTIL:
7531 p = "until ";
7532until:
7533 cmdputs(p);
7534 cmdtxt(n->nbinary.ch1);
7535 n = n->nbinary.ch2;
7536 p = "; done";
7537dodo:
7538 cmdputs("; do ");
7539dotail:
7540 cmdtxt(n);
7541 goto dotail2;
7542 case NFOR:
7543 cmdputs("for ");
7544 cmdputs(n->nfor.var);
7545 cmdputs(" in ");
7546 cmdlist(n->nfor.args, 1);
7547 n = n->nfor.body;
7548 p = "; done";
7549 goto dodo;
7550 case NDEFUN:
7551 cmdputs(n->narg.text);
7552 p = "() { ... }";
7553 goto dotail2;
7554 case NCMD:
7555 cmdlist(n->ncmd.args, 1);
7556 cmdlist(n->ncmd.redirect, 0);
7557 break;
7558 case NARG:
7559 p = n->narg.text;
7560dotail2:
7561 cmdputs(p);
7562 break;
7563 case NHERE:
7564 case NXHERE:
7565 p = "<<...";
7566 goto dotail2;
7567 case NCASE:
7568 cmdputs("case ");
7569 cmdputs(n->ncase.expr->narg.text);
7570 cmdputs(" in ");
7571 for (np = n->ncase.cases; np; np = np->nclist.next) {
7572 cmdtxt(np->nclist.pattern);
7573 cmdputs(") ");
7574 cmdtxt(np->nclist.body);
7575 cmdputs(";; ");
7576 }
7577 p = "esac";
7578 goto dotail2;
7579 case NTO:
7580 p = ">";
7581 goto redir;
7582 case NCLOBBER:
7583 p = ">|";
7584 goto redir;
7585 case NAPPEND:
7586 p = ">>";
7587 goto redir;
7588 case NTOFD:
7589 p = ">&";
7590 goto redir;
7591 case NFROM:
7592 p = "<";
7593 goto redir;
7594 case NFROMFD:
7595 p = "<&";
7596 goto redir;
7597 case NFROMTO:
7598 p = "<>";
7599redir:
7600 s[0] = n->nfile.fd + '0';
7601 s[1] = '\0';
7602 cmdputs(s);
7603 cmdputs(p);
7604 if (n->type == NTOFD || n->type == NFROMFD) {
7605 s[0] = n->ndup.dupfd + '0';
7606 p = s;
7607 goto dotail2;
7608 } else {
7609 n = n->nfile.fname;
7610 goto donode;
7611 }
7612 }
7613}
7614
7615static void
7616cmdlist(union node *np, int sep)
7617{
7618 for (; np; np = np->narg.next) {
7619 if (!sep)
7620 cmdputs(spcstr);
7621 cmdtxt(np);
7622 if (sep && np->narg.next)
7623 cmdputs(spcstr);
7624 }
7625}
7626
7627static void
7628cmdputs(const char *s)
7629{
7630 const char *p, *str;
7631 char c, cc[2] = " ";
7632 char *nextc;
7633 int subtype = 0;
7634 int quoted = 0;
7635 static const char *const vstype[16] = {
7636 nullstr, "}", "-", "+", "?", "=",
7637 "%", "%%", "#", "##", nullstr
7638 };
7639
7640 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
7641 p = s;
7642 while ((c = *p++) != 0) {
7643 str = 0;
7644 switch (c) {
7645 case CTLESC:
7646 c = *p++;
7647 break;
7648 case CTLVAR:
7649 subtype = *p++;
7650 if ((subtype & VSTYPE) == VSLENGTH)
7651 str = "${#";
7652 else
7653 str = "${";
7654 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
7655 quoted ^= 1;
7656 c = '"';
7657 } else
7658 goto dostr;
7659 break;
7660 case CTLENDVAR:
7661 quoted >>= 1;
7662 subtype = 0;
7663 if (quoted & 1) {
7664 str = "\"}";
7665 goto dostr;
7666 }
7667 c = '}';
7668 break;
7669 case CTLBACKQ:
7670 str = "$(...)";
7671 goto dostr;
7672 case CTLBACKQ+CTLQUOTE:
7673 str = "\"$(...)\"";
7674 goto dostr;
7675#ifdef CONFIG_ASH_MATH_SUPPORT
7676 case CTLARI:
7677 str = "$((";
7678 goto dostr;
7679 case CTLENDARI:
7680 str = "))";
7681 goto dostr;
7682#endif
7683 case CTLQUOTEMARK:
7684 quoted ^= 1;
7685 c = '"';
7686 break;
7687 case '=':
7688 if (subtype == 0)
7689 break;
7690 str = vstype[subtype & VSTYPE];
7691 if (subtype & VSNUL)
7692 c = ':';
7693 else
7694 c = *str++;
7695 if (c != '}')
7696 quoted <<= 1;
7697 break;
7698 case '\'':
7699 case '\\':
7700 case '"':
7701 case '$':
7702 /* These can only happen inside quotes */
7703 cc[0] = c;
7704 str = cc;
7705 c = '\\';
7706 break;
7707 default:
7708 break;
7709 }
7710 USTPUTC(c, nextc);
7711 if (!str)
7712 continue;
7713dostr:
7714 while ((c = *str++)) {
7715 USTPUTC(c, nextc);
7716 }
7717 }
7718 if (quoted & 1) {
7719 USTPUTC('"', nextc);
7720 }
7721 *nextc = 0;
7722 cmdnextc = nextc;
7723}
7724
7725
7726static void
7727showpipe(struct job *jp, FILE *out)
7728{
7729 struct procstat *sp;
7730 struct procstat *spend;
7731
7732 spend = jp->ps + jp->nprocs;
7733 for (sp = jp->ps + 1; sp < spend; sp++)
7734 fprintf(out, " | %s", sp->cmd);
7735 outcslow('\n', out);
7736 flushall();
7737}
7738
7739static void
7740xtcsetpgrp(int fd, pid_t pgrp)
7741{
7742 if (tcsetpgrp(fd, pgrp))
7743 error("Cannot set tty process group (%m)");
7744}
7745#endif /* JOBS */
7746
7747static int
7748getstatus(struct job *job) {
7749 int status;
7750 int retval;
7751
7752 status = job->ps[job->nprocs - 1].status;
7753 retval = WEXITSTATUS(status);
7754 if (!WIFEXITED(status)) {
7755#if JOBS
7756 retval = WSTOPSIG(status);
7757 if (!WIFSTOPPED(status))
7758#endif
7759 {
7760 /* XXX: limits number of signals */
7761 retval = WTERMSIG(status);
7762#if JOBS
7763 if (retval == SIGINT)
7764 job->sigint = 1;
7765#endif
7766 }
7767 retval += 128;
7768 }
7769 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
7770 jobno(job), job->nprocs, status, retval));
7771 return retval;
7772}
7773
7774#ifdef CONFIG_ASH_MAIL
7775/* $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $ */
7776
7777/*
7778 * Routines to check for mail. (Perhaps make part of main.c?)
7779 */
7780
7781#define MAXMBOXES 10
7782
7783/* times of mailboxes */
7784static time_t mailtime[MAXMBOXES];
7785/* Set if MAIL or MAILPATH is changed. */
7786static int mail_var_path_changed;
7787
7788
7789
7790/*
7791 * Print appropriate message(s) if mail has arrived.
7792 * If mail_var_path_changed is set,
7793 * then the value of MAIL has mail_var_path_changed,
7794 * so we just update the values.
7795 */
7796
7797static void
7798chkmail(void)
7799{
7800 const char *mpath;
7801 char *p;
7802 char *q;
7803 time_t *mtp;
7804 struct stackmark smark;
7805 struct stat statb;
7806
7807 setstackmark(&smark);
7808 mpath = mpathset() ? mpathval() : mailval();
7809 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
7810 p = padvance(&mpath, nullstr);
7811 if (p == NULL)
7812 break;
7813 if (*p == '\0')
7814 continue;
7815 for (q = p ; *q ; q++);
7816#ifdef DEBUG
7817 if (q[-1] != '/')
7818 abort();
7819#endif
7820 q[-1] = '\0'; /* delete trailing '/' */
7821 if (stat(p, &statb) < 0) {
7822 *mtp = 0;
7823 continue;
7824 }
7825 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
7826 fprintf(
7827 stderr, snlfmt,
7828 pathopt ? pathopt : "you have mail"
7829 );
7830 }
7831 *mtp = statb.st_mtime;
7832 }
7833 mail_var_path_changed = 0;
7834 popstackmark(&smark);
7835}
7836
7837
7838static void
7839changemail(const char *val)
7840{
7841 mail_var_path_changed++;
7842}
7843
7844#endif /* CONFIG_ASH_MAIL */
7845
7846/* $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $ */
7847
7848
7849#if PROFILE
7850static short profile_buf[16384];
7851extern int etext();
7852#endif
7853
7854static int isloginsh;
7855
7856static void read_profile(const char *);
7857
7858/*
7859 * Main routine. We initialize things, parse the arguments, execute
7860 * profiles if we're a login shell, and then call cmdloop to execute
7861 * commands. The setjmp call sets up the location to jump to when an
7862 * exception occurs. When an exception occurs the variable "state"
7863 * is used to figure out how far we had gotten.
7864 */
7865
7866int
7867ash_main(int argc, char **argv)
7868{
7869 char *shinit;
7870 volatile int state;
7871 struct jmploc jmploc;
7872 struct stackmark smark;
7873
7874#ifdef __GLIBC__
7875 dash_errno = __errno_location();
7876#endif
7877
7878#if PROFILE
7879 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7880#endif
7881 state = 0;
7882 if (setjmp(jmploc.loc)) {
7883 int status;
7884 int e;
7885
7886 reset();
7887
7888 e = exception;
7889 switch (exception) {
7890 case EXEXEC:
7891 status = exerrno;
7892 break;
7893
7894 case EXERROR:
7895 status = 2;
7896 break;
7897
7898 default:
7899 status = exitstatus;
7900 break;
7901 }
7902 exitstatus = status;
7903
7904 if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
7905 exitshell();
7906
7907 if (e == EXINT) {
7908 outcslow('\n', stderr);
7909 }
7910 popstackmark(&smark);
7911 FORCEINTON; /* enable interrupts */
7912 if (state == 1)
7913 goto state1;
7914 else if (state == 2)
7915 goto state2;
7916 else if (state == 3)
7917 goto state3;
7918 else
7919 goto state4;
7920 }
7921 handler = &jmploc;
7922#ifdef DEBUG
7923 opentrace();
7924 trputs("Shell args: "); trargs(argv);
7925#endif
7926 rootpid = getpid();
7927
7928#ifdef CONFIG_ASH_RANDOM_SUPPORT
7929 rseed = rootpid + ((time_t)time((time_t *)0));
7930#endif
7931 rootshell = 1;
7932 init();
7933 setstackmark(&smark);
7934 procargs(argc, argv);
7935#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7936 if ( iflag ) {
7937 const char *hp = lookupvar("HISTFILE");
7938
7939 if(hp == NULL ) {
7940 hp = lookupvar("HOME");
7941 if(hp != NULL) {
7942 char *defhp = concat_path_file(hp, ".ash_history");
7943 setvar("HISTFILE", defhp, 0);
7944 free(defhp);
7945 }
7946 }
7947 }
7948#endif
7949 if (argv[0] && argv[0][0] == '-')
7950 isloginsh = 1;
7951 if (isloginsh) {
7952 state = 1;
7953 read_profile("/etc/profile");
7954state1:
7955 state = 2;
7956 read_profile(".profile");
7957 }
7958state2:
7959 state = 3;
7960 if (
7961#ifndef linux
7962 getuid() == geteuid() && getgid() == getegid() &&
7963#endif
7964 iflag
7965 ) {
7966 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7967 read_profile(shinit);
7968 }
7969 }
7970state3:
7971 state = 4;
7972 if (minusc)
7973 evalstring(minusc);
7974
7975 if (sflag || minusc == NULL) {
7976#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7977 if ( iflag ) {
7978 const char *hp = lookupvar("HISTFILE");
7979
7980 if(hp != NULL )
7981 load_history ( hp );
7982 }
7983#endif
7984state4: /* XXX ??? - why isn't this before the "if" statement */
7985 cmdloop(1);
7986 }
7987#if PROFILE
7988 monitor(0);
7989#endif
7990#if GPROF
7991 {
7992 extern void _mcleanup(void);
7993 _mcleanup();
7994 }
7995#endif
7996 exitshell();
7997 /* NOTREACHED */
7998}
7999
8000
8001/*
8002 * Read and execute commands. "Top" is nonzero for the top level command
8003 * loop; it turns on prompting if the shell is interactive.
8004 */
8005
8006static void
8007cmdloop(int top)
8008{
8009 union node *n;
8010 struct stackmark smark;
8011 int inter;
8012 int numeof = 0;
8013
8014 TRACE(("cmdloop(%d) called\n", top));
8015 for (;;) {
8016 setstackmark(&smark);
8017 if (pendingsigs)
8018 dotrap();
8019#if JOBS
8020 if (jobctl)
8021 showjobs(stderr, SHOW_CHANGED);
8022#endif
8023 inter = 0;
8024 if (iflag && top) {
8025 inter++;
8026#ifdef CONFIG_ASH_MAIL
8027 chkmail();
8028#endif
8029 }
8030 n = parsecmd(inter);
8031 /* showtree(n); DEBUG */
8032 if (n == NEOF) {
8033 if (!top || numeof >= 50)
8034 break;
8035 if (!stoppedjobs()) {
8036 if (!Iflag)
8037 break;
8038 out2str("\nUse \"exit\" to leave shell.\n");
8039 }
8040 numeof++;
8041 } else if (n != NULL && nflag == 0) {
8042 job_warning = (job_warning == 2) ? 1 : 0;
8043 numeof = 0;
8044 evaltree(n, 0);
8045 }
8046 popstackmark(&smark);
8047 if (evalskip) {
8048 evalskip = 0;
8049 break;
8050 }
8051 }
8052}
8053
8054
8055/*
8056 * Read /etc/profile or .profile. Return on error.
8057 */
8058
8059static void
8060read_profile(const char *name)
8061{
8062 int fd;
8063 int xflag_set = 0;
8064 int vflag_set = 0;
8065
8066 INTOFF;
8067 if ((fd = open(name, O_RDONLY)) >= 0)
8068 setinputfd(fd, 1);
8069 INTON;
8070 if (fd < 0)
8071 return;
8072 /* -q turns off -x and -v just when executing init files */
8073 if (qflag) {
8074 if (xflag)
8075 xflag = 0, xflag_set = 1;
8076 if (vflag)
8077 vflag = 0, vflag_set = 1;
8078 }
8079 cmdloop(0);
8080 if (qflag) {
8081 if (xflag_set)
8082 xflag = 1;
8083 if (vflag_set)
8084 vflag = 1;
8085 }
8086 popfile();
8087}
8088
8089
8090/*
8091 * Read a file containing shell functions.
8092 */
8093
8094static void
8095readcmdfile(char *name)
8096{
8097 int fd;
8098
8099 INTOFF;
8100 if ((fd = open(name, O_RDONLY)) >= 0)
8101 setinputfd(fd, 1);
8102 else
8103 error("Can't open %s", name);
8104 INTON;
8105 cmdloop(0);
8106 popfile();
8107}
8108
8109
8110/*
8111 * Take commands from a file. To be compatible we should do a path
8112 * search for the file, which is necessary to find sub-commands.
8113 */
8114
8115static inline char *
8116find_dot_file(char *name)
8117{
8118 char *fullname;
8119 const char *path = pathval();
8120 struct stat statb;
8121
8122 /* don't try this for absolute or relative paths */
8123 if (strchr(name, '/'))
8124 return name;
8125
8126 while ((fullname = padvance(&path, name)) != NULL) {
8127 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
8128 /*
8129 * Don't bother freeing here, since it will
8130 * be freed by the caller.
8131 */
8132 return fullname;
8133 }
8134 stunalloc(fullname);
8135 }
8136
8137 /* not found in the PATH */
8138 error(not_found_msg, name);
8139 /* NOTREACHED */
8140}
8141
8142static int dotcmd(int argc, char **argv)
8143{
8144 struct strlist *sp;
8145 volatile struct shparam saveparam;
8146
8147 exitstatus = 0;
8148
8149 for (sp = cmdenviron; sp; sp = sp->next)
8150 setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
8151
8152 if (argc >= 2) { /* That's what SVR2 does */
8153 char *fullname;
8154 struct stackmark smark;
8155
8156 setstackmark(&smark);
8157 fullname = find_dot_file(argv[1]);
8158
8159 if (argc > 2) {
8160 saveparam = shellparam;
8161 shellparam.malloc = 0;
8162 shellparam.nparam = argc - 2;
8163 shellparam.p = argv + 2;
8164 };
8165
8166 setinputfile(fullname, 1);
8167 commandname = fullname;
8168 cmdloop(0);
8169 popfile();
8170
8171 if (argc > 2) {
8172 freeparam(&shellparam);
8173 shellparam = saveparam;
8174 };
8175
8176 popstackmark(&smark);
8177 }
8178 return exitstatus;
8179}
8180
8181
8182static int
8183exitcmd(int argc, char **argv)
8184{
8185 if (stoppedjobs())
8186 return 0;
8187 if (argc > 1)
8188 exitstatus = number(argv[1]);
8189 exraise(EXEXIT);
8190 /* NOTREACHED */
8191}
8192
8193/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
8194
8195/*
8196 * Same for malloc, realloc, but returns an error when out of space.
8197 */
8198
8199static pointer
8200ckrealloc(pointer p, size_t nbytes)
8201{
8202 p = realloc(p, nbytes);
8203 if (p == NULL)
8204 error(bb_msg_memory_exhausted);
8205 return p;
8206}
8207
8208static pointer
8209ckmalloc(size_t nbytes)
8210{
8211 return ckrealloc(NULL, nbytes);
8212}
8213
8214/*
8215 * Make a copy of a string in safe storage.
8216 */
8217
8218static char *
8219savestr(const char *s)
8220{
8221 char *p = strdup(s);
8222 if (!p)
8223 error(bb_msg_memory_exhausted);
8224 return p;
8225}
8226
8227
8228/*
8229 * Parse trees for commands are allocated in lifo order, so we use a stack
8230 * to make this more efficient, and also to avoid all sorts of exception
8231 * handling code to handle interrupts in the middle of a parse.
8232 *
8233 * The size 504 was chosen because the Ultrix malloc handles that size
8234 * well.
8235 */
8236
8237
8238static pointer
8239stalloc(size_t nbytes)
8240{
8241 char *p;
8242 size_t aligned;
8243
8244 aligned = SHELL_ALIGN(nbytes);
8245 if (aligned > stacknleft) {
8246 size_t len;
8247 size_t blocksize;
8248 struct stack_block *sp;
8249
8250 blocksize = aligned;
8251 if (blocksize < MINSIZE)
8252 blocksize = MINSIZE;
8253 len = sizeof(struct stack_block) - MINSIZE + blocksize;
8254 if (len < blocksize)
8255 error(bb_msg_memory_exhausted);
8256 INTOFF;
8257 sp = ckmalloc(len);
8258 sp->prev = stackp;
8259 stacknxt = sp->space;
8260 stacknleft = blocksize;
8261 sstrend = stacknxt + blocksize;
8262 stackp = sp;
8263 INTON;
8264 }
8265 p = stacknxt;
8266 stacknxt += aligned;
8267 stacknleft -= aligned;
8268 return p;
8269}
8270
8271
8272void
8273stunalloc(pointer p)
8274{
8275#ifdef DEBUG
8276 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
8277 write(2, "stunalloc\n", 10);
8278 abort();
8279 }
8280#endif
8281 stacknleft += stacknxt - (char *)p;
8282 stacknxt = p;
8283}
8284
8285
8286void
8287setstackmark(struct stackmark *mark)
8288{
8289 mark->stackp = stackp;
8290 mark->stacknxt = stacknxt;
8291 mark->stacknleft = stacknleft;
8292 mark->marknext = markp;
8293 markp = mark;
8294}
8295
8296
8297void
8298popstackmark(struct stackmark *mark)
8299{
8300 struct stack_block *sp;
8301
8302 INTOFF;
8303 markp = mark->marknext;
8304 while (stackp != mark->stackp) {
8305 sp = stackp;
8306 stackp = sp->prev;
8307 ckfree(sp);
8308 }
8309 stacknxt = mark->stacknxt;
8310 stacknleft = mark->stacknleft;
8311 sstrend = mark->stacknxt + mark->stacknleft;
8312 INTON;
8313}
8314
8315
8316/*
8317 * When the parser reads in a string, it wants to stick the string on the
8318 * stack and only adjust the stack pointer when it knows how big the
8319 * string is. Stackblock (defined in stack.h) returns a pointer to a block
8320 * of space on top of the stack and stackblocklen returns the length of
8321 * this block. Growstackblock will grow this space by at least one byte,
8322 * possibly moving it (like realloc). Grabstackblock actually allocates the
8323 * part of the block that has been used.
8324 */
8325
8326void
8327growstackblock(void)
8328{
8329 size_t newlen;
8330
8331 newlen = stacknleft * 2;
8332 if (newlen < stacknleft)
8333 error(bb_msg_memory_exhausted);
8334 if (newlen < 128)
8335 newlen += 128;
8336
8337 if (stacknxt == stackp->space && stackp != &stackbase) {
8338 struct stack_block *oldstackp;
8339 struct stackmark *xmark;
8340 struct stack_block *sp;
8341 struct stack_block *prevstackp;
8342 size_t grosslen;
8343
8344 INTOFF;
8345 oldstackp = stackp;
8346 sp = stackp;
8347 prevstackp = sp->prev;
8348 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
8349 sp = ckrealloc((pointer)sp, grosslen);
8350 sp->prev = prevstackp;
8351 stackp = sp;
8352 stacknxt = sp->space;
8353 stacknleft = newlen;
8354 sstrend = sp->space + newlen;
8355
8356 /*
8357 * Stack marks pointing to the start of the old block
8358 * must be relocated to point to the new block
8359 */
8360 xmark = markp;
8361 while (xmark != NULL && xmark->stackp == oldstackp) {
8362 xmark->stackp = stackp;
8363 xmark->stacknxt = stacknxt;
8364 xmark->stacknleft = stacknleft;
8365 xmark = xmark->marknext;
8366 }
8367 INTON;
8368 } else {
8369 char *oldspace = stacknxt;
8370 int oldlen = stacknleft;
8371 char *p = stalloc(newlen);
8372
8373 /* free the space we just allocated */
8374 stacknxt = memcpy(p, oldspace, oldlen);
8375 stacknleft += newlen;
8376 }
8377}
8378
8379static inline void
8380grabstackblock(size_t len)
8381{
8382 len = SHELL_ALIGN(len);
8383 stacknxt += len;
8384 stacknleft -= len;
8385}
8386
8387/*
8388 * The following routines are somewhat easier to use than the above.
8389 * The user declares a variable of type STACKSTR, which may be declared
8390 * to be a register. The macro STARTSTACKSTR initializes things. Then
8391 * the user uses the macro STPUTC to add characters to the string. In
8392 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
8393 * grown as necessary. When the user is done, she can just leave the
8394 * string there and refer to it using stackblock(). Or she can allocate
8395 * the space for it using grabstackstr(). If it is necessary to allow
8396 * someone else to use the stack temporarily and then continue to grow
8397 * the string, the user should use grabstack to allocate the space, and
8398 * then call ungrabstr(p) to return to the previous mode of operation.
8399 *
8400 * USTPUTC is like STPUTC except that it doesn't check for overflow.
8401 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
8402 * is space for at least one character.
8403 */
8404
8405void *
8406growstackstr(void)
8407{
8408 size_t len = stackblocksize();
8409 if (herefd >= 0 && len >= 1024) {
8410 bb_full_write(herefd, stackblock(), len);
8411 return stackblock();
8412 }
8413 growstackblock();
8414 return stackblock() + len;
8415}
8416
8417/*
8418 * Called from CHECKSTRSPACE.
8419 */
8420
8421char *
8422makestrspace(size_t newlen, char *p)
8423{
8424 size_t len = p - stacknxt;
8425 size_t size = stackblocksize();
8426
8427 for (;;) {
8428 size_t nleft;
8429
8430 size = stackblocksize();
8431 nleft = size - len;
8432 if (nleft >= newlen)
8433 break;
8434 growstackblock();
8435 }
8436 return stackblock() + len;
8437}
8438
8439char *
8440stnputs(const char *s, size_t n, char *p)
8441{
8442 p = makestrspace(n, p);
8443 p = mempcpy(p, s, n);
8444 return p;
8445}
8446
8447char *
8448stputs(const char *s, char *p)
8449{
8450 return stnputs(s, strlen(s), p);
8451}
8452
8453/* $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $ */
8454
8455/*
8456 * String functions.
8457 *
8458 * number(s) Convert a string of digits to an integer.
8459 * is_number(s) Return true if s is a string of digits.
8460 */
8461
8462/*
8463 * prefix -- see if pfx is a prefix of string.
8464 */
8465
8466char *
8467prefix(const char *string, const char *pfx)
8468{
8469 while (*pfx) {
8470 if (*pfx++ != *string++)
8471 return 0;
8472 }
8473 return (char *) string;
8474}
8475
8476
8477/*
8478 * Convert a string of digits to an integer, printing an error message on
8479 * failure.
8480 */
8481
8482int
8483number(const char *s)
8484{
8485
8486 if (! is_number(s))
8487 error(illnum, s);
8488 return atoi(s);
8489}
8490
8491
8492/*
8493 * Check for a valid number. This should be elsewhere.
8494 */
8495
8496int
8497is_number(const char *p)
8498{
8499 do {
8500 if (! is_digit(*p))
8501 return 0;
8502 } while (*++p != '\0');
8503 return 1;
8504}
8505
8506
8507/*
8508 * Produce a possibly single quoted string suitable as input to the shell.
8509 * The return string is allocated on the stack.
8510 */
8511
8512char *
8513single_quote(const char *s) {
8514 char *p;
8515
8516 STARTSTACKSTR(p);
8517
8518 do {
8519 char *q;
8520 size_t len;
8521
8522 len = strchrnul(s, '\'') - s;
8523
8524 q = p = makestrspace(len + 3, p);
8525
8526 *q++ = '\'';
8527 q = mempcpy(q, s, len);
8528 *q++ = '\'';
8529 s += len;
8530
8531 STADJUST(q - p, p);
8532
8533 len = strspn(s, "'");
8534 if (!len)
8535 break;
8536
8537 q = p = makestrspace(len + 3, p);
8538
8539 *q++ = '"';
8540 q = mempcpy(q, s, len);
8541 *q++ = '"';
8542 s += len;
8543
8544 STADJUST(q - p, p);
8545 } while (*s);
8546
8547 USTPUTC(0, p);
8548
8549 return stackblock();
8550}
8551
8552/*
8553 * Like strdup but works with the ash stack.
8554 */
8555
8556char *
8557sstrdup(const char *p)
8558{
8559 size_t len = strlen(p) + 1;
8560 return memcpy(stalloc(len), p, len);
8561}
8562
8563
8564static void
8565calcsize(union node *n)
8566{
8567 if (n == NULL)
8568 return;
8569 funcblocksize += nodesize[n->type];
8570 switch (n->type) {
8571 case NCMD:
8572 calcsize(n->ncmd.redirect);
8573 calcsize(n->ncmd.args);
8574 calcsize(n->ncmd.assign);
8575 break;
8576 case NPIPE:
8577 sizenodelist(n->npipe.cmdlist);
8578 break;
8579 case NREDIR:
8580 case NBACKGND:
8581 case NSUBSHELL:
8582 calcsize(n->nredir.redirect);
8583 calcsize(n->nredir.n);
8584 break;
8585 case NAND:
8586 case NOR:
8587 case NSEMI:
8588 case NWHILE:
8589 case NUNTIL:
8590 calcsize(n->nbinary.ch2);
8591 calcsize(n->nbinary.ch1);
8592 break;
8593 case NIF:
8594 calcsize(n->nif.elsepart);
8595 calcsize(n->nif.ifpart);
8596 calcsize(n->nif.test);
8597 break;
8598 case NFOR:
8599 funcstringsize += strlen(n->nfor.var) + 1;
8600 calcsize(n->nfor.body);
8601 calcsize(n->nfor.args);
8602 break;
8603 case NCASE:
8604 calcsize(n->ncase.cases);
8605 calcsize(n->ncase.expr);
8606 break;
8607 case NCLIST:
8608 calcsize(n->nclist.body);
8609 calcsize(n->nclist.pattern);
8610 calcsize(n->nclist.next);
8611 break;
8612 case NDEFUN:
8613 case NARG:
8614 sizenodelist(n->narg.backquote);
8615 funcstringsize += strlen(n->narg.text) + 1;
8616 calcsize(n->narg.next);
8617 break;
8618 case NTO:
8619 case NCLOBBER:
8620 case NFROM:
8621 case NFROMTO:
8622 case NAPPEND:
8623 calcsize(n->nfile.fname);
8624 calcsize(n->nfile.next);
8625 break;
8626 case NTOFD:
8627 case NFROMFD:
8628 calcsize(n->ndup.vname);
8629 calcsize(n->ndup.next);
8630 break;
8631 case NHERE:
8632 case NXHERE:
8633 calcsize(n->nhere.doc);
8634 calcsize(n->nhere.next);
8635 break;
8636 case NNOT:
8637 calcsize(n->nnot.com);
8638 break;
8639 };
8640}
8641
8642
8643static void
8644sizenodelist(struct nodelist *lp)
8645{
8646 while (lp) {
8647 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8648 calcsize(lp->n);
8649 lp = lp->next;
8650 }
8651}
8652
8653
8654static union node *
8655copynode(union node *n)
8656{
8657 union node *new;
8658
8659 if (n == NULL)
8660 return NULL;
8661 new = funcblock;
8662 funcblock = (char *) funcblock + nodesize[n->type];
8663 switch (n->type) {
8664 case NCMD:
8665 new->ncmd.redirect = copynode(n->ncmd.redirect);
8666 new->ncmd.args = copynode(n->ncmd.args);
8667 new->ncmd.assign = copynode(n->ncmd.assign);
8668 break;
8669 case NPIPE:
8670 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8671 new->npipe.backgnd = n->npipe.backgnd;
8672 break;
8673 case NREDIR:
8674 case NBACKGND:
8675 case NSUBSHELL:
8676 new->nredir.redirect = copynode(n->nredir.redirect);
8677 new->nredir.n = copynode(n->nredir.n);
8678 break;
8679 case NAND:
8680 case NOR:
8681 case NSEMI:
8682 case NWHILE:
8683 case NUNTIL:
8684 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8685 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8686 break;
8687 case NIF:
8688 new->nif.elsepart = copynode(n->nif.elsepart);
8689 new->nif.ifpart = copynode(n->nif.ifpart);
8690 new->nif.test = copynode(n->nif.test);
8691 break;
8692 case NFOR:
8693 new->nfor.var = nodesavestr(n->nfor.var);
8694 new->nfor.body = copynode(n->nfor.body);
8695 new->nfor.args = copynode(n->nfor.args);
8696 break;
8697 case NCASE:
8698 new->ncase.cases = copynode(n->ncase.cases);
8699 new->ncase.expr = copynode(n->ncase.expr);
8700 break;
8701 case NCLIST:
8702 new->nclist.body = copynode(n->nclist.body);
8703 new->nclist.pattern = copynode(n->nclist.pattern);
8704 new->nclist.next = copynode(n->nclist.next);
8705 break;
8706 case NDEFUN:
8707 case NARG:
8708 new->narg.backquote = copynodelist(n->narg.backquote);
8709 new->narg.text = nodesavestr(n->narg.text);
8710 new->narg.next = copynode(n->narg.next);
8711 break;
8712 case NTO:
8713 case NCLOBBER:
8714 case NFROM:
8715 case NFROMTO:
8716 case NAPPEND:
8717 new->nfile.fname = copynode(n->nfile.fname);
8718 new->nfile.fd = n->nfile.fd;
8719 new->nfile.next = copynode(n->nfile.next);
8720 break;
8721 case NTOFD:
8722 case NFROMFD:
8723 new->ndup.vname = copynode(n->ndup.vname);
8724 new->ndup.dupfd = n->ndup.dupfd;
8725 new->ndup.fd = n->ndup.fd;
8726 new->ndup.next = copynode(n->ndup.next);
8727 break;
8728 case NHERE:
8729 case NXHERE:
8730 new->nhere.doc = copynode(n->nhere.doc);
8731 new->nhere.fd = n->nhere.fd;
8732 new->nhere.next = copynode(n->nhere.next);
8733 break;
8734 case NNOT:
8735 new->nnot.com = copynode(n->nnot.com);
8736 break;
8737 };
8738 new->type = n->type;
8739 return new;
8740}
8741
8742
8743static struct nodelist *
8744copynodelist(struct nodelist *lp)
8745{
8746 struct nodelist *start;
8747 struct nodelist **lpp;
8748
8749 lpp = &start;
8750 while (lp) {
8751 *lpp = funcblock;
8752 funcblock = (char *) funcblock +
8753 SHELL_ALIGN(sizeof(struct nodelist));
8754 (*lpp)->n = copynode(lp->n);
8755 lp = lp->next;
8756 lpp = &(*lpp)->next;
8757 }
8758 *lpp = NULL;
8759 return start;
8760}
8761
8762
8763static char *
8764nodesavestr(char *s)
8765{
8766 char *rtn = funcstring;
8767
8768 funcstring = stpcpy(funcstring, s) + 1;
8769 return rtn;
8770}
8771
8772
8773/*
8774 * Free a parse tree.
8775 */
8776
8777static void
8778freefunc(struct funcnode *f)
8779{
8780 if (f && --f->count < 0)
8781 ckfree(f);
8782}
8783
8784
8785static void options(int);
8786static void setoption(int, int);
8787
8788
8789/*
8790 * Process the shell command line arguments.
8791 */
8792
8793void
8794procargs(int argc, char **argv)
8795{
8796 int i;
8797 const char *xminusc;
8798 char **xargv;
8799
8800 xargv = argv;
8801 arg0 = xargv[0];
8802 if (argc > 0)
8803 xargv++;
8804 for (i = 0; i < NOPTS; i++)
8805 optlist[i] = 2;
8806 argptr = xargv;
8807 options(1);
8808 xargv = argptr;
8809 xminusc = minusc;
8810 if (*xargv == NULL) {
8811 if (xminusc)
8812 error("-c requires an argument");
8813 sflag = 1;
8814 }
8815 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8816 iflag = 1;
8817 if (mflag == 2)
8818 mflag = iflag;
8819 for (i = 0; i < NOPTS; i++)
8820 if (optlist[i] == 2)
8821 optlist[i] = 0;
8822#if DEBUG == 2
8823 debug = 1;
8824#endif
8825 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8826 if (xminusc) {
8827 minusc = *xargv++;
8828 if (*xargv)
8829 goto setarg0;
8830 } else if (!sflag) {
8831 setinputfile(*xargv, 0);
8832setarg0:
8833 arg0 = *xargv++;
8834 commandname = arg0;
8835 }
8836
8837 shellparam.p = xargv;
8838#ifdef CONFIG_ASH_GETOPTS
8839 shellparam.optind = 1;
8840 shellparam.optoff = -1;
8841#endif
8842 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8843 while (*xargv) {
8844 shellparam.nparam++;
8845 xargv++;
8846 }
8847 optschanged();
8848}
8849
8850
8851void
8852optschanged(void)
8853{
8854#ifdef DEBUG
8855 opentrace();
8856#endif
8857 setinteractive(iflag);
8858 setjobctl(mflag);
8859}
8860
8861static inline void
8862minus_o(char *name, int val)
8863{
8864 int i;
8865
8866 if (name == NULL) {
8867 out1str("Current option settings\n");
8868 for (i = 0; i < NOPTS; i++)
8869 out1fmt("%-16s%s\n", optnames(i),
8870 optlist[i] ? "on" : "off");
8871 } else {
8872 for (i = 0; i < NOPTS; i++)
8873 if (equal(name, optnames(i))) {
8874 optlist[i] = val;
8875 return;
8876 }
8877 error("Illegal option -o %s", name);
8878 }
8879}
8880
8881/*
8882 * Process shell options. The global variable argptr contains a pointer
8883 * to the argument list; we advance it past the options.
8884 */
8885
8886static void
8887options(int cmdline)
8888{
8889 char *p;
8890 int val;
8891 int c;
8892
8893 if (cmdline)
8894 minusc = NULL;
8895 while ((p = *argptr) != NULL) {
8896 argptr++;
8897 if ((c = *p++) == '-') {
8898 val = 1;
8899 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8900 if (!cmdline) {
8901 /* "-" means turn off -x and -v */
8902 if (p[0] == '\0')
8903 xflag = vflag = 0;
8904 /* "--" means reset params */
8905 else if (*argptr == NULL)
8906 setparam(argptr);
8907 }
8908 break; /* "-" or "--" terminates options */
8909 }
8910 } else if (c == '+') {
8911 val = 0;
8912 } else {
8913 argptr--;
8914 break;
8915 }
8916 while ((c = *p++) != '\0') {
8917 if (c == 'c' && cmdline) {
8918 minusc = p; /* command is after shell args*/
8919 } else if (c == 'o') {
8920 minus_o(*argptr, val);
8921 if (*argptr)
8922 argptr++;
8923 } else if (cmdline && (c == '-')) { // long options
8924 if (strcmp(p, "login") == 0)
8925 isloginsh = 1;
8926 break;
8927 } else {
8928 setoption(c, val);
8929 }
8930 }
8931 }
8932}
8933
8934
8935static void
8936setoption(int flag, int val)
8937{
8938 int i;
8939
8940 for (i = 0; i < NOPTS; i++)
8941 if (optletters(i) == flag) {
8942 optlist[i] = val;
8943 return;
8944 }
8945 error("Illegal option -%c", flag);
8946 /* NOTREACHED */
8947}
8948
8949
8950
8951/*
8952 * Set the shell parameters.
8953 */
8954
8955void
8956setparam(char **argv)
8957{
8958 char **newparam;
8959 char **ap;
8960 int nparam;
8961
8962 for (nparam = 0 ; argv[nparam] ; nparam++);
8963 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
8964 while (*argv) {
8965 *ap++ = savestr(*argv++);
8966 }
8967 *ap = NULL;
8968 freeparam(&shellparam);
8969 shellparam.malloc = 1;
8970 shellparam.nparam = nparam;
8971 shellparam.p = newparam;
8972#ifdef CONFIG_ASH_GETOPTS
8973 shellparam.optind = 1;
8974 shellparam.optoff = -1;
8975#endif
8976}
8977
8978
8979/*
8980 * Free the list of positional parameters.
8981 */
8982
8983void
8984freeparam(volatile struct shparam *param)
8985{
8986 char **ap;
8987
8988 if (param->malloc) {
8989 for (ap = param->p ; *ap ; ap++)
8990 ckfree(*ap);
8991 ckfree(param->p);
8992 }
8993}
8994
8995
8996
8997/*
8998 * The shift builtin command.
8999 */
9000
9001int
9002shiftcmd(int argc, char **argv)
9003{
9004 int n;
9005 char **ap1, **ap2;
9006
9007 n = 1;
9008 if (argc > 1)
9009 n = number(argv[1]);
9010 if (n > shellparam.nparam)
9011 error("can't shift that many");
9012 INTOFF;
9013 shellparam.nparam -= n;
9014 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
9015 if (shellparam.malloc)
9016 ckfree(*ap1);
9017 }
9018 ap2 = shellparam.p;
9019 while ((*ap2++ = *ap1++) != NULL);
9020#ifdef CONFIG_ASH_GETOPTS
9021 shellparam.optind = 1;
9022 shellparam.optoff = -1;
9023#endif
9024 INTON;
9025 return 0;
9026}
9027
9028
9029
9030/*
9031 * The set command builtin.
9032 */
9033
9034int
9035setcmd(int argc, char **argv)
9036{
9037 if (argc == 1)
9038 return showvars(nullstr, 0, VUNSET);
9039 INTOFF;
9040 options(0);
9041 optschanged();
9042 if (*argptr != NULL) {
9043 setparam(argptr);
9044 }
9045 INTON;
9046 return 0;
9047}
9048
9049
9050#ifdef CONFIG_ASH_GETOPTS
9051static void
9052getoptsreset(value)
9053 const char *value;
9054{
9055 shellparam.optind = number(value);
9056 shellparam.optoff = -1;
9057}
9058#endif
9059
9060#ifdef CONFIG_LOCALE_SUPPORT
9061static void change_lc_all(const char *value)
9062{
9063 if (value != 0 && *value != 0)
9064 setlocale(LC_ALL, value);
9065}
9066
9067static void change_lc_ctype(const char *value)
9068{
9069 if (value != 0 && *value != 0)
9070 setlocale(LC_CTYPE, value);
9071}
9072
9073#endif
9074
9075#ifdef CONFIG_ASH_RANDOM_SUPPORT
9076/* Roughly copied from bash.. */
9077static void change_random(const char *value)
9078{
9079 if(value == NULL) {
9080 /* "get", generate */
9081 char buf[16];
9082
9083 rseed = rseed * 1103515245 + 12345;
9084 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9085 /* set without recursion */
9086 setvar(vrandom.text, buf, VNOFUNC);
9087 vrandom.flags &= ~VNOFUNC;
9088 } else {
9089 /* set/reset */
9090 rseed = strtoul(value, (char **)NULL, 10);
9091 }
9092}
9093#endif
9094
9095
9096#ifdef CONFIG_ASH_GETOPTS
9097static int
9098getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
9099{
9100 char *p, *q;
9101 char c = '?';
9102 int done = 0;
9103 int err = 0;
9104 char s[12];
9105 char **optnext;
9106
9107 if(*param_optind < 1)
9108 return 1;
9109 optnext = optfirst + *param_optind - 1;
9110
9111 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
9112 p = NULL;
9113 else
9114 p = optnext[-1] + *optoff;
9115 if (p == NULL || *p == '\0') {
9116 /* Current word is done, advance */
9117 p = *optnext;
9118 if (p == NULL || *p != '-' || *++p == '\0') {
9119atend:
9120 p = NULL;
9121 done = 1;
9122 goto out;
9123 }
9124 optnext++;
9125 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9126 goto atend;
9127 }
9128
9129 c = *p++;
9130 for (q = optstr; *q != c; ) {
9131 if (*q == '\0') {
9132 if (optstr[0] == ':') {
9133 s[0] = c;
9134 s[1] = '\0';
9135 err |= setvarsafe("OPTARG", s, 0);
9136 } else {
9137 fprintf(stderr, "Illegal option -%c\n", c);
9138 (void) unsetvar("OPTARG");
9139 }
9140 c = '?';
9141 goto out;
9142 }
9143 if (*++q == ':')
9144 q++;
9145 }
9146
9147 if (*++q == ':') {
9148 if (*p == '\0' && (p = *optnext) == NULL) {
9149 if (optstr[0] == ':') {
9150 s[0] = c;
9151 s[1] = '\0';
9152 err |= setvarsafe("OPTARG", s, 0);
9153 c = ':';
9154 } else {
9155 fprintf(stderr, "No arg for -%c option\n", c);
9156 (void) unsetvar("OPTARG");
9157 c = '?';
9158 }
9159 goto out;
9160 }
9161
9162 if (p == *optnext)
9163 optnext++;
9164 err |= setvarsafe("OPTARG", p, 0);
9165 p = NULL;
9166 } else
9167 err |= setvarsafe("OPTARG", nullstr, 0);
9168
9169out:
9170 *optoff = p ? p - *(optnext - 1) : -1;
9171 *param_optind = optnext - optfirst + 1;
9172 fmtstr(s, sizeof(s), "%d", *param_optind);
9173 err |= setvarsafe("OPTIND", s, VNOFUNC);
9174 s[0] = c;
9175 s[1] = '\0';
9176 err |= setvarsafe(optvar, s, 0);
9177 if (err) {
9178 *param_optind = 1;
9179 *optoff = -1;
9180 flushall();
9181 exraise(EXERROR);
9182 }
9183 return done;
9184}
9185
9186/*
9187 * The getopts builtin. Shellparam.optnext points to the next argument
9188 * to be processed. Shellparam.optptr points to the next character to
9189 * be processed in the current argument. If shellparam.optnext is NULL,
9190 * then it's the first time getopts has been called.
9191 */
9192
9193int
9194getoptscmd(int argc, char **argv)
9195{
9196 char **optbase;
9197
9198 if (argc < 3)
9199 error("Usage: getopts optstring var [arg]");
9200 else if (argc == 3) {
9201 optbase = shellparam.p;
9202 if (shellparam.optind > shellparam.nparam + 1) {
9203 shellparam.optind = 1;
9204 shellparam.optoff = -1;
9205 }
9206 }
9207 else {
9208 optbase = &argv[3];
9209 if (shellparam.optind > argc - 2) {
9210 shellparam.optind = 1;
9211 shellparam.optoff = -1;
9212 }
9213 }
9214
9215 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
9216 &shellparam.optoff);
9217}
9218#endif /* CONFIG_ASH_GETOPTS */
9219
9220/*
9221 * XXX - should get rid of. have all builtins use getopt(3). the
9222 * library getopt must have the BSD extension static variable "optreset"
9223 * otherwise it can't be used within the shell safely.
9224 *
9225 * Standard option processing (a la getopt) for builtin routines. The
9226 * only argument that is passed to nextopt is the option string; the
9227 * other arguments are unnecessary. It return the character, or '\0' on
9228 * end of input.
9229 */
9230
9231static int
9232nextopt(const char *optstring)
9233{
9234 char *p;
9235 const char *q;
9236 char c;
9237
9238 if ((p = optptr) == NULL || *p == '\0') {
9239 p = *argptr;
9240 if (p == NULL || *p != '-' || *++p == '\0')
9241 return '\0';
9242 argptr++;
9243 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9244 return '\0';
9245 }
9246 c = *p++;
9247 for (q = optstring ; *q != c ; ) {
9248 if (*q == '\0')
9249 error("Illegal option -%c", c);
9250 if (*++q == ':')
9251 q++;
9252 }
9253 if (*++q == ':') {
9254 if (*p == '\0' && (p = *argptr++) == NULL)
9255 error("No arg for -%c option", c);
9256 optionarg = p;
9257 p = NULL;
9258 }
9259 optptr = p;
9260 return c;
9261}
9262
9263
9264/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
9265
9266void
9267outstr(const char *p, FILE *file)
9268{
9269 INTOFF;
9270 fputs(p, file);
9271 INTON;
9272}
9273
9274void
9275flushall(void)
9276{
9277 INTOFF;
9278 fflush(stdout);
9279 fflush(stderr);
9280 INTON;
9281}
9282
9283void
9284flusherr(void)
9285{
9286 INTOFF;
9287 fflush(stderr);
9288 INTON;
9289}
9290
9291static void
9292outcslow(int c, FILE *dest)
9293{
9294 INTOFF;
9295 putc(c, dest);
9296 fflush(dest);
9297 INTON;
9298}
9299
9300
9301static int
9302out1fmt(const char *fmt, ...)
9303{
9304 va_list ap;
9305 int r;
9306
9307 INTOFF;
9308 va_start(ap, fmt);
9309 r = vprintf(fmt, ap);
9310 va_end(ap);
9311 INTON;
9312 return r;
9313}
9314
9315
9316int
9317fmtstr(char *outbuf, size_t length, const char *fmt, ...)
9318{
9319 va_list ap;
9320 int ret;
9321
9322 va_start(ap, fmt);
9323 INTOFF;
9324 ret = vsnprintf(outbuf, length, fmt, ap);
9325 va_end(ap);
9326 INTON;
9327 return ret;
9328}
9329
9330
9331
9332/* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */
9333
9334
9335/*
9336 * Shell command parser.
9337 */
9338
9339#define EOFMARKLEN 79
9340
9341
9342struct heredoc {
9343 struct heredoc *next; /* next here document in list */
9344 union node *here; /* redirection node */
9345 char *eofmark; /* string indicating end of input */
9346 int striptabs; /* if set, strip leading tabs */
9347};
9348
9349
9350
9351static struct heredoc *heredoclist; /* list of here documents to read */
9352
9353
9354static union node *list(int);
9355static union node *andor(void);
9356static union node *pipeline(void);
9357static union node *command(void);
9358static union node *simplecmd(void);
9359static union node *makename(void);
9360static void parsefname(void);
9361static void parseheredoc(void);
9362static char peektoken(void);
9363static int readtoken(void);
9364static int xxreadtoken(void);
9365static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
9366static int noexpand(char *);
9367static void synexpect(int) __attribute__((__noreturn__));
9368static void synerror(const char *) __attribute__((__noreturn__));
9369static void setprompt(int);
9370
9371
9372
9373static inline int
9374isassignment(const char *p)
9375{
9376 const char *q = endofname(p);
9377 if (p == q)
9378 return 0;
9379 return *q == '=';
9380}
9381
9382
9383/*
9384 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9385 * valid parse tree indicating a blank line.)
9386 */
9387
9388union node *
9389parsecmd(int interact)
9390{
9391 int t;
9392
9393 tokpushback = 0;
9394 doprompt = interact;
9395 if (doprompt)
9396 setprompt(doprompt);
9397 needprompt = 0;
9398 t = readtoken();
9399 if (t == TEOF)
9400 return NEOF;
9401 if (t == TNL)
9402 return NULL;
9403 tokpushback++;
9404 return list(1);
9405}
9406
9407
9408static union node *
9409list(int nlflag)
9410{
9411 union node *n1, *n2, *n3;
9412 int tok;
9413
9414 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9415 if (nlflag == 2 && peektoken())
9416 return NULL;
9417 n1 = NULL;
9418 for (;;) {
9419 n2 = andor();
9420 tok = readtoken();
9421 if (tok == TBACKGND) {
9422 if (n2->type == NPIPE) {
9423 n2->npipe.backgnd = 1;
9424 } else {
9425 if (n2->type != NREDIR) {
9426 n3 = stalloc(sizeof(struct nredir));
9427 n3->nredir.n = n2;
9428 n3->nredir.redirect = NULL;
9429 n2 = n3;
9430 }
9431 n2->type = NBACKGND;
9432 }
9433 }
9434 if (n1 == NULL) {
9435 n1 = n2;
9436 }
9437 else {
9438 n3 = (union node *)stalloc(sizeof (struct nbinary));
9439 n3->type = NSEMI;
9440 n3->nbinary.ch1 = n1;
9441 n3->nbinary.ch2 = n2;
9442 n1 = n3;
9443 }
9444 switch (tok) {
9445 case TBACKGND:
9446 case TSEMI:
9447 tok = readtoken();
9448 /* fall through */
9449 case TNL:
9450 if (tok == TNL) {
9451 parseheredoc();
9452 if (nlflag == 1)
9453 return n1;
9454 } else {
9455 tokpushback++;
9456 }
9457 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9458 if (peektoken())
9459 return n1;
9460 break;
9461 case TEOF:
9462 if (heredoclist)
9463 parseheredoc();
9464 else
9465 pungetc(); /* push back EOF on input */
9466 return n1;
9467 default:
9468 if (nlflag == 1)
9469 synexpect(-1);
9470 tokpushback++;
9471 return n1;
9472 }
9473 }
9474}
9475
9476
9477
9478static union node *
9479andor(void)
9480{
9481 union node *n1, *n2, *n3;
9482 int t;
9483
9484 n1 = pipeline();
9485 for (;;) {
9486 if ((t = readtoken()) == TAND) {
9487 t = NAND;
9488 } else if (t == TOR) {
9489 t = NOR;
9490 } else {
9491 tokpushback++;
9492 return n1;
9493 }
9494 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9495 n2 = pipeline();
9496 n3 = (union node *)stalloc(sizeof (struct nbinary));
9497 n3->type = t;
9498 n3->nbinary.ch1 = n1;
9499 n3->nbinary.ch2 = n2;
9500 n1 = n3;
9501 }
9502}
9503
9504
9505
9506static union node *
9507pipeline(void)
9508{
9509 union node *n1, *n2, *pipenode;
9510 struct nodelist *lp, *prev;
9511 int negate;
9512
9513 negate = 0;
9514 TRACE(("pipeline: entered\n"));
9515 if (readtoken() == TNOT) {
9516 negate = !negate;
9517 checkkwd = CHKKWD | CHKALIAS;
9518 } else
9519 tokpushback++;
9520 n1 = command();
9521 if (readtoken() == TPIPE) {
9522 pipenode = (union node *)stalloc(sizeof (struct npipe));
9523 pipenode->type = NPIPE;
9524 pipenode->npipe.backgnd = 0;
9525 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9526 pipenode->npipe.cmdlist = lp;
9527 lp->n = n1;
9528 do {
9529 prev = lp;
9530 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9531 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9532 lp->n = command();
9533 prev->next = lp;
9534 } while (readtoken() == TPIPE);
9535 lp->next = NULL;
9536 n1 = pipenode;
9537 }
9538 tokpushback++;
9539 if (negate) {
9540 n2 = (union node *)stalloc(sizeof (struct nnot));
9541 n2->type = NNOT;
9542 n2->nnot.com = n1;
9543 return n2;
9544 } else
9545 return n1;
9546}
9547
9548
9549
9550static union node *
9551command(void)
9552{
9553 union node *n1, *n2;
9554 union node *ap, **app;
9555 union node *cp, **cpp;
9556 union node *redir, **rpp;
9557 union node **rpp2;
9558 int t;
9559
9560 redir = NULL;
9561 rpp2 = &redir;
9562
9563 switch (readtoken()) {
9564 default:
9565 synexpect(-1);
9566 /* NOTREACHED */
9567 case TIF:
9568 n1 = (union node *)stalloc(sizeof (struct nif));
9569 n1->type = NIF;
9570 n1->nif.test = list(0);
9571 if (readtoken() != TTHEN)
9572 synexpect(TTHEN);
9573 n1->nif.ifpart = list(0);
9574 n2 = n1;
9575 while (readtoken() == TELIF) {
9576 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9577 n2 = n2->nif.elsepart;
9578 n2->type = NIF;
9579 n2->nif.test = list(0);
9580 if (readtoken() != TTHEN)
9581 synexpect(TTHEN);
9582 n2->nif.ifpart = list(0);
9583 }
9584 if (lasttoken == TELSE)
9585 n2->nif.elsepart = list(0);
9586 else {
9587 n2->nif.elsepart = NULL;
9588 tokpushback++;
9589 }
9590 t = TFI;
9591 break;
9592 case TWHILE:
9593 case TUNTIL: {
9594 int got;
9595 n1 = (union node *)stalloc(sizeof (struct nbinary));
9596 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9597 n1->nbinary.ch1 = list(0);
9598 if ((got=readtoken()) != TDO) {
9599TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
9600 synexpect(TDO);
9601 }
9602 n1->nbinary.ch2 = list(0);
9603 t = TDONE;
9604 break;
9605 }
9606 case TFOR:
9607 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9608 synerror("Bad for loop variable");
9609 n1 = (union node *)stalloc(sizeof (struct nfor));
9610 n1->type = NFOR;
9611 n1->nfor.var = wordtext;
9612 checkkwd = CHKKWD | CHKALIAS;
9613 if (readtoken() == TIN) {
9614 app = &ap;
9615 while (readtoken() == TWORD) {
9616 n2 = (union node *)stalloc(sizeof (struct narg));
9617 n2->type = NARG;
9618 n2->narg.text = wordtext;
9619 n2->narg.backquote = backquotelist;
9620 *app = n2;
9621 app = &n2->narg.next;
9622 }
9623 *app = NULL;
9624 n1->nfor.args = ap;
9625 if (lasttoken != TNL && lasttoken != TSEMI)
9626 synexpect(-1);
9627 } else {
9628 n2 = (union node *)stalloc(sizeof (struct narg));
9629 n2->type = NARG;
9630 n2->narg.text = (char *)dolatstr;
9631 n2->narg.backquote = NULL;
9632 n2->narg.next = NULL;
9633 n1->nfor.args = n2;
9634 /*
9635 * Newline or semicolon here is optional (but note
9636 * that the original Bourne shell only allowed NL).
9637 */
9638 if (lasttoken != TNL && lasttoken != TSEMI)
9639 tokpushback++;
9640 }
9641 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9642 if (readtoken() != TDO)
9643 synexpect(TDO);
9644 n1->nfor.body = list(0);
9645 t = TDONE;
9646 break;
9647 case TCASE:
9648 n1 = (union node *)stalloc(sizeof (struct ncase));
9649 n1->type = NCASE;
9650 if (readtoken() != TWORD)
9651 synexpect(TWORD);
9652 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9653 n2->type = NARG;
9654 n2->narg.text = wordtext;
9655 n2->narg.backquote = backquotelist;
9656 n2->narg.next = NULL;
9657 do {
9658 checkkwd = CHKKWD | CHKALIAS;
9659 } while (readtoken() == TNL);
9660 if (lasttoken != TIN)
9661 synexpect(TIN);
9662 cpp = &n1->ncase.cases;
9663next_case:
9664 checkkwd = CHKNL | CHKKWD;
9665 t = readtoken();
9666 while(t != TESAC) {
9667 if (lasttoken == TLP)
9668 readtoken();
9669 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9670 cp->type = NCLIST;
9671 app = &cp->nclist.pattern;
9672 for (;;) {
9673 *app = ap = (union node *)stalloc(sizeof (struct narg));
9674 ap->type = NARG;
9675 ap->narg.text = wordtext;
9676 ap->narg.backquote = backquotelist;
9677 if (readtoken() != TPIPE)
9678 break;
9679 app = &ap->narg.next;
9680 readtoken();
9681 }
9682 ap->narg.next = NULL;
9683 if (lasttoken != TRP)
9684 synexpect(TRP);
9685 cp->nclist.body = list(2);
9686
9687 cpp = &cp->nclist.next;
9688
9689 checkkwd = CHKNL | CHKKWD;
9690 if ((t = readtoken()) != TESAC) {
9691 if (t != TENDCASE)
9692 synexpect(TENDCASE);
9693 else
9694 goto next_case;
9695 }
9696 }
9697 *cpp = NULL;
9698 goto redir;
9699 case TLP:
9700 n1 = (union node *)stalloc(sizeof (struct nredir));
9701 n1->type = NSUBSHELL;
9702 n1->nredir.n = list(0);
9703 n1->nredir.redirect = NULL;
9704 t = TRP;
9705 break;
9706 case TBEGIN:
9707 n1 = list(0);
9708 t = TEND;
9709 break;
9710 case TWORD:
9711 case TREDIR:
9712 tokpushback++;
9713 return simplecmd();
9714 }
9715
9716 if (readtoken() != t)
9717 synexpect(t);
9718
9719redir:
9720 /* Now check for redirection which may follow command */
9721 checkkwd = CHKKWD | CHKALIAS;
9722 rpp = rpp2;
9723 while (readtoken() == TREDIR) {
9724 *rpp = n2 = redirnode;
9725 rpp = &n2->nfile.next;
9726 parsefname();
9727 }
9728 tokpushback++;
9729 *rpp = NULL;
9730 if (redir) {
9731 if (n1->type != NSUBSHELL) {
9732 n2 = (union node *)stalloc(sizeof (struct nredir));
9733 n2->type = NREDIR;
9734 n2->nredir.n = n1;
9735 n1 = n2;
9736 }
9737 n1->nredir.redirect = redir;
9738 }
9739
9740 return n1;
9741}
9742
9743
9744static union node *
9745simplecmd(void) {
9746 union node *args, **app;
9747 union node *n = NULL;
9748 union node *vars, **vpp;
9749 union node **rpp, *redir;
9750 int savecheckkwd;
9751
9752 args = NULL;
9753 app = &args;
9754 vars = NULL;
9755 vpp = &vars;
9756 redir = NULL;
9757 rpp = &redir;
9758
9759 savecheckkwd = CHKALIAS;
9760 for (;;) {
9761 checkkwd = savecheckkwd;
9762 switch (readtoken()) {
9763 case TWORD:
9764 n = (union node *)stalloc(sizeof (struct narg));
9765 n->type = NARG;
9766 n->narg.text = wordtext;
9767 n->narg.backquote = backquotelist;
9768 if (savecheckkwd && isassignment(wordtext)) {
9769 *vpp = n;
9770 vpp = &n->narg.next;
9771 } else {
9772 *app = n;
9773 app = &n->narg.next;
9774 savecheckkwd = 0;
9775 }
9776 break;
9777 case TREDIR:
9778 *rpp = n = redirnode;
9779 rpp = &n->nfile.next;
9780 parsefname(); /* read name of redirection file */
9781 break;
9782 case TLP:
9783 if (
9784 args && app == &args->narg.next &&
9785 !vars && !redir
9786 ) {
9787 struct builtincmd *bcmd;
9788 const char *name;
9789
9790 /* We have a function */
9791 if (readtoken() != TRP)
9792 synexpect(TRP);
9793 name = n->narg.text;
9794 if (
9795 !goodname(name) || (
9796 (bcmd = find_builtin(name)) &&
9797 IS_BUILTIN_SPECIAL(bcmd)
9798 )
9799 )
9800 synerror("Bad function name");
9801 n->type = NDEFUN;
9802 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9803 n->narg.next = command();
9804 return n;
9805 }
9806 /* fall through */
9807 default:
9808 tokpushback++;
9809 goto out;
9810 }
9811 }
9812out:
9813 *app = NULL;
9814 *vpp = NULL;
9815 *rpp = NULL;
9816 n = (union node *)stalloc(sizeof (struct ncmd));
9817 n->type = NCMD;
9818 n->ncmd.args = args;
9819 n->ncmd.assign = vars;
9820 n->ncmd.redirect = redir;
9821 return n;
9822}
9823
9824static union node *
9825makename(void)
9826{
9827 union node *n;
9828
9829 n = (union node *)stalloc(sizeof (struct narg));
9830 n->type = NARG;
9831 n->narg.next = NULL;
9832 n->narg.text = wordtext;
9833 n->narg.backquote = backquotelist;
9834 return n;
9835}
9836
9837void fixredir(union node *n, const char *text, int err)
9838{
9839 TRACE(("Fix redir %s %d\n", text, err));
9840 if (!err)
9841 n->ndup.vname = NULL;
9842
9843 if (is_digit(text[0]) && text[1] == '\0')
9844 n->ndup.dupfd = digit_val(text[0]);
9845 else if (text[0] == '-' && text[1] == '\0')
9846 n->ndup.dupfd = -1;
9847 else {
9848
9849 if (err)
9850 synerror("Bad fd number");
9851 else
9852 n->ndup.vname = makename();
9853 }
9854}
9855
9856
9857static void
9858parsefname(void)
9859{
9860 union node *n = redirnode;
9861
9862 if (readtoken() != TWORD)
9863 synexpect(-1);
9864 if (n->type == NHERE) {
9865 struct heredoc *here = heredoc;
9866 struct heredoc *p;
9867 int i;
9868
9869 if (quoteflag == 0)
9870 n->type = NXHERE;
9871 TRACE(("Here document %d\n", n->type));
9872 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9873 synerror("Illegal eof marker for << redirection");
9874 rmescapes(wordtext);
9875 here->eofmark = wordtext;
9876 here->next = NULL;
9877 if (heredoclist == NULL)
9878 heredoclist = here;
9879 else {
9880 for (p = heredoclist ; p->next ; p = p->next);
9881 p->next = here;
9882 }
9883 } else if (n->type == NTOFD || n->type == NFROMFD) {
9884 fixredir(n, wordtext, 0);
9885 } else {
9886 n->nfile.fname = makename();
9887 }
9888}
9889
9890
9891/*
9892 * Input any here documents.
9893 */
9894
9895static void
9896parseheredoc(void)
9897{
9898 struct heredoc *here;
9899 union node *n;
9900
9901 here = heredoclist;
9902 heredoclist = 0;
9903
9904 while (here) {
9905 if (needprompt) {
9906 setprompt(2);
9907 needprompt = 0;
9908 }
9909 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
9910 here->eofmark, here->striptabs);
9911 n = (union node *)stalloc(sizeof (struct narg));
9912 n->narg.type = NARG;
9913 n->narg.next = NULL;
9914 n->narg.text = wordtext;
9915 n->narg.backquote = backquotelist;
9916 here->here->nhere.doc = n;
9917 here = here->next;
9918 }
9919}
9920
9921static char peektoken(void)
9922{
9923 int t;
9924
9925 t = readtoken();
9926 tokpushback++;
9927 return tokname_array[t][0];
9928}
9929
9930static int
9931readtoken(void)
9932{
9933 int t;
9934#ifdef DEBUG
9935 int alreadyseen = tokpushback;
9936#endif
9937
9938#ifdef CONFIG_ASH_ALIAS
9939top:
9940#endif
9941
9942 t = xxreadtoken();
9943
9944 /*
9945 * eat newlines
9946 */
9947 if (checkkwd & CHKNL) {
9948 while (t == TNL) {
9949 parseheredoc();
9950 t = xxreadtoken();
9951 }
9952 }
9953
9954 if (t != TWORD || quoteflag) {
9955 goto out;
9956 }
9957
9958 /*
9959 * check for keywords
9960 */
9961 if (checkkwd & CHKKWD) {
9962 const char *const *pp;
9963
9964 if ((pp = findkwd(wordtext))) {
9965 lasttoken = t = pp - tokname_array;
9966 TRACE(("keyword %s recognized\n", tokname(t)));
9967 goto out;
9968 }
9969 }
9970
9971 if (checkkwd & CHKALIAS) {
9972#ifdef CONFIG_ASH_ALIAS
9973 struct alias *ap;
9974 if ((ap = lookupalias(wordtext, 1)) != NULL) {
9975 if (*ap->val) {
9976 pushstring(ap->val, ap);
9977 }
9978 goto top;
9979 }
9980#endif
9981 }
9982out:
9983 checkkwd = 0;
9984#ifdef DEBUG
9985 if (!alreadyseen)
9986 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9987 else
9988 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9989#endif
9990 return (t);
9991}
9992
9993
9994/*
9995 * Read the next input token.
9996 * If the token is a word, we set backquotelist to the list of cmds in
9997 * backquotes. We set quoteflag to true if any part of the word was
9998 * quoted.
9999 * If the token is TREDIR, then we set redirnode to a structure containing
10000 * the redirection.
10001 * In all cases, the variable startlinno is set to the number of the line
10002 * on which the token starts.
10003 *
10004 * [Change comment: here documents and internal procedures]
10005 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10006 * word parsing code into a separate routine. In this case, readtoken
10007 * doesn't need to have any internal procedures, but parseword does.
10008 * We could also make parseoperator in essence the main routine, and
10009 * have parseword (readtoken1?) handle both words and redirection.]
10010 */
10011
10012#define NEW_xxreadtoken
10013#ifdef NEW_xxreadtoken
10014
10015/* singles must be first! */
10016static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
10017
10018static const char xxreadtoken_tokens[] = {
10019 TNL, TLP, TRP, /* only single occurrence allowed */
10020 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10021 TEOF, /* corresponds to trailing nul */
10022 TAND, TOR, TENDCASE, /* if double occurrence */
10023};
10024
10025#define xxreadtoken_doubles \
10026 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10027#define xxreadtoken_singles \
10028 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10029
10030static int xxreadtoken()
10031{
10032 int c;
10033
10034 if (tokpushback) {
10035 tokpushback = 0;
10036 return lasttoken;
10037 }
10038 if (needprompt) {
10039 setprompt(2);
10040 needprompt = 0;
10041 }
10042 startlinno = plinno;
10043 for (;;) { /* until token or start of word found */
10044 c = pgetc_macro();
10045
10046 if ((c != ' ') && (c != '\t')
10047#ifdef CONFIG_ASH_ALIAS
10048 && (c != PEOA)
10049#endif
10050 ) {
10051 if (c == '#') {
10052 while ((c = pgetc()) != '\n' && c != PEOF);
10053 pungetc();
10054 } else if (c == '\\') {
10055 if (pgetc() != '\n') {
10056 pungetc();
10057 goto READTOKEN1;
10058 }
10059 startlinno = ++plinno;
10060 if (doprompt)
10061 setprompt(2);
10062 } else {
10063 const char *p
10064 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10065
10066 if (c != PEOF) {
10067 if (c == '\n') {
10068 plinno++;
10069 needprompt = doprompt;
10070 }
10071
10072 p = strchr(xxreadtoken_chars, c);
10073 if (p == NULL) {
10074 READTOKEN1:
10075 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10076 }
10077
10078 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10079 if (pgetc() == *p) { /* double occurrence? */
10080 p += xxreadtoken_doubles + 1;
10081 } else {
10082 pungetc();
10083 }
10084 }
10085 }
10086
10087 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10088 }
10089 }
10090 }
10091}
10092
10093
10094#else
10095#define RETURN(token) return lasttoken = token
10096
10097static int
10098xxreadtoken(void)
10099{
10100 int c;
10101
10102 if (tokpushback) {
10103 tokpushback = 0;
10104 return lasttoken;
10105 }
10106 if (needprompt) {
10107 setprompt(2);
10108 needprompt = 0;
10109 }
10110 startlinno = plinno;
10111 for (;;) { /* until token or start of word found */
10112 c = pgetc_macro();
10113 switch (c) {
10114 case ' ': case '\t':
10115#ifdef CONFIG_ASH_ALIAS
10116 case PEOA:
10117#endif
10118 continue;
10119 case '#':
10120 while ((c = pgetc()) != '\n' && c != PEOF);
10121 pungetc();
10122 continue;
10123 case '\\':
10124 if (pgetc() == '\n') {
10125 startlinno = ++plinno;
10126 if (doprompt)
10127 setprompt(2);
10128 continue;
10129 }
10130 pungetc();
10131 goto breakloop;
10132 case '\n':
10133 plinno++;
10134 needprompt = doprompt;
10135 RETURN(TNL);
10136 case PEOF:
10137 RETURN(TEOF);
10138 case '&':
10139 if (pgetc() == '&')
10140 RETURN(TAND);
10141 pungetc();
10142 RETURN(TBACKGND);
10143 case '|':
10144 if (pgetc() == '|')
10145 RETURN(TOR);
10146 pungetc();
10147 RETURN(TPIPE);
10148 case ';':
10149 if (pgetc() == ';')
10150 RETURN(TENDCASE);
10151 pungetc();
10152 RETURN(TSEMI);
10153 case '(':
10154 RETURN(TLP);
10155 case ')':
10156 RETURN(TRP);
10157 default:
10158 goto breakloop;
10159 }
10160 }
10161breakloop:
10162 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10163#undef RETURN
10164}
10165#endif /* NEW_xxreadtoken */
10166
10167
10168/*
10169 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10170 * is not NULL, read a here document. In the latter case, eofmark is the
10171 * word which marks the end of the document and striptabs is true if
10172 * leading tabs should be stripped from the document. The argument firstc
10173 * is the first character of the input token or document.
10174 *
10175 * Because C does not have internal subroutines, I have simulated them
10176 * using goto's to implement the subroutine linkage. The following macros
10177 * will run code that appears at the end of readtoken1.
10178 */
10179
10180#define CHECKEND() {goto checkend; checkend_return:;}
10181#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10182#define PARSESUB() {goto parsesub; parsesub_return:;}
10183#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10184#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10185#define PARSEARITH() {goto parsearith; parsearith_return:;}
10186
10187static int
10188readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
10189{
10190 int c = firstc;
10191 char *out;
10192 int len;
10193 char line[EOFMARKLEN + 1];
10194 struct nodelist *bqlist;
10195 int quotef;
10196 int dblquote;
10197 int varnest; /* levels of variables expansion */
10198 int arinest; /* levels of arithmetic expansion */
10199 int parenlevel; /* levels of parens in arithmetic */
10200 int dqvarnest; /* levels of variables expansion within double quotes */
10201 int oldstyle;
10202 int prevsyntax; /* syntax before arithmetic */
10203#if __GNUC__
10204 /* Avoid longjmp clobbering */
10205 (void) &out;
10206 (void) &quotef;
10207 (void) &dblquote;
10208 (void) &varnest;
10209 (void) &arinest;
10210 (void) &parenlevel;
10211 (void) &dqvarnest;
10212 (void) &oldstyle;
10213 (void) &prevsyntax;
10214 (void) &syntax;
10215#endif
10216
10217 startlinno = plinno;
10218 dblquote = 0;
10219 if (syntax == DQSYNTAX)
10220 dblquote = 1;
10221 quotef = 0;
10222 bqlist = NULL;
10223 varnest = 0;
10224 arinest = 0;
10225 parenlevel = 0;
10226 dqvarnest = 0;
10227
10228 STARTSTACKSTR(out);
10229 loop: { /* for each line, until end of word */
10230 CHECKEND(); /* set c to PEOF if at end of here document */
10231 for (;;) { /* until end of line or end of word */
10232 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
10233 switch(SIT(c, syntax)) {
10234 case CNL: /* '\n' */
10235 if (syntax == BASESYNTAX)
10236 goto endword; /* exit outer loop */
10237 USTPUTC(c, out);
10238 plinno++;
10239 if (doprompt)
10240 setprompt(2);
10241 c = pgetc();
10242 goto loop; /* continue outer loop */
10243 case CWORD:
10244 USTPUTC(c, out);
10245 break;
10246 case CCTL:
10247 if (eofmark == NULL || dblquote)
10248 USTPUTC(CTLESC, out);
10249 USTPUTC(c, out);
10250 break;
10251 case CBACK: /* backslash */
10252 c = pgetc2();
10253 if (c == PEOF) {
10254 USTPUTC(CTLESC, out);
10255 USTPUTC('\\', out);
10256 pungetc();
10257 } else if (c == '\n') {
10258 if (doprompt)
10259 setprompt(2);
10260 } else {
10261 if (
10262 dblquote &&
10263 c != '\\' && c != '`' &&
10264 c != '$' && (
10265 c != '"' ||
10266 eofmark != NULL
10267 )
10268 ) {
10269 USTPUTC(CTLESC, out);
10270 USTPUTC('\\', out);
10271 }
10272 if (SIT(c, SQSYNTAX) == CCTL)
10273 USTPUTC(CTLESC, out);
10274 USTPUTC(c, out);
10275 quotef++;
10276 }
10277 break;
10278 case CSQUOTE:
10279 syntax = SQSYNTAX;
10280quotemark:
10281 if (eofmark == NULL) {
10282 USTPUTC(CTLQUOTEMARK, out);
10283 }
10284 break;
10285 case CDQUOTE:
10286 syntax = DQSYNTAX;
10287 dblquote = 1;
10288 goto quotemark;
10289 case CENDQUOTE:
10290 if (eofmark != NULL && arinest == 0 &&
10291 varnest == 0) {
10292 USTPUTC(c, out);
10293 } else {
10294 if (dqvarnest == 0) {
10295 syntax = BASESYNTAX;
10296 dblquote = 0;
10297 }
10298 quotef++;
10299 goto quotemark;
10300 }
10301 break;
10302 case CVAR: /* '$' */
10303 PARSESUB(); /* parse substitution */
10304 break;
10305 case CENDVAR: /* '}' */
10306 if (varnest > 0) {
10307 varnest--;
10308 if (dqvarnest > 0) {
10309 dqvarnest--;
10310 }
10311 USTPUTC(CTLENDVAR, out);
10312 } else {
10313 USTPUTC(c, out);
10314 }
10315 break;
10316#ifdef CONFIG_ASH_MATH_SUPPORT
10317 case CLP: /* '(' in arithmetic */
10318 parenlevel++;
10319 USTPUTC(c, out);
10320 break;
10321 case CRP: /* ')' in arithmetic */
10322 if (parenlevel > 0) {
10323 USTPUTC(c, out);
10324 --parenlevel;
10325 } else {
10326 if (pgetc() == ')') {
10327 if (--arinest == 0) {
10328 USTPUTC(CTLENDARI, out);
10329 syntax = prevsyntax;
10330 if (syntax == DQSYNTAX)
10331 dblquote = 1;
10332 else
10333 dblquote = 0;
10334 } else
10335 USTPUTC(')', out);
10336 } else {
10337 /*
10338 * unbalanced parens
10339 * (don't 2nd guess - no error)
10340 */
10341 pungetc();
10342 USTPUTC(')', out);
10343 }
10344 }
10345 break;
10346#endif
10347 case CBQUOTE: /* '`' */
10348 PARSEBACKQOLD();
10349 break;
10350 case CENDFILE:
10351 goto endword; /* exit outer loop */
10352 case CIGN:
10353 break;
10354 default:
10355 if (varnest == 0)
10356 goto endword; /* exit outer loop */
10357#ifdef CONFIG_ASH_ALIAS
10358 if (c != PEOA)
10359#endif
10360 USTPUTC(c, out);
10361
10362 }
10363 c = pgetc_macro();
10364 }
10365 }
10366endword:
10367#ifdef CONFIG_ASH_MATH_SUPPORT
10368 if (syntax == ARISYNTAX)
10369 synerror("Missing '))'");
10370#endif
10371 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10372 synerror("Unterminated quoted string");
10373 if (varnest != 0) {
10374 startlinno = plinno;
10375 /* { */
10376 synerror("Missing '}'");
10377 }
10378 USTPUTC('\0', out);
10379 len = out - (char *)stackblock();
10380 out = stackblock();
10381 if (eofmark == NULL) {
10382 if ((c == '>' || c == '<')
10383 && quotef == 0
10384 && len <= 2
10385 && (*out == '\0' || is_digit(*out))) {
10386 PARSEREDIR();
10387 return lasttoken = TREDIR;
10388 } else {
10389 pungetc();
10390 }
10391 }
10392 quoteflag = quotef;
10393 backquotelist = bqlist;
10394 grabstackblock(len);
10395 wordtext = out;
10396 return lasttoken = TWORD;
10397/* end of readtoken routine */
10398
10399
10400
10401/*
10402 * Check to see whether we are at the end of the here document. When this
10403 * is called, c is set to the first character of the next input line. If
10404 * we are at the end of the here document, this routine sets the c to PEOF.
10405 */
10406
10407checkend: {
10408 if (eofmark) {
10409#ifdef CONFIG_ASH_ALIAS
10410 if (c == PEOA) {
10411 c = pgetc2();
10412 }
10413#endif
10414 if (striptabs) {
10415 while (c == '\t') {
10416 c = pgetc2();
10417 }
10418 }
10419 if (c == *eofmark) {
10420 if (pfgets(line, sizeof line) != NULL) {
10421 char *p, *q;
10422
10423 p = line;
10424 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10425 if (*p == '\n' && *q == '\0') {
10426 c = PEOF;
10427 plinno++;
10428 needprompt = doprompt;
10429 } else {
10430 pushstring(line, NULL);
10431 }
10432 }
10433 }
10434 }
10435 goto checkend_return;
10436}
10437
10438
10439/*
10440 * Parse a redirection operator. The variable "out" points to a string
10441 * specifying the fd to be redirected. The variable "c" contains the
10442 * first character of the redirection operator.
10443 */
10444
10445parseredir: {
10446 char fd = *out;
10447 union node *np;
10448
10449 np = (union node *)stalloc(sizeof (struct nfile));
10450 if (c == '>') {
10451 np->nfile.fd = 1;
10452 c = pgetc();
10453 if (c == '>')
10454 np->type = NAPPEND;
10455 else if (c == '|')
10456 np->type = NCLOBBER;
10457 else if (c == '&')
10458 np->type = NTOFD;
10459 else {
10460 np->type = NTO;
10461 pungetc();
10462 }
10463 } else { /* c == '<' */
10464 np->nfile.fd = 0;
10465 switch (c = pgetc()) {
10466 case '<':
10467 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10468 np = (union node *)stalloc(sizeof (struct nhere));
10469 np->nfile.fd = 0;
10470 }
10471 np->type = NHERE;
10472 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10473 heredoc->here = np;
10474 if ((c = pgetc()) == '-') {
10475 heredoc->striptabs = 1;
10476 } else {
10477 heredoc->striptabs = 0;
10478 pungetc();
10479 }
10480 break;
10481
10482 case '&':
10483 np->type = NFROMFD;
10484 break;
10485
10486 case '>':
10487 np->type = NFROMTO;
10488 break;
10489
10490 default:
10491 np->type = NFROM;
10492 pungetc();
10493 break;
10494 }
10495 }
10496 if (fd != '\0')
10497 np->nfile.fd = digit_val(fd);
10498 redirnode = np;
10499 goto parseredir_return;
10500}
10501
10502
10503/*
10504 * Parse a substitution. At this point, we have read the dollar sign
10505 * and nothing else.
10506 */
10507
10508parsesub: {
10509 int subtype;
10510 int typeloc;
10511 int flags;
10512 char *p;
10513 static const char types[] = "}-+?=";
10514
10515 c = pgetc();
10516 if (
10517 c <= PEOA_OR_PEOF ||
10518 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10519 ) {
10520 USTPUTC('$', out);
10521 pungetc();
10522 } else if (c == '(') { /* $(command) or $((arith)) */
10523 if (pgetc() == '(') {
10524#ifdef CONFIG_ASH_MATH_SUPPORT
10525 PARSEARITH();
10526#else
10527 synerror("We unsupport $((arith))");
10528#endif
10529 } else {
10530 pungetc();
10531 PARSEBACKQNEW();
10532 }
10533 } else {
10534 USTPUTC(CTLVAR, out);
10535 typeloc = out - (char *)stackblock();
10536 USTPUTC(VSNORMAL, out);
10537 subtype = VSNORMAL;
10538 if (c == '{') {
10539 c = pgetc();
10540 if (c == '#') {
10541 if ((c = pgetc()) == '}')
10542 c = '#';
10543 else
10544 subtype = VSLENGTH;
10545 }
10546 else
10547 subtype = 0;
10548 }
10549 if (c > PEOA_OR_PEOF && is_name(c)) {
10550 do {
10551 STPUTC(c, out);
10552 c = pgetc();
10553 } while (c > PEOA_OR_PEOF && is_in_name(c));
10554 } else if (is_digit(c)) {
10555 do {
10556 STPUTC(c, out);
10557 c = pgetc();
10558 } while (is_digit(c));
10559 }
10560 else if (is_special(c)) {
10561 USTPUTC(c, out);
10562 c = pgetc();
10563 }
10564 else
10565badsub: synerror("Bad substitution");
10566
10567 STPUTC('=', out);
10568 flags = 0;
10569 if (subtype == 0) {
10570 switch (c) {
10571 case ':':
10572 flags = VSNUL;
10573 c = pgetc();
10574 /*FALLTHROUGH*/
10575 default:
10576 p = strchr(types, c);
10577 if (p == NULL)
10578 goto badsub;
10579 subtype = p - types + VSNORMAL;
10580 break;
10581 case '%':
10582 case '#':
10583 {
10584 int cc = c;
10585 subtype = c == '#' ? VSTRIMLEFT :
10586 VSTRIMRIGHT;
10587 c = pgetc();
10588 if (c == cc)
10589 subtype++;
10590 else
10591 pungetc();
10592 break;
10593 }
10594 }
10595 } else {
10596 pungetc();
10597 }
10598 if (dblquote || arinest)
10599 flags |= VSQUOTE;
10600 *((char *)stackblock() + typeloc) = subtype | flags;
10601 if (subtype != VSNORMAL) {
10602 varnest++;
10603 if (dblquote || arinest) {
10604 dqvarnest++;
10605 }
10606 }
10607 }
10608 goto parsesub_return;
10609}
10610
10611
10612/*
10613 * Called to parse command substitutions. Newstyle is set if the command
10614 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10615 * list of commands (passed by reference), and savelen is the number of
10616 * characters on the top of the stack which must be preserved.
10617 */
10618
10619parsebackq: {
10620 struct nodelist **nlpp;
10621 int savepbq;
10622 union node *n;
10623 char *volatile str;
10624 struct jmploc jmploc;
10625 struct jmploc *volatile savehandler;
10626 size_t savelen;
10627 int saveprompt;
10628#ifdef __GNUC__
10629 (void) &saveprompt;
10630#endif
10631
10632 savepbq = parsebackquote;
10633 if (setjmp(jmploc.loc)) {
10634 if (str)
10635 ckfree(str);
10636 parsebackquote = 0;
10637 handler = savehandler;
10638 longjmp(handler->loc, 1);
10639 }
10640 INTOFF;
10641 str = NULL;
10642 savelen = out - (char *)stackblock();
10643 if (savelen > 0) {
10644 str = ckmalloc(savelen);
10645 memcpy(str, stackblock(), savelen);
10646 }
10647 savehandler = handler;
10648 handler = &jmploc;
10649 INTON;
10650 if (oldstyle) {
10651 /* We must read until the closing backquote, giving special
10652 treatment to some slashes, and then push the string and
10653 reread it as input, interpreting it normally. */
10654 char *pout;
10655 int pc;
10656 size_t psavelen;
10657 char *pstr;
10658
10659
10660 STARTSTACKSTR(pout);
10661 for (;;) {
10662 if (needprompt) {
10663 setprompt(2);
10664 needprompt = 0;
10665 }
10666 switch (pc = pgetc()) {
10667 case '`':
10668 goto done;
10669
10670 case '\\':
10671 if ((pc = pgetc()) == '\n') {
10672 plinno++;
10673 if (doprompt)
10674 setprompt(2);
10675 /*
10676 * If eating a newline, avoid putting
10677 * the newline into the new character
10678 * stream (via the STPUTC after the
10679 * switch).
10680 */
10681 continue;
10682 }
10683 if (pc != '\\' && pc != '`' && pc != '$'
10684 && (!dblquote || pc != '"'))
10685 STPUTC('\\', pout);
10686 if (pc > PEOA_OR_PEOF) {
10687 break;
10688 }
10689 /* fall through */
10690
10691 case PEOF:
10692#ifdef CONFIG_ASH_ALIAS
10693 case PEOA:
10694#endif
10695 startlinno = plinno;
10696 synerror("EOF in backquote substitution");
10697
10698 case '\n':
10699 plinno++;
10700 needprompt = doprompt;
10701 break;
10702
10703 default:
10704 break;
10705 }
10706 STPUTC(pc, pout);
10707 }
10708done:
10709 STPUTC('\0', pout);
10710 psavelen = pout - (char *)stackblock();
10711 if (psavelen > 0) {
10712 pstr = grabstackstr(pout);
10713 setinputstring(pstr);
10714 }
10715 }
10716 nlpp = &bqlist;
10717 while (*nlpp)
10718 nlpp = &(*nlpp)->next;
10719 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
10720 (*nlpp)->next = NULL;
10721 parsebackquote = oldstyle;
10722
10723 if (oldstyle) {
10724 saveprompt = doprompt;
10725 doprompt = 0;
10726 }
10727
10728 n = list(2);
10729
10730 if (oldstyle)
10731 doprompt = saveprompt;
10732 else {
10733 if (readtoken() != TRP)
10734 synexpect(TRP);
10735 }
10736
10737 (*nlpp)->n = n;
10738 if (oldstyle) {
10739 /*
10740 * Start reading from old file again, ignoring any pushed back
10741 * tokens left from the backquote parsing
10742 */
10743 popfile();
10744 tokpushback = 0;
10745 }
10746 while (stackblocksize() <= savelen)
10747 growstackblock();
10748 STARTSTACKSTR(out);
10749 if (str) {
10750 memcpy(out, str, savelen);
10751 STADJUST(savelen, out);
10752 INTOFF;
10753 ckfree(str);
10754 str = NULL;
10755 INTON;
10756 }
10757 parsebackquote = savepbq;
10758 handler = savehandler;
10759 if (arinest || dblquote)
10760 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10761 else
10762 USTPUTC(CTLBACKQ, out);
10763 if (oldstyle)
10764 goto parsebackq_oldreturn;
10765 else
10766 goto parsebackq_newreturn;
10767}
10768
10769#ifdef CONFIG_ASH_MATH_SUPPORT
10770/*
10771 * Parse an arithmetic expansion (indicate start of one and set state)
10772 */
10773parsearith: {
10774
10775 if (++arinest == 1) {
10776 prevsyntax = syntax;
10777 syntax = ARISYNTAX;
10778 USTPUTC(CTLARI, out);
10779 if (dblquote)
10780 USTPUTC('"',out);
10781 else
10782 USTPUTC(' ',out);
10783 } else {
10784 /*
10785 * we collapse embedded arithmetic expansion to
10786 * parenthesis, which should be equivalent
10787 */
10788 USTPUTC('(', out);
10789 }
10790 goto parsearith_return;
10791}
10792#endif
10793
10794} /* end of readtoken */
10795
10796
10797
10798/*
10799 * Returns true if the text contains nothing to expand (no dollar signs
10800 * or backquotes).
10801 */
10802
10803static int
10804noexpand(char *text)
10805{
10806 char *p;
10807 char c;
10808
10809 p = text;
10810 while ((c = *p++) != '\0') {
10811 if (c == CTLQUOTEMARK)
10812 continue;
10813 if (c == CTLESC)
10814 p++;
10815 else if (SIT(c, BASESYNTAX) == CCTL)
10816 return 0;
10817 }
10818 return 1;
10819}
10820
10821
10822/*
10823 * Return of a legal variable name (a letter or underscore followed by zero or
10824 * more letters, underscores, and digits).
10825 */
10826
10827static char *
10828endofname(const char *name)
10829{
10830 char *p;
10831
10832 p = (char *) name;
10833 if (! is_name(*p))
10834 return p;
10835 while (*++p) {
10836 if (! is_in_name(*p))
10837 break;
10838 }
10839 return p;
10840}
10841
10842
10843/*
10844 * Called when an unexpected token is read during the parse. The argument
10845 * is the token that is expected, or -1 if more than one type of token can
10846 * occur at this point.
10847 */
10848
10849static void synexpect(int token)
10850{
10851 char msg[64];
10852 int l;
10853
10854 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10855 if (token >= 0)
10856 sprintf(msg + l, " (expecting %s)", tokname(token));
10857 synerror(msg);
10858 /* NOTREACHED */
10859}
10860
10861static void
10862synerror(const char *msg)
10863{
10864 error("Syntax error: %s", msg);
10865 /* NOTREACHED */
10866}
10867
10868
10869/*
10870 * called by editline -- any expansions to the prompt
10871 * should be added here.
10872 */
10873
10874static void setprompt(int whichprompt)
10875{
10876 const char *prompt;
10877
10878 switch (whichprompt) {
10879 case 1:
10880 prompt = ps1val();
10881 break;
10882 case 2:
10883 prompt = ps2val();
10884 break;
10885 default: /* 0 */
10886 prompt = nullstr;
10887 }
10888 putprompt(prompt);
10889}
10890
10891
10892static const char *const *findkwd(const char *s)
10893{
10894 return bsearch(s, tokname_array + KWDOFFSET,
10895 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
10896 sizeof(const char *), pstrcmp);
10897}
10898
10899/* $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
10900
10901/*
10902 * Code for dealing with input/output redirection.
10903 */
10904
10905#define EMPTY -2 /* marks an unused slot in redirtab */
10906#ifndef PIPE_BUF
10907# define PIPESIZE 4096 /* amount of buffering in a pipe */
10908#else
10909# define PIPESIZE PIPE_BUF
10910#endif
10911
10912/*
10913 * Open a file in noclobber mode.
10914 * The code was copied from bash.
10915 */
10916static inline int
10917noclobberopen(const char *fname)
10918{
10919 int r, fd;
10920 struct stat finfo, finfo2;
10921
10922 /*
10923 * If the file exists and is a regular file, return an error
10924 * immediately.
10925 */
10926 r = stat(fname, &finfo);
10927 if (r == 0 && S_ISREG(finfo.st_mode)) {
10928 errno = EEXIST;
10929 return -1;
10930 }
10931
10932 /*
10933 * If the file was not present (r != 0), make sure we open it
10934 * exclusively so that if it is created before we open it, our open
10935 * will fail. Make sure that we do not truncate an existing file.
10936 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10937 * file was not a regular file, we leave O_EXCL off.
10938 */
10939 if (r != 0)
10940 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10941 fd = open(fname, O_WRONLY|O_CREAT, 0666);
10942
10943 /* If the open failed, return the file descriptor right away. */
10944 if (fd < 0)
10945 return fd;
10946
10947 /*
10948 * OK, the open succeeded, but the file may have been changed from a
10949 * non-regular file to a regular file between the stat and the open.
10950 * We are assuming that the O_EXCL open handles the case where FILENAME
10951 * did not exist and is symlinked to an existing file between the stat
10952 * and open.
10953 */
10954
10955 /*
10956 * If we can open it and fstat the file descriptor, and neither check
10957 * revealed that it was a regular file, and the file has not been
10958 * replaced, return the file descriptor.
10959 */
10960 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
10961 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
10962 return fd;
10963
10964 /* The file has been replaced. badness. */
10965 close(fd);
10966 errno = EEXIST;
10967 return -1;
10968}
10969
10970/*
10971 * Handle here documents. Normally we fork off a process to write the
10972 * data to a pipe. If the document is short, we can stuff the data in
10973 * the pipe without forking.
10974 */
10975
10976static inline int
10977openhere(union node *redir)
10978{
10979 int pip[2];
10980 size_t len = 0;
10981
10982 if (pipe(pip) < 0)
10983 error("Pipe call failed");
10984 if (redir->type == NHERE) {
10985 len = strlen(redir->nhere.doc->narg.text);
10986 if (len <= PIPESIZE) {
10987 bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
10988 goto out;
10989 }
10990 }
10991 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
10992 close(pip[0]);
10993 signal(SIGINT, SIG_IGN);
10994 signal(SIGQUIT, SIG_IGN);
10995 signal(SIGHUP, SIG_IGN);
10996#ifdef SIGTSTP
10997 signal(SIGTSTP, SIG_IGN);
10998#endif
10999 signal(SIGPIPE, SIG_DFL);
11000 if (redir->type == NHERE)
11001 bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
11002 else
11003 expandhere(redir->nhere.doc, pip[1]);
11004 _exit(0);
11005 }
11006out:
11007 close(pip[1]);
11008 return pip[0];
11009}
11010
11011static int
11012openredirect(union node *redir)
11013{
11014 char *fname;
11015 int f;
11016
11017 switch (redir->nfile.type) {
11018 case NFROM:
11019 fname = redir->nfile.expfname;
11020 if ((f = open(fname, O_RDONLY)) < 0)
11021 goto eopen;
11022 break;
11023 case NFROMTO:
11024 fname = redir->nfile.expfname;
11025 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
11026 goto ecreate;
11027 break;
11028 case NTO:
11029 /* Take care of noclobber mode. */
11030 if (Cflag) {
11031 fname = redir->nfile.expfname;
11032 if ((f = noclobberopen(fname)) < 0)
11033 goto ecreate;
11034 break;
11035 }
11036 /* FALLTHROUGH */
11037 case NCLOBBER:
11038 fname = redir->nfile.expfname;
11039 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
11040 goto ecreate;
11041 break;
11042 case NAPPEND:
11043 fname = redir->nfile.expfname;
11044 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
11045 goto ecreate;
11046 break;
11047 default:
11048#ifdef DEBUG
11049 abort();
11050#endif
11051 /* Fall through to eliminate warning. */
11052 case NTOFD:
11053 case NFROMFD:
11054 f = -1;
11055 break;
11056 case NHERE:
11057 case NXHERE:
11058 f = openhere(redir);
11059 break;
11060 }
11061
11062 return f;
11063ecreate:
11064 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11065eopen:
11066 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11067}
11068
11069static inline void
11070dupredirect(union node *redir, int f)
11071{
11072 int fd = redir->nfile.fd;
11073
11074 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
11075 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
11076 copyfd(redir->ndup.dupfd, fd);
11077 }
11078 return;
11079 }
11080
11081 if (f != fd) {
11082 copyfd(f, fd);
11083 close(f);
11084 }
11085 return;
11086}
11087
11088/*
11089 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11090 * old file descriptors are stashed away so that the redirection can be
11091 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11092 * standard output, and the standard error if it becomes a duplicate of
11093 * stdout, is saved in memory.
11094 */
11095
11096static void
11097redirect(union node *redir, int flags)
11098{
11099 union node *n;
11100 struct redirtab *sv;
11101 int i;
11102 int fd;
11103 int newfd;
11104 int *p;
11105 nullredirs++;
11106 if (!redir) {
11107 return;
11108 }
11109 sv = NULL;
11110 INTOFF;
11111 if (flags & REDIR_PUSH) {
11112 struct redirtab *q;
11113 q = ckmalloc(sizeof (struct redirtab));
11114 q->next = redirlist;
11115 redirlist = q;
11116 q->nullredirs = nullredirs - 1;
11117 for (i = 0 ; i < 10 ; i++)
11118 q->renamed[i] = EMPTY;
11119 nullredirs = 0;
11120 sv = q;
11121 }
11122 n = redir;
11123 do {
11124 fd = n->nfile.fd;
11125 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
11126 n->ndup.dupfd == fd)
11127 continue; /* redirect from/to same file descriptor */
11128
11129 newfd = openredirect(n);
11130 if (fd == newfd)
11131 continue;
11132 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
11133 i = fcntl(fd, F_DUPFD, 10);
11134
11135 if (i == -1) {
11136 i = errno;
11137 if (i != EBADF) {
11138 close(newfd);
11139 errno = i;
11140 error("%d: %m", fd);
11141 /* NOTREACHED */
11142 }
11143 } else {
11144 *p = i;
11145 close(fd);
11146 }
11147 } else {
11148 close(fd);
11149 }
11150 dupredirect(n, newfd);
11151 } while ((n = n->nfile.next));
11152 INTON;
11153 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
11154 preverrout_fd = sv->renamed[2];
11155}
11156
11157
11158/*
11159 * Undo the effects of the last redirection.
11160 */
11161
11162void
11163popredir(int drop)
11164{
11165 struct redirtab *rp;
11166 int i;
11167
11168 if (--nullredirs >= 0)
11169 return;
11170 INTOFF;
11171 rp = redirlist;
11172 for (i = 0 ; i < 10 ; i++) {
11173 if (rp->renamed[i] != EMPTY) {
11174 if (!drop) {
11175 close(i);
11176 copyfd(rp->renamed[i], i);
11177 }
11178 close(rp->renamed[i]);
11179 }
11180 }
11181 redirlist = rp->next;
11182 nullredirs = rp->nullredirs;
11183 ckfree(rp);
11184 INTON;
11185}
11186
11187/*
11188 * Undo all redirections. Called on error or interrupt.
11189 */
11190
11191/*
11192 * Discard all saved file descriptors.
11193 */
11194
11195void
11196clearredir(int drop)
11197{
11198 for (;;) {
11199 nullredirs = 0;
11200 if (!redirlist)
11201 break;
11202 popredir(drop);
11203 }
11204}
11205
11206
11207/*
11208 * Copy a file descriptor to be >= to. Returns -1
11209 * if the source file descriptor is closed, EMPTY if there are no unused
11210 * file descriptors left.
11211 */
11212
11213int
11214copyfd(int from, int to)
11215{
11216 int newfd;
11217
11218 newfd = fcntl(from, F_DUPFD, to);
11219 if (newfd < 0) {
11220 if (errno == EMFILE)
11221 return EMPTY;
11222 else
11223 error("%d: %m", from);
11224 }
11225 return newfd;
11226}
11227
11228
11229int
11230redirectsafe(union node *redir, int flags)
11231{
11232 int err;
11233 volatile int saveint;
11234 struct jmploc *volatile savehandler = handler;
11235 struct jmploc jmploc;
11236
11237 SAVEINT(saveint);
11238 if (!(err = setjmp(jmploc.loc) * 2)) {
11239 handler = &jmploc;
11240 redirect(redir, flags);
11241 }
11242 handler = savehandler;
11243 if (err && exception != EXERROR)
11244 longjmp(handler->loc, 1);
11245 RESTOREINT(saveint);
11246 return err;
11247}
11248
11249/* $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $ */
11250
11251#ifdef DEBUG
11252static void shtree(union node *, int, char *, FILE*);
11253static void shcmd(union node *, FILE *);
11254static void sharg(union node *, FILE *);
11255static void indent(int, char *, FILE *);
11256static void trstring(char *);
11257
11258
11259void
11260showtree(union node *n)
11261{
11262 trputs("showtree called\n");
11263 shtree(n, 1, NULL, stdout);
11264}
11265
11266
11267static void
11268shtree(union node *n, int ind, char *pfx, FILE *fp)
11269{
11270 struct nodelist *lp;
11271 const char *s;
11272
11273 if (n == NULL)
11274 return;
11275
11276 indent(ind, pfx, fp);
11277 switch(n->type) {
11278 case NSEMI:
11279 s = "; ";
11280 goto binop;
11281 case NAND:
11282 s = " && ";
11283 goto binop;
11284 case NOR:
11285 s = " || ";
11286binop:
11287 shtree(n->nbinary.ch1, ind, NULL, fp);
11288 /* if (ind < 0) */
11289 fputs(s, fp);
11290 shtree(n->nbinary.ch2, ind, NULL, fp);
11291 break;
11292 case NCMD:
11293 shcmd(n, fp);
11294 if (ind >= 0)
11295 putc('\n', fp);
11296 break;
11297 case NPIPE:
11298 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11299 shcmd(lp->n, fp);
11300 if (lp->next)
11301 fputs(" | ", fp);
11302 }
11303 if (n->npipe.backgnd)
11304 fputs(" &", fp);
11305 if (ind >= 0)
11306 putc('\n', fp);
11307 break;
11308 default:
11309 fprintf(fp, "<node type %d>", n->type);
11310 if (ind >= 0)
11311 putc('\n', fp);
11312 break;
11313 }
11314}
11315
11316
11317static void
11318shcmd(union node *cmd, FILE *fp)
11319{
11320 union node *np;
11321 int first;
11322 const char *s;
11323 int dftfd;
11324
11325 first = 1;
11326 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11327 if (! first)
11328 putchar(' ');
11329 sharg(np, fp);
11330 first = 0;
11331 }
11332 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11333 if (! first)
11334 putchar(' ');
11335 switch (np->nfile.type) {
11336 case NTO: s = ">"; dftfd = 1; break;
11337 case NCLOBBER: s = ">|"; dftfd = 1; break;
11338 case NAPPEND: s = ">>"; dftfd = 1; break;
11339 case NTOFD: s = ">&"; dftfd = 1; break;
11340 case NFROM: s = "<"; dftfd = 0; break;
11341 case NFROMFD: s = "<&"; dftfd = 0; break;
11342 case NFROMTO: s = "<>"; dftfd = 0; break;
11343 default: s = "*error*"; dftfd = 0; break;
11344 }
11345 if (np->nfile.fd != dftfd)
11346 fprintf(fp, "%d", np->nfile.fd);
11347 fputs(s, fp);
11348 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11349 fprintf(fp, "%d", np->ndup.dupfd);
11350 } else {
11351 sharg(np->nfile.fname, fp);
11352 }
11353 first = 0;
11354 }
11355}
11356
11357
11358
11359static void
11360sharg(union node *arg, FILE *fp)
11361{
11362 char *p;
11363 struct nodelist *bqlist;
11364 int subtype;
11365
11366 if (arg->type != NARG) {
11367 out1fmt("<node type %d>\n", arg->type);
11368 abort();
11369 }
11370 bqlist = arg->narg.backquote;
11371 for (p = arg->narg.text ; *p ; p++) {
11372 switch (*p) {
11373 case CTLESC:
11374 putc(*++p, fp);
11375 break;
11376 case CTLVAR:
11377 putc('$', fp);
11378 putc('{', fp);
11379 subtype = *++p;
11380 if (subtype == VSLENGTH)
11381 putc('#', fp);
11382
11383 while (*p != '=')
11384 putc(*p++, fp);
11385
11386 if (subtype & VSNUL)
11387 putc(':', fp);
11388
11389 switch (subtype & VSTYPE) {
11390 case VSNORMAL:
11391 putc('}', fp);
11392 break;
11393 case VSMINUS:
11394 putc('-', fp);
11395 break;
11396 case VSPLUS:
11397 putc('+', fp);
11398 break;
11399 case VSQUESTION:
11400 putc('?', fp);
11401 break;
11402 case VSASSIGN:
11403 putc('=', fp);
11404 break;
11405 case VSTRIMLEFT:
11406 putc('#', fp);
11407 break;
11408 case VSTRIMLEFTMAX:
11409 putc('#', fp);
11410 putc('#', fp);
11411 break;
11412 case VSTRIMRIGHT:
11413 putc('%', fp);
11414 break;
11415 case VSTRIMRIGHTMAX:
11416 putc('%', fp);
11417 putc('%', fp);
11418 break;
11419 case VSLENGTH:
11420 break;
11421 default:
11422 out1fmt("<subtype %d>", subtype);
11423 }
11424 break;
11425 case CTLENDVAR:
11426 putc('}', fp);
11427 break;
11428 case CTLBACKQ:
11429 case CTLBACKQ|CTLQUOTE:
11430 putc('$', fp);
11431 putc('(', fp);
11432 shtree(bqlist->n, -1, NULL, fp);
11433 putc(')', fp);
11434 break;
11435 default:
11436 putc(*p, fp);
11437 break;
11438 }
11439 }
11440}
11441
11442
11443static void
11444indent(int amount, char *pfx, FILE *fp)
11445{
11446 int i;
11447
11448 for (i = 0 ; i < amount ; i++) {
11449 if (pfx && i == amount - 1)
11450 fputs(pfx, fp);
11451 putc('\t', fp);
11452 }
11453}
11454
11455
11456
11457/*
11458 * Debugging stuff.
11459 */
11460
11461
11462FILE *tracefile;
11463
11464
11465void
11466trputc(int c)
11467{
11468 if (debug != 1)
11469 return;
11470 putc(c, tracefile);
11471}
11472
11473void
11474trace(const char *fmt, ...)
11475{
11476 va_list va;
11477
11478 if (debug != 1)
11479 return;
11480 va_start(va, fmt);
11481 (void) vfprintf(tracefile, fmt, va);
11482 va_end(va);
11483}
11484
11485void
11486tracev(const char *fmt, va_list va)
11487{
11488 if (debug != 1)
11489 return;
11490 (void) vfprintf(tracefile, fmt, va);
11491}
11492
11493
11494void
11495trputs(const char *s)
11496{
11497 if (debug != 1)
11498 return;
11499 fputs(s, tracefile);
11500}
11501
11502
11503static void
11504trstring(char *s)
11505{
11506 char *p;
11507 char c;
11508
11509 if (debug != 1)
11510 return;
11511 putc('"', tracefile);
11512 for (p = s ; *p ; p++) {
11513 switch (*p) {
11514 case '\n': c = 'n'; goto backslash;
11515 case '\t': c = 't'; goto backslash;
11516 case '\r': c = 'r'; goto backslash;
11517 case '"': c = '"'; goto backslash;
11518 case '\\': c = '\\'; goto backslash;
11519 case CTLESC: c = 'e'; goto backslash;
11520 case CTLVAR: c = 'v'; goto backslash;
11521 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11522 case CTLBACKQ: c = 'q'; goto backslash;
11523 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
11524backslash: putc('\\', tracefile);
11525 putc(c, tracefile);
11526 break;
11527 default:
11528 if (*p >= ' ' && *p <= '~')
11529 putc(*p, tracefile);
11530 else {
11531 putc('\\', tracefile);
11532 putc(*p >> 6 & 03, tracefile);
11533 putc(*p >> 3 & 07, tracefile);
11534 putc(*p & 07, tracefile);
11535 }
11536 break;
11537 }
11538 }
11539 putc('"', tracefile);
11540}
11541
11542
11543void
11544trargs(char **ap)
11545{
11546 if (debug != 1)
11547 return;
11548 while (*ap) {
11549 trstring(*ap++);
11550 if (*ap)
11551 putc(' ', tracefile);
11552 else
11553 putc('\n', tracefile);
11554 }
11555}
11556
11557
11558void
11559opentrace(void)
11560{
11561 char s[100];
11562#ifdef O_APPEND
11563 int flags;
11564#endif
11565
11566 if (debug != 1) {
11567 if (tracefile)
11568 fflush(tracefile);
11569 /* leave open because libedit might be using it */
11570 return;
11571 }
11572 scopy("./trace", s);
11573 if (tracefile) {
11574 if (!freopen(s, "a", tracefile)) {
11575 fprintf(stderr, "Can't re-open %s\n", s);
11576 debug = 0;
11577 return;
11578 }
11579 } else {
11580 if ((tracefile = fopen(s, "a")) == NULL) {
11581 fprintf(stderr, "Can't open %s\n", s);
11582 debug = 0;
11583 return;
11584 }
11585 }
11586#ifdef O_APPEND
11587 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11588 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11589#endif
11590 setlinebuf(tracefile);
11591 fputs("\nTracing started.\n", tracefile);
11592}
11593#endif /* DEBUG */
11594
11595
11596/* $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $ */
11597
11598/*
11599 * Sigmode records the current value of the signal handlers for the various
11600 * modes. A value of zero means that the current handler is not known.
11601 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
11602 */
11603
11604#define S_DFL 1 /* default signal handling (SIG_DFL) */
11605#define S_CATCH 2 /* signal is caught */
11606#define S_IGN 3 /* signal is ignored (SIG_IGN) */
11607#define S_HARD_IGN 4 /* signal is ignored permenantly */
11608#define S_RESET 5 /* temporary - to reset a hard ignored sig */
11609
11610
11611
11612/*
11613 * The trap builtin.
11614 */
11615
11616int
11617trapcmd(int argc, char **argv)
11618{
11619 char *action;
11620 char **ap;
11621 int signo;
11622
11623 nextopt(nullstr);
11624 ap = argptr;
11625 if (!*ap) {
11626 for (signo = 0 ; signo < NSIG ; signo++) {
11627 if (trap[signo] != NULL) {
11628 const char *sn;
11629
11630 sn = u_signal_names(0, &signo, 0);
11631 if (sn == NULL)
11632 sn = "???";
11633 out1fmt("trap -- %s %s\n",
11634 single_quote(trap[signo]), sn);
11635 }
11636 }
11637 return 0;
11638 }
11639 if (!ap[1])
11640 action = NULL;
11641 else
11642 action = *ap++;
11643 while (*ap) {
11644 if ((signo = decode_signal(*ap, 0)) < 0)
11645 error("%s: bad trap", *ap);
11646 INTOFF;
11647 if (action) {
11648 if (action[0] == '-' && action[1] == '\0')
11649 action = NULL;
11650 else
11651 action = savestr(action);
11652 }
11653 if (trap[signo])
11654 ckfree(trap[signo]);
11655 trap[signo] = action;
11656 if (signo != 0)
11657 setsignal(signo);
11658 INTON;
11659 ap++;
11660 }
11661 return 0;
11662}
11663
11664
11665/*
11666 * Clear traps on a fork.
11667 */
11668
11669void
11670clear_traps(void)
11671{
11672 char **tp;
11673
11674 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
11675 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
11676 INTOFF;
11677 ckfree(*tp);
11678 *tp = NULL;
11679 if (tp != &trap[0])
11680 setsignal(tp - trap);
11681 INTON;
11682 }
11683 }
11684}
11685
11686
11687/*
11688 * Set the signal handler for the specified signal. The routine figures
11689 * out what it should be set to.
11690 */
11691
11692void
11693setsignal(int signo)
11694{
11695 int action;
11696 char *t, tsig;
11697 struct sigaction act;
11698
11699 if ((t = trap[signo]) == NULL)
11700 action = S_DFL;
11701 else if (*t != '\0')
11702 action = S_CATCH;
11703 else
11704 action = S_IGN;
11705 if (rootshell && action == S_DFL) {
11706 switch (signo) {
11707 case SIGINT:
11708 if (iflag || minusc || sflag == 0)
11709 action = S_CATCH;
11710 break;
11711 case SIGQUIT:
11712#ifdef DEBUG
11713 if (debug)
11714 break;
11715#endif
11716 /* FALLTHROUGH */
11717 case SIGTERM:
11718 if (iflag)
11719 action = S_IGN;
11720 break;
11721#if JOBS
11722 case SIGTSTP:
11723 case SIGTTOU:
11724 if (mflag)
11725 action = S_IGN;
11726 break;
11727#endif
11728 }
11729 }
11730
11731 t = &sigmode[signo - 1];
11732 tsig = *t;
11733 if (tsig == 0) {
11734 /*
11735 * current setting unknown
11736 */
11737 if (sigaction(signo, 0, &act) == -1) {
11738 /*
11739 * Pretend it worked; maybe we should give a warning
11740 * here, but other shells don't. We don't alter
11741 * sigmode, so that we retry every time.
11742 */
11743 return;
11744 }
11745 if (act.sa_handler == SIG_IGN) {
11746 if (mflag && (signo == SIGTSTP ||
11747 signo == SIGTTIN || signo == SIGTTOU)) {
11748 tsig = S_IGN; /* don't hard ignore these */
11749 } else
11750 tsig = S_HARD_IGN;
11751 } else {
11752 tsig = S_RESET; /* force to be set */
11753 }
11754 }
11755 if (tsig == S_HARD_IGN || tsig == action)
11756 return;
11757 switch (action) {
11758 case S_CATCH:
11759 act.sa_handler = onsig;
11760 break;
11761 case S_IGN:
11762 act.sa_handler = SIG_IGN;
11763 break;
11764 default:
11765 act.sa_handler = SIG_DFL;
11766 }
11767 *t = action;
11768 act.sa_flags = 0;
11769 sigfillset(&act.sa_mask);
11770 sigaction(signo, &act, 0);
11771}
11772
11773/*
11774 * Ignore a signal.
11775 */
11776
11777void
11778ignoresig(int signo)
11779{
11780 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11781 signal(signo, SIG_IGN);
11782 }
11783 sigmode[signo - 1] = S_HARD_IGN;
11784}
11785
11786
11787/*
11788 * Signal handler.
11789 */
11790
11791void
11792onsig(int signo)
11793{
11794 gotsig[signo - 1] = 1;
11795 pendingsigs = signo;
11796
11797 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
11798 if (!suppressint)
11799 onint();
11800 intpending = 1;
11801 }
11802}
11803
11804
11805/*
11806 * Called to execute a trap. Perhaps we should avoid entering new trap
11807 * handlers while we are executing a trap handler.
11808 */
11809
11810void
11811dotrap(void)
11812{
11813 char *p;
11814 char *q;
11815 int savestatus;
11816
11817 savestatus = exitstatus;
11818 q = gotsig;
11819 while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) {
11820 *p = 0;
11821 p = trap[p - q + 1];
11822 if (!p)
11823 continue;
11824 evalstring(p);
11825 exitstatus = savestatus;
11826 }
11827}
11828
11829
11830/*
11831 * Controls whether the shell is interactive or not.
11832 */
11833
11834void
11835setinteractive(int on)
11836{
11837 static int is_interactive;
11838
11839 if (++on == is_interactive)
11840 return;
11841 is_interactive = on;
11842 setsignal(SIGINT);
11843 setsignal(SIGQUIT);
11844 setsignal(SIGTERM);
11845#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11846 if(is_interactive > 1) {
11847 /* Looks like they want an interactive shell */
11848 static int do_banner;
11849
11850 if(!do_banner) {
11851 out1fmt(
11852 "\n\n" BB_BANNER " Built-in shell (ash)\n"
11853 "Enter 'help' for a list of built-in commands.\n\n");
11854 do_banner++;
11855 }
11856 }
11857#endif
11858}
11859
11860
11861#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11862/*** List the available builtins ***/
11863
11864static int helpcmd(int argc, char **argv)
11865{
11866 int col, i;
11867
11868 out1fmt("\nBuilt-in commands:\n-------------------\n");
11869 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11870 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
11871 builtincmd[i].name + 1);
11872 if (col > 60) {
11873 out1fmt("\n");
11874 col = 0;
11875 }
11876 }
11877#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
11878 {
11879 extern const struct BB_applet applets[];
11880 extern const size_t NUM_APPLETS;
11881
11882 for (i = 0; i < NUM_APPLETS; i++) {
11883
11884 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11885 if (col > 60) {
11886 out1fmt("\n");
11887 col = 0;
11888 }
11889 }
11890 }
11891#endif
11892 out1fmt("\n\n");
11893 return EXIT_SUCCESS;
11894}
11895#endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */
11896
11897/*
11898 * Called to exit the shell.
11899 */
11900
11901void
11902exitshell(void)
11903{
11904 struct jmploc loc;
11905 char *p;
11906 int status;
11907 int jmp;
11908
11909 jmp = setjmp(loc.loc);
11910 status = exitstatus;
11911 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
11912 if (jmp)
11913 goto out;
11914 handler = &loc;
11915 if ((p = trap[0]) != NULL && *p != '\0') {
11916 trap[0] = NULL;
11917 evalstring(p);
11918 }
11919 flushall();
11920 setjobctl(0);
11921#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
11922 if (iflag && rootshell) {
11923 const char *hp = lookupvar("HISTFILE");
11924
11925 if(hp != NULL )
11926 save_history ( hp );
11927 }
11928#endif
11929out:
11930 _exit(status);
11931 /* NOTREACHED */
11932}
11933
11934static int decode_signal(const char *string, int minsig)
11935{
11936 int signo;
11937 const char *name = u_signal_names(string, &signo, minsig);
11938
11939 return name ? signo : -1;
11940}
11941
11942/* $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $ */
11943
11944static struct var *vartab[VTABSIZE];
11945
11946static int vpcmp(const void *, const void *);
11947static struct var **findvar(struct var **, const char *);
11948
11949/*
11950 * Initialize the variable symbol tables and import the environment
11951 */
11952
11953
11954#ifdef CONFIG_ASH_GETOPTS
11955/*
11956 * Safe version of setvar, returns 1 on success 0 on failure.
11957 */
11958
11959int
11960setvarsafe(const char *name, const char *val, int flags)
11961{
11962 int err;
11963 volatile int saveint;
11964 struct jmploc *volatile savehandler = handler;
11965 struct jmploc jmploc;
11966
11967 SAVEINT(saveint);
11968 if (setjmp(jmploc.loc))
11969 err = 1;
11970 else {
11971 handler = &jmploc;
11972 setvar(name, val, flags);
11973 err = 0;
11974 }
11975 handler = savehandler;
11976 RESTOREINT(saveint);
11977 return err;
11978}
11979#endif
11980
11981/*
11982 * Set the value of a variable. The flags argument is ored with the
11983 * flags of the variable. If val is NULL, the variable is unset.
11984 */
11985
11986static void
11987setvar(const char *name, const char *val, int flags)
11988{
11989 char *p, *q;
11990 size_t namelen;
11991 char *nameeq;
11992 size_t vallen;
11993
11994 q = endofname(name);
11995 p = strchrnul(q, '=');
11996 namelen = p - name;
11997 if (!namelen || p != q)
11998 error("%.*s: bad variable name", namelen, name);
11999 vallen = 0;
12000 if (val == NULL) {
12001 flags |= VUNSET;
12002 } else {
12003 vallen = strlen(val);
12004 }
12005 INTOFF;
12006 p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
12007 *p++ = '\0';
12008 if (vallen) {
12009 p[-1] = '=';
12010 p = mempcpy(p, val, vallen);
12011 }
12012 *p = '\0';
12013 setvareq(nameeq, flags | VNOSAVE);
12014 INTON;
12015}
12016
12017
12018/*
12019 * Same as setvar except that the variable and value are passed in
12020 * the first argument as name=value. Since the first argument will
12021 * be actually stored in the table, it should not be a string that
12022 * will go away.
12023 * Called with interrupts off.
12024 */
12025
12026void
12027setvareq(char *s, int flags)
12028{
12029 struct var *vp, **vpp;
12030
12031 vpp = hashvar(s);
12032 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
12033 vp = *findvar(vpp, s);
12034 if (vp) {
12035 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
12036 const char *n;
12037
12038 if (flags & VNOSAVE)
12039 free(s);
12040 n = vp->text;
12041 error("%.*s: is read only", strchrnul(n, '=') - n, n);
12042 }
12043
12044 if (flags & VNOSET)
12045 return;
12046
12047 if (vp->func && (flags & VNOFUNC) == 0)
12048 (*vp->func)(strchrnul(s, '=') + 1);
12049
12050 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12051 ckfree(vp->text);
12052
12053 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
12054 } else {
12055 if (flags & VNOSET)
12056 return;
12057 /* not found */
12058 vp = ckmalloc(sizeof (*vp));
12059 vp->next = *vpp;
12060 vp->func = NULL;
12061 *vpp = vp;
12062 }
12063 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
12064 s = savestr(s);
12065 vp->text = s;
12066 vp->flags = flags;
12067}
12068
12069
12070/*
12071 * Process a linked list of variable assignments.
12072 */
12073
12074static void
12075listsetvar(struct strlist *list_set_var, int flags)
12076{
12077 struct strlist *lp = list_set_var;
12078
12079 if (!lp)
12080 return;
12081 INTOFF;
12082 do {
12083 setvareq(lp->text, flags);
12084 } while ((lp = lp->next));
12085 INTON;
12086}
12087
12088
12089/*
12090 * Find the value of a variable. Returns NULL if not set.
12091 */
12092
12093static char *
12094lookupvar(const char *name)
12095{
12096 struct var *v;
12097
12098 if ((v = *findvar(hashvar(name), name))) {
12099#ifdef DYNAMIC_VAR
12100 /*
12101 * Dynamic variables are implemented roughly the same way they are
12102 * in bash. Namely, they're "special" so long as they aren't unset.
12103 * As soon as they're unset, they're no longer dynamic, and dynamic
12104 * lookup will no longer happen at that point. -- PFM.
12105 */
12106 if((v->flags & VDYNAMIC))
12107 (*v->func)(NULL);
12108#endif
12109 if(!(v->flags & VUNSET))
12110 return strchrnul(v->text, '=') + 1;
12111 }
12112
12113 return NULL;
12114}
12115
12116
12117/*
12118 * Search the environment of a builtin command.
12119 */
12120
12121static char *
12122bltinlookup(const char *name)
12123{
12124 struct strlist *sp;
12125
12126 for (sp = cmdenviron ; sp ; sp = sp->next) {
12127 if (varequal(sp->text, name))
12128 return strchrnul(sp->text, '=') + 1;
12129 }
12130 return lookupvar(name);
12131}
12132
12133
12134/*
12135 * Generate a list of variables satisfying the given conditions.
12136 */
12137
12138static char **
12139listvars(int on, int off, char ***end)
12140{
12141 struct var **vpp;
12142 struct var *vp;
12143 char **ep;
12144 int mask;
12145
12146 STARTSTACKSTR(ep);
12147 vpp = vartab;
12148 mask = on | off;
12149 do {
12150 for (vp = *vpp ; vp ; vp = vp->next)
12151 if ((vp->flags & mask) == on) {
12152 if (ep == stackstrend())
12153 ep = growstackstr();
12154 *ep++ = (char *) vp->text;
12155 }
12156 } while (++vpp < vartab + VTABSIZE);
12157 if (ep == stackstrend())
12158 ep = growstackstr();
12159 if (end)
12160 *end = ep;
12161 *ep++ = NULL;
12162 return grabstackstr(ep);
12163}
12164
12165
12166/*
12167 * POSIX requires that 'set' (but not export or readonly) output the
12168 * variables in lexicographic order - by the locale's collating order (sigh).
12169 * Maybe we could keep them in an ordered balanced binary tree
12170 * instead of hashed lists.
12171 * For now just roll 'em through qsort for printing...
12172 */
12173
12174static int
12175showvars(const char *sep_prefix, int on, int off)
12176{
12177 const char *sep;
12178 char **ep, **epend;
12179
12180 ep = listvars(on, off, &epend);
12181 qsort(ep, epend - ep, sizeof(char *), vpcmp);
12182
12183 sep = *sep_prefix ? spcstr : sep_prefix;
12184
12185 for (; ep < epend; ep++) {
12186 const char *p;
12187 const char *q;
12188
12189 p = strchrnul(*ep, '=');
12190 q = nullstr;
12191 if (*p)
12192 q = single_quote(++p);
12193
12194 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
12195 }
12196
12197 return 0;
12198}
12199
12200
12201
12202/*
12203 * The export and readonly commands.
12204 */
12205
12206static int
12207exportcmd(int argc, char **argv)
12208{
12209 struct var *vp;
12210 char *name;
12211 const char *p;
12212 char **aptr;
12213 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
12214 int notp;
12215
12216 notp = nextopt("p") - 'p';
12217 if (notp && ((name = *(aptr = argptr)))) {
12218 do {
12219 if ((p = strchr(name, '=')) != NULL) {
12220 p++;
12221 } else {
12222 if ((vp = *findvar(hashvar(name), name))) {
12223 vp->flags |= flag;
12224 continue;
12225 }
12226 }
12227 setvar(name, p, flag);
12228 } while ((name = *++aptr) != NULL);
12229 } else {
12230 showvars(argv[0], flag, 0);
12231 }
12232 return 0;
12233}
12234
12235
12236/*
12237 * Make a variable a local variable. When a variable is made local, it's
12238 * value and flags are saved in a localvar structure. The saved values
12239 * will be restored when the shell function returns. We handle the name
12240 * "-" as a special case.
12241 */
12242
12243static inline void
12244mklocal(char *name)
12245{
12246 struct localvar *lvp;
12247 struct var **vpp;
12248 struct var *vp;
12249
12250 INTOFF;
12251 lvp = ckmalloc(sizeof (struct localvar));
12252 if (name[0] == '-' && name[1] == '\0') {
12253 char *p;
12254 p = ckmalloc(sizeof(optlist));
12255 lvp->text = memcpy(p, optlist, sizeof(optlist));
12256 vp = NULL;
12257 } else {
12258 char *eq;
12259
12260 vpp = hashvar(name);
12261 vp = *findvar(vpp, name);
12262 eq = strchr(name, '=');
12263 if (vp == NULL) {
12264 if (eq)
12265 setvareq(name, VSTRFIXED);
12266 else
12267 setvar(name, NULL, VSTRFIXED);
12268 vp = *vpp; /* the new variable */
12269 lvp->flags = VUNSET;
12270 } else {
12271 lvp->text = vp->text;
12272 lvp->flags = vp->flags;
12273 vp->flags |= VSTRFIXED|VTEXTFIXED;
12274 if (eq)
12275 setvareq(name, 0);
12276 }
12277 }
12278 lvp->vp = vp;
12279 lvp->next = localvars;
12280 localvars = lvp;
12281 INTON;
12282}
12283
12284/*
12285 * The "local" command.
12286 */
12287
12288static int
12289localcmd(int argc, char **argv)
12290{
12291 char *name;
12292
12293 argv = argptr;
12294 while ((name = *argv++) != NULL) {
12295 mklocal(name);
12296 }
12297 return 0;
12298}
12299
12300
12301/*
12302 * Called after a function returns.
12303 * Interrupts must be off.
12304 */
12305
12306static void
12307poplocalvars(void)
12308{
12309 struct localvar *lvp;
12310 struct var *vp;
12311
12312 while ((lvp = localvars) != NULL) {
12313 localvars = lvp->next;
12314 vp = lvp->vp;
12315 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
12316 if (vp == NULL) { /* $- saved */
12317 memcpy(optlist, lvp->text, sizeof(optlist));
12318 ckfree(lvp->text);
12319 optschanged();
12320 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
12321 unsetvar(vp->text);
12322 } else {
12323 if (vp->func)
12324 (*vp->func)(strchrnul(lvp->text, '=') + 1);
12325 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12326 ckfree(vp->text);
12327 vp->flags = lvp->flags;
12328 vp->text = lvp->text;
12329 }
12330 ckfree(lvp);
12331 }
12332}
12333
12334
12335/*
12336 * The unset builtin command. We unset the function before we unset the
12337 * variable to allow a function to be unset when there is a readonly variable
12338 * with the same name.
12339 */
12340
12341int
12342unsetcmd(int argc, char **argv)
12343{
12344 char **ap;
12345 int i;
12346 int flag = 0;
12347 int ret = 0;
12348
12349 while ((i = nextopt("vf")) != '\0') {
12350 flag = i;
12351 }
12352
12353 for (ap = argptr; *ap ; ap++) {
12354 if (flag != 'f') {
12355 i = unsetvar(*ap);
12356 ret |= i;
12357 if (!(i & 2))
12358 continue;
12359 }
12360 if (flag != 'v')
12361 unsetfunc(*ap);
12362 }
12363 return ret & 1;
12364}
12365
12366
12367/*
12368 * Unset the specified variable.
12369 */
12370
12371int
12372unsetvar(const char *s)
12373{
12374 struct var **vpp;
12375 struct var *vp;
12376 int retval;
12377
12378 vpp = findvar(hashvar(s), s);
12379 vp = *vpp;
12380 retval = 2;
12381 if (vp) {
12382 int flags = vp->flags;
12383
12384 retval = 1;
12385 if (flags & VREADONLY)
12386 goto out;
12387#ifdef DYNAMIC_VAR
12388 vp->flags &= ~VDYNAMIC;
12389#endif
12390 if (flags & VUNSET)
12391 goto ok;
12392 if ((flags & VSTRFIXED) == 0) {
12393 INTOFF;
12394 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
12395 ckfree(vp->text);
12396 *vpp = vp->next;
12397 ckfree(vp);
12398 INTON;
12399 } else {
12400 setvar(s, 0, 0);
12401 vp->flags &= ~VEXPORT;
12402 }
12403ok:
12404 retval = 0;
12405 }
12406
12407out:
12408 return retval;
12409}
12410
12411
12412
12413/*
12414 * Find the appropriate entry in the hash table from the name.
12415 */
12416
12417static struct var **
12418hashvar(const char *p)
12419{
12420 unsigned int hashval;
12421
12422 hashval = ((unsigned char) *p) << 4;
12423 while (*p && *p != '=')
12424 hashval += (unsigned char) *p++;
12425 return &vartab[hashval % VTABSIZE];
12426}
12427
12428
12429
12430/*
12431 * Compares two strings up to the first = or '\0'. The first
12432 * string must be terminated by '='; the second may be terminated by
12433 * either '=' or '\0'.
12434 */
12435
12436int
12437varcmp(const char *p, const char *q)
12438{
12439 int c, d;
12440
12441 while ((c = *p) == (d = *q)) {
12442 if (!c || c == '=')
12443 goto out;
12444 p++;
12445 q++;
12446 }
12447 if (c == '=')
12448 c = 0;
12449 if (d == '=')
12450 d = 0;
12451out:
12452 return c - d;
12453}
12454
12455static int
12456vpcmp(const void *a, const void *b)
12457{
12458 return varcmp(*(const char **)a, *(const char **)b);
12459}
12460
12461static struct var **
12462findvar(struct var **vpp, const char *name)
12463{
12464 for (; *vpp; vpp = &(*vpp)->next) {
12465 if (varequal((*vpp)->text, name)) {
12466 break;
12467 }
12468 }
12469 return vpp;
12470}
12471/* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */
12472
12473#include <sys/times.h>
12474
12475static const unsigned char timescmd_str[] = {
12476 ' ', offsetof(struct tms, tms_utime),
12477 '\n', offsetof(struct tms, tms_stime),
12478 ' ', offsetof(struct tms, tms_cutime),
12479 '\n', offsetof(struct tms, tms_cstime),
12480 0
12481};
12482
12483static int timescmd(int ac, char **av)
12484{
12485 long int clk_tck, s, t;
12486 const unsigned char *p;
12487 struct tms buf;
12488
12489 clk_tck = sysconf(_SC_CLK_TCK);
12490 times(&buf);
12491
12492 p = timescmd_str;
12493 do {
12494 t = *(clock_t *)(((char *) &buf) + p[1]);
12495 s = t / clk_tck;
12496 out1fmt("%ldm%ld.%.3lds%c",
12497 s/60, s%60,
12498 ((t - s * clk_tck) * 1000) / clk_tck,
12499 p[0]);
12500 } while (*(p += 2));
12501
12502 return 0;
12503}
12504
12505#ifdef CONFIG_ASH_MATH_SUPPORT
12506static arith_t
12507dash_arith(const char *s)
12508{
12509 arith_t result;
12510 int errcode = 0;
12511
12512 INTOFF;
12513 result = arith(s, &errcode);
12514 if (errcode < 0) {
12515 if (errcode == -3)
12516 error("exponent less than 0");
12517 else if (errcode == -2)
12518 error("divide by zero");
12519 else if (errcode == -5)
12520 error("expression recursion loop detected");
12521 else
12522 synerror(s);
12523 }
12524 INTON;
12525
12526 return (result);
12527}
12528
12529
12530/*
12531 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12532 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12533 *
12534 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12535 */
12536
12537static int
12538letcmd(int argc, char **argv)
12539{
12540 char **ap;
12541 arith_t i;
12542
12543 ap = argv + 1;
12544 if(!*ap)
12545 error("expression expected");
12546 for (ap = argv + 1; *ap; ap++) {
12547 i = dash_arith(*ap);
12548 }
12549
12550 return (!i);
12551}
12552#endif /* CONFIG_ASH_MATH_SUPPORT */
12553
12554/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
12555
12556/*
12557 * Miscellaneous builtins.
12558 */
12559
12560#undef rflag
12561
12562#ifdef __GLIBC__
12563#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
12564typedef enum __rlimit_resource rlim_t;
12565#endif
12566#endif
12567
12568
12569/*
12570 * The read builtin. The -e option causes backslashes to escape the
12571 * following character.
12572 *
12573 * This uses unbuffered input, which may be avoidable in some cases.
12574 */
12575
12576static int
12577readcmd(int argc, char **argv)
12578{
12579 char **ap;
12580 int backslash;
12581 char c;
12582 int rflag;
12583 char *prompt;
12584 const char *ifs;
12585 char *p;
12586 int startword;
12587 int status;
12588 int i;
12589
12590 rflag = 0;
12591 prompt = NULL;
12592 while ((i = nextopt("p:r")) != '\0') {
12593 if (i == 'p')
12594 prompt = optionarg;
12595 else
12596 rflag = 1;
12597 }
12598 if (prompt && isatty(0)) {
12599 out2str(prompt);
12600 }
12601 if (*(ap = argptr) == NULL)
12602 error("arg count");
12603 if ((ifs = bltinlookup("IFS")) == NULL)
12604 ifs = defifs;
12605 status = 0;
12606 startword = 1;
12607 backslash = 0;
12608 STARTSTACKSTR(p);
12609 for (;;) {
12610 if (read(0, &c, 1) != 1) {
12611 status = 1;
12612 break;
12613 }
12614 if (c == '\0')
12615 continue;
12616 if (backslash) {
12617 backslash = 0;
12618 if (c != '\n')
12619 goto put;
12620 continue;
12621 }
12622 if (!rflag && c == '\\') {
12623 backslash++;
12624 continue;
12625 }
12626 if (c == '\n')
12627 break;
12628 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12629 continue;
12630 }
12631 startword = 0;
12632 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12633 STACKSTRNUL(p);
12634 setvar(*ap, stackblock(), 0);
12635 ap++;
12636 startword = 1;
12637 STARTSTACKSTR(p);
12638 } else {
12639put:
12640 STPUTC(c, p);
12641 }
12642 }
12643 STACKSTRNUL(p);
12644 /* Remove trailing blanks */
12645 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12646 *p = '\0';
12647 setvar(*ap, stackblock(), 0);
12648 while (*++ap != NULL)
12649 setvar(*ap, nullstr, 0);
12650 return status;
12651}
12652
12653
12654static int umaskcmd(int argc, char **argv)
12655{
12656 static const char permuser[3] = "ugo";
12657 static const char permmode[3] = "rwx";
12658 static const short int permmask[] = {
12659 S_IRUSR, S_IWUSR, S_IXUSR,
12660 S_IRGRP, S_IWGRP, S_IXGRP,
12661 S_IROTH, S_IWOTH, S_IXOTH
12662 };
12663
12664 char *ap;
12665 mode_t mask;
12666 int i;
12667 int symbolic_mode = 0;
12668
12669 while (nextopt("S") != '\0') {
12670 symbolic_mode = 1;
12671 }
12672
12673 INTOFF;
12674 mask = umask(0);
12675 umask(mask);
12676 INTON;
12677
12678 if ((ap = *argptr) == NULL) {
12679 if (symbolic_mode) {
12680 char buf[18];
12681 char *p = buf;
12682
12683 for (i = 0; i < 3; i++) {
12684 int j;
12685
12686 *p++ = permuser[i];
12687 *p++ = '=';
12688 for (j = 0; j < 3; j++) {
12689 if ((mask & permmask[3 * i + j]) == 0) {
12690 *p++ = permmode[j];
12691 }
12692 }
12693 *p++ = ',';
12694 }
12695 *--p = 0;
12696 puts(buf);
12697 } else {
12698 out1fmt("%.4o\n", mask);
12699 }
12700 } else {
12701 if (is_digit((unsigned char) *ap)) {
12702 mask = 0;
12703 do {
12704 if (*ap >= '8' || *ap < '0')
12705 error(illnum, argv[1]);
12706 mask = (mask << 3) + (*ap - '0');
12707 } while (*++ap != '\0');
12708 umask(mask);
12709 } else {
12710 mask = ~mask & 0777;
12711 if (!bb_parse_mode(ap, &mask)) {
12712 error("Illegal mode: %s", ap);
12713 }
12714 umask(~mask & 0777);
12715 }
12716 }
12717 return 0;
12718}
12719
12720/*
12721 * ulimit builtin
12722 *
12723 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12724 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12725 * ash by J.T. Conklin.
12726 *
12727 * Public domain.
12728 */
12729
12730struct limits {
12731 const char *name;
12732 int cmd;
12733 int factor; /* multiply by to get rlim_{cur,max} values */
12734 char option;
12735};
12736
12737static const struct limits limits[] = {
12738#ifdef RLIMIT_CPU
12739 { "time(seconds)", RLIMIT_CPU, 1, 't' },
12740#endif
12741#ifdef RLIMIT_FSIZE
12742 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
12743#endif
12744#ifdef RLIMIT_DATA
12745 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
12746#endif
12747#ifdef RLIMIT_STACK
12748 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
12749#endif
12750#ifdef RLIMIT_CORE
12751 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
12752#endif
12753#ifdef RLIMIT_RSS
12754 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
12755#endif
12756#ifdef RLIMIT_MEMLOCK
12757 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
12758#endif
12759#ifdef RLIMIT_NPROC
12760 { "process", RLIMIT_NPROC, 1, 'p' },
12761#endif
12762#ifdef RLIMIT_NOFILE
12763 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
12764#endif
12765#ifdef RLIMIT_AS
12766 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
12767#endif
12768#ifdef RLIMIT_LOCKS
12769 { "locks", RLIMIT_LOCKS, 1, 'w' },
12770#endif
12771 { (char *) 0, 0, 0, '\0' }
12772};
12773
12774enum limtype { SOFT = 0x1, HARD = 0x2 };
12775
12776static void printlim(enum limtype how, const struct rlimit *limit,
12777 const struct limits *l)
12778{
12779 rlim_t val;
12780
12781 val = limit->rlim_max;
12782 if (how & SOFT)
12783 val = limit->rlim_cur;
12784
12785 if (val == RLIM_INFINITY)
12786 out1fmt("unlimited\n");
12787 else {
12788 val /= l->factor;
12789 out1fmt("%lld\n", (long long) val);
12790 }
12791}
12792
12793int
12794ulimitcmd(int argc, char **argv)
12795{
12796 int c;
12797 rlim_t val = 0;
12798 enum limtype how = SOFT | HARD;
12799 const struct limits *l;
12800 int set, all = 0;
12801 int optc, what;
12802 struct rlimit limit;
12803
12804 what = 'f';
12805 while ((optc = nextopt("HSa"
12806#ifdef RLIMIT_CPU
12807 "t"
12808#endif
12809#ifdef RLIMIT_FSIZE
12810 "f"
12811#endif
12812#ifdef RLIMIT_DATA
12813 "d"
12814#endif
12815#ifdef RLIMIT_STACK
12816 "s"
12817#endif
12818#ifdef RLIMIT_CORE
12819 "c"
12820#endif
12821#ifdef RLIMIT_RSS
12822 "m"
12823#endif
12824#ifdef RLIMIT_MEMLOCK
12825 "l"
12826#endif
12827#ifdef RLIMIT_NPROC
12828 "p"
12829#endif
12830#ifdef RLIMIT_NOFILE
12831 "n"
12832#endif
12833#ifdef RLIMIT_AS
12834 "v"
12835#endif
12836#ifdef RLIMIT_LOCKS
12837 "w"
12838#endif
12839 )) != '\0')
12840 switch (optc) {
12841 case 'H':
12842 how = HARD;
12843 break;
12844 case 'S':
12845 how = SOFT;
12846 break;
12847 case 'a':
12848 all = 1;
12849 break;
12850 default:
12851 what = optc;
12852 }
12853
12854 for (l = limits; l->option != what; l++)
12855 ;
12856
12857 set = *argptr ? 1 : 0;
12858 if (set) {
12859 char *p = *argptr;
12860
12861 if (all || argptr[1])
12862 error("too many arguments");
12863 if (strncmp(p, "unlimited\n", 9) == 0)
12864 val = RLIM_INFINITY;
12865 else {
12866 val = (rlim_t) 0;
12867
12868 while ((c = *p++) >= '0' && c <= '9')
12869 {
12870 val = (val * 10) + (long)(c - '0');
12871 if (val < (rlim_t) 0)
12872 break;
12873 }
12874 if (c)
12875 error("bad number");
12876 val *= l->factor;
12877 }
12878 }
12879 if (all) {
12880 for (l = limits; l->name; l++) {
12881 getrlimit(l->cmd, &limit);
12882 out1fmt("%-20s ", l->name);
12883 printlim(how, &limit, l);
12884 }
12885 return 0;
12886 }
12887
12888 getrlimit(l->cmd, &limit);
12889 if (set) {
12890 if (how & HARD)
12891 limit.rlim_max = val;
12892 if (how & SOFT)
12893 limit.rlim_cur = val;
12894 if (setrlimit(l->cmd, &limit) < 0)
12895 error("error setting limit (%m)");
12896 } else {
12897 printlim(how, &limit, l);
12898 }
12899 return 0;
12900}
12901
12902
12903#ifdef CONFIG_ASH_MATH_SUPPORT
12904
12905/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12906
12907 Permission is hereby granted, free of charge, to any person obtaining
12908 a copy of this software and associated documentation files (the
12909 "Software"), to deal in the Software without restriction, including
12910 without limitation the rights to use, copy, modify, merge, publish,
12911 distribute, sublicense, and/or sell copies of the Software, and to
12912 permit persons to whom the Software is furnished to do so, subject to
12913 the following conditions:
12914
12915 The above copyright notice and this permission notice shall be
12916 included in all copies or substantial portions of the Software.
12917
12918 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12919 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12920 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12921 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12922 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12923 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12924 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12925*/
12926
12927/* This is my infix parser/evaluator. It is optimized for size, intended
12928 * as a replacement for yacc-based parsers. However, it may well be faster
12929 * than a comparable parser written in yacc. The supported operators are
12930 * listed in #defines below. Parens, order of operations, and error handling
12931 * are supported. This code is thread safe. The exact expression format should
12932 * be that which POSIX specifies for shells. */
12933
12934/* The code uses a simple two-stack algorithm. See
12935 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
12936 * for a detailed explanation of the infix-to-postfix algorithm on which
12937 * this is based (this code differs in that it applies operators immediately
12938 * to the stack instead of adding them to a queue to end up with an
12939 * expression). */
12940
12941/* To use the routine, call it with an expression string and error return
12942 * pointer */
12943
12944/*
12945 * Aug 24, 2001 Manuel Novoa III
12946 *
12947 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12948 *
12949 * 1) In arith_apply():
12950 * a) Cached values of *numptr and &(numptr[-1]).
12951 * b) Removed redundant test for zero denominator.
12952 *
12953 * 2) In arith():
12954 * a) Eliminated redundant code for processing operator tokens by moving
12955 * to a table-based implementation. Also folded handling of parens
12956 * into the table.
12957 * b) Combined all 3 loops which called arith_apply to reduce generated
12958 * code size at the cost of speed.
12959 *
12960 * 3) The following expressions were treated as valid by the original code:
12961 * 1() , 0! , 1 ( *3 ) .
12962 * These bugs have been fixed by internally enclosing the expression in
12963 * parens and then checking that all binary ops and right parens are
12964 * preceded by a valid expression (NUM_TOKEN).
12965 *
12966 * Note: It may be desirable to replace Aaron's test for whitespace with
12967 * ctype's isspace() if it is used by another busybox applet or if additional
12968 * whitespace chars should be considered. Look below the "#include"s for a
12969 * precompiler test.
12970 */
12971
12972/*
12973 * Aug 26, 2001 Manuel Novoa III
12974 *
12975 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12976 *
12977 * Merge in Aaron's comments previously posted to the busybox list,
12978 * modified slightly to take account of my changes to the code.
12979 *
12980 */
12981
12982/*
12983 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12984 *
12985 * - allow access to variable,
12986 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12987 * - realize assign syntax (VAR=expr, +=, *= etc)
12988 * - realize exponentiation (** operator)
12989 * - realize comma separated - expr, expr
12990 * - realise ++expr --expr expr++ expr--
12991 * - realise expr ? expr : expr (but, second expr calculate always)
12992 * - allow hexadecimal and octal numbers
12993 * - was restored loses XOR operator
12994 * - remove one goto label, added three ;-)
12995 * - protect $((num num)) as true zero expr (Manuel`s error)
12996 * - always use special isspace(), see comment from bash ;-)
12997 */
12998
12999
13000#define arith_isspace(arithval) \
13001 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13002
13003
13004typedef unsigned char operator;
13005
13006/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
13007 * precedence, and 3 high bits are an ID unique across operators of that
13008 * precedence. The ID portion is so that multiple operators can have the
13009 * same precedence, ensuring that the leftmost one is evaluated first.
13010 * Consider * and /. */
13011
13012#define tok_decl(prec,id) (((id)<<5)|(prec))
13013#define PREC(op) ((op) & 0x1F)
13014
13015#define TOK_LPAREN tok_decl(0,0)
13016
13017#define TOK_COMMA tok_decl(1,0)
13018
13019#define TOK_ASSIGN tok_decl(2,0)
13020#define TOK_AND_ASSIGN tok_decl(2,1)
13021#define TOK_OR_ASSIGN tok_decl(2,2)
13022#define TOK_XOR_ASSIGN tok_decl(2,3)
13023#define TOK_PLUS_ASSIGN tok_decl(2,4)
13024#define TOK_MINUS_ASSIGN tok_decl(2,5)
13025#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13026#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13027
13028#define TOK_MUL_ASSIGN tok_decl(3,0)
13029#define TOK_DIV_ASSIGN tok_decl(3,1)
13030#define TOK_REM_ASSIGN tok_decl(3,2)
13031
13032/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
13033#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
13034
13035/* conditional is right associativity too */
13036#define TOK_CONDITIONAL tok_decl(4,0)
13037#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13038
13039#define TOK_OR tok_decl(5,0)
13040
13041#define TOK_AND tok_decl(6,0)
13042
13043#define TOK_BOR tok_decl(7,0)
13044
13045#define TOK_BXOR tok_decl(8,0)
13046
13047#define TOK_BAND tok_decl(9,0)
13048
13049#define TOK_EQ tok_decl(10,0)
13050#define TOK_NE tok_decl(10,1)
13051
13052#define TOK_LT tok_decl(11,0)
13053#define TOK_GT tok_decl(11,1)
13054#define TOK_GE tok_decl(11,2)
13055#define TOK_LE tok_decl(11,3)
13056
13057#define TOK_LSHIFT tok_decl(12,0)
13058#define TOK_RSHIFT tok_decl(12,1)
13059
13060#define TOK_ADD tok_decl(13,0)
13061#define TOK_SUB tok_decl(13,1)
13062
13063#define TOK_MUL tok_decl(14,0)
13064#define TOK_DIV tok_decl(14,1)
13065#define TOK_REM tok_decl(14,2)
13066
13067/* exponent is right associativity */
13068#define TOK_EXPONENT tok_decl(15,1)
13069
13070/* For now unary operators. */
13071#define UNARYPREC 16
13072#define TOK_BNOT tok_decl(UNARYPREC,0)
13073#define TOK_NOT tok_decl(UNARYPREC,1)
13074
13075#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13076#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13077
13078#define PREC_PRE (UNARYPREC+2)
13079
13080#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13081#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13082
13083#define PREC_POST (UNARYPREC+3)
13084
13085#define TOK_POST_INC tok_decl(PREC_POST, 0)
13086#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13087
13088#define SPEC_PREC (UNARYPREC+4)
13089
13090#define TOK_NUM tok_decl(SPEC_PREC, 0)
13091#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13092
13093#define NUMPTR (*numstackptr)
13094
13095static inline int tok_have_assign(operator op)
13096{
13097 operator prec = PREC(op);
13098
13099 convert_prec_is_assing(prec);
13100 return (prec == PREC(TOK_ASSIGN) ||
13101 prec == PREC_PRE || prec == PREC_POST);
13102}
13103
13104static inline int is_right_associativity(operator prec)
13105{
13106 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
13107 prec == PREC(TOK_CONDITIONAL));
13108}
13109
13110
13111typedef struct ARITCH_VAR_NUM {
13112 arith_t val;
13113 arith_t contidional_second_val;
13114 char contidional_second_val_initialized;
13115 char *var; /* if NULL then is regular number,
13116 else is variable name */
13117} v_n_t;
13118
13119
13120typedef struct CHK_VAR_RECURSIVE_LOOPED {
13121 const char *var;
13122 struct CHK_VAR_RECURSIVE_LOOPED *next;
13123} chk_var_recursive_looped_t;
13124
13125static chk_var_recursive_looped_t *prev_chk_var_recursive;
13126
13127
13128static int arith_lookup_val(v_n_t *t)
13129{
13130 if(t->var) {
13131 const char * p = lookupvar(t->var);
13132
13133 if(p) {
13134 int errcode;
13135
13136 /* recursive try as expression */
13137 chk_var_recursive_looped_t *cur;
13138 chk_var_recursive_looped_t cur_save;
13139
13140 for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
13141 if(strcmp(cur->var, t->var) == 0) {
13142 /* expression recursion loop detected */
13143 return -5;
13144 }
13145 }
13146 /* save current lookuped var name */
13147 cur = prev_chk_var_recursive;
13148 cur_save.var = t->var;
13149 cur_save.next = cur;
13150 prev_chk_var_recursive = &cur_save;
13151
13152 t->val = arith (p, &errcode);
13153 /* restore previous ptr after recursiving */
13154 prev_chk_var_recursive = cur;
13155 return errcode;
13156 } else {
13157 /* allow undefined var as 0 */
13158 t->val = 0;
13159 }
13160 }
13161 return 0;
13162}
13163
13164/* "applying" a token means performing it on the top elements on the integer
13165 * stack. For a unary operator it will only change the top element, but a
13166 * binary operator will pop two arguments and push a result */
13167static inline int
13168arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
13169{
13170 v_n_t *numptr_m1;
13171 arith_t numptr_val, rez;
13172 int ret_arith_lookup_val;
13173
13174 if (NUMPTR == numstack) goto err; /* There is no operator that can work
13175 without arguments */
13176 numptr_m1 = NUMPTR - 1;
13177
13178 /* check operand is var with noninteger value */
13179 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13180 if(ret_arith_lookup_val)
13181 return ret_arith_lookup_val;
13182
13183 rez = numptr_m1->val;
13184 if (op == TOK_UMINUS)
13185 rez *= -1;
13186 else if (op == TOK_NOT)
13187 rez = !rez;
13188 else if (op == TOK_BNOT)
13189 rez = ~rez;
13190 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13191 rez++;
13192 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13193 rez--;
13194 else if (op != TOK_UPLUS) {
13195 /* Binary operators */
13196
13197 /* check and binary operators need two arguments */
13198 if (numptr_m1 == numstack) goto err;
13199
13200 /* ... and they pop one */
13201 --NUMPTR;
13202 numptr_val = rez;
13203 if (op == TOK_CONDITIONAL) {
13204 if(! numptr_m1->contidional_second_val_initialized) {
13205 /* protect $((expr1 ? expr2)) without ": expr" */
13206 goto err;
13207 }
13208 rez = numptr_m1->contidional_second_val;
13209 } else if(numptr_m1->contidional_second_val_initialized) {
13210 /* protect $((expr1 : expr2)) without "expr ? " */
13211 goto err;
13212 }
13213 numptr_m1 = NUMPTR - 1;
13214 if(op != TOK_ASSIGN) {
13215 /* check operand is var with noninteger value for not '=' */
13216 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13217 if(ret_arith_lookup_val)
13218 return ret_arith_lookup_val;
13219 }
13220 if (op == TOK_CONDITIONAL) {
13221 numptr_m1->contidional_second_val = rez;
13222 }
13223 rez = numptr_m1->val;
13224 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
13225 rez |= numptr_val;
13226 else if (op == TOK_OR)
13227 rez = numptr_val || rez;
13228 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
13229 rez &= numptr_val;
13230 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
13231 rez ^= numptr_val;
13232 else if (op == TOK_AND)
13233 rez = rez && numptr_val;
13234 else if (op == TOK_EQ)
13235 rez = (rez == numptr_val);
13236 else if (op == TOK_NE)
13237 rez = (rez != numptr_val);
13238 else if (op == TOK_GE)
13239 rez = (rez >= numptr_val);
13240 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
13241 rez >>= numptr_val;
13242 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
13243 rez <<= numptr_val;
13244 else if (op == TOK_GT)
13245 rez = (rez > numptr_val);
13246 else if (op == TOK_LT)
13247 rez = (rez < numptr_val);
13248 else if (op == TOK_LE)
13249 rez = (rez <= numptr_val);
13250 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
13251 rez *= numptr_val;
13252 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
13253 rez += numptr_val;
13254 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
13255 rez -= numptr_val;
13256 else if (op == TOK_ASSIGN || op == TOK_COMMA)
13257 rez = numptr_val;
13258 else if (op == TOK_CONDITIONAL_SEP) {
13259 if (numptr_m1 == numstack) {
13260 /* protect $((expr : expr)) without "expr ? " */
13261 goto err;
13262 }
13263 numptr_m1->contidional_second_val_initialized = op;
13264 numptr_m1->contidional_second_val = numptr_val;
13265 }
13266 else if (op == TOK_CONDITIONAL) {
13267 rez = rez ?
13268 numptr_val : numptr_m1->contidional_second_val;
13269 }
13270 else if(op == TOK_EXPONENT) {
13271 if(numptr_val < 0)
13272 return -3; /* exponent less than 0 */
13273 else {
13274 arith_t c = 1;
13275
13276 if(numptr_val)
13277 while(numptr_val--)
13278 c *= rez;
13279 rez = c;
13280 }
13281 }
13282 else if(numptr_val==0) /* zero divisor check */
13283 return -2;
13284 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
13285 rez /= numptr_val;
13286 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
13287 rez %= numptr_val;
13288 }
13289 if(tok_have_assign(op)) {
13290 char buf[32];
13291
13292 if(numptr_m1->var == NULL) {
13293 /* Hmm, 1=2 ? */
13294 goto err;
13295 }
13296 /* save to shell variable */
13297 snprintf(buf, sizeof(buf), "%lld", (long long) rez);
13298 setvar(numptr_m1->var, buf, 0);
13299 /* after saving, make previous value for v++ or v-- */
13300 if(op == TOK_POST_INC)
13301 rez--;
13302 else if(op == TOK_POST_DEC)
13303 rez++;
13304 }
13305 numptr_m1->val = rez;
13306 /* protect geting var value, is number now */
13307 numptr_m1->var = NULL;
13308 return 0;
13309err: return(-1);
13310}
13311
13312/* longest must first */
13313static const char op_tokens[] = {
13314 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13315 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13316 '<','<', 0, TOK_LSHIFT,
13317 '>','>', 0, TOK_RSHIFT,
13318 '|','|', 0, TOK_OR,
13319 '&','&', 0, TOK_AND,
13320 '!','=', 0, TOK_NE,
13321 '<','=', 0, TOK_LE,
13322 '>','=', 0, TOK_GE,
13323 '=','=', 0, TOK_EQ,
13324 '|','=', 0, TOK_OR_ASSIGN,
13325 '&','=', 0, TOK_AND_ASSIGN,
13326 '*','=', 0, TOK_MUL_ASSIGN,
13327 '/','=', 0, TOK_DIV_ASSIGN,
13328 '%','=', 0, TOK_REM_ASSIGN,
13329 '+','=', 0, TOK_PLUS_ASSIGN,
13330 '-','=', 0, TOK_MINUS_ASSIGN,
13331 '-','-', 0, TOK_POST_DEC,
13332 '^','=', 0, TOK_XOR_ASSIGN,
13333 '+','+', 0, TOK_POST_INC,
13334 '*','*', 0, TOK_EXPONENT,
13335 '!', 0, TOK_NOT,
13336 '<', 0, TOK_LT,
13337 '>', 0, TOK_GT,
13338 '=', 0, TOK_ASSIGN,
13339 '|', 0, TOK_BOR,
13340 '&', 0, TOK_BAND,
13341 '*', 0, TOK_MUL,
13342 '/', 0, TOK_DIV,
13343 '%', 0, TOK_REM,
13344 '+', 0, TOK_ADD,
13345 '-', 0, TOK_SUB,
13346 '^', 0, TOK_BXOR,
13347 /* uniq */
13348 '~', 0, TOK_BNOT,
13349 ',', 0, TOK_COMMA,
13350 '?', 0, TOK_CONDITIONAL,
13351 ':', 0, TOK_CONDITIONAL_SEP,
13352 ')', 0, TOK_RPAREN,
13353 '(', 0, TOK_LPAREN,
13354 0
13355};
13356/* ptr to ")" */
13357#define endexpression &op_tokens[sizeof(op_tokens)-7]
13358
13359
13360static arith_t arith (const char *expr, int *perrcode)
13361{
13362 register char arithval; /* Current character under analysis */
13363 operator lasttok, op;
13364 operator prec;
13365
13366 const char *p = endexpression;
13367 int errcode;
13368
13369 size_t datasizes = strlen(expr) + 2;
13370
13371 /* Stack of integers */
13372 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13373 * in any given correct or incorrect expression is left as an exercise to
13374 * the reader. */
13375 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
13376 *numstackptr = numstack;
13377 /* Stack of operator tokens */
13378 operator *stack = alloca((datasizes) * sizeof(operator)),
13379 *stackptr = stack;
13380
13381 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13382 *perrcode = errcode = 0;
13383
13384 while(1) {
13385 if ((arithval = *expr) == 0) {
13386 if (p == endexpression) {
13387 /* Null expression. */
13388 return 0;
13389 }
13390
13391 /* This is only reached after all tokens have been extracted from the
13392 * input stream. If there are still tokens on the operator stack, they
13393 * are to be applied in order. At the end, there should be a final
13394 * result on the integer stack */
13395
13396 if (expr != endexpression + 1) {
13397 /* If we haven't done so already, */
13398 /* append a closing right paren */
13399 expr = endexpression;
13400 /* and let the loop process it. */
13401 continue;
13402 }
13403 /* At this point, we're done with the expression. */
13404 if (numstackptr != numstack+1) {
13405 /* ... but if there isn't, it's bad */
13406 err:
13407 return (*perrcode = -1);
13408 }
13409 if(numstack->var) {
13410 /* expression is $((var)) only, lookup now */
13411 errcode = arith_lookup_val(numstack);
13412 }
13413 ret:
13414 *perrcode = errcode;
13415 return numstack->val;
13416 } else {
13417 /* Continue processing the expression. */
13418 if (arith_isspace(arithval)) {
13419 /* Skip whitespace */
13420 goto prologue;
13421 }
13422 if((p = endofname(expr)) != expr) {
13423 size_t var_name_size = (p-expr) + 1; /* trailing zero */
13424
13425 numstackptr->var = alloca(var_name_size);
13426 safe_strncpy(numstackptr->var, expr, var_name_size);
13427 expr = p;
13428 num:
13429 numstackptr->contidional_second_val_initialized = 0;
13430 numstackptr++;
13431 lasttok = TOK_NUM;
13432 continue;
13433 } else if (is_digit(arithval)) {
13434 numstackptr->var = NULL;
13435 numstackptr->val = strtoll(expr, (char **) &expr, 0);
13436 goto num;
13437 }
13438 for(p = op_tokens; ; p++) {
13439 const char *o;
13440
13441 if(*p == 0) {
13442 /* strange operator not found */
13443 goto err;
13444 }
13445 for(o = expr; *p && *o == *p; p++)
13446 o++;
13447 if(! *p) {
13448 /* found */
13449 expr = o - 1;
13450 break;
13451 }
13452 /* skip tail uncompared token */
13453 while(*p)
13454 p++;
13455 /* skip zero delim */
13456 p++;
13457 }
13458 op = p[1];
13459
13460 /* post grammar: a++ reduce to num */
13461 if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13462 lasttok = TOK_NUM;
13463
13464 /* Plus and minus are binary (not unary) _only_ if the last
13465 * token was as number, or a right paren (which pretends to be
13466 * a number, since it evaluates to one). Think about it.
13467 * It makes sense. */
13468 if (lasttok != TOK_NUM) {
13469 switch(op) {
13470 case TOK_ADD:
13471 op = TOK_UPLUS;
13472 break;
13473 case TOK_SUB:
13474 op = TOK_UMINUS;
13475 break;
13476 case TOK_POST_INC:
13477 op = TOK_PRE_INC;
13478 break;
13479 case TOK_POST_DEC:
13480 op = TOK_PRE_DEC;
13481 break;
13482 }
13483 }
13484 /* We don't want a unary operator to cause recursive descent on the
13485 * stack, because there can be many in a row and it could cause an
13486 * operator to be evaluated before its argument is pushed onto the
13487 * integer stack. */
13488 /* But for binary operators, "apply" everything on the operator
13489 * stack until we find an operator with a lesser priority than the
13490 * one we have just extracted. */
13491 /* Left paren is given the lowest priority so it will never be
13492 * "applied" in this way.
13493 * if associativity is right and priority eq, applied also skip
13494 */
13495 prec = PREC(op);
13496 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13497 /* not left paren or unary */
13498 if (lasttok != TOK_NUM) {
13499 /* binary op must be preceded by a num */
13500 goto err;
13501 }
13502 while (stackptr != stack) {
13503 if (op == TOK_RPAREN) {
13504 /* The algorithm employed here is simple: while we don't
13505 * hit an open paren nor the bottom of the stack, pop
13506 * tokens and apply them */
13507 if (stackptr[-1] == TOK_LPAREN) {
13508 --stackptr;
13509 /* Any operator directly after a */
13510 lasttok = TOK_NUM;
13511 /* close paren should consider itself binary */
13512 goto prologue;
13513 }
13514 } else {
13515 operator prev_prec = PREC(stackptr[-1]);
13516
13517 convert_prec_is_assing(prec);
13518 convert_prec_is_assing(prev_prec);
13519 if (prev_prec < prec)
13520 break;
13521 /* check right assoc */
13522 if(prev_prec == prec && is_right_associativity(prec))
13523 break;
13524 }
13525 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13526 if(errcode) goto ret;
13527 }
13528 if (op == TOK_RPAREN) {
13529 goto err;
13530 }
13531 }
13532
13533 /* Push this operator to the stack and remember it. */
13534 *stackptr++ = lasttok = op;
13535
13536 prologue:
13537 ++expr;
13538 }
13539 }
13540}
13541#endif /* CONFIG_ASH_MATH_SUPPORT */
13542
13543
13544#ifdef DEBUG
13545const char *bb_applet_name = "debug stuff usage";
13546int main(int argc, char **argv)
13547{
13548 return ash_main(argc, argv);
13549}
13550#endif
13551
13552/*-
13553 * Copyright (c) 1989, 1991, 1993, 1994
13554 * The Regents of the University of California. All rights reserved.
13555 *
13556 * This code is derived from software contributed to Berkeley by
13557 * Kenneth Almquist.
13558 *
13559 * Redistribution and use in source and binary forms, with or without
13560 * modification, are permitted provided that the following conditions
13561 * are met:
13562 * 1. Redistributions of source code must retain the above copyright
13563 * notice, this list of conditions and the following disclaimer.
13564 * 2. Redistributions in binary form must reproduce the above copyright
13565 * notice, this list of conditions and the following disclaimer in the
13566 * documentation and/or other materials provided with the distribution.
13567 *
13568 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
13569 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
13570 *
13571 * 4. Neither the name of the University nor the names of its contributors
13572 * may be used to endorse or promote products derived from this software
13573 * without specific prior written permission.
13574 *
13575 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13576 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13577 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13578 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13579 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13580 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13581 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13582 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13583 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13584 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13585 * SUCH DAMAGE.
13586 */