blob: 167232c332756fc30f5792bde5607f671301f8a1 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
54#define _GNU_SOURCE
55#endif
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000056#include "busybox.h" /* for struct bb_applet */
Denis Vlasenkob012b102007-02-19 22:43:01 +000057#include <paths.h>
58#include <setjmp.h>
59#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000060#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000061#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000062#endif
Denis Vlasenko5b340832007-05-17 23:02:14 +000063extern char **environ;
Eric Andersencb57d552001-06-28 07:25:16 +000064
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000070/* ============ Misc helpers */
71
72#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
73
74/* C99 say: "char" declaration may be signed or unsigned default */
75#define signed_char2int(sc) ((int)((signed char)sc))
76
77
Denis Vlasenkob012b102007-02-19 22:43:01 +000078/* ============ Shell options */
79
80static const char *const optletters_optnames[] = {
81 "e" "errexit",
82 "f" "noglob",
83 "I" "ignoreeof",
84 "i" "interactive",
85 "m" "monitor",
86 "n" "noexec",
87 "s" "stdin",
88 "x" "xtrace",
89 "v" "verbose",
90 "C" "noclobber",
91 "a" "allexport",
92 "b" "notify",
93 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000094 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +000095#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000096 ,"\0" "nolog"
97 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +000098#endif
99};
100
101#define optletters(n) optletters_optnames[(n)][0]
102#define optnames(n) (&optletters_optnames[(n)][1])
103
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000104enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000105
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000106static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000107
108#define eflag optlist[0]
109#define fflag optlist[1]
110#define Iflag optlist[2]
111#define iflag optlist[3]
112#define mflag optlist[4]
113#define nflag optlist[5]
114#define sflag optlist[6]
115#define xflag optlist[7]
116#define vflag optlist[8]
117#define Cflag optlist[9]
118#define aflag optlist[10]
119#define bflag optlist[11]
120#define uflag optlist[12]
121#define viflag optlist[13]
122#if DEBUG
123#define nolog optlist[14]
124#define debug optlist[15]
125#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000126
127
Denis Vlasenkob012b102007-02-19 22:43:01 +0000128/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000129
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000130static char nullstr[1] ALIGN1; /* zero length string */
131static const char homestr[] ALIGN1 = "HOME";
132static const char snlfmt[] ALIGN1 = "%s\n";
133static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000134
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000135static char *minusc; /* argument to -c option */
Denis Vlasenkocc571512007-02-23 21:10:35 +0000136
Denis Vlasenkob012b102007-02-19 22:43:01 +0000137/* pid of main shell */
138static int rootpid;
139/* shell level: 0 for the main shell, 1 for its children, and so on */
140static int shlvl;
141#define rootshell (!shlvl)
142/* trap handler commands */
143static char *trap[NSIG];
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +0000144static smallint isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000145/* current value of signal */
146static char sigmode[NSIG - 1];
147/* indicates specified signal received */
148static char gotsig[NSIG - 1];
149static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000150
151
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000152/* ============ Interrupts / exceptions */
153
154/*
Eric Andersenc470f442003-07-28 09:56:35 +0000155 * We enclose jmp_buf in a structure so that we can declare pointers to
156 * jump locations. The global variable handler contains the location to
157 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000158 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000159 * exception handlers, the user should save the value of handler on entry
160 * to an inner scope, set handler to point to a jmploc structure for the
161 * inner scope, and restore handler on exit from the scope.
162 */
Eric Andersenc470f442003-07-28 09:56:35 +0000163struct jmploc {
164 jmp_buf loc;
165};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000166static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000167static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000168/* exceptions */
169#define EXINT 0 /* SIGINT received */
170#define EXERROR 1 /* a generic error */
171#define EXSHELLPROC 2 /* execute a shell procedure */
172#define EXEXEC 3 /* command execution failed */
173#define EXEXIT 4 /* exit the shell */
174#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000175static volatile int suppressint;
176static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000177/* do we generate EXSIG events */
178static int exsig;
179/* last pending signal */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000180static volatile sig_atomic_t pendingsig;
Eric Andersen2870d962001-07-02 17:27:21 +0000181
182/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000183 * Sigmode records the current value of the signal handlers for the various
184 * modes. A value of zero means that the current handler is not known.
185 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
186 */
187
188#define S_DFL 1 /* default signal handling (SIG_DFL) */
189#define S_CATCH 2 /* signal is caught */
190#define S_IGN 3 /* signal is ignored (SIG_IGN) */
191#define S_HARD_IGN 4 /* signal is ignored permenantly */
192#define S_RESET 5 /* temporary - to reset a hard ignored sig */
193
194/*
Eric Andersen2870d962001-07-02 17:27:21 +0000195 * These macros allow the user to suspend the handling of interrupt signals
196 * over a period of time. This is similar to SIGHOLD to or sigblock, but
197 * much more efficient and portable. (But hacking the kernel is so much
198 * more fun than worrying about efficiency and portability. :-))
199 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000200#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000201 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000202 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000203 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000204 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000205
206/*
207 * Called to raise an exception. Since C doesn't include exceptions, we
208 * just do a longjmp to the exception handler. The type of exception is
209 * stored in the global variable "exception".
210 */
211static void raise_exception(int) ATTRIBUTE_NORETURN;
212static void
213raise_exception(int e)
214{
215#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000216 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000217 abort();
218#endif
219 INT_OFF;
220 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000221 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000222}
223
224/*
225 * Called from trap.c when a SIGINT is received. (If the user specifies
226 * that SIGINT is to be trapped or ignored using the trap builtin, then
227 * this routine is not called.) Suppressint is nonzero when interrupts
228 * are held using the INT_OFF macro. (The test for iflag is just
229 * defensive programming.)
230 */
231static void raise_interrupt(void) ATTRIBUTE_NORETURN;
232static void
233raise_interrupt(void)
234{
235 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000236 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000237
238 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000239 /* Signal is not automatically re-enabled after it is raised,
240 * do it ourself */
241 sigemptyset(&mask);
242 sigprocmask(SIG_SETMASK, &mask, 0);
243 /* pendingsig = 0; - now done in onsig() */
244
Denis Vlasenkob012b102007-02-19 22:43:01 +0000245 i = EXSIG;
246 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
247 if (!(rootshell && iflag)) {
248 signal(SIGINT, SIG_DFL);
249 raise(SIGINT);
250 }
251 i = EXINT;
252 }
253 raise_exception(i);
254 /* NOTREACHED */
255}
256
257#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000258static void
259int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000260{
261 if (--suppressint == 0 && intpending) {
262 raise_interrupt();
263 }
264}
265#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000266static void
267force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000268{
269 suppressint = 0;
270 if (intpending)
271 raise_interrupt();
272}
273#define FORCE_INT_ON force_int_on()
274#else
275#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000276 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000277 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000278 if (--suppressint == 0 && intpending) \
279 raise_interrupt(); \
280 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000282 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 xbarrier(); \
284 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000285 if (intpending) \
286 raise_interrupt(); \
287 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000288#endif /* ASH_OPTIMIZE_FOR_SIZE */
289
290#define SAVE_INT(v) ((v) = suppressint)
291
292#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000293 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000294 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000295 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000296 if (suppressint == 0 && intpending) \
297 raise_interrupt(); \
298 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299
300#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000301 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000302 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000303 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000304 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000305 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000306 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000307/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000308
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000309/*
310 * Ignore a signal. Only one usage site - in forkchild()
311 */
312static void
313ignoresig(int signo)
314{
315 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
316 signal(signo, SIG_IGN);
317 }
318 sigmode[signo - 1] = S_HARD_IGN;
319}
320
321/*
322 * Signal handler. Only one usage site - in setsignal()
323 */
324static void
325onsig(int signo)
326{
327 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000328 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000329
330 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000331 if (!suppressint) {
332 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000333 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000334 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000335 intpending = 1;
336 }
337}
338
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000339
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000340/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000341
Eric Andersenc470f442003-07-28 09:56:35 +0000342static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000343outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000344{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000345 INT_OFF;
346 fputs(p, file);
347 INT_ON;
348}
349
350static void
351flush_stdout_stderr(void)
352{
353 INT_OFF;
354 fflush(stdout);
355 fflush(stderr);
356 INT_ON;
357}
358
359static void
360flush_stderr(void)
361{
362 INT_OFF;
363 fflush(stderr);
364 INT_ON;
365}
366
367static void
368outcslow(int c, FILE *dest)
369{
370 INT_OFF;
371 putc(c, dest);
372 fflush(dest);
373 INT_ON;
374}
375
376static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
377static int
378out1fmt(const char *fmt, ...)
379{
380 va_list ap;
381 int r;
382
383 INT_OFF;
384 va_start(ap, fmt);
385 r = vprintf(fmt, ap);
386 va_end(ap);
387 INT_ON;
388 return r;
389}
390
391static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
392static int
393fmtstr(char *outbuf, size_t length, const char *fmt, ...)
394{
395 va_list ap;
396 int ret;
397
398 va_start(ap, fmt);
399 INT_OFF;
400 ret = vsnprintf(outbuf, length, fmt, ap);
401 va_end(ap);
402 INT_ON;
403 return ret;
404}
405
406static void
407out1str(const char *p)
408{
409 outstr(p, stdout);
410}
411
412static void
413out2str(const char *p)
414{
415 outstr(p, stderr);
416 flush_stderr();
417}
418
419
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000420/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000421
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000422/* control characters in argument strings */
423#define CTLESC '\201' /* escape next character */
424#define CTLVAR '\202' /* variable defn */
425#define CTLENDVAR '\203'
426#define CTLBACKQ '\204'
427#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
428/* CTLBACKQ | CTLQUOTE == '\205' */
429#define CTLARI '\206' /* arithmetic expression */
430#define CTLENDARI '\207'
431#define CTLQUOTEMARK '\210'
432
433/* variable substitution byte (follows CTLVAR) */
434#define VSTYPE 0x0f /* type of variable substitution */
435#define VSNUL 0x10 /* colon--treat the empty string as unset */
436#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
437
438/* values of VSTYPE field */
439#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
440#define VSMINUS 0x2 /* ${var-text} */
441#define VSPLUS 0x3 /* ${var+text} */
442#define VSQUESTION 0x4 /* ${var?message} */
443#define VSASSIGN 0x5 /* ${var=text} */
444#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
445#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
446#define VSTRIMLEFT 0x8 /* ${var#pattern} */
447#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
448#define VSLENGTH 0xa /* ${#var} */
449
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000450static const char dolatstr[] ALIGN1 = {
451 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
452};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000453
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000454#define NCMD 0
455#define NPIPE 1
456#define NREDIR 2
457#define NBACKGND 3
458#define NSUBSHELL 4
459#define NAND 5
460#define NOR 6
461#define NSEMI 7
462#define NIF 8
463#define NWHILE 9
464#define NUNTIL 10
465#define NFOR 11
466#define NCASE 12
467#define NCLIST 13
468#define NDEFUN 14
469#define NARG 15
470#define NTO 16
471#define NCLOBBER 17
472#define NFROM 18
473#define NFROMTO 19
474#define NAPPEND 20
475#define NTOFD 21
476#define NFROMFD 22
477#define NHERE 23
478#define NXHERE 24
479#define NNOT 25
480
481union node;
482
483struct ncmd {
484 int type;
485 union node *assign;
486 union node *args;
487 union node *redirect;
488};
489
490struct npipe {
491 int type;
492 int backgnd;
493 struct nodelist *cmdlist;
494};
495
496struct nredir {
497 int type;
498 union node *n;
499 union node *redirect;
500};
501
502struct nbinary {
503 int type;
504 union node *ch1;
505 union node *ch2;
506};
507
508struct nif {
509 int type;
510 union node *test;
511 union node *ifpart;
512 union node *elsepart;
513};
514
515struct nfor {
516 int type;
517 union node *args;
518 union node *body;
519 char *var;
520};
521
522struct ncase {
523 int type;
524 union node *expr;
525 union node *cases;
526};
527
528struct nclist {
529 int type;
530 union node *next;
531 union node *pattern;
532 union node *body;
533};
534
535struct narg {
536 int type;
537 union node *next;
538 char *text;
539 struct nodelist *backquote;
540};
541
542struct nfile {
543 int type;
544 union node *next;
545 int fd;
546 union node *fname;
547 char *expfname;
548};
549
550struct ndup {
551 int type;
552 union node *next;
553 int fd;
554 int dupfd;
555 union node *vname;
556};
557
558struct nhere {
559 int type;
560 union node *next;
561 int fd;
562 union node *doc;
563};
564
565struct nnot {
566 int type;
567 union node *com;
568};
569
570union node {
571 int type;
572 struct ncmd ncmd;
573 struct npipe npipe;
574 struct nredir nredir;
575 struct nbinary nbinary;
576 struct nif nif;
577 struct nfor nfor;
578 struct ncase ncase;
579 struct nclist nclist;
580 struct narg narg;
581 struct nfile nfile;
582 struct ndup ndup;
583 struct nhere nhere;
584 struct nnot nnot;
585};
586
587struct nodelist {
588 struct nodelist *next;
589 union node *n;
590};
591
592struct funcnode {
593 int count;
594 union node n;
595};
596
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000597/*
598 * Free a parse tree.
599 */
600static void
601freefunc(struct funcnode *f)
602{
603 if (f && --f->count < 0)
604 free(f);
605}
606
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607
608/* ============ Debugging output */
609
610#if DEBUG
611
612static FILE *tracefile;
613
614static void
615trace_printf(const char *fmt, ...)
616{
617 va_list va;
618
619 if (debug != 1)
620 return;
621 va_start(va, fmt);
622 vfprintf(tracefile, fmt, va);
623 va_end(va);
624}
625
626static void
627trace_vprintf(const char *fmt, va_list va)
628{
629 if (debug != 1)
630 return;
631 vfprintf(tracefile, fmt, va);
632}
633
634static void
635trace_puts(const char *s)
636{
637 if (debug != 1)
638 return;
639 fputs(s, tracefile);
640}
641
642static void
643trace_puts_quoted(char *s)
644{
645 char *p;
646 char c;
647
648 if (debug != 1)
649 return;
650 putc('"', tracefile);
651 for (p = s; *p; p++) {
652 switch (*p) {
653 case '\n': c = 'n'; goto backslash;
654 case '\t': c = 't'; goto backslash;
655 case '\r': c = 'r'; goto backslash;
656 case '"': c = '"'; goto backslash;
657 case '\\': c = '\\'; goto backslash;
658 case CTLESC: c = 'e'; goto backslash;
659 case CTLVAR: c = 'v'; goto backslash;
660 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
661 case CTLBACKQ: c = 'q'; goto backslash;
662 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
663 backslash:
664 putc('\\', tracefile);
665 putc(c, tracefile);
666 break;
667 default:
668 if (*p >= ' ' && *p <= '~')
669 putc(*p, tracefile);
670 else {
671 putc('\\', tracefile);
672 putc(*p >> 6 & 03, tracefile);
673 putc(*p >> 3 & 07, tracefile);
674 putc(*p & 07, tracefile);
675 }
676 break;
677 }
678 }
679 putc('"', tracefile);
680}
681
682static void
683trace_puts_args(char **ap)
684{
685 if (debug != 1)
686 return;
687 if (!*ap)
688 return;
689 while (1) {
690 trace_puts_quoted(*ap);
691 if (!*++ap) {
692 putc('\n', tracefile);
693 break;
694 }
695 putc(' ', tracefile);
696 }
697}
698
699static void
700opentrace(void)
701{
702 char s[100];
703#ifdef O_APPEND
704 int flags;
705#endif
706
707 if (debug != 1) {
708 if (tracefile)
709 fflush(tracefile);
710 /* leave open because libedit might be using it */
711 return;
712 }
713 strcpy(s, "./trace");
714 if (tracefile) {
715 if (!freopen(s, "a", tracefile)) {
716 fprintf(stderr, "Can't re-open %s\n", s);
717 debug = 0;
718 return;
719 }
720 } else {
721 tracefile = fopen(s, "a");
722 if (tracefile == NULL) {
723 fprintf(stderr, "Can't open %s\n", s);
724 debug = 0;
725 return;
726 }
727 }
728#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000729 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000730 if (flags >= 0)
731 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
732#endif
733 setlinebuf(tracefile);
734 fputs("\nTracing started.\n", tracefile);
735}
736
737static void
738indent(int amount, char *pfx, FILE *fp)
739{
740 int i;
741
742 for (i = 0; i < amount; i++) {
743 if (pfx && i == amount - 1)
744 fputs(pfx, fp);
745 putc('\t', fp);
746 }
747}
748
749/* little circular references here... */
750static void shtree(union node *n, int ind, char *pfx, FILE *fp);
751
752static void
753sharg(union node *arg, FILE *fp)
754{
755 char *p;
756 struct nodelist *bqlist;
757 int subtype;
758
759 if (arg->type != NARG) {
760 out1fmt("<node type %d>\n", arg->type);
761 abort();
762 }
763 bqlist = arg->narg.backquote;
764 for (p = arg->narg.text; *p; p++) {
765 switch (*p) {
766 case CTLESC:
767 putc(*++p, fp);
768 break;
769 case CTLVAR:
770 putc('$', fp);
771 putc('{', fp);
772 subtype = *++p;
773 if (subtype == VSLENGTH)
774 putc('#', fp);
775
776 while (*p != '=')
777 putc(*p++, fp);
778
779 if (subtype & VSNUL)
780 putc(':', fp);
781
782 switch (subtype & VSTYPE) {
783 case VSNORMAL:
784 putc('}', fp);
785 break;
786 case VSMINUS:
787 putc('-', fp);
788 break;
789 case VSPLUS:
790 putc('+', fp);
791 break;
792 case VSQUESTION:
793 putc('?', fp);
794 break;
795 case VSASSIGN:
796 putc('=', fp);
797 break;
798 case VSTRIMLEFT:
799 putc('#', fp);
800 break;
801 case VSTRIMLEFTMAX:
802 putc('#', fp);
803 putc('#', fp);
804 break;
805 case VSTRIMRIGHT:
806 putc('%', fp);
807 break;
808 case VSTRIMRIGHTMAX:
809 putc('%', fp);
810 putc('%', fp);
811 break;
812 case VSLENGTH:
813 break;
814 default:
815 out1fmt("<subtype %d>", subtype);
816 }
817 break;
818 case CTLENDVAR:
819 putc('}', fp);
820 break;
821 case CTLBACKQ:
822 case CTLBACKQ|CTLQUOTE:
823 putc('$', fp);
824 putc('(', fp);
825 shtree(bqlist->n, -1, NULL, fp);
826 putc(')', fp);
827 break;
828 default:
829 putc(*p, fp);
830 break;
831 }
832 }
833}
834
835static void
836shcmd(union node *cmd, FILE *fp)
837{
838 union node *np;
839 int first;
840 const char *s;
841 int dftfd;
842
843 first = 1;
844 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000845 if (!first)
846 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000847 sharg(np, fp);
848 first = 0;
849 }
850 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000851 if (!first)
852 putc(' ', fp);
853 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000854 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000855 case NTO: s = ">>"+1; dftfd = 1; break;
856 case NCLOBBER: s = ">|"; dftfd = 1; break;
857 case NAPPEND: s = ">>"; dftfd = 1; break;
858 case NTOFD: s = ">&"; dftfd = 1; break;
859 case NFROM: s = "<"; break;
860 case NFROMFD: s = "<&"; break;
861 case NFROMTO: s = "<>"; break;
862 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000863 }
864 if (np->nfile.fd != dftfd)
865 fprintf(fp, "%d", np->nfile.fd);
866 fputs(s, fp);
867 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
868 fprintf(fp, "%d", np->ndup.dupfd);
869 } else {
870 sharg(np->nfile.fname, fp);
871 }
872 first = 0;
873 }
874}
875
876static void
877shtree(union node *n, int ind, char *pfx, FILE *fp)
878{
879 struct nodelist *lp;
880 const char *s;
881
882 if (n == NULL)
883 return;
884
885 indent(ind, pfx, fp);
886 switch (n->type) {
887 case NSEMI:
888 s = "; ";
889 goto binop;
890 case NAND:
891 s = " && ";
892 goto binop;
893 case NOR:
894 s = " || ";
895 binop:
896 shtree(n->nbinary.ch1, ind, NULL, fp);
897 /* if (ind < 0) */
898 fputs(s, fp);
899 shtree(n->nbinary.ch2, ind, NULL, fp);
900 break;
901 case NCMD:
902 shcmd(n, fp);
903 if (ind >= 0)
904 putc('\n', fp);
905 break;
906 case NPIPE:
907 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
908 shcmd(lp->n, fp);
909 if (lp->next)
910 fputs(" | ", fp);
911 }
912 if (n->npipe.backgnd)
913 fputs(" &", fp);
914 if (ind >= 0)
915 putc('\n', fp);
916 break;
917 default:
918 fprintf(fp, "<node type %d>", n->type);
919 if (ind >= 0)
920 putc('\n', fp);
921 break;
922 }
923}
924
925static void
926showtree(union node *n)
927{
928 trace_puts("showtree called\n");
929 shtree(n, 1, NULL, stdout);
930}
931
932#define TRACE(param) trace_printf param
933#define TRACEV(param) trace_vprintf param
934
935#else
936
937#define TRACE(param)
938#define TRACEV(param)
939
940#endif /* DEBUG */
941
942
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000943/* ============ Parser data */
944
945/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000946 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
947 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000948struct strlist {
949 struct strlist *next;
950 char *text;
951};
952
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000953#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000955#endif
956
Denis Vlasenkob012b102007-02-19 22:43:01 +0000957struct strpush {
958 struct strpush *prev; /* preceding string on stack */
959 char *prevstring;
960 int prevnleft;
961#if ENABLE_ASH_ALIAS
962 struct alias *ap; /* if push was associated with an alias */
963#endif
964 char *string; /* remember the string since it may change */
965};
966
967struct parsefile {
968 struct parsefile *prev; /* preceding file on stack */
969 int linno; /* current line */
970 int fd; /* file descriptor (or -1 if string) */
971 int nleft; /* number of chars left in this line */
972 int lleft; /* number of chars left in this buffer */
973 char *nextc; /* next char in buffer */
974 char *buf; /* input buffer */
975 struct strpush *strpush; /* for pushing strings at this level */
976 struct strpush basestrpush; /* so pushing one is fast */
977};
978
979static struct parsefile basepf; /* top level input file */
980static struct parsefile *parsefile = &basepf; /* current input file */
981static int startlinno; /* line # where last token started */
982static char *commandname; /* currently executing command */
983static struct strlist *cmdenviron; /* environment for builtin command */
984static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000985
986
987/* ============ Message printing */
988
989static void
990ash_vmsg(const char *msg, va_list ap)
991{
992 fprintf(stderr, "%s: ", arg0);
993 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000994 if (strcmp(arg0, commandname))
995 fprintf(stderr, "%s: ", commandname);
996 if (!iflag || parsefile->fd)
997 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +0000998 }
Denis Vlasenkob012b102007-02-19 22:43:01 +0000999 vfprintf(stderr, msg, ap);
1000 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001001}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001002
1003/*
1004 * Exverror is called to raise the error exception. If the second argument
1005 * is not NULL then error prints an error message using printf style
1006 * formatting. It then raises the error exception.
1007 */
1008static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1009static void
1010ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001011{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001012#if DEBUG
1013 if (msg) {
1014 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1015 TRACEV((msg, ap));
1016 TRACE(("\") pid=%d\n", getpid()));
1017 } else
1018 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1019 if (msg)
1020#endif
1021 ash_vmsg(msg, ap);
1022
1023 flush_stdout_stderr();
1024 raise_exception(cond);
1025 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001026}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001027
1028static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1029static void
1030ash_msg_and_raise_error(const char *msg, ...)
1031{
1032 va_list ap;
1033
1034 va_start(ap, msg);
1035 ash_vmsg_and_raise(EXERROR, msg, ap);
1036 /* NOTREACHED */
1037 va_end(ap);
1038}
1039
1040static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1041static void
1042ash_msg_and_raise(int cond, const char *msg, ...)
1043{
1044 va_list ap;
1045
1046 va_start(ap, msg);
1047 ash_vmsg_and_raise(cond, msg, ap);
1048 /* NOTREACHED */
1049 va_end(ap);
1050}
1051
1052/*
1053 * error/warning routines for external builtins
1054 */
1055static void
1056ash_msg(const char *fmt, ...)
1057{
1058 va_list ap;
1059
1060 va_start(ap, fmt);
1061 ash_vmsg(fmt, ap);
1062 va_end(ap);
1063}
1064
1065/*
1066 * Return a string describing an error. The returned string may be a
1067 * pointer to a static buffer that will be overwritten on the next call.
1068 * Action describes the operation that got the error.
1069 */
1070static const char *
1071errmsg(int e, const char *em)
1072{
1073 if (e == ENOENT || e == ENOTDIR) {
1074 return em;
1075 }
1076 return strerror(e);
1077}
1078
1079
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001080/* ============ Memory allocation */
1081
1082/*
1083 * It appears that grabstackstr() will barf with such alignments
1084 * because stalloc() will return a string allocated in a new stackblock.
1085 */
1086#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1087enum {
1088 /* Most machines require the value returned from malloc to be aligned
1089 * in some way. The following macro will get this right
1090 * on many machines. */
1091 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1092 /* Minimum size of a block */
1093 MINSIZE = SHELL_ALIGN(504),
1094};
1095
1096struct stack_block {
1097 struct stack_block *prev;
1098 char space[MINSIZE];
1099};
1100
1101struct stackmark {
1102 struct stack_block *stackp;
1103 char *stacknxt;
1104 size_t stacknleft;
1105 struct stackmark *marknext;
1106};
1107
1108static struct stack_block stackbase;
1109static struct stack_block *stackp = &stackbase;
1110static struct stackmark *markp;
1111static char *stacknxt = stackbase.space;
1112static size_t stacknleft = MINSIZE;
1113static char *sstrend = stackbase.space + MINSIZE;
1114static int herefd = -1;
1115
1116#define stackblock() ((void *)stacknxt)
1117#define stackblocksize() stacknleft
1118
1119static void *
1120ckrealloc(void * p, size_t nbytes)
1121{
1122 p = realloc(p, nbytes);
1123 if (!p)
1124 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1125 return p;
1126}
1127
1128static void *
1129ckmalloc(size_t nbytes)
1130{
1131 return ckrealloc(NULL, nbytes);
1132}
1133
1134/*
1135 * Make a copy of a string in safe storage.
1136 */
1137static char *
1138ckstrdup(const char *s)
1139{
1140 char *p = strdup(s);
1141 if (!p)
1142 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1143 return p;
1144}
1145
1146/*
1147 * Parse trees for commands are allocated in lifo order, so we use a stack
1148 * to make this more efficient, and also to avoid all sorts of exception
1149 * handling code to handle interrupts in the middle of a parse.
1150 *
1151 * The size 504 was chosen because the Ultrix malloc handles that size
1152 * well.
1153 */
1154static void *
1155stalloc(size_t nbytes)
1156{
1157 char *p;
1158 size_t aligned;
1159
1160 aligned = SHELL_ALIGN(nbytes);
1161 if (aligned > stacknleft) {
1162 size_t len;
1163 size_t blocksize;
1164 struct stack_block *sp;
1165
1166 blocksize = aligned;
1167 if (blocksize < MINSIZE)
1168 blocksize = MINSIZE;
1169 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1170 if (len < blocksize)
1171 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1172 INT_OFF;
1173 sp = ckmalloc(len);
1174 sp->prev = stackp;
1175 stacknxt = sp->space;
1176 stacknleft = blocksize;
1177 sstrend = stacknxt + blocksize;
1178 stackp = sp;
1179 INT_ON;
1180 }
1181 p = stacknxt;
1182 stacknxt += aligned;
1183 stacknleft -= aligned;
1184 return p;
1185}
1186
1187static void
1188stunalloc(void *p)
1189{
1190#if DEBUG
1191 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1192 write(2, "stunalloc\n", 10);
1193 abort();
1194 }
1195#endif
1196 stacknleft += stacknxt - (char *)p;
1197 stacknxt = p;
1198}
1199
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001200/*
1201 * Like strdup but works with the ash stack.
1202 */
1203static char *
1204ststrdup(const char *p)
1205{
1206 size_t len = strlen(p) + 1;
1207 return memcpy(stalloc(len), p, len);
1208}
1209
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001210static void
1211setstackmark(struct stackmark *mark)
1212{
1213 mark->stackp = stackp;
1214 mark->stacknxt = stacknxt;
1215 mark->stacknleft = stacknleft;
1216 mark->marknext = markp;
1217 markp = mark;
1218}
1219
1220static void
1221popstackmark(struct stackmark *mark)
1222{
1223 struct stack_block *sp;
1224
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001225 if (!mark->stackp)
1226 return;
1227
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001228 INT_OFF;
1229 markp = mark->marknext;
1230 while (stackp != mark->stackp) {
1231 sp = stackp;
1232 stackp = sp->prev;
1233 free(sp);
1234 }
1235 stacknxt = mark->stacknxt;
1236 stacknleft = mark->stacknleft;
1237 sstrend = mark->stacknxt + mark->stacknleft;
1238 INT_ON;
1239}
1240
1241/*
1242 * When the parser reads in a string, it wants to stick the string on the
1243 * stack and only adjust the stack pointer when it knows how big the
1244 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1245 * of space on top of the stack and stackblocklen returns the length of
1246 * this block. Growstackblock will grow this space by at least one byte,
1247 * possibly moving it (like realloc). Grabstackblock actually allocates the
1248 * part of the block that has been used.
1249 */
1250static void
1251growstackblock(void)
1252{
1253 size_t newlen;
1254
1255 newlen = stacknleft * 2;
1256 if (newlen < stacknleft)
1257 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1258 if (newlen < 128)
1259 newlen += 128;
1260
1261 if (stacknxt == stackp->space && stackp != &stackbase) {
1262 struct stack_block *oldstackp;
1263 struct stackmark *xmark;
1264 struct stack_block *sp;
1265 struct stack_block *prevstackp;
1266 size_t grosslen;
1267
1268 INT_OFF;
1269 oldstackp = stackp;
1270 sp = stackp;
1271 prevstackp = sp->prev;
1272 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1273 sp = ckrealloc(sp, grosslen);
1274 sp->prev = prevstackp;
1275 stackp = sp;
1276 stacknxt = sp->space;
1277 stacknleft = newlen;
1278 sstrend = sp->space + newlen;
1279
1280 /*
1281 * Stack marks pointing to the start of the old block
1282 * must be relocated to point to the new block
1283 */
1284 xmark = markp;
1285 while (xmark != NULL && xmark->stackp == oldstackp) {
1286 xmark->stackp = stackp;
1287 xmark->stacknxt = stacknxt;
1288 xmark->stacknleft = stacknleft;
1289 xmark = xmark->marknext;
1290 }
1291 INT_ON;
1292 } else {
1293 char *oldspace = stacknxt;
1294 int oldlen = stacknleft;
1295 char *p = stalloc(newlen);
1296
1297 /* free the space we just allocated */
1298 stacknxt = memcpy(p, oldspace, oldlen);
1299 stacknleft += newlen;
1300 }
1301}
1302
1303static void
1304grabstackblock(size_t len)
1305{
1306 len = SHELL_ALIGN(len);
1307 stacknxt += len;
1308 stacknleft -= len;
1309}
1310
1311/*
1312 * The following routines are somewhat easier to use than the above.
1313 * The user declares a variable of type STACKSTR, which may be declared
1314 * to be a register. The macro STARTSTACKSTR initializes things. Then
1315 * the user uses the macro STPUTC to add characters to the string. In
1316 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1317 * grown as necessary. When the user is done, she can just leave the
1318 * string there and refer to it using stackblock(). Or she can allocate
1319 * the space for it using grabstackstr(). If it is necessary to allow
1320 * someone else to use the stack temporarily and then continue to grow
1321 * the string, the user should use grabstack to allocate the space, and
1322 * then call ungrabstr(p) to return to the previous mode of operation.
1323 *
1324 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1325 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1326 * is space for at least one character.
1327 */
1328static void *
1329growstackstr(void)
1330{
1331 size_t len = stackblocksize();
1332 if (herefd >= 0 && len >= 1024) {
1333 full_write(herefd, stackblock(), len);
1334 return stackblock();
1335 }
1336 growstackblock();
1337 return stackblock() + len;
1338}
1339
1340/*
1341 * Called from CHECKSTRSPACE.
1342 */
1343static char *
1344makestrspace(size_t newlen, char *p)
1345{
1346 size_t len = p - stacknxt;
1347 size_t size = stackblocksize();
1348
1349 for (;;) {
1350 size_t nleft;
1351
1352 size = stackblocksize();
1353 nleft = size - len;
1354 if (nleft >= newlen)
1355 break;
1356 growstackblock();
1357 }
1358 return stackblock() + len;
1359}
1360
1361static char *
1362stack_nputstr(const char *s, size_t n, char *p)
1363{
1364 p = makestrspace(n, p);
1365 p = memcpy(p, s, n) + n;
1366 return p;
1367}
1368
1369static char *
1370stack_putstr(const char *s, char *p)
1371{
1372 return stack_nputstr(s, strlen(s), p);
1373}
1374
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001375static char *
1376_STPUTC(int c, char *p)
1377{
1378 if (p == sstrend)
1379 p = growstackstr();
1380 *p++ = c;
1381 return p;
1382}
1383
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001384#define STARTSTACKSTR(p) ((p) = stackblock())
1385#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001386#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001387 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001388 char *q = (p); \
1389 size_t l = (n); \
1390 size_t m = sstrend - q; \
1391 if (l > m) \
1392 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001393 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001394#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001395#define STACKSTRNUL(p) \
1396 do { \
1397 if ((p) == sstrend) \
1398 p = growstackstr(); \
1399 *p = '\0'; \
1400 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001401#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001402#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001403#define STADJUST(amount, p) (p += (amount))
1404
1405#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1406#define ungrabstackstr(s, p) stunalloc((s))
1407#define stackstrend() ((void *)sstrend)
1408
1409
1410/* ============ String helpers */
1411
1412/*
1413 * prefix -- see if pfx is a prefix of string.
1414 */
1415static char *
1416prefix(const char *string, const char *pfx)
1417{
1418 while (*pfx) {
1419 if (*pfx++ != *string++)
1420 return 0;
1421 }
1422 return (char *) string;
1423}
1424
1425/*
1426 * Check for a valid number. This should be elsewhere.
1427 */
1428static int
1429is_number(const char *p)
1430{
1431 do {
1432 if (!isdigit(*p))
1433 return 0;
1434 } while (*++p != '\0');
1435 return 1;
1436}
1437
1438/*
1439 * Convert a string of digits to an integer, printing an error message on
1440 * failure.
1441 */
1442static int
1443number(const char *s)
1444{
1445 if (!is_number(s))
1446 ash_msg_and_raise_error(illnum, s);
1447 return atoi(s);
1448}
1449
1450/*
1451 * Produce a possibly single quoted string suitable as input to the shell.
1452 * The return string is allocated on the stack.
1453 */
1454static char *
1455single_quote(const char *s)
1456{
1457 char *p;
1458
1459 STARTSTACKSTR(p);
1460
1461 do {
1462 char *q;
1463 size_t len;
1464
1465 len = strchrnul(s, '\'') - s;
1466
1467 q = p = makestrspace(len + 3, p);
1468
1469 *q++ = '\'';
1470 q = memcpy(q, s, len) + len;
1471 *q++ = '\'';
1472 s += len;
1473
1474 STADJUST(q - p, p);
1475
1476 len = strspn(s, "'");
1477 if (!len)
1478 break;
1479
1480 q = p = makestrspace(len + 3, p);
1481
1482 *q++ = '"';
1483 q = memcpy(q, s, len) + len;
1484 *q++ = '"';
1485 s += len;
1486
1487 STADJUST(q - p, p);
1488 } while (*s);
1489
1490 USTPUTC(0, p);
1491
1492 return stackblock();
1493}
1494
1495
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001496/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001497
1498static char **argptr; /* argument list for builtin commands */
1499static char *optionarg; /* set by nextopt (like getopt) */
1500static char *optptr; /* used by nextopt */
1501
1502/*
1503 * XXX - should get rid of. have all builtins use getopt(3). the
1504 * library getopt must have the BSD extension static variable "optreset"
1505 * otherwise it can't be used within the shell safely.
1506 *
1507 * Standard option processing (a la getopt) for builtin routines. The
1508 * only argument that is passed to nextopt is the option string; the
1509 * other arguments are unnecessary. It return the character, or '\0' on
1510 * end of input.
1511 */
1512static int
1513nextopt(const char *optstring)
1514{
1515 char *p;
1516 const char *q;
1517 char c;
1518
1519 p = optptr;
1520 if (p == NULL || *p == '\0') {
1521 p = *argptr;
1522 if (p == NULL || *p != '-' || *++p == '\0')
1523 return '\0';
1524 argptr++;
1525 if (LONE_DASH(p)) /* check for "--" */
1526 return '\0';
1527 }
1528 c = *p++;
1529 for (q = optstring; *q != c; ) {
1530 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001531 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001532 if (*++q == ':')
1533 q++;
1534 }
1535 if (*++q == ':') {
1536 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001537 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001538 optionarg = p;
1539 p = NULL;
1540 }
1541 optptr = p;
1542 return c;
1543}
1544
1545
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001546/* ============ Math support definitions */
1547
1548#if ENABLE_ASH_MATH_SUPPORT_64
1549typedef int64_t arith_t;
1550#define arith_t_type long long
1551#else
1552typedef long arith_t;
1553#define arith_t_type long
1554#endif
1555
1556#if ENABLE_ASH_MATH_SUPPORT
1557static arith_t dash_arith(const char *);
1558static arith_t arith(const char *expr, int *perrcode);
1559#endif
1560
1561#if ENABLE_ASH_RANDOM_SUPPORT
1562static unsigned long rseed;
1563#ifndef DYNAMIC_VAR
1564#define DYNAMIC_VAR
1565#endif
1566#endif
1567
1568
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001569/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001570
1571/* flags */
1572#define VEXPORT 0x01 /* variable is exported */
1573#define VREADONLY 0x02 /* variable cannot be modified */
1574#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1575#define VTEXTFIXED 0x08 /* text is statically allocated */
1576#define VSTACK 0x10 /* text is allocated on the stack */
1577#define VUNSET 0x20 /* the variable is not set */
1578#define VNOFUNC 0x40 /* don't call the callback function */
1579#define VNOSET 0x80 /* do not set variable - just readonly test */
1580#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1581#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001582# define VDYNAMIC 0x200 /* dynamic variable */
1583#else
1584# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001585#endif
1586
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001587#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001588static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001589#define defifs (defifsvar + 4)
1590#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001591static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001592#endif
1593
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001594struct shparam {
1595 int nparam; /* # of positional parameters (without $0) */
1596 unsigned char malloc; /* if parameter list dynamically allocated */
1597 char **p; /* parameter list */
1598#if ENABLE_ASH_GETOPTS
1599 int optind; /* next parameter to be processed by getopts */
1600 int optoff; /* used by getopts */
1601#endif
1602};
1603
1604static struct shparam shellparam; /* $@ current positional parameters */
1605
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001606/*
1607 * Free the list of positional parameters.
1608 */
1609static void
1610freeparam(volatile struct shparam *param)
1611{
1612 char **ap;
1613
1614 if (param->malloc) {
1615 for (ap = param->p; *ap; ap++)
1616 free(*ap);
1617 free(param->p);
1618 }
1619}
1620
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001621#if ENABLE_ASH_GETOPTS
1622static void
1623getoptsreset(const char *value)
1624{
1625 shellparam.optind = number(value);
1626 shellparam.optoff = -1;
1627}
1628#endif
1629
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001630struct var {
1631 struct var *next; /* next entry in hash list */
1632 int flags; /* flags are defined above */
1633 const char *text; /* name=value */
1634 void (*func)(const char *); /* function to be called when */
1635 /* the variable gets set/unset */
1636};
1637
1638struct localvar {
1639 struct localvar *next; /* next local variable in list */
1640 struct var *vp; /* the variable that was made local */
1641 int flags; /* saved flags */
1642 const char *text; /* saved text */
1643};
1644
1645/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001646#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001647static void
1648change_lc_all(const char *value)
1649{
1650 if (value && *value != '\0')
1651 setlocale(LC_ALL, value);
1652}
1653static void
1654change_lc_ctype(const char *value)
1655{
1656 if (value && *value != '\0')
1657 setlocale(LC_CTYPE, value);
1658}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001659#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660#if ENABLE_ASH_MAIL
1661static void chkmail(void);
1662static void changemail(const char *);
1663#endif
1664static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001665#if ENABLE_ASH_RANDOM_SUPPORT
1666static void change_random(const char *);
1667#endif
1668
1669static struct var varinit[] = {
1670#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001671 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001672#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001673 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001674#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001675#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001676 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1677 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001678#endif
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00001679 { NULL, VSTRFIXED|VTEXTFIXED, bb_PATH_root_path, changepath },
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001680 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1681 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1682 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001683#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001684 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001685#endif
1686#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001687 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001688#endif
1689#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001690 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1691 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#endif
1693#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001694 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001695#endif
1696};
1697
1698#define vifs varinit[0]
1699#if ENABLE_ASH_MAIL
1700#define vmail (&vifs)[1]
1701#define vmpath (&vmail)[1]
1702#else
1703#define vmpath vifs
1704#endif
1705#define vpath (&vmpath)[1]
1706#define vps1 (&vpath)[1]
1707#define vps2 (&vps1)[1]
1708#define vps4 (&vps2)[1]
1709#define voptind (&vps4)[1]
1710#if ENABLE_ASH_GETOPTS
1711#define vrandom (&voptind)[1]
1712#else
1713#define vrandom (&vps4)[1]
1714#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715
1716/*
1717 * The following macros access the values of the above variables.
1718 * They have to skip over the name. They return the null string
1719 * for unset variables.
1720 */
1721#define ifsval() (vifs.text + 4)
1722#define ifsset() ((vifs.flags & VUNSET) == 0)
1723#define mailval() (vmail.text + 5)
1724#define mpathval() (vmpath.text + 9)
1725#define pathval() (vpath.text + 5)
1726#define ps1val() (vps1.text + 4)
1727#define ps2val() (vps2.text + 4)
1728#define ps4val() (vps4.text + 4)
1729#define optindval() (voptind.text + 7)
1730
1731#define mpathset() ((vmpath.flags & VUNSET) == 0)
1732
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733/*
1734 * The parsefile structure pointed to by the global variable parsefile
1735 * contains information about the current file being read.
1736 */
1737struct redirtab {
1738 struct redirtab *next;
1739 int renamed[10];
1740 int nullredirs;
1741};
1742
1743static struct redirtab *redirlist;
1744static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1746
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#define VTABSIZE 39
1748
1749static struct var *vartab[VTABSIZE];
1750
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1752#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1753
1754/*
1755 * Return of a legal variable name (a letter or underscore followed by zero or
1756 * more letters, underscores, and digits).
1757 */
1758static char *
1759endofname(const char *name)
1760{
1761 char *p;
1762
1763 p = (char *) name;
1764 if (!is_name(*p))
1765 return p;
1766 while (*++p) {
1767 if (!is_in_name(*p))
1768 break;
1769 }
1770 return p;
1771}
1772
1773/*
1774 * Compares two strings up to the first = or '\0'. The first
1775 * string must be terminated by '='; the second may be terminated by
1776 * either '=' or '\0'.
1777 */
1778static int
1779varcmp(const char *p, const char *q)
1780{
1781 int c, d;
1782
1783 while ((c = *p) == (d = *q)) {
1784 if (!c || c == '=')
1785 goto out;
1786 p++;
1787 q++;
1788 }
1789 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001790 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001792 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793 out:
1794 return c - d;
1795}
1796
1797static int
1798varequal(const char *a, const char *b)
1799{
1800 return !varcmp(a, b);
1801}
1802
1803/*
1804 * Find the appropriate entry in the hash table from the name.
1805 */
1806static struct var **
1807hashvar(const char *p)
1808{
1809 unsigned hashval;
1810
1811 hashval = ((unsigned char) *p) << 4;
1812 while (*p && *p != '=')
1813 hashval += (unsigned char) *p++;
1814 return &vartab[hashval % VTABSIZE];
1815}
1816
1817static int
1818vpcmp(const void *a, const void *b)
1819{
1820 return varcmp(*(const char **)a, *(const char **)b);
1821}
1822
1823/*
1824 * This routine initializes the builtin variables.
1825 */
1826static void
1827initvar(void)
1828{
1829 struct var *vp;
1830 struct var *end;
1831 struct var **vpp;
1832
1833 /*
1834 * PS1 depends on uid
1835 */
1836#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1837 vps1.text = "PS1=\\w \\$ ";
1838#else
1839 if (!geteuid())
1840 vps1.text = "PS1=# ";
1841#endif
1842 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001843 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844 do {
1845 vpp = hashvar(vp->text);
1846 vp->next = *vpp;
1847 *vpp = vp;
1848 } while (++vp < end);
1849}
1850
1851static struct var **
1852findvar(struct var **vpp, const char *name)
1853{
1854 for (; *vpp; vpp = &(*vpp)->next) {
1855 if (varequal((*vpp)->text, name)) {
1856 break;
1857 }
1858 }
1859 return vpp;
1860}
1861
1862/*
1863 * Find the value of a variable. Returns NULL if not set.
1864 */
1865static char *
1866lookupvar(const char *name)
1867{
1868 struct var *v;
1869
1870 v = *findvar(hashvar(name), name);
1871 if (v) {
1872#ifdef DYNAMIC_VAR
1873 /*
1874 * Dynamic variables are implemented roughly the same way they are
1875 * in bash. Namely, they're "special" so long as they aren't unset.
1876 * As soon as they're unset, they're no longer dynamic, and dynamic
1877 * lookup will no longer happen at that point. -- PFM.
1878 */
1879 if ((v->flags & VDYNAMIC))
1880 (*v->func)(NULL);
1881#endif
1882 if (!(v->flags & VUNSET))
1883 return strchrnul(v->text, '=') + 1;
1884 }
1885 return NULL;
1886}
1887
1888/*
1889 * Search the environment of a builtin command.
1890 */
1891static char *
1892bltinlookup(const char *name)
1893{
1894 struct strlist *sp;
1895
1896 for (sp = cmdenviron; sp; sp = sp->next) {
1897 if (varequal(sp->text, name))
1898 return strchrnul(sp->text, '=') + 1;
1899 }
1900 return lookupvar(name);
1901}
1902
1903/*
1904 * Same as setvar except that the variable and value are passed in
1905 * the first argument as name=value. Since the first argument will
1906 * be actually stored in the table, it should not be a string that
1907 * will go away.
1908 * Called with interrupts off.
1909 */
1910static void
1911setvareq(char *s, int flags)
1912{
1913 struct var *vp, **vpp;
1914
1915 vpp = hashvar(s);
1916 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1917 vp = *findvar(vpp, s);
1918 if (vp) {
1919 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1920 const char *n;
1921
1922 if (flags & VNOSAVE)
1923 free(s);
1924 n = vp->text;
1925 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1926 }
1927
1928 if (flags & VNOSET)
1929 return;
1930
1931 if (vp->func && (flags & VNOFUNC) == 0)
1932 (*vp->func)(strchrnul(s, '=') + 1);
1933
1934 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1935 free((char*)vp->text);
1936
1937 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1938 } else {
1939 if (flags & VNOSET)
1940 return;
1941 /* not found */
1942 vp = ckmalloc(sizeof(*vp));
1943 vp->next = *vpp;
1944 vp->func = NULL;
1945 *vpp = vp;
1946 }
1947 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1948 s = ckstrdup(s);
1949 vp->text = s;
1950 vp->flags = flags;
1951}
1952
1953/*
1954 * Set the value of a variable. The flags argument is ored with the
1955 * flags of the variable. If val is NULL, the variable is unset.
1956 */
1957static void
1958setvar(const char *name, const char *val, int flags)
1959{
1960 char *p, *q;
1961 size_t namelen;
1962 char *nameeq;
1963 size_t vallen;
1964
1965 q = endofname(name);
1966 p = strchrnul(q, '=');
1967 namelen = p - name;
1968 if (!namelen || p != q)
1969 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1970 vallen = 0;
1971 if (val == NULL) {
1972 flags |= VUNSET;
1973 } else {
1974 vallen = strlen(val);
1975 }
1976 INT_OFF;
1977 nameeq = ckmalloc(namelen + vallen + 2);
1978 p = memcpy(nameeq, name, namelen) + namelen;
1979 if (val) {
1980 *p++ = '=';
1981 p = memcpy(p, val, vallen) + vallen;
1982 }
1983 *p = '\0';
1984 setvareq(nameeq, flags | VNOSAVE);
1985 INT_ON;
1986}
1987
1988#if ENABLE_ASH_GETOPTS
1989/*
1990 * Safe version of setvar, returns 1 on success 0 on failure.
1991 */
1992static int
1993setvarsafe(const char *name, const char *val, int flags)
1994{
1995 int err;
1996 volatile int saveint;
1997 struct jmploc *volatile savehandler = exception_handler;
1998 struct jmploc jmploc;
1999
2000 SAVE_INT(saveint);
2001 if (setjmp(jmploc.loc))
2002 err = 1;
2003 else {
2004 exception_handler = &jmploc;
2005 setvar(name, val, flags);
2006 err = 0;
2007 }
2008 exception_handler = savehandler;
2009 RESTORE_INT(saveint);
2010 return err;
2011}
2012#endif
2013
2014/*
2015 * Unset the specified variable.
2016 */
2017static int
2018unsetvar(const char *s)
2019{
2020 struct var **vpp;
2021 struct var *vp;
2022 int retval;
2023
2024 vpp = findvar(hashvar(s), s);
2025 vp = *vpp;
2026 retval = 2;
2027 if (vp) {
2028 int flags = vp->flags;
2029
2030 retval = 1;
2031 if (flags & VREADONLY)
2032 goto out;
2033#ifdef DYNAMIC_VAR
2034 vp->flags &= ~VDYNAMIC;
2035#endif
2036 if (flags & VUNSET)
2037 goto ok;
2038 if ((flags & VSTRFIXED) == 0) {
2039 INT_OFF;
2040 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2041 free((char*)vp->text);
2042 *vpp = vp->next;
2043 free(vp);
2044 INT_ON;
2045 } else {
2046 setvar(s, 0, 0);
2047 vp->flags &= ~VEXPORT;
2048 }
2049 ok:
2050 retval = 0;
2051 }
2052 out:
2053 return retval;
2054}
2055
2056/*
2057 * Process a linked list of variable assignments.
2058 */
2059static void
2060listsetvar(struct strlist *list_set_var, int flags)
2061{
2062 struct strlist *lp = list_set_var;
2063
2064 if (!lp)
2065 return;
2066 INT_OFF;
2067 do {
2068 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002069 lp = lp->next;
2070 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002071 INT_ON;
2072}
2073
2074/*
2075 * Generate a list of variables satisfying the given conditions.
2076 */
2077static char **
2078listvars(int on, int off, char ***end)
2079{
2080 struct var **vpp;
2081 struct var *vp;
2082 char **ep;
2083 int mask;
2084
2085 STARTSTACKSTR(ep);
2086 vpp = vartab;
2087 mask = on | off;
2088 do {
2089 for (vp = *vpp; vp; vp = vp->next) {
2090 if ((vp->flags & mask) == on) {
2091 if (ep == stackstrend())
2092 ep = growstackstr();
2093 *ep++ = (char *) vp->text;
2094 }
2095 }
2096 } while (++vpp < vartab + VTABSIZE);
2097 if (ep == stackstrend())
2098 ep = growstackstr();
2099 if (end)
2100 *end = ep;
2101 *ep++ = NULL;
2102 return grabstackstr(ep);
2103}
2104
2105
2106/* ============ Path search helper
2107 *
2108 * The variable path (passed by reference) should be set to the start
2109 * of the path before the first call; padvance will update
2110 * this value as it proceeds. Successive calls to padvance will return
2111 * the possible path expansions in sequence. If an option (indicated by
2112 * a percent sign) appears in the path entry then the global variable
2113 * pathopt will be set to point to it; otherwise pathopt will be set to
2114 * NULL.
2115 */
2116static const char *pathopt; /* set by padvance */
2117
2118static char *
2119padvance(const char **path, const char *name)
2120{
2121 const char *p;
2122 char *q;
2123 const char *start;
2124 size_t len;
2125
2126 if (*path == NULL)
2127 return NULL;
2128 start = *path;
2129 for (p = start; *p && *p != ':' && *p != '%'; p++);
2130 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2131 while (stackblocksize() < len)
2132 growstackblock();
2133 q = stackblock();
2134 if (p != start) {
2135 memcpy(q, start, p - start);
2136 q += p - start;
2137 *q++ = '/';
2138 }
2139 strcpy(q, name);
2140 pathopt = NULL;
2141 if (*p == '%') {
2142 pathopt = ++p;
2143 while (*p && *p != ':') p++;
2144 }
2145 if (*p == ':')
2146 *path = p + 1;
2147 else
2148 *path = NULL;
2149 return stalloc(len);
2150}
2151
2152
2153/* ============ Prompt */
2154
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002155static smallint doprompt; /* if set, prompt the user */
2156static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002157
2158#if ENABLE_FEATURE_EDITING
2159static line_input_t *line_input_state;
2160static const char *cmdedit_prompt;
2161static void
2162putprompt(const char *s)
2163{
2164 if (ENABLE_ASH_EXPAND_PRMT) {
2165 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002166 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002167 return;
2168 }
2169 cmdedit_prompt = s;
2170}
2171#else
2172static void
2173putprompt(const char *s)
2174{
2175 out2str(s);
2176}
2177#endif
2178
2179#if ENABLE_ASH_EXPAND_PRMT
2180/* expandstr() needs parsing machinery, so it is far away ahead... */
2181static const char *expandstr(const char *ps);
2182#else
2183#define expandstr(s) s
2184#endif
2185
2186static void
2187setprompt(int whichprompt)
2188{
2189 const char *prompt;
2190#if ENABLE_ASH_EXPAND_PRMT
2191 struct stackmark smark;
2192#endif
2193
2194 needprompt = 0;
2195
2196 switch (whichprompt) {
2197 case 1:
2198 prompt = ps1val();
2199 break;
2200 case 2:
2201 prompt = ps2val();
2202 break;
2203 default: /* 0 */
2204 prompt = nullstr;
2205 }
2206#if ENABLE_ASH_EXPAND_PRMT
2207 setstackmark(&smark);
2208 stalloc(stackblocksize());
2209#endif
2210 putprompt(expandstr(prompt));
2211#if ENABLE_ASH_EXPAND_PRMT
2212 popstackmark(&smark);
2213#endif
2214}
2215
2216
2217/* ============ The cd and pwd commands */
2218
2219#define CD_PHYSICAL 1
2220#define CD_PRINT 2
2221
2222static int docd(const char *, int);
2223
2224static char *curdir = nullstr; /* current working directory */
2225static char *physdir = nullstr; /* physical working directory */
2226
2227static int
2228cdopt(void)
2229{
2230 int flags = 0;
2231 int i, j;
2232
2233 j = 'L';
2234 while ((i = nextopt("LP"))) {
2235 if (i != j) {
2236 flags ^= CD_PHYSICAL;
2237 j = i;
2238 }
2239 }
2240
2241 return flags;
2242}
2243
2244/*
2245 * Update curdir (the name of the current directory) in response to a
2246 * cd command.
2247 */
2248static const char *
2249updatepwd(const char *dir)
2250{
2251 char *new;
2252 char *p;
2253 char *cdcomppath;
2254 const char *lim;
2255
2256 cdcomppath = ststrdup(dir);
2257 STARTSTACKSTR(new);
2258 if (*dir != '/') {
2259 if (curdir == nullstr)
2260 return 0;
2261 new = stack_putstr(curdir, new);
2262 }
2263 new = makestrspace(strlen(dir) + 2, new);
2264 lim = stackblock() + 1;
2265 if (*dir != '/') {
2266 if (new[-1] != '/')
2267 USTPUTC('/', new);
2268 if (new > lim && *lim == '/')
2269 lim++;
2270 } else {
2271 USTPUTC('/', new);
2272 cdcomppath++;
2273 if (dir[1] == '/' && dir[2] != '/') {
2274 USTPUTC('/', new);
2275 cdcomppath++;
2276 lim++;
2277 }
2278 }
2279 p = strtok(cdcomppath, "/");
2280 while (p) {
2281 switch (*p) {
2282 case '.':
2283 if (p[1] == '.' && p[2] == '\0') {
2284 while (new > lim) {
2285 STUNPUTC(new);
2286 if (new[-1] == '/')
2287 break;
2288 }
2289 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002290 }
2291 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002292 break;
2293 /* fall through */
2294 default:
2295 new = stack_putstr(p, new);
2296 USTPUTC('/', new);
2297 }
2298 p = strtok(0, "/");
2299 }
2300 if (new > lim)
2301 STUNPUTC(new);
2302 *new = 0;
2303 return stackblock();
2304}
2305
2306/*
2307 * Find out what the current directory is. If we already know the current
2308 * directory, this routine returns immediately.
2309 */
2310static char *
2311getpwd(void)
2312{
2313 char *dir = getcwd(0, 0);
2314 return dir ? dir : nullstr;
2315}
2316
2317static void
2318setpwd(const char *val, int setold)
2319{
2320 char *oldcur, *dir;
2321
2322 oldcur = dir = curdir;
2323
2324 if (setold) {
2325 setvar("OLDPWD", oldcur, VEXPORT);
2326 }
2327 INT_OFF;
2328 if (physdir != nullstr) {
2329 if (physdir != oldcur)
2330 free(physdir);
2331 physdir = nullstr;
2332 }
2333 if (oldcur == val || !val) {
2334 char *s = getpwd();
2335 physdir = s;
2336 if (!val)
2337 dir = s;
2338 } else
2339 dir = ckstrdup(val);
2340 if (oldcur != dir && oldcur != nullstr) {
2341 free(oldcur);
2342 }
2343 curdir = dir;
2344 INT_ON;
2345 setvar("PWD", dir, VEXPORT);
2346}
2347
2348static void hashcd(void);
2349
2350/*
2351 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2352 * know that the current directory has changed.
2353 */
2354static int
2355docd(const char *dest, int flags)
2356{
2357 const char *dir = 0;
2358 int err;
2359
2360 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2361
2362 INT_OFF;
2363 if (!(flags & CD_PHYSICAL)) {
2364 dir = updatepwd(dest);
2365 if (dir)
2366 dest = dir;
2367 }
2368 err = chdir(dest);
2369 if (err)
2370 goto out;
2371 setpwd(dir, 1);
2372 hashcd();
2373 out:
2374 INT_ON;
2375 return err;
2376}
2377
2378static int
2379cdcmd(int argc, char **argv)
2380{
2381 const char *dest;
2382 const char *path;
2383 const char *p;
2384 char c;
2385 struct stat statb;
2386 int flags;
2387
2388 flags = cdopt();
2389 dest = *argptr;
2390 if (!dest)
2391 dest = bltinlookup(homestr);
2392 else if (LONE_DASH(dest)) {
2393 dest = bltinlookup("OLDPWD");
2394 flags |= CD_PRINT;
2395 }
2396 if (!dest)
2397 dest = nullstr;
2398 if (*dest == '/')
2399 goto step7;
2400 if (*dest == '.') {
2401 c = dest[1];
2402 dotdot:
2403 switch (c) {
2404 case '\0':
2405 case '/':
2406 goto step6;
2407 case '.':
2408 c = dest[2];
2409 if (c != '.')
2410 goto dotdot;
2411 }
2412 }
2413 if (!*dest)
2414 dest = ".";
2415 path = bltinlookup("CDPATH");
2416 if (!path) {
2417 step6:
2418 step7:
2419 p = dest;
2420 goto docd;
2421 }
2422 do {
2423 c = *path;
2424 p = padvance(&path, dest);
2425 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2426 if (c && c != ':')
2427 flags |= CD_PRINT;
2428 docd:
2429 if (!docd(p, flags))
2430 goto out;
2431 break;
2432 }
2433 } while (path);
2434 ash_msg_and_raise_error("can't cd to %s", dest);
2435 /* NOTREACHED */
2436 out:
2437 if (flags & CD_PRINT)
2438 out1fmt(snlfmt, curdir);
2439 return 0;
2440}
2441
2442static int
2443pwdcmd(int argc, char **argv)
2444{
2445 int flags;
2446 const char *dir = curdir;
2447
2448 flags = cdopt();
2449 if (flags) {
2450 if (physdir == nullstr)
2451 setpwd(dir, 0);
2452 dir = physdir;
2453 }
2454 out1fmt(snlfmt, dir);
2455 return 0;
2456}
2457
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002458
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002459/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002460
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002461#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002462#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002463
Eric Andersenc470f442003-07-28 09:56:35 +00002464/* Syntax classes */
2465#define CWORD 0 /* character is nothing special */
2466#define CNL 1 /* newline character */
2467#define CBACK 2 /* a backslash character */
2468#define CSQUOTE 3 /* single quote */
2469#define CDQUOTE 4 /* double quote */
2470#define CENDQUOTE 5 /* a terminating quote */
2471#define CBQUOTE 6 /* backwards single quote */
2472#define CVAR 7 /* a dollar sign */
2473#define CENDVAR 8 /* a '}' character */
2474#define CLP 9 /* a left paren in arithmetic */
2475#define CRP 10 /* a right paren in arithmetic */
2476#define CENDFILE 11 /* end of file */
2477#define CCTL 12 /* like CWORD, except it must be escaped */
2478#define CSPCL 13 /* these terminate a word */
2479#define CIGN 14 /* character should be ignored */
2480
Denis Vlasenko131ae172007-02-18 13:00:19 +00002481#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002482#define SYNBASE 130
2483#define PEOF -130
2484#define PEOA -129
2485#define PEOA_OR_PEOF PEOA
2486#else
2487#define SYNBASE 129
2488#define PEOF -129
2489#define PEOA_OR_PEOF PEOF
2490#endif
2491
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002492/* number syntax index */
2493#define BASESYNTAX 0 /* not in quotes */
2494#define DQSYNTAX 1 /* in double quotes */
2495#define SQSYNTAX 2 /* in single quotes */
2496#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002497
Denis Vlasenko131ae172007-02-18 13:00:19 +00002498#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002499#define USE_SIT_FUNCTION
2500#endif
2501
Denis Vlasenko131ae172007-02-18 13:00:19 +00002502#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002503static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002504#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002505 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002506#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002507 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2508 { CNL, CNL, CNL, CNL }, /* 2, \n */
2509 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2510 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2511 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2512 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2513 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2514 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2515 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2516 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2517 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002518#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002519 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2520 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2521 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002522#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002523};
Eric Andersenc470f442003-07-28 09:56:35 +00002524#else
2525static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002526#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002527 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002528#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002529 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2530 { CNL, CNL, CNL }, /* 2, \n */
2531 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2532 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2533 { CVAR, CVAR, CWORD }, /* 5, $ */
2534 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2535 { CSPCL, CWORD, CWORD }, /* 7, ( */
2536 { CSPCL, CWORD, CWORD }, /* 8, ) */
2537 { CBACK, CBACK, CCTL }, /* 9, \ */
2538 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2539 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002540#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002541 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2542 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2543 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002544#endif
2545};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002546#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002547
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002548#ifdef USE_SIT_FUNCTION
2549
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002550static int
2551SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002552{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002553 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002554#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002555 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002556 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2557 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2558 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2559 11, 3 /* "}~" */
2560 };
2561#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002562 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002563 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2564 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2565 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2566 10, 2 /* "}~" */
2567 };
2568#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002569 const char *s;
2570 int indx;
2571
Eric Andersenc470f442003-07-28 09:56:35 +00002572 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002573 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002574#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002575 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002576 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002577 else
2578#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002579#define U_C(c) ((unsigned char)(c))
2580
2581 if ((unsigned char)c >= (unsigned char)(CTLESC)
2582 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2583 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002584 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002585 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002586 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002587 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002588 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002589 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002590 }
2591 return S_I_T[indx][syntax];
2592}
2593
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002594#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002595
Denis Vlasenko131ae172007-02-18 13:00:19 +00002596#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002597#define CSPCL_CIGN_CIGN_CIGN 0
2598#define CSPCL_CWORD_CWORD_CWORD 1
2599#define CNL_CNL_CNL_CNL 2
2600#define CWORD_CCTL_CCTL_CWORD 3
2601#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2602#define CVAR_CVAR_CWORD_CVAR 5
2603#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2604#define CSPCL_CWORD_CWORD_CLP 7
2605#define CSPCL_CWORD_CWORD_CRP 8
2606#define CBACK_CBACK_CCTL_CBACK 9
2607#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2608#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2609#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2610#define CWORD_CWORD_CWORD_CWORD 13
2611#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002612#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002613#define CSPCL_CWORD_CWORD_CWORD 0
2614#define CNL_CNL_CNL_CNL 1
2615#define CWORD_CCTL_CCTL_CWORD 2
2616#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2617#define CVAR_CVAR_CWORD_CVAR 4
2618#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2619#define CSPCL_CWORD_CWORD_CLP 6
2620#define CSPCL_CWORD_CWORD_CRP 7
2621#define CBACK_CBACK_CCTL_CBACK 8
2622#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2623#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2624#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2625#define CWORD_CWORD_CWORD_CWORD 12
2626#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002627#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002628
2629static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002630 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002631 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002632#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002633 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2634#endif
2635 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2636 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2637 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2638 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2639 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2640 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2641 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2642 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2643 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002644 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2645 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2646 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2647 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2648 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2649 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2773 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2774 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2796 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002797 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002798 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2799 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2800 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2801 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002802 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002803 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2804 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2805 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2806 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2807 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2808 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2809 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2810 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2811 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2812 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2814 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2815 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2816 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2821 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2822 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2823 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2824 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2825 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2826 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2827 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2828 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2829 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2830 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2831 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2832 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2855 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2856 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2857 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2860 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2861 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2862 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2888 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2889 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002891};
2892
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002893#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2894
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002895#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002896
Eric Andersen2870d962001-07-02 17:27:21 +00002897
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002898/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002899
Denis Vlasenko131ae172007-02-18 13:00:19 +00002900#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002901
2902#define ALIASINUSE 1
2903#define ALIASDEAD 2
2904
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002905#define ATABSIZE 39
2906
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002907struct alias {
2908 struct alias *next;
2909 char *name;
2910 char *val;
2911 int flag;
2912};
2913
Eric Andersen2870d962001-07-02 17:27:21 +00002914static struct alias *atab[ATABSIZE];
2915
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002916static struct alias **
2917__lookupalias(const char *name) {
2918 unsigned int hashval;
2919 struct alias **app;
2920 const char *p;
2921 unsigned int ch;
2922
2923 p = name;
2924
2925 ch = (unsigned char)*p;
2926 hashval = ch << 4;
2927 while (ch) {
2928 hashval += ch;
2929 ch = (unsigned char)*++p;
2930 }
2931 app = &atab[hashval % ATABSIZE];
2932
2933 for (; *app; app = &(*app)->next) {
2934 if (strcmp(name, (*app)->name) == 0) {
2935 break;
2936 }
2937 }
2938
2939 return app;
2940}
2941
2942static struct alias *
2943lookupalias(const char *name, int check)
2944{
2945 struct alias *ap = *__lookupalias(name);
2946
2947 if (check && ap && (ap->flag & ALIASINUSE))
2948 return NULL;
2949 return ap;
2950}
2951
2952static struct alias *
2953freealias(struct alias *ap)
2954{
2955 struct alias *next;
2956
2957 if (ap->flag & ALIASINUSE) {
2958 ap->flag |= ALIASDEAD;
2959 return ap;
2960 }
2961
2962 next = ap->next;
2963 free(ap->name);
2964 free(ap->val);
2965 free(ap);
2966 return next;
2967}
Eric Andersencb57d552001-06-28 07:25:16 +00002968
Eric Andersenc470f442003-07-28 09:56:35 +00002969static void
2970setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002971{
2972 struct alias *ap, **app;
2973
2974 app = __lookupalias(name);
2975 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002976 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002977 if (ap) {
2978 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002979 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002980 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002981 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002982 ap->flag &= ~ALIASDEAD;
2983 } else {
2984 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002985 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002986 ap->name = ckstrdup(name);
2987 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002988 ap->flag = 0;
2989 ap->next = 0;
2990 *app = ap;
2991 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002992 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002993}
2994
Eric Andersenc470f442003-07-28 09:56:35 +00002995static int
2996unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00002997{
Eric Andersencb57d552001-06-28 07:25:16 +00002998 struct alias **app;
2999
3000 app = __lookupalias(name);
3001
3002 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003003 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003004 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003005 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003006 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003007 }
3008
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003009 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003010}
3011
Eric Andersenc470f442003-07-28 09:56:35 +00003012static void
3013rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003014{
Eric Andersencb57d552001-06-28 07:25:16 +00003015 struct alias *ap, **app;
3016 int i;
3017
Denis Vlasenkob012b102007-02-19 22:43:01 +00003018 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003019 for (i = 0; i < ATABSIZE; i++) {
3020 app = &atab[i];
3021 for (ap = *app; ap; ap = *app) {
3022 *app = freealias(*app);
3023 if (ap == *app) {
3024 app = &ap->next;
3025 }
3026 }
3027 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003028 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003029}
3030
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003031static void
3032printalias(const struct alias *ap)
3033{
3034 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3035}
3036
Eric Andersencb57d552001-06-28 07:25:16 +00003037/*
3038 * TODO - sort output
3039 */
Eric Andersenc470f442003-07-28 09:56:35 +00003040static int
3041aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003042{
3043 char *n, *v;
3044 int ret = 0;
3045 struct alias *ap;
3046
3047 if (argc == 1) {
3048 int i;
3049
3050 for (i = 0; i < ATABSIZE; i++)
3051 for (ap = atab[i]; ap; ap = ap->next) {
3052 printalias(ap);
3053 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003054 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003055 }
3056 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003057 v = strchr(n+1, '=');
3058 if (v == NULL) { /* n+1: funny ksh stuff */
3059 ap = *__lookupalias(n);
3060 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003061 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003062 ret = 1;
3063 } else
3064 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003065 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003066 *v++ = '\0';
3067 setalias(n, v);
3068 }
3069 }
3070
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003071 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003072}
3073
Eric Andersenc470f442003-07-28 09:56:35 +00003074static int
3075unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003076{
3077 int i;
3078
3079 while ((i = nextopt("a")) != '\0') {
3080 if (i == 'a') {
3081 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003082 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003083 }
3084 }
3085 for (i = 0; *argptr; argptr++) {
3086 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003087 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003088 i = 1;
3089 }
3090 }
3091
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003092 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003093}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003094
Denis Vlasenko131ae172007-02-18 13:00:19 +00003095#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003096
Eric Andersenc470f442003-07-28 09:56:35 +00003097
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003098/* ============ jobs.c */
3099
3100/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3101#define FORK_FG 0
3102#define FORK_BG 1
3103#define FORK_NOJOB 2
3104
3105/* mode flags for showjob(s) */
3106#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3107#define SHOW_PID 0x04 /* include process pid */
3108#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3109
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003110/*
3111 * A job structure contains information about a job. A job is either a
3112 * single process or a set of processes contained in a pipeline. In the
3113 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3114 * array of pids.
3115 */
3116
3117struct procstat {
3118 pid_t pid; /* process id */
3119 int status; /* last process status from wait() */
3120 char *cmd; /* text of command being run */
3121};
3122
3123struct job {
3124 struct procstat ps0; /* status of process */
3125 struct procstat *ps; /* status or processes when more than one */
3126#if JOBS
3127 int stopstatus; /* status of a stopped job */
3128#endif
3129 uint32_t
3130 nprocs: 16, /* number of processes */
3131 state: 8,
3132#define JOBRUNNING 0 /* at least one proc running */
3133#define JOBSTOPPED 1 /* all procs are stopped */
3134#define JOBDONE 2 /* all procs are completed */
3135#if JOBS
3136 sigint: 1, /* job was killed by SIGINT */
3137 jobctl: 1, /* job running under job control */
3138#endif
3139 waited: 1, /* true if this entry has been waited for */
3140 used: 1, /* true if this entry is in used */
3141 changed: 1; /* true if status has changed */
3142 struct job *prev_job; /* previous job */
3143};
3144
3145static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003146static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003147
3148static struct job *makejob(union node *, int);
3149static int forkshell(struct job *, union node *, int);
3150static int waitforjob(struct job *);
3151
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003152#if !JOBS
3153enum { jobctl = 0 };
3154#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003155#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003156static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003157static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003158#endif
3159
3160/*
3161 * Set the signal handler for the specified signal. The routine figures
3162 * out what it should be set to.
3163 */
3164static void
3165setsignal(int signo)
3166{
3167 int action;
3168 char *t, tsig;
3169 struct sigaction act;
3170
3171 t = trap[signo];
3172 if (t == NULL)
3173 action = S_DFL;
3174 else if (*t != '\0')
3175 action = S_CATCH;
3176 else
3177 action = S_IGN;
3178 if (rootshell && action == S_DFL) {
3179 switch (signo) {
3180 case SIGINT:
3181 if (iflag || minusc || sflag == 0)
3182 action = S_CATCH;
3183 break;
3184 case SIGQUIT:
3185#if DEBUG
3186 if (debug)
3187 break;
3188#endif
3189 /* FALLTHROUGH */
3190 case SIGTERM:
3191 if (iflag)
3192 action = S_IGN;
3193 break;
3194#if JOBS
3195 case SIGTSTP:
3196 case SIGTTOU:
3197 if (mflag)
3198 action = S_IGN;
3199 break;
3200#endif
3201 }
3202 }
3203
3204 t = &sigmode[signo - 1];
3205 tsig = *t;
3206 if (tsig == 0) {
3207 /*
3208 * current setting unknown
3209 */
3210 if (sigaction(signo, 0, &act) == -1) {
3211 /*
3212 * Pretend it worked; maybe we should give a warning
3213 * here, but other shells don't. We don't alter
3214 * sigmode, so that we retry every time.
3215 */
3216 return;
3217 }
3218 if (act.sa_handler == SIG_IGN) {
3219 if (mflag
3220 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3221 ) {
3222 tsig = S_IGN; /* don't hard ignore these */
3223 } else
3224 tsig = S_HARD_IGN;
3225 } else {
3226 tsig = S_RESET; /* force to be set */
3227 }
3228 }
3229 if (tsig == S_HARD_IGN || tsig == action)
3230 return;
3231 switch (action) {
3232 case S_CATCH:
3233 act.sa_handler = onsig;
3234 break;
3235 case S_IGN:
3236 act.sa_handler = SIG_IGN;
3237 break;
3238 default:
3239 act.sa_handler = SIG_DFL;
3240 }
3241 *t = action;
3242 act.sa_flags = 0;
3243 sigfillset(&act.sa_mask);
3244 sigaction(signo, &act, 0);
3245}
3246
3247/* mode flags for set_curjob */
3248#define CUR_DELETE 2
3249#define CUR_RUNNING 1
3250#define CUR_STOPPED 0
3251
3252/* mode flags for dowait */
3253#define DOWAIT_NORMAL 0
3254#define DOWAIT_BLOCK 1
3255
3256#if JOBS
3257/* pgrp of shell on invocation */
3258static int initialpgrp;
3259static int ttyfd = -1;
3260#endif
3261/* array of jobs */
3262static struct job *jobtab;
3263/* size of array */
3264static unsigned njobs;
3265/* current job */
3266static struct job *curjob;
3267/* number of presumed living untracked jobs */
3268static int jobless;
3269
3270static void
3271set_curjob(struct job *jp, unsigned mode)
3272{
3273 struct job *jp1;
3274 struct job **jpp, **curp;
3275
3276 /* first remove from list */
3277 jpp = curp = &curjob;
3278 do {
3279 jp1 = *jpp;
3280 if (jp1 == jp)
3281 break;
3282 jpp = &jp1->prev_job;
3283 } while (1);
3284 *jpp = jp1->prev_job;
3285
3286 /* Then re-insert in correct position */
3287 jpp = curp;
3288 switch (mode) {
3289 default:
3290#if DEBUG
3291 abort();
3292#endif
3293 case CUR_DELETE:
3294 /* job being deleted */
3295 break;
3296 case CUR_RUNNING:
3297 /* newly created job or backgrounded job,
3298 put after all stopped jobs. */
3299 do {
3300 jp1 = *jpp;
3301#if JOBS
3302 if (!jp1 || jp1->state != JOBSTOPPED)
3303#endif
3304 break;
3305 jpp = &jp1->prev_job;
3306 } while (1);
3307 /* FALLTHROUGH */
3308#if JOBS
3309 case CUR_STOPPED:
3310#endif
3311 /* newly stopped job - becomes curjob */
3312 jp->prev_job = *jpp;
3313 *jpp = jp;
3314 break;
3315 }
3316}
3317
3318#if JOBS || DEBUG
3319static int
3320jobno(const struct job *jp)
3321{
3322 return jp - jobtab + 1;
3323}
3324#endif
3325
3326/*
3327 * Convert a job name to a job structure.
3328 */
3329static struct job *
3330getjob(const char *name, int getctl)
3331{
3332 struct job *jp;
3333 struct job *found;
3334 const char *err_msg = "No such job: %s";
3335 unsigned num;
3336 int c;
3337 const char *p;
3338 char *(*match)(const char *, const char *);
3339
3340 jp = curjob;
3341 p = name;
3342 if (!p)
3343 goto currentjob;
3344
3345 if (*p != '%')
3346 goto err;
3347
3348 c = *++p;
3349 if (!c)
3350 goto currentjob;
3351
3352 if (!p[1]) {
3353 if (c == '+' || c == '%') {
3354 currentjob:
3355 err_msg = "No current job";
3356 goto check;
3357 }
3358 if (c == '-') {
3359 if (jp)
3360 jp = jp->prev_job;
3361 err_msg = "No previous job";
3362 check:
3363 if (!jp)
3364 goto err;
3365 goto gotit;
3366 }
3367 }
3368
3369 if (is_number(p)) {
3370 num = atoi(p);
3371 if (num < njobs) {
3372 jp = jobtab + num - 1;
3373 if (jp->used)
3374 goto gotit;
3375 goto err;
3376 }
3377 }
3378
3379 match = prefix;
3380 if (*p == '?') {
3381 match = strstr;
3382 p++;
3383 }
3384
3385 found = 0;
3386 while (1) {
3387 if (!jp)
3388 goto err;
3389 if (match(jp->ps[0].cmd, p)) {
3390 if (found)
3391 goto err;
3392 found = jp;
3393 err_msg = "%s: ambiguous";
3394 }
3395 jp = jp->prev_job;
3396 }
3397
3398 gotit:
3399#if JOBS
3400 err_msg = "job %s not created under job control";
3401 if (getctl && jp->jobctl == 0)
3402 goto err;
3403#endif
3404 return jp;
3405 err:
3406 ash_msg_and_raise_error(err_msg, name);
3407}
3408
3409/*
3410 * Mark a job structure as unused.
3411 */
3412static void
3413freejob(struct job *jp)
3414{
3415 struct procstat *ps;
3416 int i;
3417
3418 INT_OFF;
3419 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3420 if (ps->cmd != nullstr)
3421 free(ps->cmd);
3422 }
3423 if (jp->ps != &jp->ps0)
3424 free(jp->ps);
3425 jp->used = 0;
3426 set_curjob(jp, CUR_DELETE);
3427 INT_ON;
3428}
3429
3430#if JOBS
3431static void
3432xtcsetpgrp(int fd, pid_t pgrp)
3433{
3434 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003435 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003436}
3437
3438/*
3439 * Turn job control on and off.
3440 *
3441 * Note: This code assumes that the third arg to ioctl is a character
3442 * pointer, which is true on Berkeley systems but not System V. Since
3443 * System V doesn't have job control yet, this isn't a problem now.
3444 *
3445 * Called with interrupts off.
3446 */
3447static void
3448setjobctl(int on)
3449{
3450 int fd;
3451 int pgrp;
3452
3453 if (on == jobctl || rootshell == 0)
3454 return;
3455 if (on) {
3456 int ofd;
3457 ofd = fd = open(_PATH_TTY, O_RDWR);
3458 if (fd < 0) {
3459 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3460 * That sometimes helps to acquire controlling tty.
3461 * Obviously, a workaround for bugs when someone
3462 * failed to provide a controlling tty to bash! :) */
3463 fd += 3;
3464 while (!isatty(fd) && --fd >= 0)
3465 ;
3466 }
3467 fd = fcntl(fd, F_DUPFD, 10);
3468 close(ofd);
3469 if (fd < 0)
3470 goto out;
3471 fcntl(fd, F_SETFD, FD_CLOEXEC);
3472 do { /* while we are in the background */
3473 pgrp = tcgetpgrp(fd);
3474 if (pgrp < 0) {
3475 out:
3476 ash_msg("can't access tty; job control turned off");
3477 mflag = on = 0;
3478 goto close;
3479 }
3480 if (pgrp == getpgrp())
3481 break;
3482 killpg(0, SIGTTIN);
3483 } while (1);
3484 initialpgrp = pgrp;
3485
3486 setsignal(SIGTSTP);
3487 setsignal(SIGTTOU);
3488 setsignal(SIGTTIN);
3489 pgrp = rootpid;
3490 setpgid(0, pgrp);
3491 xtcsetpgrp(fd, pgrp);
3492 } else {
3493 /* turning job control off */
3494 fd = ttyfd;
3495 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003496 /* was xtcsetpgrp, but this can make exiting ash
3497 * with pty already deleted loop forever */
3498 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003499 setpgid(0, pgrp);
3500 setsignal(SIGTSTP);
3501 setsignal(SIGTTOU);
3502 setsignal(SIGTTIN);
3503 close:
3504 close(fd);
3505 fd = -1;
3506 }
3507 ttyfd = fd;
3508 jobctl = on;
3509}
3510
3511static int
3512killcmd(int argc, char **argv)
3513{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003514 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3515 int i = 1;
3516 do {
3517 if (argv[i][0] == '%') {
3518 struct job *jp = getjob(argv[i], 0);
3519 unsigned pid = jp->ps[0].pid;
3520 /* Enough space for ' -NNN<nul>' */
3521 argv[i] = alloca(sizeof(int)*3 + 3);
3522 /* kill_main has matching code to expect
3523 * leading space. Needed to not confuse
3524 * negative pids with "kill -SIGNAL_NO" syntax */
3525 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003526 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003527 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003528 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003529 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003530}
3531
3532static void
3533showpipe(struct job *jp, FILE *out)
3534{
3535 struct procstat *sp;
3536 struct procstat *spend;
3537
3538 spend = jp->ps + jp->nprocs;
3539 for (sp = jp->ps + 1; sp < spend; sp++)
3540 fprintf(out, " | %s", sp->cmd);
3541 outcslow('\n', out);
3542 flush_stdout_stderr();
3543}
3544
3545
3546static int
3547restartjob(struct job *jp, int mode)
3548{
3549 struct procstat *ps;
3550 int i;
3551 int status;
3552 pid_t pgid;
3553
3554 INT_OFF;
3555 if (jp->state == JOBDONE)
3556 goto out;
3557 jp->state = JOBRUNNING;
3558 pgid = jp->ps->pid;
3559 if (mode == FORK_FG)
3560 xtcsetpgrp(ttyfd, pgid);
3561 killpg(pgid, SIGCONT);
3562 ps = jp->ps;
3563 i = jp->nprocs;
3564 do {
3565 if (WIFSTOPPED(ps->status)) {
3566 ps->status = -1;
3567 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003568 ps++;
3569 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003570 out:
3571 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3572 INT_ON;
3573 return status;
3574}
3575
3576static int
3577fg_bgcmd(int argc, char **argv)
3578{
3579 struct job *jp;
3580 FILE *out;
3581 int mode;
3582 int retval;
3583
3584 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3585 nextopt(nullstr);
3586 argv = argptr;
3587 out = stdout;
3588 do {
3589 jp = getjob(*argv, 1);
3590 if (mode == FORK_BG) {
3591 set_curjob(jp, CUR_RUNNING);
3592 fprintf(out, "[%d] ", jobno(jp));
3593 }
3594 outstr(jp->ps->cmd, out);
3595 showpipe(jp, out);
3596 retval = restartjob(jp, mode);
3597 } while (*argv && *++argv);
3598 return retval;
3599}
3600#endif
3601
3602static int
3603sprint_status(char *s, int status, int sigonly)
3604{
3605 int col;
3606 int st;
3607
3608 col = 0;
3609 if (!WIFEXITED(status)) {
3610#if JOBS
3611 if (WIFSTOPPED(status))
3612 st = WSTOPSIG(status);
3613 else
3614#endif
3615 st = WTERMSIG(status);
3616 if (sigonly) {
3617 if (st == SIGINT || st == SIGPIPE)
3618 goto out;
3619#if JOBS
3620 if (WIFSTOPPED(status))
3621 goto out;
3622#endif
3623 }
3624 st &= 0x7f;
3625 col = fmtstr(s, 32, strsignal(st));
3626 if (WCOREDUMP(status)) {
3627 col += fmtstr(s + col, 16, " (core dumped)");
3628 }
3629 } else if (!sigonly) {
3630 st = WEXITSTATUS(status);
3631 if (st)
3632 col = fmtstr(s, 16, "Done(%d)", st);
3633 else
3634 col = fmtstr(s, 16, "Done");
3635 }
3636 out:
3637 return col;
3638}
3639
3640/*
3641 * Do a wait system call. If job control is compiled in, we accept
3642 * stopped processes. If block is zero, we return a value of zero
3643 * rather than blocking.
3644 *
3645 * System V doesn't have a non-blocking wait system call. It does
3646 * have a SIGCLD signal that is sent to a process when one of it's
3647 * children dies. The obvious way to use SIGCLD would be to install
3648 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3649 * was received, and have waitproc bump another counter when it got
3650 * the status of a process. Waitproc would then know that a wait
3651 * system call would not block if the two counters were different.
3652 * This approach doesn't work because if a process has children that
3653 * have not been waited for, System V will send it a SIGCLD when it
3654 * installs a signal handler for SIGCLD. What this means is that when
3655 * a child exits, the shell will be sent SIGCLD signals continuously
3656 * until is runs out of stack space, unless it does a wait call before
3657 * restoring the signal handler. The code below takes advantage of
3658 * this (mis)feature by installing a signal handler for SIGCLD and
3659 * then checking to see whether it was called. If there are any
3660 * children to be waited for, it will be.
3661 *
3662 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3663 * waits at all. In this case, the user will not be informed when
3664 * a background process until the next time she runs a real program
3665 * (as opposed to running a builtin command or just typing return),
3666 * and the jobs command may give out of date information.
3667 */
3668static int
3669waitproc(int block, int *status)
3670{
3671 int flags = 0;
3672
3673#if JOBS
3674 if (jobctl)
3675 flags |= WUNTRACED;
3676#endif
3677 if (block == 0)
3678 flags |= WNOHANG;
3679 return wait3(status, flags, (struct rusage *)NULL);
3680}
3681
3682/*
3683 * Wait for a process to terminate.
3684 */
3685static int
3686dowait(int block, struct job *job)
3687{
3688 int pid;
3689 int status;
3690 struct job *jp;
3691 struct job *thisjob;
3692 int state;
3693
3694 TRACE(("dowait(%d) called\n", block));
3695 pid = waitproc(block, &status);
3696 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3697 if (pid <= 0)
3698 return pid;
3699 INT_OFF;
3700 thisjob = NULL;
3701 for (jp = curjob; jp; jp = jp->prev_job) {
3702 struct procstat *sp;
3703 struct procstat *spend;
3704 if (jp->state == JOBDONE)
3705 continue;
3706 state = JOBDONE;
3707 spend = jp->ps + jp->nprocs;
3708 sp = jp->ps;
3709 do {
3710 if (sp->pid == pid) {
3711 TRACE(("Job %d: changing status of proc %d "
3712 "from 0x%x to 0x%x\n",
3713 jobno(jp), pid, sp->status, status));
3714 sp->status = status;
3715 thisjob = jp;
3716 }
3717 if (sp->status == -1)
3718 state = JOBRUNNING;
3719#if JOBS
3720 if (state == JOBRUNNING)
3721 continue;
3722 if (WIFSTOPPED(sp->status)) {
3723 jp->stopstatus = sp->status;
3724 state = JOBSTOPPED;
3725 }
3726#endif
3727 } while (++sp < spend);
3728 if (thisjob)
3729 goto gotjob;
3730 }
3731#if JOBS
3732 if (!WIFSTOPPED(status))
3733#endif
3734
3735 jobless--;
3736 goto out;
3737
3738 gotjob:
3739 if (state != JOBRUNNING) {
3740 thisjob->changed = 1;
3741
3742 if (thisjob->state != state) {
3743 TRACE(("Job %d: changing state from %d to %d\n",
3744 jobno(thisjob), thisjob->state, state));
3745 thisjob->state = state;
3746#if JOBS
3747 if (state == JOBSTOPPED) {
3748 set_curjob(thisjob, CUR_STOPPED);
3749 }
3750#endif
3751 }
3752 }
3753
3754 out:
3755 INT_ON;
3756
3757 if (thisjob && thisjob == job) {
3758 char s[48 + 1];
3759 int len;
3760
3761 len = sprint_status(s, status, 1);
3762 if (len) {
3763 s[len] = '\n';
3764 s[len + 1] = 0;
3765 out2str(s);
3766 }
3767 }
3768 return pid;
3769}
3770
3771#if JOBS
3772static void
3773showjob(FILE *out, struct job *jp, int mode)
3774{
3775 struct procstat *ps;
3776 struct procstat *psend;
3777 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003778 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779 char s[80];
3780
3781 ps = jp->ps;
3782
3783 if (mode & SHOW_PGID) {
3784 /* just output process (group) id of pipeline */
3785 fprintf(out, "%d\n", ps->pid);
3786 return;
3787 }
3788
3789 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003790 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791
3792 if (jp == curjob)
3793 s[col - 2] = '+';
3794 else if (curjob && jp == curjob->prev_job)
3795 s[col - 2] = '-';
3796
3797 if (mode & SHOW_PID)
3798 col += fmtstr(s + col, 16, "%d ", ps->pid);
3799
3800 psend = ps + jp->nprocs;
3801
3802 if (jp->state == JOBRUNNING) {
3803 strcpy(s + col, "Running");
3804 col += sizeof("Running") - 1;
3805 } else {
3806 int status = psend[-1].status;
3807 if (jp->state == JOBSTOPPED)
3808 status = jp->stopstatus;
3809 col += sprint_status(s + col, status, 0);
3810 }
3811
3812 goto start;
3813
3814 do {
3815 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003816 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003817 start:
3818 fprintf(out, "%s%*c%s",
3819 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3820 );
3821 if (!(mode & SHOW_PID)) {
3822 showpipe(jp, out);
3823 break;
3824 }
3825 if (++ps == psend) {
3826 outcslow('\n', out);
3827 break;
3828 }
3829 } while (1);
3830
3831 jp->changed = 0;
3832
3833 if (jp->state == JOBDONE) {
3834 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3835 freejob(jp);
3836 }
3837}
3838
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839/*
3840 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3841 * statuses have changed since the last call to showjobs.
3842 */
3843static void
3844showjobs(FILE *out, int mode)
3845{
3846 struct job *jp;
3847
3848 TRACE(("showjobs(%x) called\n", mode));
3849
3850 /* If not even one one job changed, there is nothing to do */
3851 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3852 continue;
3853
3854 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003855 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003856 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003857 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003858 }
3859}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003860
3861static int
3862jobscmd(int argc, char **argv)
3863{
3864 int mode, m;
3865
3866 mode = 0;
3867 while ((m = nextopt("lp"))) {
3868 if (m == 'l')
3869 mode = SHOW_PID;
3870 else
3871 mode = SHOW_PGID;
3872 }
3873
3874 argv = argptr;
3875 if (*argv) {
3876 do
3877 showjob(stdout, getjob(*argv,0), mode);
3878 while (*++argv);
3879 } else
3880 showjobs(stdout, mode);
3881
3882 return 0;
3883}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884#endif /* JOBS */
3885
3886static int
3887getstatus(struct job *job)
3888{
3889 int status;
3890 int retval;
3891
3892 status = job->ps[job->nprocs - 1].status;
3893 retval = WEXITSTATUS(status);
3894 if (!WIFEXITED(status)) {
3895#if JOBS
3896 retval = WSTOPSIG(status);
3897 if (!WIFSTOPPED(status))
3898#endif
3899 {
3900 /* XXX: limits number of signals */
3901 retval = WTERMSIG(status);
3902#if JOBS
3903 if (retval == SIGINT)
3904 job->sigint = 1;
3905#endif
3906 }
3907 retval += 128;
3908 }
3909 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3910 jobno(job), job->nprocs, status, retval));
3911 return retval;
3912}
3913
3914static int
3915waitcmd(int argc, char **argv)
3916{
3917 struct job *job;
3918 int retval;
3919 struct job *jp;
3920
3921 EXSIGON;
3922
3923 nextopt(nullstr);
3924 retval = 0;
3925
3926 argv = argptr;
3927 if (!*argv) {
3928 /* wait for all jobs */
3929 for (;;) {
3930 jp = curjob;
3931 while (1) {
3932 if (!jp) {
3933 /* no running procs */
3934 goto out;
3935 }
3936 if (jp->state == JOBRUNNING)
3937 break;
3938 jp->waited = 1;
3939 jp = jp->prev_job;
3940 }
3941 dowait(DOWAIT_BLOCK, 0);
3942 }
3943 }
3944
3945 retval = 127;
3946 do {
3947 if (**argv != '%') {
3948 pid_t pid = number(*argv);
3949 job = curjob;
3950 goto start;
3951 do {
3952 if (job->ps[job->nprocs - 1].pid == pid)
3953 break;
3954 job = job->prev_job;
3955 start:
3956 if (!job)
3957 goto repeat;
3958 } while (1);
3959 } else
3960 job = getjob(*argv, 0);
3961 /* loop until process terminated or stopped */
3962 while (job->state == JOBRUNNING)
3963 dowait(DOWAIT_BLOCK, 0);
3964 job->waited = 1;
3965 retval = getstatus(job);
3966 repeat:
3967 ;
3968 } while (*++argv);
3969
3970 out:
3971 return retval;
3972}
3973
3974static struct job *
3975growjobtab(void)
3976{
3977 size_t len;
3978 ptrdiff_t offset;
3979 struct job *jp, *jq;
3980
3981 len = njobs * sizeof(*jp);
3982 jq = jobtab;
3983 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3984
3985 offset = (char *)jp - (char *)jq;
3986 if (offset) {
3987 /* Relocate pointers */
3988 size_t l = len;
3989
3990 jq = (struct job *)((char *)jq + l);
3991 while (l) {
3992 l -= sizeof(*jp);
3993 jq--;
3994#define joff(p) ((struct job *)((char *)(p) + l))
3995#define jmove(p) (p) = (void *)((char *)(p) + offset)
3996 if (joff(jp)->ps == &jq->ps0)
3997 jmove(joff(jp)->ps);
3998 if (joff(jp)->prev_job)
3999 jmove(joff(jp)->prev_job);
4000 }
4001 if (curjob)
4002 jmove(curjob);
4003#undef joff
4004#undef jmove
4005 }
4006
4007 njobs += 4;
4008 jobtab = jp;
4009 jp = (struct job *)((char *)jp + len);
4010 jq = jp + 3;
4011 do {
4012 jq->used = 0;
4013 } while (--jq >= jp);
4014 return jp;
4015}
4016
4017/*
4018 * Return a new job structure.
4019 * Called with interrupts off.
4020 */
4021static struct job *
4022makejob(union node *node, int nprocs)
4023{
4024 int i;
4025 struct job *jp;
4026
4027 for (i = njobs, jp = jobtab; ; jp++) {
4028 if (--i < 0) {
4029 jp = growjobtab();
4030 break;
4031 }
4032 if (jp->used == 0)
4033 break;
4034 if (jp->state != JOBDONE || !jp->waited)
4035 continue;
4036#if JOBS
4037 if (jobctl)
4038 continue;
4039#endif
4040 freejob(jp);
4041 break;
4042 }
4043 memset(jp, 0, sizeof(*jp));
4044#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004045 /* jp->jobctl is a bitfield.
4046 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 if (jobctl)
4048 jp->jobctl = 1;
4049#endif
4050 jp->prev_job = curjob;
4051 curjob = jp;
4052 jp->used = 1;
4053 jp->ps = &jp->ps0;
4054 if (nprocs > 1) {
4055 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4056 }
4057 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4058 jobno(jp)));
4059 return jp;
4060}
4061
4062#if JOBS
4063/*
4064 * Return a string identifying a command (to be printed by the
4065 * jobs command).
4066 */
4067static char *cmdnextc;
4068
4069static void
4070cmdputs(const char *s)
4071{
4072 const char *p, *str;
4073 char c, cc[2] = " ";
4074 char *nextc;
4075 int subtype = 0;
4076 int quoted = 0;
4077 static const char vstype[VSTYPE + 1][4] = {
4078 "", "}", "-", "+", "?", "=",
4079 "%", "%%", "#", "##"
4080 };
4081
4082 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4083 p = s;
4084 while ((c = *p++) != 0) {
4085 str = 0;
4086 switch (c) {
4087 case CTLESC:
4088 c = *p++;
4089 break;
4090 case CTLVAR:
4091 subtype = *p++;
4092 if ((subtype & VSTYPE) == VSLENGTH)
4093 str = "${#";
4094 else
4095 str = "${";
4096 if (!(subtype & VSQUOTE) == !(quoted & 1))
4097 goto dostr;
4098 quoted ^= 1;
4099 c = '"';
4100 break;
4101 case CTLENDVAR:
4102 str = "\"}" + !(quoted & 1);
4103 quoted >>= 1;
4104 subtype = 0;
4105 goto dostr;
4106 case CTLBACKQ:
4107 str = "$(...)";
4108 goto dostr;
4109 case CTLBACKQ+CTLQUOTE:
4110 str = "\"$(...)\"";
4111 goto dostr;
4112#if ENABLE_ASH_MATH_SUPPORT
4113 case CTLARI:
4114 str = "$((";
4115 goto dostr;
4116 case CTLENDARI:
4117 str = "))";
4118 goto dostr;
4119#endif
4120 case CTLQUOTEMARK:
4121 quoted ^= 1;
4122 c = '"';
4123 break;
4124 case '=':
4125 if (subtype == 0)
4126 break;
4127 if ((subtype & VSTYPE) != VSNORMAL)
4128 quoted <<= 1;
4129 str = vstype[subtype & VSTYPE];
4130 if (subtype & VSNUL)
4131 c = ':';
4132 else
4133 goto checkstr;
4134 break;
4135 case '\'':
4136 case '\\':
4137 case '"':
4138 case '$':
4139 /* These can only happen inside quotes */
4140 cc[0] = c;
4141 str = cc;
4142 c = '\\';
4143 break;
4144 default:
4145 break;
4146 }
4147 USTPUTC(c, nextc);
4148 checkstr:
4149 if (!str)
4150 continue;
4151 dostr:
4152 while ((c = *str++)) {
4153 USTPUTC(c, nextc);
4154 }
4155 }
4156 if (quoted & 1) {
4157 USTPUTC('"', nextc);
4158 }
4159 *nextc = 0;
4160 cmdnextc = nextc;
4161}
4162
4163/* cmdtxt() and cmdlist() call each other */
4164static void cmdtxt(union node *n);
4165
4166static void
4167cmdlist(union node *np, int sep)
4168{
4169 for (; np; np = np->narg.next) {
4170 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004171 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172 cmdtxt(np);
4173 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004174 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 }
4176}
4177
4178static void
4179cmdtxt(union node *n)
4180{
4181 union node *np;
4182 struct nodelist *lp;
4183 const char *p;
4184 char s[2];
4185
4186 if (!n)
4187 return;
4188 switch (n->type) {
4189 default:
4190#if DEBUG
4191 abort();
4192#endif
4193 case NPIPE:
4194 lp = n->npipe.cmdlist;
4195 for (;;) {
4196 cmdtxt(lp->n);
4197 lp = lp->next;
4198 if (!lp)
4199 break;
4200 cmdputs(" | ");
4201 }
4202 break;
4203 case NSEMI:
4204 p = "; ";
4205 goto binop;
4206 case NAND:
4207 p = " && ";
4208 goto binop;
4209 case NOR:
4210 p = " || ";
4211 binop:
4212 cmdtxt(n->nbinary.ch1);
4213 cmdputs(p);
4214 n = n->nbinary.ch2;
4215 goto donode;
4216 case NREDIR:
4217 case NBACKGND:
4218 n = n->nredir.n;
4219 goto donode;
4220 case NNOT:
4221 cmdputs("!");
4222 n = n->nnot.com;
4223 donode:
4224 cmdtxt(n);
4225 break;
4226 case NIF:
4227 cmdputs("if ");
4228 cmdtxt(n->nif.test);
4229 cmdputs("; then ");
4230 n = n->nif.ifpart;
4231 if (n->nif.elsepart) {
4232 cmdtxt(n);
4233 cmdputs("; else ");
4234 n = n->nif.elsepart;
4235 }
4236 p = "; fi";
4237 goto dotail;
4238 case NSUBSHELL:
4239 cmdputs("(");
4240 n = n->nredir.n;
4241 p = ")";
4242 goto dotail;
4243 case NWHILE:
4244 p = "while ";
4245 goto until;
4246 case NUNTIL:
4247 p = "until ";
4248 until:
4249 cmdputs(p);
4250 cmdtxt(n->nbinary.ch1);
4251 n = n->nbinary.ch2;
4252 p = "; done";
4253 dodo:
4254 cmdputs("; do ");
4255 dotail:
4256 cmdtxt(n);
4257 goto dotail2;
4258 case NFOR:
4259 cmdputs("for ");
4260 cmdputs(n->nfor.var);
4261 cmdputs(" in ");
4262 cmdlist(n->nfor.args, 1);
4263 n = n->nfor.body;
4264 p = "; done";
4265 goto dodo;
4266 case NDEFUN:
4267 cmdputs(n->narg.text);
4268 p = "() { ... }";
4269 goto dotail2;
4270 case NCMD:
4271 cmdlist(n->ncmd.args, 1);
4272 cmdlist(n->ncmd.redirect, 0);
4273 break;
4274 case NARG:
4275 p = n->narg.text;
4276 dotail2:
4277 cmdputs(p);
4278 break;
4279 case NHERE:
4280 case NXHERE:
4281 p = "<<...";
4282 goto dotail2;
4283 case NCASE:
4284 cmdputs("case ");
4285 cmdputs(n->ncase.expr->narg.text);
4286 cmdputs(" in ");
4287 for (np = n->ncase.cases; np; np = np->nclist.next) {
4288 cmdtxt(np->nclist.pattern);
4289 cmdputs(") ");
4290 cmdtxt(np->nclist.body);
4291 cmdputs(";; ");
4292 }
4293 p = "esac";
4294 goto dotail2;
4295 case NTO:
4296 p = ">";
4297 goto redir;
4298 case NCLOBBER:
4299 p = ">|";
4300 goto redir;
4301 case NAPPEND:
4302 p = ">>";
4303 goto redir;
4304 case NTOFD:
4305 p = ">&";
4306 goto redir;
4307 case NFROM:
4308 p = "<";
4309 goto redir;
4310 case NFROMFD:
4311 p = "<&";
4312 goto redir;
4313 case NFROMTO:
4314 p = "<>";
4315 redir:
4316 s[0] = n->nfile.fd + '0';
4317 s[1] = '\0';
4318 cmdputs(s);
4319 cmdputs(p);
4320 if (n->type == NTOFD || n->type == NFROMFD) {
4321 s[0] = n->ndup.dupfd + '0';
4322 p = s;
4323 goto dotail2;
4324 }
4325 n = n->nfile.fname;
4326 goto donode;
4327 }
4328}
4329
4330static char *
4331commandtext(union node *n)
4332{
4333 char *name;
4334
4335 STARTSTACKSTR(cmdnextc);
4336 cmdtxt(n);
4337 name = stackblock();
4338 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4339 name, cmdnextc, cmdnextc));
4340 return ckstrdup(name);
4341}
4342#endif /* JOBS */
4343
4344/*
4345 * Fork off a subshell. If we are doing job control, give the subshell its
4346 * own process group. Jp is a job structure that the job is to be added to.
4347 * N is the command that will be evaluated by the child. Both jp and n may
4348 * be NULL. The mode parameter can be one of the following:
4349 * FORK_FG - Fork off a foreground process.
4350 * FORK_BG - Fork off a background process.
4351 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4352 * process group even if job control is on.
4353 *
4354 * When job control is turned off, background processes have their standard
4355 * input redirected to /dev/null (except for the second and later processes
4356 * in a pipeline).
4357 *
4358 * Called with interrupts off.
4359 */
4360/*
4361 * Clear traps on a fork.
4362 */
4363static void
4364clear_traps(void)
4365{
4366 char **tp;
4367
4368 for (tp = trap; tp < &trap[NSIG]; tp++) {
4369 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4370 INT_OFF;
4371 free(*tp);
4372 *tp = NULL;
4373 if (tp != &trap[0])
4374 setsignal(tp - trap);
4375 INT_ON;
4376 }
4377 }
4378}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004379
4380/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004381static void closescript(void);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004382/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383static void
4384forkchild(struct job *jp, union node *n, int mode)
4385{
4386 int oldlvl;
4387
4388 TRACE(("Child shell %d\n", getpid()));
4389 oldlvl = shlvl;
4390 shlvl++;
4391
4392 closescript();
4393 clear_traps();
4394#if JOBS
4395 /* do job control only in root shell */
4396 jobctl = 0;
4397 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4398 pid_t pgrp;
4399
4400 if (jp->nprocs == 0)
4401 pgrp = getpid();
4402 else
4403 pgrp = jp->ps[0].pid;
4404 /* This can fail because we are doing it in the parent also */
4405 (void)setpgid(0, pgrp);
4406 if (mode == FORK_FG)
4407 xtcsetpgrp(ttyfd, pgrp);
4408 setsignal(SIGTSTP);
4409 setsignal(SIGTTOU);
4410 } else
4411#endif
4412 if (mode == FORK_BG) {
4413 ignoresig(SIGINT);
4414 ignoresig(SIGQUIT);
4415 if (jp->nprocs == 0) {
4416 close(0);
4417 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004418 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004419 }
4420 }
4421 if (!oldlvl && iflag) {
4422 setsignal(SIGINT);
4423 setsignal(SIGQUIT);
4424 setsignal(SIGTERM);
4425 }
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004426#if JOBS
4427 /* For "jobs | cat" to work like in bash, we must retain list of jobs
4428 * in child, but we do need to remove ourself */
Denis Vlasenkod4293c72007-07-18 21:35:43 +00004429 if (jp)
4430 freejob(jp);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004431#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004432 for (jp = curjob; jp; jp = jp->prev_job)
4433 freejob(jp);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004434#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004435 jobless = 0;
4436}
4437
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004438/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004439static void
4440forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4441{
4442 TRACE(("In parent shell: child = %d\n", pid));
4443 if (!jp) {
4444 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4445 jobless++;
4446 return;
4447 }
4448#if JOBS
4449 if (mode != FORK_NOJOB && jp->jobctl) {
4450 int pgrp;
4451
4452 if (jp->nprocs == 0)
4453 pgrp = pid;
4454 else
4455 pgrp = jp->ps[0].pid;
4456 /* This can fail because we are doing it in the child also */
4457 setpgid(pid, pgrp);
4458 }
4459#endif
4460 if (mode == FORK_BG) {
4461 backgndpid = pid; /* set $! */
4462 set_curjob(jp, CUR_RUNNING);
4463 }
4464 if (jp) {
4465 struct procstat *ps = &jp->ps[jp->nprocs++];
4466 ps->pid = pid;
4467 ps->status = -1;
4468 ps->cmd = nullstr;
4469#if JOBS
4470 if (jobctl && n)
4471 ps->cmd = commandtext(n);
4472#endif
4473 }
4474}
4475
4476static int
4477forkshell(struct job *jp, union node *n, int mode)
4478{
4479 int pid;
4480
4481 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4482 pid = fork();
4483 if (pid < 0) {
4484 TRACE(("Fork failed, errno=%d", errno));
4485 if (jp)
4486 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004487 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488 }
4489 if (pid == 0)
4490 forkchild(jp, n, mode);
4491 else
4492 forkparent(jp, n, mode, pid);
4493 return pid;
4494}
4495
4496/*
4497 * Wait for job to finish.
4498 *
4499 * Under job control we have the problem that while a child process is
4500 * running interrupts generated by the user are sent to the child but not
4501 * to the shell. This means that an infinite loop started by an inter-
4502 * active user may be hard to kill. With job control turned off, an
4503 * interactive user may place an interactive program inside a loop. If
4504 * the interactive program catches interrupts, the user doesn't want
4505 * these interrupts to also abort the loop. The approach we take here
4506 * is to have the shell ignore interrupt signals while waiting for a
4507 * foreground process to terminate, and then send itself an interrupt
4508 * signal if the child process was terminated by an interrupt signal.
4509 * Unfortunately, some programs want to do a bit of cleanup and then
4510 * exit on interrupt; unless these processes terminate themselves by
4511 * sending a signal to themselves (instead of calling exit) they will
4512 * confuse this approach.
4513 *
4514 * Called with interrupts off.
4515 */
4516static int
4517waitforjob(struct job *jp)
4518{
4519 int st;
4520
4521 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4522 while (jp->state == JOBRUNNING) {
4523 dowait(DOWAIT_BLOCK, jp);
4524 }
4525 st = getstatus(jp);
4526#if JOBS
4527 if (jp->jobctl) {
4528 xtcsetpgrp(ttyfd, rootpid);
4529 /*
4530 * This is truly gross.
4531 * If we're doing job control, then we did a TIOCSPGRP which
4532 * caused us (the shell) to no longer be in the controlling
4533 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4534 * intuit from the subprocess exit status whether a SIGINT
4535 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4536 */
4537 if (jp->sigint)
4538 raise(SIGINT);
4539 }
4540 if (jp->state == JOBDONE)
4541#endif
4542 freejob(jp);
4543 return st;
4544}
4545
4546/*
4547 * return 1 if there are stopped jobs, otherwise 0
4548 */
4549static int
4550stoppedjobs(void)
4551{
4552 struct job *jp;
4553 int retval;
4554
4555 retval = 0;
4556 if (job_warning)
4557 goto out;
4558 jp = curjob;
4559 if (jp && jp->state == JOBSTOPPED) {
4560 out2str("You have stopped jobs.\n");
4561 job_warning = 2;
4562 retval++;
4563 }
4564 out:
4565 return retval;
4566}
4567
4568
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004569/* ============ redir.c
4570 *
4571 * Code for dealing with input/output redirection.
4572 */
4573
4574#define EMPTY -2 /* marks an unused slot in redirtab */
4575#ifndef PIPE_BUF
4576# define PIPESIZE 4096 /* amount of buffering in a pipe */
4577#else
4578# define PIPESIZE PIPE_BUF
4579#endif
4580
4581/*
4582 * Open a file in noclobber mode.
4583 * The code was copied from bash.
4584 */
4585static int
4586noclobberopen(const char *fname)
4587{
4588 int r, fd;
4589 struct stat finfo, finfo2;
4590
4591 /*
4592 * If the file exists and is a regular file, return an error
4593 * immediately.
4594 */
4595 r = stat(fname, &finfo);
4596 if (r == 0 && S_ISREG(finfo.st_mode)) {
4597 errno = EEXIST;
4598 return -1;
4599 }
4600
4601 /*
4602 * If the file was not present (r != 0), make sure we open it
4603 * exclusively so that if it is created before we open it, our open
4604 * will fail. Make sure that we do not truncate an existing file.
4605 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4606 * file was not a regular file, we leave O_EXCL off.
4607 */
4608 if (r != 0)
4609 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4610 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4611
4612 /* If the open failed, return the file descriptor right away. */
4613 if (fd < 0)
4614 return fd;
4615
4616 /*
4617 * OK, the open succeeded, but the file may have been changed from a
4618 * non-regular file to a regular file between the stat and the open.
4619 * We are assuming that the O_EXCL open handles the case where FILENAME
4620 * did not exist and is symlinked to an existing file between the stat
4621 * and open.
4622 */
4623
4624 /*
4625 * If we can open it and fstat the file descriptor, and neither check
4626 * revealed that it was a regular file, and the file has not been
4627 * replaced, return the file descriptor.
4628 */
4629 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4630 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4631 return fd;
4632
4633 /* The file has been replaced. badness. */
4634 close(fd);
4635 errno = EEXIST;
4636 return -1;
4637}
4638
4639/*
4640 * Handle here documents. Normally we fork off a process to write the
4641 * data to a pipe. If the document is short, we can stuff the data in
4642 * the pipe without forking.
4643 */
4644/* openhere needs this forward reference */
4645static void expandhere(union node *arg, int fd);
4646static int
4647openhere(union node *redir)
4648{
4649 int pip[2];
4650 size_t len = 0;
4651
4652 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004653 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004654 if (redir->type == NHERE) {
4655 len = strlen(redir->nhere.doc->narg.text);
4656 if (len <= PIPESIZE) {
4657 full_write(pip[1], redir->nhere.doc->narg.text, len);
4658 goto out;
4659 }
4660 }
4661 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4662 close(pip[0]);
4663 signal(SIGINT, SIG_IGN);
4664 signal(SIGQUIT, SIG_IGN);
4665 signal(SIGHUP, SIG_IGN);
4666#ifdef SIGTSTP
4667 signal(SIGTSTP, SIG_IGN);
4668#endif
4669 signal(SIGPIPE, SIG_DFL);
4670 if (redir->type == NHERE)
4671 full_write(pip[1], redir->nhere.doc->narg.text, len);
4672 else
4673 expandhere(redir->nhere.doc, pip[1]);
4674 _exit(0);
4675 }
4676 out:
4677 close(pip[1]);
4678 return pip[0];
4679}
4680
4681static int
4682openredirect(union node *redir)
4683{
4684 char *fname;
4685 int f;
4686
4687 switch (redir->nfile.type) {
4688 case NFROM:
4689 fname = redir->nfile.expfname;
4690 f = open(fname, O_RDONLY);
4691 if (f < 0)
4692 goto eopen;
4693 break;
4694 case NFROMTO:
4695 fname = redir->nfile.expfname;
4696 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4697 if (f < 0)
4698 goto ecreate;
4699 break;
4700 case NTO:
4701 /* Take care of noclobber mode. */
4702 if (Cflag) {
4703 fname = redir->nfile.expfname;
4704 f = noclobberopen(fname);
4705 if (f < 0)
4706 goto ecreate;
4707 break;
4708 }
4709 /* FALLTHROUGH */
4710 case NCLOBBER:
4711 fname = redir->nfile.expfname;
4712 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4713 if (f < 0)
4714 goto ecreate;
4715 break;
4716 case NAPPEND:
4717 fname = redir->nfile.expfname;
4718 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4719 if (f < 0)
4720 goto ecreate;
4721 break;
4722 default:
4723#if DEBUG
4724 abort();
4725#endif
4726 /* Fall through to eliminate warning. */
4727 case NTOFD:
4728 case NFROMFD:
4729 f = -1;
4730 break;
4731 case NHERE:
4732 case NXHERE:
4733 f = openhere(redir);
4734 break;
4735 }
4736
4737 return f;
4738 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004739 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004740 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004741 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004742}
4743
4744/*
4745 * Copy a file descriptor to be >= to. Returns -1
4746 * if the source file descriptor is closed, EMPTY if there are no unused
4747 * file descriptors left.
4748 */
4749static int
4750copyfd(int from, int to)
4751{
4752 int newfd;
4753
4754 newfd = fcntl(from, F_DUPFD, to);
4755 if (newfd < 0) {
4756 if (errno == EMFILE)
4757 return EMPTY;
4758 ash_msg_and_raise_error("%d: %m", from);
4759 }
4760 return newfd;
4761}
4762
4763static void
4764dupredirect(union node *redir, int f)
4765{
4766 int fd = redir->nfile.fd;
4767
4768 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4769 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4770 copyfd(redir->ndup.dupfd, fd);
4771 }
4772 return;
4773 }
4774
4775 if (f != fd) {
4776 copyfd(f, fd);
4777 close(f);
4778 }
4779}
4780
4781/*
4782 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4783 * old file descriptors are stashed away so that the redirection can be
4784 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4785 * standard output, and the standard error if it becomes a duplicate of
4786 * stdout, is saved in memory.
4787 */
4788/* flags passed to redirect */
4789#define REDIR_PUSH 01 /* save previous values of file descriptors */
4790#define REDIR_SAVEFD2 03 /* set preverrout */
4791static void
4792redirect(union node *redir, int flags)
4793{
4794 union node *n;
4795 struct redirtab *sv;
4796 int i;
4797 int fd;
4798 int newfd;
4799 int *p;
4800 nullredirs++;
4801 if (!redir) {
4802 return;
4803 }
4804 sv = NULL;
4805 INT_OFF;
4806 if (flags & REDIR_PUSH) {
4807 struct redirtab *q;
4808 q = ckmalloc(sizeof(struct redirtab));
4809 q->next = redirlist;
4810 redirlist = q;
4811 q->nullredirs = nullredirs - 1;
4812 for (i = 0; i < 10; i++)
4813 q->renamed[i] = EMPTY;
4814 nullredirs = 0;
4815 sv = q;
4816 }
4817 n = redir;
4818 do {
4819 fd = n->nfile.fd;
4820 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4821 && n->ndup.dupfd == fd)
4822 continue; /* redirect from/to same file descriptor */
4823
4824 newfd = openredirect(n);
4825 if (fd == newfd)
4826 continue;
4827 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4828 i = fcntl(fd, F_DUPFD, 10);
4829
4830 if (i == -1) {
4831 i = errno;
4832 if (i != EBADF) {
4833 close(newfd);
4834 errno = i;
4835 ash_msg_and_raise_error("%d: %m", fd);
4836 /* NOTREACHED */
4837 }
4838 } else {
4839 *p = i;
4840 close(fd);
4841 }
4842 } else {
4843 close(fd);
4844 }
4845 dupredirect(n, newfd);
4846 } while ((n = n->nfile.next));
4847 INT_ON;
4848 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4849 preverrout_fd = sv->renamed[2];
4850}
4851
4852/*
4853 * Undo the effects of the last redirection.
4854 */
4855static void
4856popredir(int drop)
4857{
4858 struct redirtab *rp;
4859 int i;
4860
4861 if (--nullredirs >= 0)
4862 return;
4863 INT_OFF;
4864 rp = redirlist;
4865 for (i = 0; i < 10; i++) {
4866 if (rp->renamed[i] != EMPTY) {
4867 if (!drop) {
4868 close(i);
4869 copyfd(rp->renamed[i], i);
4870 }
4871 close(rp->renamed[i]);
4872 }
4873 }
4874 redirlist = rp->next;
4875 nullredirs = rp->nullredirs;
4876 free(rp);
4877 INT_ON;
4878}
4879
4880/*
4881 * Undo all redirections. Called on error or interrupt.
4882 */
4883
4884/*
4885 * Discard all saved file descriptors.
4886 */
4887static void
4888clearredir(int drop)
4889{
4890 for (;;) {
4891 nullredirs = 0;
4892 if (!redirlist)
4893 break;
4894 popredir(drop);
4895 }
4896}
4897
4898static int
4899redirectsafe(union node *redir, int flags)
4900{
4901 int err;
4902 volatile int saveint;
4903 struct jmploc *volatile savehandler = exception_handler;
4904 struct jmploc jmploc;
4905
4906 SAVE_INT(saveint);
4907 err = setjmp(jmploc.loc) * 2;
4908 if (!err) {
4909 exception_handler = &jmploc;
4910 redirect(redir, flags);
4911 }
4912 exception_handler = savehandler;
4913 if (err && exception != EXERROR)
4914 longjmp(exception_handler->loc, 1);
4915 RESTORE_INT(saveint);
4916 return err;
4917}
4918
4919
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004920/* ============ Routines to expand arguments to commands
4921 *
4922 * We have to deal with backquotes, shell variables, and file metacharacters.
4923 */
4924
4925/*
4926 * expandarg flags
4927 */
4928#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4929#define EXP_TILDE 0x2 /* do normal tilde expansion */
4930#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4931#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4932#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4933#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4934#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4935#define EXP_WORD 0x80 /* expand word in parameter expansion */
4936#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4937/*
4938 * _rmescape() flags
4939 */
4940#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4941#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4942#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4943#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4944#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4945
4946/*
4947 * Structure specifying which parts of the string should be searched
4948 * for IFS characters.
4949 */
4950struct ifsregion {
4951 struct ifsregion *next; /* next region in list */
4952 int begoff; /* offset of start of region */
4953 int endoff; /* offset of end of region */
4954 int nulonly; /* search for nul bytes only */
4955};
4956
4957struct arglist {
4958 struct strlist *list;
4959 struct strlist **lastp;
4960};
4961
4962/* output of current string */
4963static char *expdest;
4964/* list of back quote expressions */
4965static struct nodelist *argbackq;
4966/* first struct in list of ifs regions */
4967static struct ifsregion ifsfirst;
4968/* last struct in list */
4969static struct ifsregion *ifslastp;
4970/* holds expanded arg list */
4971static struct arglist exparg;
4972
4973/*
4974 * Our own itoa().
4975 */
4976static int
4977cvtnum(arith_t num)
4978{
4979 int len;
4980
4981 expdest = makestrspace(32, expdest);
4982#if ENABLE_ASH_MATH_SUPPORT_64
4983 len = fmtstr(expdest, 32, "%lld", (long long) num);
4984#else
4985 len = fmtstr(expdest, 32, "%ld", num);
4986#endif
4987 STADJUST(len, expdest);
4988 return len;
4989}
4990
4991static size_t
4992esclen(const char *start, const char *p)
4993{
4994 size_t esc = 0;
4995
4996 while (p > start && *--p == CTLESC) {
4997 esc++;
4998 }
4999 return esc;
5000}
5001
5002/*
5003 * Remove any CTLESC characters from a string.
5004 */
5005static char *
5006_rmescapes(char *str, int flag)
5007{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005008 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005009
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005010 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005011 unsigned inquotes;
5012 int notescaped;
5013 int globbing;
5014
5015 p = strpbrk(str, qchars);
5016 if (!p) {
5017 return str;
5018 }
5019 q = p;
5020 r = str;
5021 if (flag & RMESCAPE_ALLOC) {
5022 size_t len = p - str;
5023 size_t fulllen = len + strlen(p) + 1;
5024
5025 if (flag & RMESCAPE_GROW) {
5026 r = makestrspace(fulllen, expdest);
5027 } else if (flag & RMESCAPE_HEAP) {
5028 r = ckmalloc(fulllen);
5029 } else {
5030 r = stalloc(fulllen);
5031 }
5032 q = r;
5033 if (len > 0) {
5034 q = memcpy(q, str, len) + len;
5035 }
5036 }
5037 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5038 globbing = flag & RMESCAPE_GLOB;
5039 notescaped = globbing;
5040 while (*p) {
5041 if (*p == CTLQUOTEMARK) {
5042 inquotes = ~inquotes;
5043 p++;
5044 notescaped = globbing;
5045 continue;
5046 }
5047 if (*p == '\\') {
5048 /* naked back slash */
5049 notescaped = 0;
5050 goto copy;
5051 }
5052 if (*p == CTLESC) {
5053 p++;
5054 if (notescaped && inquotes && *p != '/') {
5055 *q++ = '\\';
5056 }
5057 }
5058 notescaped = globbing;
5059 copy:
5060 *q++ = *p++;
5061 }
5062 *q = '\0';
5063 if (flag & RMESCAPE_GROW) {
5064 expdest = r;
5065 STADJUST(q - r + 1, expdest);
5066 }
5067 return r;
5068}
5069#define rmescapes(p) _rmescapes((p), 0)
5070
5071#define pmatch(a, b) !fnmatch((a), (b), 0)
5072
5073/*
5074 * Prepare a pattern for a expmeta (internal glob(3)) call.
5075 *
5076 * Returns an stalloced string.
5077 */
5078static char *
5079preglob(const char *pattern, int quoted, int flag)
5080{
5081 flag |= RMESCAPE_GLOB;
5082 if (quoted) {
5083 flag |= RMESCAPE_QUOTED;
5084 }
5085 return _rmescapes((char *)pattern, flag);
5086}
5087
5088/*
5089 * Put a string on the stack.
5090 */
5091static void
5092memtodest(const char *p, size_t len, int syntax, int quotes)
5093{
5094 char *q = expdest;
5095
5096 q = makestrspace(len * 2, q);
5097
5098 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005099 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005100 if (!c)
5101 continue;
5102 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5103 USTPUTC(CTLESC, q);
5104 USTPUTC(c, q);
5105 }
5106
5107 expdest = q;
5108}
5109
5110static void
5111strtodest(const char *p, int syntax, int quotes)
5112{
5113 memtodest(p, strlen(p), syntax, quotes);
5114}
5115
5116/*
5117 * Record the fact that we have to scan this region of the
5118 * string for IFS characters.
5119 */
5120static void
5121recordregion(int start, int end, int nulonly)
5122{
5123 struct ifsregion *ifsp;
5124
5125 if (ifslastp == NULL) {
5126 ifsp = &ifsfirst;
5127 } else {
5128 INT_OFF;
5129 ifsp = ckmalloc(sizeof(*ifsp));
5130 ifsp->next = NULL;
5131 ifslastp->next = ifsp;
5132 INT_ON;
5133 }
5134 ifslastp = ifsp;
5135 ifslastp->begoff = start;
5136 ifslastp->endoff = end;
5137 ifslastp->nulonly = nulonly;
5138}
5139
5140static void
5141removerecordregions(int endoff)
5142{
5143 if (ifslastp == NULL)
5144 return;
5145
5146 if (ifsfirst.endoff > endoff) {
5147 while (ifsfirst.next != NULL) {
5148 struct ifsregion *ifsp;
5149 INT_OFF;
5150 ifsp = ifsfirst.next->next;
5151 free(ifsfirst.next);
5152 ifsfirst.next = ifsp;
5153 INT_ON;
5154 }
5155 if (ifsfirst.begoff > endoff)
5156 ifslastp = NULL;
5157 else {
5158 ifslastp = &ifsfirst;
5159 ifsfirst.endoff = endoff;
5160 }
5161 return;
5162 }
5163
5164 ifslastp = &ifsfirst;
5165 while (ifslastp->next && ifslastp->next->begoff < endoff)
5166 ifslastp=ifslastp->next;
5167 while (ifslastp->next != NULL) {
5168 struct ifsregion *ifsp;
5169 INT_OFF;
5170 ifsp = ifslastp->next->next;
5171 free(ifslastp->next);
5172 ifslastp->next = ifsp;
5173 INT_ON;
5174 }
5175 if (ifslastp->endoff > endoff)
5176 ifslastp->endoff = endoff;
5177}
5178
5179static char *
5180exptilde(char *startp, char *p, int flag)
5181{
5182 char c;
5183 char *name;
5184 struct passwd *pw;
5185 const char *home;
5186 int quotes = flag & (EXP_FULL | EXP_CASE);
5187 int startloc;
5188
5189 name = p + 1;
5190
5191 while ((c = *++p) != '\0') {
5192 switch (c) {
5193 case CTLESC:
5194 return startp;
5195 case CTLQUOTEMARK:
5196 return startp;
5197 case ':':
5198 if (flag & EXP_VARTILDE)
5199 goto done;
5200 break;
5201 case '/':
5202 case CTLENDVAR:
5203 goto done;
5204 }
5205 }
5206 done:
5207 *p = '\0';
5208 if (*name == '\0') {
5209 home = lookupvar(homestr);
5210 } else {
5211 pw = getpwnam(name);
5212 if (pw == NULL)
5213 goto lose;
5214 home = pw->pw_dir;
5215 }
5216 if (!home || !*home)
5217 goto lose;
5218 *p = c;
5219 startloc = expdest - (char *)stackblock();
5220 strtodest(home, SQSYNTAX, quotes);
5221 recordregion(startloc, expdest - (char *)stackblock(), 0);
5222 return p;
5223 lose:
5224 *p = c;
5225 return startp;
5226}
5227
5228/*
5229 * Execute a command inside back quotes. If it's a builtin command, we
5230 * want to save its output in a block obtained from malloc. Otherwise
5231 * we fork off a subprocess and get the output of the command via a pipe.
5232 * Should be called with interrupts off.
5233 */
5234struct backcmd { /* result of evalbackcmd */
5235 int fd; /* file descriptor to read from */
5236 char *buf; /* buffer */
5237 int nleft; /* number of chars in buffer */
5238 struct job *jp; /* job structure for command */
5239};
5240
5241/* These forward decls are needed to use "eval" code for backticks handling: */
5242static int back_exitstatus; /* exit status of backquoted command */
5243#define EV_EXIT 01 /* exit after evaluating tree */
5244static void evaltree(union node *, int);
5245
5246static void
5247evalbackcmd(union node *n, struct backcmd *result)
5248{
5249 int saveherefd;
5250
5251 result->fd = -1;
5252 result->buf = NULL;
5253 result->nleft = 0;
5254 result->jp = NULL;
5255 if (n == NULL) {
5256 goto out;
5257 }
5258
5259 saveherefd = herefd;
5260 herefd = -1;
5261
5262 {
5263 int pip[2];
5264 struct job *jp;
5265
5266 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005267 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005268 jp = makejob(n, 1);
5269 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5270 FORCE_INT_ON;
5271 close(pip[0]);
5272 if (pip[1] != 1) {
5273 close(1);
5274 copyfd(pip[1], 1);
5275 close(pip[1]);
5276 }
5277 eflag = 0;
5278 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5279 /* NOTREACHED */
5280 }
5281 close(pip[1]);
5282 result->fd = pip[0];
5283 result->jp = jp;
5284 }
5285 herefd = saveherefd;
5286 out:
5287 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5288 result->fd, result->buf, result->nleft, result->jp));
5289}
5290
5291/*
5292 * Expand stuff in backwards quotes.
5293 */
5294static void
5295expbackq(union node *cmd, int quoted, int quotes)
5296{
5297 struct backcmd in;
5298 int i;
5299 char buf[128];
5300 char *p;
5301 char *dest;
5302 int startloc;
5303 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5304 struct stackmark smark;
5305
5306 INT_OFF;
5307 setstackmark(&smark);
5308 dest = expdest;
5309 startloc = dest - (char *)stackblock();
5310 grabstackstr(dest);
5311 evalbackcmd(cmd, &in);
5312 popstackmark(&smark);
5313
5314 p = in.buf;
5315 i = in.nleft;
5316 if (i == 0)
5317 goto read;
5318 for (;;) {
5319 memtodest(p, i, syntax, quotes);
5320 read:
5321 if (in.fd < 0)
5322 break;
5323 i = safe_read(in.fd, buf, sizeof(buf));
5324 TRACE(("expbackq: read returns %d\n", i));
5325 if (i <= 0)
5326 break;
5327 p = buf;
5328 }
5329
5330 if (in.buf)
5331 free(in.buf);
5332 if (in.fd >= 0) {
5333 close(in.fd);
5334 back_exitstatus = waitforjob(in.jp);
5335 }
5336 INT_ON;
5337
5338 /* Eat all trailing newlines */
5339 dest = expdest;
5340 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5341 STUNPUTC(dest);
5342 expdest = dest;
5343
5344 if (quoted == 0)
5345 recordregion(startloc, dest - (char *)stackblock(), 0);
5346 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5347 (dest - (char *)stackblock()) - startloc,
5348 (dest - (char *)stackblock()) - startloc,
5349 stackblock() + startloc));
5350}
5351
5352#if ENABLE_ASH_MATH_SUPPORT
5353/*
5354 * Expand arithmetic expression. Backup to start of expression,
5355 * evaluate, place result in (backed up) result, adjust string position.
5356 */
5357static void
5358expari(int quotes)
5359{
5360 char *p, *start;
5361 int begoff;
5362 int flag;
5363 int len;
5364
5365 /* ifsfree(); */
5366
5367 /*
5368 * This routine is slightly over-complicated for
5369 * efficiency. Next we scan backwards looking for the
5370 * start of arithmetic.
5371 */
5372 start = stackblock();
5373 p = expdest - 1;
5374 *p = '\0';
5375 p--;
5376 do {
5377 int esc;
5378
5379 while (*p != CTLARI) {
5380 p--;
5381#if DEBUG
5382 if (p < start) {
5383 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5384 }
5385#endif
5386 }
5387
5388 esc = esclen(start, p);
5389 if (!(esc % 2)) {
5390 break;
5391 }
5392
5393 p -= esc + 1;
5394 } while (1);
5395
5396 begoff = p - start;
5397
5398 removerecordregions(begoff);
5399
5400 flag = p[1];
5401
5402 expdest = p;
5403
5404 if (quotes)
5405 rmescapes(p + 2);
5406
5407 len = cvtnum(dash_arith(p + 2));
5408
5409 if (flag != '"')
5410 recordregion(begoff, begoff + len, 0);
5411}
5412#endif
5413
5414/* argstr needs it */
5415static char *evalvar(char *p, int flag);
5416
5417/*
5418 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5419 * characters to allow for further processing. Otherwise treat
5420 * $@ like $* since no splitting will be performed.
5421 */
5422static void
5423argstr(char *p, int flag)
5424{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005425 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005426 '=',
5427 ':',
5428 CTLQUOTEMARK,
5429 CTLENDVAR,
5430 CTLESC,
5431 CTLVAR,
5432 CTLBACKQ,
5433 CTLBACKQ | CTLQUOTE,
5434#if ENABLE_ASH_MATH_SUPPORT
5435 CTLENDARI,
5436#endif
5437 0
5438 };
5439 const char *reject = spclchars;
5440 int c;
5441 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5442 int breakall = flag & EXP_WORD;
5443 int inquotes;
5444 size_t length;
5445 int startloc;
5446
5447 if (!(flag & EXP_VARTILDE)) {
5448 reject += 2;
5449 } else if (flag & EXP_VARTILDE2) {
5450 reject++;
5451 }
5452 inquotes = 0;
5453 length = 0;
5454 if (flag & EXP_TILDE) {
5455 char *q;
5456
5457 flag &= ~EXP_TILDE;
5458 tilde:
5459 q = p;
5460 if (*q == CTLESC && (flag & EXP_QWORD))
5461 q++;
5462 if (*q == '~')
5463 p = exptilde(p, q, flag);
5464 }
5465 start:
5466 startloc = expdest - (char *)stackblock();
5467 for (;;) {
5468 length += strcspn(p + length, reject);
5469 c = p[length];
5470 if (c && (!(c & 0x80)
5471#if ENABLE_ASH_MATH_SUPPORT
5472 || c == CTLENDARI
5473#endif
5474 )) {
5475 /* c == '=' || c == ':' || c == CTLENDARI */
5476 length++;
5477 }
5478 if (length > 0) {
5479 int newloc;
5480 expdest = stack_nputstr(p, length, expdest);
5481 newloc = expdest - (char *)stackblock();
5482 if (breakall && !inquotes && newloc > startloc) {
5483 recordregion(startloc, newloc, 0);
5484 }
5485 startloc = newloc;
5486 }
5487 p += length + 1;
5488 length = 0;
5489
5490 switch (c) {
5491 case '\0':
5492 goto breakloop;
5493 case '=':
5494 if (flag & EXP_VARTILDE2) {
5495 p--;
5496 continue;
5497 }
5498 flag |= EXP_VARTILDE2;
5499 reject++;
5500 /* fall through */
5501 case ':':
5502 /*
5503 * sort of a hack - expand tildes in variable
5504 * assignments (after the first '=' and after ':'s).
5505 */
5506 if (*--p == '~') {
5507 goto tilde;
5508 }
5509 continue;
5510 }
5511
5512 switch (c) {
5513 case CTLENDVAR: /* ??? */
5514 goto breakloop;
5515 case CTLQUOTEMARK:
5516 /* "$@" syntax adherence hack */
5517 if (
5518 !inquotes &&
5519 !memcmp(p, dolatstr, 4) &&
5520 (p[4] == CTLQUOTEMARK || (
5521 p[4] == CTLENDVAR &&
5522 p[5] == CTLQUOTEMARK
5523 ))
5524 ) {
5525 p = evalvar(p + 1, flag) + 1;
5526 goto start;
5527 }
5528 inquotes = !inquotes;
5529 addquote:
5530 if (quotes) {
5531 p--;
5532 length++;
5533 startloc++;
5534 }
5535 break;
5536 case CTLESC:
5537 startloc++;
5538 length++;
5539 goto addquote;
5540 case CTLVAR:
5541 p = evalvar(p, flag);
5542 goto start;
5543 case CTLBACKQ:
5544 c = 0;
5545 case CTLBACKQ|CTLQUOTE:
5546 expbackq(argbackq->n, c, quotes);
5547 argbackq = argbackq->next;
5548 goto start;
5549#if ENABLE_ASH_MATH_SUPPORT
5550 case CTLENDARI:
5551 p--;
5552 expari(quotes);
5553 goto start;
5554#endif
5555 }
5556 }
5557 breakloop:
5558 ;
5559}
5560
5561static char *
5562scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5563 int zero)
5564{
5565 char *loc;
5566 char *loc2;
5567 char c;
5568
5569 loc = startp;
5570 loc2 = rmesc;
5571 do {
5572 int match;
5573 const char *s = loc2;
5574 c = *loc2;
5575 if (zero) {
5576 *loc2 = '\0';
5577 s = rmesc;
5578 }
5579 match = pmatch(str, s);
5580 *loc2 = c;
5581 if (match)
5582 return loc;
5583 if (quotes && *loc == CTLESC)
5584 loc++;
5585 loc++;
5586 loc2++;
5587 } while (c);
5588 return 0;
5589}
5590
5591static char *
5592scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5593 int zero)
5594{
5595 int esc = 0;
5596 char *loc;
5597 char *loc2;
5598
5599 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5600 int match;
5601 char c = *loc2;
5602 const char *s = loc2;
5603 if (zero) {
5604 *loc2 = '\0';
5605 s = rmesc;
5606 }
5607 match = pmatch(str, s);
5608 *loc2 = c;
5609 if (match)
5610 return loc;
5611 loc--;
5612 if (quotes) {
5613 if (--esc < 0) {
5614 esc = esclen(startp, loc);
5615 }
5616 if (esc % 2) {
5617 esc--;
5618 loc--;
5619 }
5620 }
5621 }
5622 return 0;
5623}
5624
5625static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5626static void
5627varunset(const char *end, const char *var, const char *umsg, int varflags)
5628{
5629 const char *msg;
5630 const char *tail;
5631
5632 tail = nullstr;
5633 msg = "parameter not set";
5634 if (umsg) {
5635 if (*end == CTLENDVAR) {
5636 if (varflags & VSNUL)
5637 tail = " or null";
5638 } else
5639 msg = umsg;
5640 }
5641 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5642}
5643
5644static const char *
5645subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5646{
5647 char *startp;
5648 char *loc;
5649 int saveherefd = herefd;
5650 struct nodelist *saveargbackq = argbackq;
5651 int amount;
5652 char *rmesc, *rmescend;
5653 int zero;
5654 char *(*scan)(char *, char *, char *, char *, int , int);
5655
5656 herefd = -1;
5657 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5658 STPUTC('\0', expdest);
5659 herefd = saveherefd;
5660 argbackq = saveargbackq;
5661 startp = stackblock() + startloc;
5662
5663 switch (subtype) {
5664 case VSASSIGN:
5665 setvar(str, startp, 0);
5666 amount = startp - expdest;
5667 STADJUST(amount, expdest);
5668 return startp;
5669
5670 case VSQUESTION:
5671 varunset(p, str, startp, varflags);
5672 /* NOTREACHED */
5673 }
5674
5675 subtype -= VSTRIMRIGHT;
5676#if DEBUG
5677 if (subtype < 0 || subtype > 3)
5678 abort();
5679#endif
5680
5681 rmesc = startp;
5682 rmescend = stackblock() + strloc;
5683 if (quotes) {
5684 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5685 if (rmesc != startp) {
5686 rmescend = expdest;
5687 startp = stackblock() + startloc;
5688 }
5689 }
5690 rmescend--;
5691 str = stackblock() + strloc;
5692 preglob(str, varflags & VSQUOTE, 0);
5693
5694 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5695 zero = subtype >> 1;
5696 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5697 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5698
5699 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5700 if (loc) {
5701 if (zero) {
5702 memmove(startp, loc, str - loc);
5703 loc = startp + (str - loc) - 1;
5704 }
5705 *loc = '\0';
5706 amount = loc - expdest;
5707 STADJUST(amount, expdest);
5708 }
5709 return loc;
5710}
5711
5712/*
5713 * Add the value of a specialized variable to the stack string.
5714 */
5715static ssize_t
5716varvalue(char *name, int varflags, int flags)
5717{
5718 int num;
5719 char *p;
5720 int i;
5721 int sep = 0;
5722 int sepq = 0;
5723 ssize_t len = 0;
5724 char **ap;
5725 int syntax;
5726 int quoted = varflags & VSQUOTE;
5727 int subtype = varflags & VSTYPE;
5728 int quotes = flags & (EXP_FULL | EXP_CASE);
5729
5730 if (quoted && (flags & EXP_FULL))
5731 sep = 1 << CHAR_BIT;
5732
5733 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5734 switch (*name) {
5735 case '$':
5736 num = rootpid;
5737 goto numvar;
5738 case '?':
5739 num = exitstatus;
5740 goto numvar;
5741 case '#':
5742 num = shellparam.nparam;
5743 goto numvar;
5744 case '!':
5745 num = backgndpid;
5746 if (num == 0)
5747 return -1;
5748 numvar:
5749 len = cvtnum(num);
5750 break;
5751 case '-':
5752 p = makestrspace(NOPTS, expdest);
5753 for (i = NOPTS - 1; i >= 0; i--) {
5754 if (optlist[i]) {
5755 USTPUTC(optletters(i), p);
5756 len++;
5757 }
5758 }
5759 expdest = p;
5760 break;
5761 case '@':
5762 if (sep)
5763 goto param;
5764 /* fall through */
5765 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005766 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005767 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5768 sepq = 1;
5769 param:
5770 ap = shellparam.p;
5771 if (!ap)
5772 return -1;
5773 while ((p = *ap++)) {
5774 size_t partlen;
5775
5776 partlen = strlen(p);
5777 len += partlen;
5778
5779 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5780 memtodest(p, partlen, syntax, quotes);
5781
5782 if (*ap && sep) {
5783 char *q;
5784
5785 len++;
5786 if (subtype == VSPLUS || subtype == VSLENGTH) {
5787 continue;
5788 }
5789 q = expdest;
5790 if (sepq)
5791 STPUTC(CTLESC, q);
5792 STPUTC(sep, q);
5793 expdest = q;
5794 }
5795 }
5796 return len;
5797 case '0':
5798 case '1':
5799 case '2':
5800 case '3':
5801 case '4':
5802 case '5':
5803 case '6':
5804 case '7':
5805 case '8':
5806 case '9':
5807 num = atoi(name);
5808 if (num < 0 || num > shellparam.nparam)
5809 return -1;
5810 p = num ? shellparam.p[num - 1] : arg0;
5811 goto value;
5812 default:
5813 p = lookupvar(name);
5814 value:
5815 if (!p)
5816 return -1;
5817
5818 len = strlen(p);
5819 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5820 memtodest(p, len, syntax, quotes);
5821 return len;
5822 }
5823
5824 if (subtype == VSPLUS || subtype == VSLENGTH)
5825 STADJUST(-len, expdest);
5826 return len;
5827}
5828
5829/*
5830 * Expand a variable, and return a pointer to the next character in the
5831 * input string.
5832 */
5833static char *
5834evalvar(char *p, int flag)
5835{
5836 int subtype;
5837 int varflags;
5838 char *var;
5839 int patloc;
5840 int c;
5841 int startloc;
5842 ssize_t varlen;
5843 int easy;
5844 int quotes;
5845 int quoted;
5846
5847 quotes = flag & (EXP_FULL | EXP_CASE);
5848 varflags = *p++;
5849 subtype = varflags & VSTYPE;
5850 quoted = varflags & VSQUOTE;
5851 var = p;
5852 easy = (!quoted || (*var == '@' && shellparam.nparam));
5853 startloc = expdest - (char *)stackblock();
5854 p = strchr(p, '=') + 1;
5855
5856 again:
5857 varlen = varvalue(var, varflags, flag);
5858 if (varflags & VSNUL)
5859 varlen--;
5860
5861 if (subtype == VSPLUS) {
5862 varlen = -1 - varlen;
5863 goto vsplus;
5864 }
5865
5866 if (subtype == VSMINUS) {
5867 vsplus:
5868 if (varlen < 0) {
5869 argstr(
5870 p, flag | EXP_TILDE |
5871 (quoted ? EXP_QWORD : EXP_WORD)
5872 );
5873 goto end;
5874 }
5875 if (easy)
5876 goto record;
5877 goto end;
5878 }
5879
5880 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5881 if (varlen < 0) {
5882 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5883 varflags &= ~VSNUL;
5884 /*
5885 * Remove any recorded regions beyond
5886 * start of variable
5887 */
5888 removerecordregions(startloc);
5889 goto again;
5890 }
5891 goto end;
5892 }
5893 if (easy)
5894 goto record;
5895 goto end;
5896 }
5897
5898 if (varlen < 0 && uflag)
5899 varunset(p, var, 0, 0);
5900
5901 if (subtype == VSLENGTH) {
5902 cvtnum(varlen > 0 ? varlen : 0);
5903 goto record;
5904 }
5905
5906 if (subtype == VSNORMAL) {
5907 if (!easy)
5908 goto end;
5909 record:
5910 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5911 goto end;
5912 }
5913
5914#if DEBUG
5915 switch (subtype) {
5916 case VSTRIMLEFT:
5917 case VSTRIMLEFTMAX:
5918 case VSTRIMRIGHT:
5919 case VSTRIMRIGHTMAX:
5920 break;
5921 default:
5922 abort();
5923 }
5924#endif
5925
5926 if (varlen >= 0) {
5927 /*
5928 * Terminate the string and start recording the pattern
5929 * right after it
5930 */
5931 STPUTC('\0', expdest);
5932 patloc = expdest - (char *)stackblock();
5933 if (subevalvar(p, NULL, patloc, subtype,
5934 startloc, varflags, quotes) == 0) {
5935 int amount = expdest - (
5936 (char *)stackblock() + patloc - 1
5937 );
5938 STADJUST(-amount, expdest);
5939 }
5940 /* Remove any recorded regions beyond start of variable */
5941 removerecordregions(startloc);
5942 goto record;
5943 }
5944
5945 end:
5946 if (subtype != VSNORMAL) { /* skip to end of alternative */
5947 int nesting = 1;
5948 for (;;) {
5949 c = *p++;
5950 if (c == CTLESC)
5951 p++;
5952 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5953 if (varlen >= 0)
5954 argbackq = argbackq->next;
5955 } else if (c == CTLVAR) {
5956 if ((*p++ & VSTYPE) != VSNORMAL)
5957 nesting++;
5958 } else if (c == CTLENDVAR) {
5959 if (--nesting == 0)
5960 break;
5961 }
5962 }
5963 }
5964 return p;
5965}
5966
5967/*
5968 * Break the argument string into pieces based upon IFS and add the
5969 * strings to the argument list. The regions of the string to be
5970 * searched for IFS characters have been stored by recordregion.
5971 */
5972static void
5973ifsbreakup(char *string, struct arglist *arglist)
5974{
5975 struct ifsregion *ifsp;
5976 struct strlist *sp;
5977 char *start;
5978 char *p;
5979 char *q;
5980 const char *ifs, *realifs;
5981 int ifsspc;
5982 int nulonly;
5983
5984 start = string;
5985 if (ifslastp != NULL) {
5986 ifsspc = 0;
5987 nulonly = 0;
5988 realifs = ifsset() ? ifsval() : defifs;
5989 ifsp = &ifsfirst;
5990 do {
5991 p = string + ifsp->begoff;
5992 nulonly = ifsp->nulonly;
5993 ifs = nulonly ? nullstr : realifs;
5994 ifsspc = 0;
5995 while (p < string + ifsp->endoff) {
5996 q = p;
5997 if (*p == CTLESC)
5998 p++;
5999 if (!strchr(ifs, *p)) {
6000 p++;
6001 continue;
6002 }
6003 if (!nulonly)
6004 ifsspc = (strchr(defifs, *p) != NULL);
6005 /* Ignore IFS whitespace at start */
6006 if (q == start && ifsspc) {
6007 p++;
6008 start = p;
6009 continue;
6010 }
6011 *q = '\0';
6012 sp = stalloc(sizeof(*sp));
6013 sp->text = start;
6014 *arglist->lastp = sp;
6015 arglist->lastp = &sp->next;
6016 p++;
6017 if (!nulonly) {
6018 for (;;) {
6019 if (p >= string + ifsp->endoff) {
6020 break;
6021 }
6022 q = p;
6023 if (*p == CTLESC)
6024 p++;
6025 if (strchr(ifs, *p) == NULL ) {
6026 p = q;
6027 break;
6028 } else if (strchr(defifs, *p) == NULL) {
6029 if (ifsspc) {
6030 p++;
6031 ifsspc = 0;
6032 } else {
6033 p = q;
6034 break;
6035 }
6036 } else
6037 p++;
6038 }
6039 }
6040 start = p;
6041 } /* while */
6042 ifsp = ifsp->next;
6043 } while (ifsp != NULL);
6044 if (nulonly)
6045 goto add;
6046 }
6047
6048 if (!*start)
6049 return;
6050
6051 add:
6052 sp = stalloc(sizeof(*sp));
6053 sp->text = start;
6054 *arglist->lastp = sp;
6055 arglist->lastp = &sp->next;
6056}
6057
6058static void
6059ifsfree(void)
6060{
6061 struct ifsregion *p;
6062
6063 INT_OFF;
6064 p = ifsfirst.next;
6065 do {
6066 struct ifsregion *ifsp;
6067 ifsp = p->next;
6068 free(p);
6069 p = ifsp;
6070 } while (p);
6071 ifslastp = NULL;
6072 ifsfirst.next = NULL;
6073 INT_ON;
6074}
6075
6076/*
6077 * Add a file name to the list.
6078 */
6079static void
6080addfname(const char *name)
6081{
6082 struct strlist *sp;
6083
6084 sp = stalloc(sizeof(*sp));
6085 sp->text = ststrdup(name);
6086 *exparg.lastp = sp;
6087 exparg.lastp = &sp->next;
6088}
6089
6090static char *expdir;
6091
6092/*
6093 * Do metacharacter (i.e. *, ?, [...]) expansion.
6094 */
6095static void
6096expmeta(char *enddir, char *name)
6097{
6098 char *p;
6099 const char *cp;
6100 char *start;
6101 char *endname;
6102 int metaflag;
6103 struct stat statb;
6104 DIR *dirp;
6105 struct dirent *dp;
6106 int atend;
6107 int matchdot;
6108
6109 metaflag = 0;
6110 start = name;
6111 for (p = name; *p; p++) {
6112 if (*p == '*' || *p == '?')
6113 metaflag = 1;
6114 else if (*p == '[') {
6115 char *q = p + 1;
6116 if (*q == '!')
6117 q++;
6118 for (;;) {
6119 if (*q == '\\')
6120 q++;
6121 if (*q == '/' || *q == '\0')
6122 break;
6123 if (*++q == ']') {
6124 metaflag = 1;
6125 break;
6126 }
6127 }
6128 } else if (*p == '\\')
6129 p++;
6130 else if (*p == '/') {
6131 if (metaflag)
6132 goto out;
6133 start = p + 1;
6134 }
6135 }
6136 out:
6137 if (metaflag == 0) { /* we've reached the end of the file name */
6138 if (enddir != expdir)
6139 metaflag++;
6140 p = name;
6141 do {
6142 if (*p == '\\')
6143 p++;
6144 *enddir++ = *p;
6145 } while (*p++);
6146 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6147 addfname(expdir);
6148 return;
6149 }
6150 endname = p;
6151 if (name < start) {
6152 p = name;
6153 do {
6154 if (*p == '\\')
6155 p++;
6156 *enddir++ = *p++;
6157 } while (p < start);
6158 }
6159 if (enddir == expdir) {
6160 cp = ".";
6161 } else if (enddir == expdir + 1 && *expdir == '/') {
6162 cp = "/";
6163 } else {
6164 cp = expdir;
6165 enddir[-1] = '\0';
6166 }
6167 dirp = opendir(cp);
6168 if (dirp == NULL)
6169 return;
6170 if (enddir != expdir)
6171 enddir[-1] = '/';
6172 if (*endname == 0) {
6173 atend = 1;
6174 } else {
6175 atend = 0;
6176 *endname++ = '\0';
6177 }
6178 matchdot = 0;
6179 p = start;
6180 if (*p == '\\')
6181 p++;
6182 if (*p == '.')
6183 matchdot++;
6184 while (! intpending && (dp = readdir(dirp)) != NULL) {
6185 if (dp->d_name[0] == '.' && ! matchdot)
6186 continue;
6187 if (pmatch(start, dp->d_name)) {
6188 if (atend) {
6189 strcpy(enddir, dp->d_name);
6190 addfname(expdir);
6191 } else {
6192 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6193 continue;
6194 p[-1] = '/';
6195 expmeta(p, endname);
6196 }
6197 }
6198 }
6199 closedir(dirp);
6200 if (! atend)
6201 endname[-1] = '/';
6202}
6203
6204static struct strlist *
6205msort(struct strlist *list, int len)
6206{
6207 struct strlist *p, *q = NULL;
6208 struct strlist **lpp;
6209 int half;
6210 int n;
6211
6212 if (len <= 1)
6213 return list;
6214 half = len >> 1;
6215 p = list;
6216 for (n = half; --n >= 0; ) {
6217 q = p;
6218 p = p->next;
6219 }
6220 q->next = NULL; /* terminate first half of list */
6221 q = msort(list, half); /* sort first half of list */
6222 p = msort(p, len - half); /* sort second half */
6223 lpp = &list;
6224 for (;;) {
6225#if ENABLE_LOCALE_SUPPORT
6226 if (strcoll(p->text, q->text) < 0)
6227#else
6228 if (strcmp(p->text, q->text) < 0)
6229#endif
6230 {
6231 *lpp = p;
6232 lpp = &p->next;
6233 p = *lpp;
6234 if (p == NULL) {
6235 *lpp = q;
6236 break;
6237 }
6238 } else {
6239 *lpp = q;
6240 lpp = &q->next;
6241 q = *lpp;
6242 if (q == NULL) {
6243 *lpp = p;
6244 break;
6245 }
6246 }
6247 }
6248 return list;
6249}
6250
6251/*
6252 * Sort the results of file name expansion. It calculates the number of
6253 * strings to sort and then calls msort (short for merge sort) to do the
6254 * work.
6255 */
6256static struct strlist *
6257expsort(struct strlist *str)
6258{
6259 int len;
6260 struct strlist *sp;
6261
6262 len = 0;
6263 for (sp = str; sp; sp = sp->next)
6264 len++;
6265 return msort(str, len);
6266}
6267
6268static void
6269expandmeta(struct strlist *str, int flag)
6270{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006271 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006272 '*', '?', '[', 0
6273 };
6274 /* TODO - EXP_REDIR */
6275
6276 while (str) {
6277 struct strlist **savelastp;
6278 struct strlist *sp;
6279 char *p;
6280
6281 if (fflag)
6282 goto nometa;
6283 if (!strpbrk(str->text, metachars))
6284 goto nometa;
6285 savelastp = exparg.lastp;
6286
6287 INT_OFF;
6288 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6289 {
6290 int i = strlen(str->text);
6291 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6292 }
6293
6294 expmeta(expdir, p);
6295 free(expdir);
6296 if (p != str->text)
6297 free(p);
6298 INT_ON;
6299 if (exparg.lastp == savelastp) {
6300 /*
6301 * no matches
6302 */
6303 nometa:
6304 *exparg.lastp = str;
6305 rmescapes(str->text);
6306 exparg.lastp = &str->next;
6307 } else {
6308 *exparg.lastp = NULL;
6309 *savelastp = sp = expsort(*savelastp);
6310 while (sp->next != NULL)
6311 sp = sp->next;
6312 exparg.lastp = &sp->next;
6313 }
6314 str = str->next;
6315 }
6316}
6317
6318/*
6319 * Perform variable substitution and command substitution on an argument,
6320 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6321 * perform splitting and file name expansion. When arglist is NULL, perform
6322 * here document expansion.
6323 */
6324static void
6325expandarg(union node *arg, struct arglist *arglist, int flag)
6326{
6327 struct strlist *sp;
6328 char *p;
6329
6330 argbackq = arg->narg.backquote;
6331 STARTSTACKSTR(expdest);
6332 ifsfirst.next = NULL;
6333 ifslastp = NULL;
6334 argstr(arg->narg.text, flag);
6335 p = _STPUTC('\0', expdest);
6336 expdest = p - 1;
6337 if (arglist == NULL) {
6338 return; /* here document expanded */
6339 }
6340 p = grabstackstr(p);
6341 exparg.lastp = &exparg.list;
6342 /*
6343 * TODO - EXP_REDIR
6344 */
6345 if (flag & EXP_FULL) {
6346 ifsbreakup(p, &exparg);
6347 *exparg.lastp = NULL;
6348 exparg.lastp = &exparg.list;
6349 expandmeta(exparg.list, flag);
6350 } else {
6351 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6352 rmescapes(p);
6353 sp = stalloc(sizeof(*sp));
6354 sp->text = p;
6355 *exparg.lastp = sp;
6356 exparg.lastp = &sp->next;
6357 }
6358 if (ifsfirst.next)
6359 ifsfree();
6360 *exparg.lastp = NULL;
6361 if (exparg.list) {
6362 *arglist->lastp = exparg.list;
6363 arglist->lastp = exparg.lastp;
6364 }
6365}
6366
6367/*
6368 * Expand shell variables and backquotes inside a here document.
6369 */
6370static void
6371expandhere(union node *arg, int fd)
6372{
6373 herefd = fd;
6374 expandarg(arg, (struct arglist *)NULL, 0);
6375 full_write(fd, stackblock(), expdest - (char *)stackblock());
6376}
6377
6378/*
6379 * Returns true if the pattern matches the string.
6380 */
6381static int
6382patmatch(char *pattern, const char *string)
6383{
6384 return pmatch(preglob(pattern, 0, 0), string);
6385}
6386
6387/*
6388 * See if a pattern matches in a case statement.
6389 */
6390static int
6391casematch(union node *pattern, char *val)
6392{
6393 struct stackmark smark;
6394 int result;
6395
6396 setstackmark(&smark);
6397 argbackq = pattern->narg.backquote;
6398 STARTSTACKSTR(expdest);
6399 ifslastp = NULL;
6400 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6401 STACKSTRNUL(expdest);
6402 result = patmatch(stackblock(), val);
6403 popstackmark(&smark);
6404 return result;
6405}
6406
6407
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006408/* ============ find_command */
6409
6410struct builtincmd {
6411 const char *name;
6412 int (*builtin)(int, char **);
6413 /* unsigned flags; */
6414};
6415#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6416#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6417#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6418
6419struct cmdentry {
6420 int cmdtype;
6421 union param {
6422 int index;
6423 const struct builtincmd *cmd;
6424 struct funcnode *func;
6425 } u;
6426};
6427/* values of cmdtype */
6428#define CMDUNKNOWN -1 /* no entry in table for command */
6429#define CMDNORMAL 0 /* command is an executable program */
6430#define CMDFUNCTION 1 /* command is a shell function */
6431#define CMDBUILTIN 2 /* command is a shell builtin */
6432
6433/* action to find_command() */
6434#define DO_ERR 0x01 /* prints errors */
6435#define DO_ABS 0x02 /* checks absolute paths */
6436#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6437#define DO_ALTPATH 0x08 /* using alternate path */
6438#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6439
6440static void find_command(char *, struct cmdentry *, int, const char *);
6441
6442
6443/* ============ Hashing commands */
6444
6445/*
6446 * When commands are first encountered, they are entered in a hash table.
6447 * This ensures that a full path search will not have to be done for them
6448 * on each invocation.
6449 *
6450 * We should investigate converting to a linear search, even though that
6451 * would make the command name "hash" a misnomer.
6452 */
6453
6454#define CMDTABLESIZE 31 /* should be prime */
6455#define ARB 1 /* actual size determined at run time */
6456
6457struct tblentry {
6458 struct tblentry *next; /* next entry in hash chain */
6459 union param param; /* definition of builtin function */
6460 short cmdtype; /* index identifying command */
6461 char rehash; /* if set, cd done since entry created */
6462 char cmdname[ARB]; /* name of command */
6463};
6464
6465static struct tblentry *cmdtable[CMDTABLESIZE];
6466static int builtinloc = -1; /* index in path of %builtin, or -1 */
6467
6468static void
6469tryexec(char *cmd, char **argv, char **envp)
6470{
6471 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006472
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006473#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006474 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006475 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006476
6477 a = find_applet_by_name(cmd);
6478 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006479 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006480 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006481 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006482 }
6483 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006484 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006485 /* If they called chroot or otherwise made the binary no longer
6486 * executable, fall through */
6487 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006488 }
6489#endif
6490
6491 repeat:
6492#ifdef SYSV
6493 do {
6494 execve(cmd, argv, envp);
6495 } while (errno == EINTR);
6496#else
6497 execve(cmd, argv, envp);
6498#endif
6499 if (repeated++) {
6500 free(argv);
6501 } else if (errno == ENOEXEC) {
6502 char **ap;
6503 char **new;
6504
6505 for (ap = argv; *ap; ap++)
6506 ;
6507 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6508 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006509 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006510 ap += 2;
6511 argv++;
6512 while ((*ap++ = *argv++))
6513 ;
6514 argv = new;
6515 goto repeat;
6516 }
6517}
6518
6519/*
6520 * Exec a program. Never returns. If you change this routine, you may
6521 * have to change the find_command routine as well.
6522 */
6523#define environment() listvars(VEXPORT, VUNSET, 0)
6524static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6525static void
6526shellexec(char **argv, const char *path, int idx)
6527{
6528 char *cmdname;
6529 int e;
6530 char **envp;
6531 int exerrno;
6532
6533 clearredir(1);
6534 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006535 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006536#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006537 || find_applet_by_name(argv[0])
6538#endif
6539 ) {
6540 tryexec(argv[0], argv, envp);
6541 e = errno;
6542 } else {
6543 e = ENOENT;
6544 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6545 if (--idx < 0 && pathopt == NULL) {
6546 tryexec(cmdname, argv, envp);
6547 if (errno != ENOENT && errno != ENOTDIR)
6548 e = errno;
6549 }
6550 stunalloc(cmdname);
6551 }
6552 }
6553
6554 /* Map to POSIX errors */
6555 switch (e) {
6556 case EACCES:
6557 exerrno = 126;
6558 break;
6559 case ENOENT:
6560 exerrno = 127;
6561 break;
6562 default:
6563 exerrno = 2;
6564 break;
6565 }
6566 exitstatus = exerrno;
6567 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6568 argv[0], e, suppressint ));
6569 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6570 /* NOTREACHED */
6571}
6572
6573static void
6574printentry(struct tblentry *cmdp)
6575{
6576 int idx;
6577 const char *path;
6578 char *name;
6579
6580 idx = cmdp->param.index;
6581 path = pathval();
6582 do {
6583 name = padvance(&path, cmdp->cmdname);
6584 stunalloc(name);
6585 } while (--idx >= 0);
6586 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6587}
6588
6589/*
6590 * Clear out command entries. The argument specifies the first entry in
6591 * PATH which has changed.
6592 */
6593static void
6594clearcmdentry(int firstchange)
6595{
6596 struct tblentry **tblp;
6597 struct tblentry **pp;
6598 struct tblentry *cmdp;
6599
6600 INT_OFF;
6601 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6602 pp = tblp;
6603 while ((cmdp = *pp) != NULL) {
6604 if ((cmdp->cmdtype == CMDNORMAL &&
6605 cmdp->param.index >= firstchange)
6606 || (cmdp->cmdtype == CMDBUILTIN &&
6607 builtinloc >= firstchange)
6608 ) {
6609 *pp = cmdp->next;
6610 free(cmdp);
6611 } else {
6612 pp = &cmdp->next;
6613 }
6614 }
6615 }
6616 INT_ON;
6617}
6618
6619/*
6620 * Locate a command in the command hash table. If "add" is nonzero,
6621 * add the command to the table if it is not already present. The
6622 * variable "lastcmdentry" is set to point to the address of the link
6623 * pointing to the entry, so that delete_cmd_entry can delete the
6624 * entry.
6625 *
6626 * Interrupts must be off if called with add != 0.
6627 */
6628static struct tblentry **lastcmdentry;
6629
6630static struct tblentry *
6631cmdlookup(const char *name, int add)
6632{
6633 unsigned int hashval;
6634 const char *p;
6635 struct tblentry *cmdp;
6636 struct tblentry **pp;
6637
6638 p = name;
6639 hashval = (unsigned char)*p << 4;
6640 while (*p)
6641 hashval += (unsigned char)*p++;
6642 hashval &= 0x7FFF;
6643 pp = &cmdtable[hashval % CMDTABLESIZE];
6644 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6645 if (strcmp(cmdp->cmdname, name) == 0)
6646 break;
6647 pp = &cmdp->next;
6648 }
6649 if (add && cmdp == NULL) {
6650 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6651 + strlen(name) + 1);
6652 cmdp->next = NULL;
6653 cmdp->cmdtype = CMDUNKNOWN;
6654 strcpy(cmdp->cmdname, name);
6655 }
6656 lastcmdentry = pp;
6657 return cmdp;
6658}
6659
6660/*
6661 * Delete the command entry returned on the last lookup.
6662 */
6663static void
6664delete_cmd_entry(void)
6665{
6666 struct tblentry *cmdp;
6667
6668 INT_OFF;
6669 cmdp = *lastcmdentry;
6670 *lastcmdentry = cmdp->next;
6671 if (cmdp->cmdtype == CMDFUNCTION)
6672 freefunc(cmdp->param.func);
6673 free(cmdp);
6674 INT_ON;
6675}
6676
6677/*
6678 * Add a new command entry, replacing any existing command entry for
6679 * the same name - except special builtins.
6680 */
6681static void
6682addcmdentry(char *name, struct cmdentry *entry)
6683{
6684 struct tblentry *cmdp;
6685
6686 cmdp = cmdlookup(name, 1);
6687 if (cmdp->cmdtype == CMDFUNCTION) {
6688 freefunc(cmdp->param.func);
6689 }
6690 cmdp->cmdtype = entry->cmdtype;
6691 cmdp->param = entry->u;
6692 cmdp->rehash = 0;
6693}
6694
6695static int
6696hashcmd(int argc, char **argv)
6697{
6698 struct tblentry **pp;
6699 struct tblentry *cmdp;
6700 int c;
6701 struct cmdentry entry;
6702 char *name;
6703
6704 while ((c = nextopt("r")) != '\0') {
6705 clearcmdentry(0);
6706 return 0;
6707 }
6708 if (*argptr == NULL) {
6709 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6710 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6711 if (cmdp->cmdtype == CMDNORMAL)
6712 printentry(cmdp);
6713 }
6714 }
6715 return 0;
6716 }
6717 c = 0;
6718 while ((name = *argptr) != NULL) {
6719 cmdp = cmdlookup(name, 0);
6720 if (cmdp != NULL
6721 && (cmdp->cmdtype == CMDNORMAL
6722 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6723 delete_cmd_entry();
6724 find_command(name, &entry, DO_ERR, pathval());
6725 if (entry.cmdtype == CMDUNKNOWN)
6726 c = 1;
6727 argptr++;
6728 }
6729 return c;
6730}
6731
6732/*
6733 * Called when a cd is done. Marks all commands so the next time they
6734 * are executed they will be rehashed.
6735 */
6736static void
6737hashcd(void)
6738{
6739 struct tblentry **pp;
6740 struct tblentry *cmdp;
6741
6742 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6743 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6744 if (cmdp->cmdtype == CMDNORMAL || (
6745 cmdp->cmdtype == CMDBUILTIN &&
6746 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6747 builtinloc > 0
6748 ))
6749 cmdp->rehash = 1;
6750 }
6751 }
6752}
6753
6754/*
6755 * Fix command hash table when PATH changed.
6756 * Called before PATH is changed. The argument is the new value of PATH;
6757 * pathval() still returns the old value at this point.
6758 * Called with interrupts off.
6759 */
6760static void
6761changepath(const char *newval)
6762{
6763 const char *old, *new;
6764 int idx;
6765 int firstchange;
6766 int idx_bltin;
6767
6768 old = pathval();
6769 new = newval;
6770 firstchange = 9999; /* assume no change */
6771 idx = 0;
6772 idx_bltin = -1;
6773 for (;;) {
6774 if (*old != *new) {
6775 firstchange = idx;
6776 if ((*old == '\0' && *new == ':')
6777 || (*old == ':' && *new == '\0'))
6778 firstchange++;
6779 old = new; /* ignore subsequent differences */
6780 }
6781 if (*new == '\0')
6782 break;
6783 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6784 idx_bltin = idx;
6785 if (*new == ':') {
6786 idx++;
6787 }
6788 new++, old++;
6789 }
6790 if (builtinloc < 0 && idx_bltin >= 0)
6791 builtinloc = idx_bltin; /* zap builtins */
6792 if (builtinloc >= 0 && idx_bltin < 0)
6793 firstchange = 0;
6794 clearcmdentry(firstchange);
6795 builtinloc = idx_bltin;
6796}
6797
6798#define TEOF 0
6799#define TNL 1
6800#define TREDIR 2
6801#define TWORD 3
6802#define TSEMI 4
6803#define TBACKGND 5
6804#define TAND 6
6805#define TOR 7
6806#define TPIPE 8
6807#define TLP 9
6808#define TRP 10
6809#define TENDCASE 11
6810#define TENDBQUOTE 12
6811#define TNOT 13
6812#define TCASE 14
6813#define TDO 15
6814#define TDONE 16
6815#define TELIF 17
6816#define TELSE 18
6817#define TESAC 19
6818#define TFI 20
6819#define TFOR 21
6820#define TIF 22
6821#define TIN 23
6822#define TTHEN 24
6823#define TUNTIL 25
6824#define TWHILE 26
6825#define TBEGIN 27
6826#define TEND 28
6827
6828/* first char is indicating which tokens mark the end of a list */
6829static const char *const tokname_array[] = {
6830 "\1end of file",
6831 "\0newline",
6832 "\0redirection",
6833 "\0word",
6834 "\0;",
6835 "\0&",
6836 "\0&&",
6837 "\0||",
6838 "\0|",
6839 "\0(",
6840 "\1)",
6841 "\1;;",
6842 "\1`",
6843#define KWDOFFSET 13
6844 /* the following are keywords */
6845 "\0!",
6846 "\0case",
6847 "\1do",
6848 "\1done",
6849 "\1elif",
6850 "\1else",
6851 "\1esac",
6852 "\1fi",
6853 "\0for",
6854 "\0if",
6855 "\0in",
6856 "\1then",
6857 "\0until",
6858 "\0while",
6859 "\0{",
6860 "\1}",
6861};
6862
6863static const char *
6864tokname(int tok)
6865{
6866 static char buf[16];
6867
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006868//try this:
6869//if (tok < TSEMI) return tokname_array[tok] + 1;
6870//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6871//return buf;
6872
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006873 if (tok >= TSEMI)
6874 buf[0] = '"';
6875 sprintf(buf + (tok >= TSEMI), "%s%c",
6876 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6877 return buf;
6878}
6879
6880/* Wrapper around strcmp for qsort/bsearch/... */
6881static int
6882pstrcmp(const void *a, const void *b)
6883{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006884 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006885}
6886
6887static const char *const *
6888findkwd(const char *s)
6889{
6890 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006891 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6892 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006893}
6894
6895/*
6896 * Locate and print what a word is...
6897 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006898static int
6899describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900{
6901 struct cmdentry entry;
6902 struct tblentry *cmdp;
6903#if ENABLE_ASH_ALIAS
6904 const struct alias *ap;
6905#endif
6906 const char *path = pathval();
6907
6908 if (describe_command_verbose) {
6909 out1str(command);
6910 }
6911
6912 /* First look at the keywords */
6913 if (findkwd(command)) {
6914 out1str(describe_command_verbose ? " is a shell keyword" : command);
6915 goto out;
6916 }
6917
6918#if ENABLE_ASH_ALIAS
6919 /* Then look at the aliases */
6920 ap = lookupalias(command, 0);
6921 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006922 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006923 out1str("alias ");
6924 printalias(ap);
6925 return 0;
6926 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006927 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006928 goto out;
6929 }
6930#endif
6931 /* Then check if it is a tracked alias */
6932 cmdp = cmdlookup(command, 0);
6933 if (cmdp != NULL) {
6934 entry.cmdtype = cmdp->cmdtype;
6935 entry.u = cmdp->param;
6936 } else {
6937 /* Finally use brute force */
6938 find_command(command, &entry, DO_ABS, path);
6939 }
6940
6941 switch (entry.cmdtype) {
6942 case CMDNORMAL: {
6943 int j = entry.u.index;
6944 char *p;
6945 if (j == -1) {
6946 p = command;
6947 } else {
6948 do {
6949 p = padvance(&path, command);
6950 stunalloc(p);
6951 } while (--j >= 0);
6952 }
6953 if (describe_command_verbose) {
6954 out1fmt(" is%s %s",
6955 (cmdp ? " a tracked alias for" : nullstr), p
6956 );
6957 } else {
6958 out1str(p);
6959 }
6960 break;
6961 }
6962
6963 case CMDFUNCTION:
6964 if (describe_command_verbose) {
6965 out1str(" is a shell function");
6966 } else {
6967 out1str(command);
6968 }
6969 break;
6970
6971 case CMDBUILTIN:
6972 if (describe_command_verbose) {
6973 out1fmt(" is a %sshell builtin",
6974 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6975 "special " : nullstr
6976 );
6977 } else {
6978 out1str(command);
6979 }
6980 break;
6981
6982 default:
6983 if (describe_command_verbose) {
6984 out1str(": not found\n");
6985 }
6986 return 127;
6987 }
6988 out:
6989 outstr("\n", stdout);
6990 return 0;
6991}
6992
6993static int
6994typecmd(int argc, char **argv)
6995{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006996 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006997 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00006998 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006999
Denis Vlasenko46846e22007-05-20 13:08:31 +00007000 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007001 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007002 i++;
7003 verbose = 0;
7004 }
7005 while (i < argc) {
7006 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007007 }
7008 return err;
7009}
7010
7011#if ENABLE_ASH_CMDCMD
7012static int
7013commandcmd(int argc, char **argv)
7014{
7015 int c;
7016 enum {
7017 VERIFY_BRIEF = 1,
7018 VERIFY_VERBOSE = 2,
7019 } verify = 0;
7020
7021 while ((c = nextopt("pvV")) != '\0')
7022 if (c == 'V')
7023 verify |= VERIFY_VERBOSE;
7024 else if (c == 'v')
7025 verify |= VERIFY_BRIEF;
7026#if DEBUG
7027 else if (c != 'p')
7028 abort();
7029#endif
7030 if (verify)
7031 return describe_command(*argptr, verify - VERIFY_BRIEF);
7032
7033 return 0;
7034}
7035#endif
7036
7037
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007038/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007039
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007040static int funcblocksize; /* size of structures in function */
7041static int funcstringsize; /* size of strings in node */
7042static void *funcblock; /* block to allocate function from */
7043static char *funcstring; /* block to allocate strings from */
7044
Eric Andersencb57d552001-06-28 07:25:16 +00007045/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007046#define EV_EXIT 01 /* exit after evaluating tree */
7047#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7048#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007049
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050static const short nodesize[26] = {
7051 SHELL_ALIGN(sizeof(struct ncmd)),
7052 SHELL_ALIGN(sizeof(struct npipe)),
7053 SHELL_ALIGN(sizeof(struct nredir)),
7054 SHELL_ALIGN(sizeof(struct nredir)),
7055 SHELL_ALIGN(sizeof(struct nredir)),
7056 SHELL_ALIGN(sizeof(struct nbinary)),
7057 SHELL_ALIGN(sizeof(struct nbinary)),
7058 SHELL_ALIGN(sizeof(struct nbinary)),
7059 SHELL_ALIGN(sizeof(struct nif)),
7060 SHELL_ALIGN(sizeof(struct nbinary)),
7061 SHELL_ALIGN(sizeof(struct nbinary)),
7062 SHELL_ALIGN(sizeof(struct nfor)),
7063 SHELL_ALIGN(sizeof(struct ncase)),
7064 SHELL_ALIGN(sizeof(struct nclist)),
7065 SHELL_ALIGN(sizeof(struct narg)),
7066 SHELL_ALIGN(sizeof(struct narg)),
7067 SHELL_ALIGN(sizeof(struct nfile)),
7068 SHELL_ALIGN(sizeof(struct nfile)),
7069 SHELL_ALIGN(sizeof(struct nfile)),
7070 SHELL_ALIGN(sizeof(struct nfile)),
7071 SHELL_ALIGN(sizeof(struct nfile)),
7072 SHELL_ALIGN(sizeof(struct ndup)),
7073 SHELL_ALIGN(sizeof(struct ndup)),
7074 SHELL_ALIGN(sizeof(struct nhere)),
7075 SHELL_ALIGN(sizeof(struct nhere)),
7076 SHELL_ALIGN(sizeof(struct nnot)),
7077};
7078
7079static void calcsize(union node *n);
7080
7081static void
7082sizenodelist(struct nodelist *lp)
7083{
7084 while (lp) {
7085 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7086 calcsize(lp->n);
7087 lp = lp->next;
7088 }
7089}
7090
7091static void
7092calcsize(union node *n)
7093{
7094 if (n == NULL)
7095 return;
7096 funcblocksize += nodesize[n->type];
7097 switch (n->type) {
7098 case NCMD:
7099 calcsize(n->ncmd.redirect);
7100 calcsize(n->ncmd.args);
7101 calcsize(n->ncmd.assign);
7102 break;
7103 case NPIPE:
7104 sizenodelist(n->npipe.cmdlist);
7105 break;
7106 case NREDIR:
7107 case NBACKGND:
7108 case NSUBSHELL:
7109 calcsize(n->nredir.redirect);
7110 calcsize(n->nredir.n);
7111 break;
7112 case NAND:
7113 case NOR:
7114 case NSEMI:
7115 case NWHILE:
7116 case NUNTIL:
7117 calcsize(n->nbinary.ch2);
7118 calcsize(n->nbinary.ch1);
7119 break;
7120 case NIF:
7121 calcsize(n->nif.elsepart);
7122 calcsize(n->nif.ifpart);
7123 calcsize(n->nif.test);
7124 break;
7125 case NFOR:
7126 funcstringsize += strlen(n->nfor.var) + 1;
7127 calcsize(n->nfor.body);
7128 calcsize(n->nfor.args);
7129 break;
7130 case NCASE:
7131 calcsize(n->ncase.cases);
7132 calcsize(n->ncase.expr);
7133 break;
7134 case NCLIST:
7135 calcsize(n->nclist.body);
7136 calcsize(n->nclist.pattern);
7137 calcsize(n->nclist.next);
7138 break;
7139 case NDEFUN:
7140 case NARG:
7141 sizenodelist(n->narg.backquote);
7142 funcstringsize += strlen(n->narg.text) + 1;
7143 calcsize(n->narg.next);
7144 break;
7145 case NTO:
7146 case NCLOBBER:
7147 case NFROM:
7148 case NFROMTO:
7149 case NAPPEND:
7150 calcsize(n->nfile.fname);
7151 calcsize(n->nfile.next);
7152 break;
7153 case NTOFD:
7154 case NFROMFD:
7155 calcsize(n->ndup.vname);
7156 calcsize(n->ndup.next);
7157 break;
7158 case NHERE:
7159 case NXHERE:
7160 calcsize(n->nhere.doc);
7161 calcsize(n->nhere.next);
7162 break;
7163 case NNOT:
7164 calcsize(n->nnot.com);
7165 break;
7166 };
7167}
7168
7169static char *
7170nodeckstrdup(char *s)
7171{
7172 char *rtn = funcstring;
7173
7174 strcpy(funcstring, s);
7175 funcstring += strlen(s) + 1;
7176 return rtn;
7177}
7178
7179static union node *copynode(union node *);
7180
7181static struct nodelist *
7182copynodelist(struct nodelist *lp)
7183{
7184 struct nodelist *start;
7185 struct nodelist **lpp;
7186
7187 lpp = &start;
7188 while (lp) {
7189 *lpp = funcblock;
7190 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7191 (*lpp)->n = copynode(lp->n);
7192 lp = lp->next;
7193 lpp = &(*lpp)->next;
7194 }
7195 *lpp = NULL;
7196 return start;
7197}
7198
7199static union node *
7200copynode(union node *n)
7201{
7202 union node *new;
7203
7204 if (n == NULL)
7205 return NULL;
7206 new = funcblock;
7207 funcblock = (char *) funcblock + nodesize[n->type];
7208
7209 switch (n->type) {
7210 case NCMD:
7211 new->ncmd.redirect = copynode(n->ncmd.redirect);
7212 new->ncmd.args = copynode(n->ncmd.args);
7213 new->ncmd.assign = copynode(n->ncmd.assign);
7214 break;
7215 case NPIPE:
7216 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7217 new->npipe.backgnd = n->npipe.backgnd;
7218 break;
7219 case NREDIR:
7220 case NBACKGND:
7221 case NSUBSHELL:
7222 new->nredir.redirect = copynode(n->nredir.redirect);
7223 new->nredir.n = copynode(n->nredir.n);
7224 break;
7225 case NAND:
7226 case NOR:
7227 case NSEMI:
7228 case NWHILE:
7229 case NUNTIL:
7230 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7231 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7232 break;
7233 case NIF:
7234 new->nif.elsepart = copynode(n->nif.elsepart);
7235 new->nif.ifpart = copynode(n->nif.ifpart);
7236 new->nif.test = copynode(n->nif.test);
7237 break;
7238 case NFOR:
7239 new->nfor.var = nodeckstrdup(n->nfor.var);
7240 new->nfor.body = copynode(n->nfor.body);
7241 new->nfor.args = copynode(n->nfor.args);
7242 break;
7243 case NCASE:
7244 new->ncase.cases = copynode(n->ncase.cases);
7245 new->ncase.expr = copynode(n->ncase.expr);
7246 break;
7247 case NCLIST:
7248 new->nclist.body = copynode(n->nclist.body);
7249 new->nclist.pattern = copynode(n->nclist.pattern);
7250 new->nclist.next = copynode(n->nclist.next);
7251 break;
7252 case NDEFUN:
7253 case NARG:
7254 new->narg.backquote = copynodelist(n->narg.backquote);
7255 new->narg.text = nodeckstrdup(n->narg.text);
7256 new->narg.next = copynode(n->narg.next);
7257 break;
7258 case NTO:
7259 case NCLOBBER:
7260 case NFROM:
7261 case NFROMTO:
7262 case NAPPEND:
7263 new->nfile.fname = copynode(n->nfile.fname);
7264 new->nfile.fd = n->nfile.fd;
7265 new->nfile.next = copynode(n->nfile.next);
7266 break;
7267 case NTOFD:
7268 case NFROMFD:
7269 new->ndup.vname = copynode(n->ndup.vname);
7270 new->ndup.dupfd = n->ndup.dupfd;
7271 new->ndup.fd = n->ndup.fd;
7272 new->ndup.next = copynode(n->ndup.next);
7273 break;
7274 case NHERE:
7275 case NXHERE:
7276 new->nhere.doc = copynode(n->nhere.doc);
7277 new->nhere.fd = n->nhere.fd;
7278 new->nhere.next = copynode(n->nhere.next);
7279 break;
7280 case NNOT:
7281 new->nnot.com = copynode(n->nnot.com);
7282 break;
7283 };
7284 new->type = n->type;
7285 return new;
7286}
7287
7288/*
7289 * Make a copy of a parse tree.
7290 */
7291static struct funcnode *
7292copyfunc(union node *n)
7293{
7294 struct funcnode *f;
7295 size_t blocksize;
7296
7297 funcblocksize = offsetof(struct funcnode, n);
7298 funcstringsize = 0;
7299 calcsize(n);
7300 blocksize = funcblocksize;
7301 f = ckmalloc(blocksize + funcstringsize);
7302 funcblock = (char *) f + offsetof(struct funcnode, n);
7303 funcstring = (char *) f + blocksize;
7304 copynode(n);
7305 f->count = 0;
7306 return f;
7307}
7308
7309/*
7310 * Define a shell function.
7311 */
7312static void
7313defun(char *name, union node *func)
7314{
7315 struct cmdentry entry;
7316
7317 INT_OFF;
7318 entry.cmdtype = CMDFUNCTION;
7319 entry.u.func = copyfunc(func);
7320 addcmdentry(name, &entry);
7321 INT_ON;
7322}
7323
7324static int evalskip; /* set if we are skipping commands */
7325/* reasons for skipping commands (see comment on breakcmd routine) */
7326#define SKIPBREAK (1 << 0)
7327#define SKIPCONT (1 << 1)
7328#define SKIPFUNC (1 << 2)
7329#define SKIPFILE (1 << 3)
7330#define SKIPEVAL (1 << 4)
7331static int skipcount; /* number of levels to skip */
7332static int funcnest; /* depth of function calls */
7333
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007334/* forward decl way out to parsing code - dotrap needs it */
7335static int evalstring(char *s, int mask);
7336
7337/*
7338 * Called to execute a trap. Perhaps we should avoid entering new trap
7339 * handlers while we are executing a trap handler.
7340 */
7341static int
7342dotrap(void)
7343{
7344 char *p;
7345 char *q;
7346 int i;
7347 int savestatus;
7348 int skip = 0;
7349
7350 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007351 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007352 xbarrier();
7353
7354 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7355 if (!*q)
7356 continue;
7357 *q = '\0';
7358
7359 p = trap[i + 1];
7360 if (!p)
7361 continue;
7362 skip = evalstring(p, SKIPEVAL);
7363 exitstatus = savestatus;
7364 if (skip)
7365 break;
7366 }
7367
7368 return skip;
7369}
7370
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007371/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007372static void evalloop(union node *, int);
7373static void evalfor(union node *, int);
7374static void evalcase(union node *, int);
7375static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007376static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007377static void evalpipe(union node *, int);
7378static void evalcommand(union node *, int);
7379static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007380static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007381
Eric Andersen62483552001-07-10 06:09:16 +00007382/*
Eric Andersenc470f442003-07-28 09:56:35 +00007383 * Evaluate a parse tree. The value is left in the global variable
7384 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007385 */
Eric Andersenc470f442003-07-28 09:56:35 +00007386static void
7387evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007388{
Eric Andersenc470f442003-07-28 09:56:35 +00007389 int checkexit = 0;
7390 void (*evalfn)(union node *, int);
7391 unsigned isor;
7392 int status;
7393 if (n == NULL) {
7394 TRACE(("evaltree(NULL) called\n"));
7395 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007396 }
Eric Andersenc470f442003-07-28 09:56:35 +00007397 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007398 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007399 switch (n->type) {
7400 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007401#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007402 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007403 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007404 break;
7405#endif
7406 case NNOT:
7407 evaltree(n->nnot.com, EV_TESTED);
7408 status = !exitstatus;
7409 goto setstatus;
7410 case NREDIR:
7411 expredir(n->nredir.redirect);
7412 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7413 if (!status) {
7414 evaltree(n->nredir.n, flags & EV_TESTED);
7415 status = exitstatus;
7416 }
7417 popredir(0);
7418 goto setstatus;
7419 case NCMD:
7420 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007421 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007422 if (eflag && !(flags & EV_TESTED))
7423 checkexit = ~0;
7424 goto calleval;
7425 case NFOR:
7426 evalfn = evalfor;
7427 goto calleval;
7428 case NWHILE:
7429 case NUNTIL:
7430 evalfn = evalloop;
7431 goto calleval;
7432 case NSUBSHELL:
7433 case NBACKGND:
7434 evalfn = evalsubshell;
7435 goto calleval;
7436 case NPIPE:
7437 evalfn = evalpipe;
7438 goto checkexit;
7439 case NCASE:
7440 evalfn = evalcase;
7441 goto calleval;
7442 case NAND:
7443 case NOR:
7444 case NSEMI:
7445#if NAND + 1 != NOR
7446#error NAND + 1 != NOR
7447#endif
7448#if NOR + 1 != NSEMI
7449#error NOR + 1 != NSEMI
7450#endif
7451 isor = n->type - NAND;
7452 evaltree(
7453 n->nbinary.ch1,
7454 (flags | ((isor >> 1) - 1)) & EV_TESTED
7455 );
7456 if (!exitstatus == isor)
7457 break;
7458 if (!evalskip) {
7459 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007460 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007461 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007462 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007463 evalfn(n, flags);
7464 break;
7465 }
7466 break;
7467 case NIF:
7468 evaltree(n->nif.test, EV_TESTED);
7469 if (evalskip)
7470 break;
7471 if (exitstatus == 0) {
7472 n = n->nif.ifpart;
7473 goto evaln;
7474 } else if (n->nif.elsepart) {
7475 n = n->nif.elsepart;
7476 goto evaln;
7477 }
7478 goto success;
7479 case NDEFUN:
7480 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007481 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007482 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007483 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007484 exitstatus = status;
7485 break;
7486 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007487 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007488 if ((checkexit & exitstatus))
7489 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007490 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007491 goto exexit;
7492
7493 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007494 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007495 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007496 }
Eric Andersen62483552001-07-10 06:09:16 +00007497}
7498
Eric Andersenc470f442003-07-28 09:56:35 +00007499#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7500static
7501#endif
7502void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7503
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007504static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007505
7506static void
7507evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007508{
7509 int status;
7510
7511 loopnest++;
7512 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007513 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007514 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007515 int i;
7516
Eric Andersencb57d552001-06-28 07:25:16 +00007517 evaltree(n->nbinary.ch1, EV_TESTED);
7518 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007519 skipping:
7520 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007521 evalskip = 0;
7522 continue;
7523 }
7524 if (evalskip == SKIPBREAK && --skipcount <= 0)
7525 evalskip = 0;
7526 break;
7527 }
Eric Andersenc470f442003-07-28 09:56:35 +00007528 i = exitstatus;
7529 if (n->type != NWHILE)
7530 i = !i;
7531 if (i != 0)
7532 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007533 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007534 status = exitstatus;
7535 if (evalskip)
7536 goto skipping;
7537 }
7538 loopnest--;
7539 exitstatus = status;
7540}
7541
Eric Andersenc470f442003-07-28 09:56:35 +00007542static void
7543evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007544{
7545 struct arglist arglist;
7546 union node *argp;
7547 struct strlist *sp;
7548 struct stackmark smark;
7549
7550 setstackmark(&smark);
7551 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007552 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007553 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007554 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007555 if (evalskip)
7556 goto out;
7557 }
7558 *arglist.lastp = NULL;
7559
7560 exitstatus = 0;
7561 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007562 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007563 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007564 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007565 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007566 if (evalskip) {
7567 if (evalskip == SKIPCONT && --skipcount <= 0) {
7568 evalskip = 0;
7569 continue;
7570 }
7571 if (evalskip == SKIPBREAK && --skipcount <= 0)
7572 evalskip = 0;
7573 break;
7574 }
7575 }
7576 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007577 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007578 popstackmark(&smark);
7579}
7580
Eric Andersenc470f442003-07-28 09:56:35 +00007581static void
7582evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007583{
7584 union node *cp;
7585 union node *patp;
7586 struct arglist arglist;
7587 struct stackmark smark;
7588
7589 setstackmark(&smark);
7590 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007591 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007592 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007593 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7594 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007595 if (casematch(patp, arglist.list->text)) {
7596 if (evalskip == 0) {
7597 evaltree(cp->nclist.body, flags);
7598 }
7599 goto out;
7600 }
7601 }
7602 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007603 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007604 popstackmark(&smark);
7605}
7606
Eric Andersenc470f442003-07-28 09:56:35 +00007607/*
7608 * Kick off a subshell to evaluate a tree.
7609 */
Eric Andersenc470f442003-07-28 09:56:35 +00007610static void
7611evalsubshell(union node *n, int flags)
7612{
7613 struct job *jp;
7614 int backgnd = (n->type == NBACKGND);
7615 int status;
7616
7617 expredir(n->nredir.redirect);
7618 if (!backgnd && flags & EV_EXIT && !trap[0])
7619 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007620 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007621 jp = makejob(n, 1);
7622 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007623 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007624 flags |= EV_EXIT;
7625 if (backgnd)
7626 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007627 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007628 redirect(n->nredir.redirect, 0);
7629 evaltreenr(n->nredir.n, flags);
7630 /* never returns */
7631 }
7632 status = 0;
7633 if (! backgnd)
7634 status = waitforjob(jp);
7635 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007636 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007637}
7638
Eric Andersenc470f442003-07-28 09:56:35 +00007639/*
7640 * Compute the names of the files in a redirection list.
7641 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007642static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007643static void
7644expredir(union node *n)
7645{
7646 union node *redir;
7647
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007648 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007649 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007650
7651 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007652 fn.lastp = &fn.list;
7653 switch (redir->type) {
7654 case NFROMTO:
7655 case NFROM:
7656 case NTO:
7657 case NCLOBBER:
7658 case NAPPEND:
7659 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7660 redir->nfile.expfname = fn.list->text;
7661 break;
7662 case NFROMFD:
7663 case NTOFD:
7664 if (redir->ndup.vname) {
7665 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007666 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007667 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007668 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007669 }
7670 break;
7671 }
7672 }
7673}
7674
Eric Andersencb57d552001-06-28 07:25:16 +00007675/*
Eric Andersencb57d552001-06-28 07:25:16 +00007676 * Evaluate a pipeline. All the processes in the pipeline are children
7677 * of the process creating the pipeline. (This differs from some versions
7678 * of the shell, which make the last process in a pipeline the parent
7679 * of all the rest.)
7680 */
Eric Andersenc470f442003-07-28 09:56:35 +00007681static void
7682evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007683{
7684 struct job *jp;
7685 struct nodelist *lp;
7686 int pipelen;
7687 int prevfd;
7688 int pip[2];
7689
Eric Andersenc470f442003-07-28 09:56:35 +00007690 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007691 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007692 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007693 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007694 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007695 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007696 jp = makejob(n, pipelen);
7697 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007698 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007699 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007700 pip[1] = -1;
7701 if (lp->next) {
7702 if (pipe(pip) < 0) {
7703 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007704 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007705 }
7706 }
7707 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007708 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007709 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007710 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007711 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007712 if (prevfd > 0) {
7713 dup2(prevfd, 0);
7714 close(prevfd);
7715 }
7716 if (pip[1] > 1) {
7717 dup2(pip[1], 1);
7718 close(pip[1]);
7719 }
Eric Andersenc470f442003-07-28 09:56:35 +00007720 evaltreenr(lp->n, flags);
7721 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007722 }
7723 if (prevfd >= 0)
7724 close(prevfd);
7725 prevfd = pip[0];
7726 close(pip[1]);
7727 }
Eric Andersencb57d552001-06-28 07:25:16 +00007728 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007729 exitstatus = waitforjob(jp);
7730 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007731 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007732 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007733}
7734
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007735/*
7736 * Controls whether the shell is interactive or not.
7737 */
7738static void
7739setinteractive(int on)
7740{
7741 static int is_interactive;
7742
7743 if (++on == is_interactive)
7744 return;
7745 is_interactive = on;
7746 setsignal(SIGINT);
7747 setsignal(SIGQUIT);
7748 setsignal(SIGTERM);
7749#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7750 if (is_interactive > 1) {
7751 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007752 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007753
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007754 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007755 out1fmt(
7756 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007757 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007758 "Enter 'help' for a list of built-in commands."
7759 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007760 bb_banner);
7761 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007762 }
7763 }
7764#endif
7765}
7766
7767#if ENABLE_FEATURE_EDITING_VI
7768#define setvimode(on) do { \
7769 if (on) line_input_state->flags |= VI_MODE; \
7770 else line_input_state->flags &= ~VI_MODE; \
7771} while (0)
7772#else
7773#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7774#endif
7775
7776static void
7777optschanged(void)
7778{
7779#if DEBUG
7780 opentrace();
7781#endif
7782 setinteractive(iflag);
7783 setjobctl(mflag);
7784 setvimode(viflag);
7785}
7786
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007787static struct localvar *localvars;
7788
7789/*
7790 * Called after a function returns.
7791 * Interrupts must be off.
7792 */
7793static void
7794poplocalvars(void)
7795{
7796 struct localvar *lvp;
7797 struct var *vp;
7798
7799 while ((lvp = localvars) != NULL) {
7800 localvars = lvp->next;
7801 vp = lvp->vp;
7802 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7803 if (vp == NULL) { /* $- saved */
7804 memcpy(optlist, lvp->text, sizeof(optlist));
7805 free((char*)lvp->text);
7806 optschanged();
7807 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7808 unsetvar(vp->text);
7809 } else {
7810 if (vp->func)
7811 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7812 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7813 free((char*)vp->text);
7814 vp->flags = lvp->flags;
7815 vp->text = lvp->text;
7816 }
7817 free(lvp);
7818 }
7819}
7820
7821static int
7822evalfun(struct funcnode *func, int argc, char **argv, int flags)
7823{
7824 volatile struct shparam saveparam;
7825 struct localvar *volatile savelocalvars;
7826 struct jmploc *volatile savehandler;
7827 struct jmploc jmploc;
7828 int e;
7829
7830 saveparam = shellparam;
7831 savelocalvars = localvars;
7832 e = setjmp(jmploc.loc);
7833 if (e) {
7834 goto funcdone;
7835 }
7836 INT_OFF;
7837 savehandler = exception_handler;
7838 exception_handler = &jmploc;
7839 localvars = NULL;
7840 shellparam.malloc = 0;
7841 func->count++;
7842 funcnest++;
7843 INT_ON;
7844 shellparam.nparam = argc - 1;
7845 shellparam.p = argv + 1;
7846#if ENABLE_ASH_GETOPTS
7847 shellparam.optind = 1;
7848 shellparam.optoff = -1;
7849#endif
7850 evaltree(&func->n, flags & EV_TESTED);
7851funcdone:
7852 INT_OFF;
7853 funcnest--;
7854 freefunc(func);
7855 poplocalvars();
7856 localvars = savelocalvars;
7857 freeparam(&shellparam);
7858 shellparam = saveparam;
7859 exception_handler = savehandler;
7860 INT_ON;
7861 evalskip &= ~SKIPFUNC;
7862 return e;
7863}
7864
Denis Vlasenko131ae172007-02-18 13:00:19 +00007865#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007866static char **
7867parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007868{
7869 char *cp, c;
7870
7871 for (;;) {
7872 cp = *++argv;
7873 if (!cp)
7874 return 0;
7875 if (*cp++ != '-')
7876 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007877 c = *cp++;
7878 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007879 break;
7880 if (c == '-' && !*cp) {
7881 argv++;
7882 break;
7883 }
7884 do {
7885 switch (c) {
7886 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007887 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007888 break;
7889 default:
7890 /* run 'typecmd' for other options */
7891 return 0;
7892 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007893 c = *cp++;
7894 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007895 }
7896 return argv;
7897}
7898#endif
7899
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007900/*
7901 * Make a variable a local variable. When a variable is made local, it's
7902 * value and flags are saved in a localvar structure. The saved values
7903 * will be restored when the shell function returns. We handle the name
7904 * "-" as a special case.
7905 */
7906static void
7907mklocal(char *name)
7908{
7909 struct localvar *lvp;
7910 struct var **vpp;
7911 struct var *vp;
7912
7913 INT_OFF;
7914 lvp = ckmalloc(sizeof(struct localvar));
7915 if (LONE_DASH(name)) {
7916 char *p;
7917 p = ckmalloc(sizeof(optlist));
7918 lvp->text = memcpy(p, optlist, sizeof(optlist));
7919 vp = NULL;
7920 } else {
7921 char *eq;
7922
7923 vpp = hashvar(name);
7924 vp = *findvar(vpp, name);
7925 eq = strchr(name, '=');
7926 if (vp == NULL) {
7927 if (eq)
7928 setvareq(name, VSTRFIXED);
7929 else
7930 setvar(name, NULL, VSTRFIXED);
7931 vp = *vpp; /* the new variable */
7932 lvp->flags = VUNSET;
7933 } else {
7934 lvp->text = vp->text;
7935 lvp->flags = vp->flags;
7936 vp->flags |= VSTRFIXED|VTEXTFIXED;
7937 if (eq)
7938 setvareq(name, 0);
7939 }
7940 }
7941 lvp->vp = vp;
7942 lvp->next = localvars;
7943 localvars = lvp;
7944 INT_ON;
7945}
7946
7947/*
7948 * The "local" command.
7949 */
7950static int
7951localcmd(int argc, char **argv)
7952{
7953 char *name;
7954
7955 argv = argptr;
7956 while ((name = *argv++) != NULL) {
7957 mklocal(name);
7958 }
7959 return 0;
7960}
7961
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007962static int
7963falsecmd(int argc, char **argv)
7964{
7965 return 1;
7966}
7967
7968static int
7969truecmd(int argc, char **argv)
7970{
7971 return 0;
7972}
7973
7974static int
7975execcmd(int argc, char **argv)
7976{
7977 if (argc > 1) {
7978 iflag = 0; /* exit on error */
7979 mflag = 0;
7980 optschanged();
7981 shellexec(argv + 1, pathval(), 0);
7982 }
7983 return 0;
7984}
7985
7986/*
7987 * The return command.
7988 */
7989static int
7990returncmd(int argc, char **argv)
7991{
7992 /*
7993 * If called outside a function, do what ksh does;
7994 * skip the rest of the file.
7995 */
7996 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
7997 return argv[1] ? number(argv[1]) : exitstatus;
7998}
7999
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008000/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008001static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008002static int dotcmd(int, char **);
8003static int evalcmd(int, char **);
8004#if ENABLE_ASH_BUILTIN_ECHO
8005static int echocmd(int, char **);
8006#endif
8007#if ENABLE_ASH_BUILTIN_TEST
8008static int testcmd(int, char **);
8009#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008010static int exitcmd(int, char **);
8011static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008012#if ENABLE_ASH_GETOPTS
8013static int getoptscmd(int, char **);
8014#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008015#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8016static int helpcmd(int argc, char **argv);
8017#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008018#if ENABLE_ASH_MATH_SUPPORT
8019static int letcmd(int, char **);
8020#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008021static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008022static int setcmd(int, char **);
8023static int shiftcmd(int, char **);
8024static int timescmd(int, char **);
8025static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008026static int umaskcmd(int, char **);
8027static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008028static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008029
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008030#define BUILTIN_NOSPEC "0"
8031#define BUILTIN_SPECIAL "1"
8032#define BUILTIN_REGULAR "2"
8033#define BUILTIN_SPEC_REG "3"
8034#define BUILTIN_ASSIGN "4"
8035#define BUILTIN_SPEC_ASSG "5"
8036#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008037#define BUILTIN_SPEC_REG_ASSG "7"
8038
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008039/* make sure to keep these in proper order since it is searched via bsearch() */
8040static const struct builtincmd builtintab[] = {
8041 { BUILTIN_SPEC_REG ".", dotcmd },
8042 { BUILTIN_SPEC_REG ":", truecmd },
8043#if ENABLE_ASH_BUILTIN_TEST
8044 { BUILTIN_REGULAR "[", testcmd },
8045 { BUILTIN_REGULAR "[[", testcmd },
8046#endif
8047#if ENABLE_ASH_ALIAS
8048 { BUILTIN_REG_ASSG "alias", aliascmd },
8049#endif
8050#if JOBS
8051 { BUILTIN_REGULAR "bg", fg_bgcmd },
8052#endif
8053 { BUILTIN_SPEC_REG "break", breakcmd },
8054 { BUILTIN_REGULAR "cd", cdcmd },
8055 { BUILTIN_NOSPEC "chdir", cdcmd },
8056#if ENABLE_ASH_CMDCMD
8057 { BUILTIN_REGULAR "command", commandcmd },
8058#endif
8059 { BUILTIN_SPEC_REG "continue", breakcmd },
8060#if ENABLE_ASH_BUILTIN_ECHO
8061 { BUILTIN_REGULAR "echo", echocmd },
8062#endif
8063 { BUILTIN_SPEC_REG "eval", evalcmd },
8064 { BUILTIN_SPEC_REG "exec", execcmd },
8065 { BUILTIN_SPEC_REG "exit", exitcmd },
8066 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8067 { BUILTIN_REGULAR "false", falsecmd },
8068#if JOBS
8069 { BUILTIN_REGULAR "fg", fg_bgcmd },
8070#endif
8071#if ENABLE_ASH_GETOPTS
8072 { BUILTIN_REGULAR "getopts", getoptscmd },
8073#endif
8074 { BUILTIN_NOSPEC "hash", hashcmd },
8075#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8076 { BUILTIN_NOSPEC "help", helpcmd },
8077#endif
8078#if JOBS
8079 { BUILTIN_REGULAR "jobs", jobscmd },
8080 { BUILTIN_REGULAR "kill", killcmd },
8081#endif
8082#if ENABLE_ASH_MATH_SUPPORT
8083 { BUILTIN_NOSPEC "let", letcmd },
8084#endif
8085 { BUILTIN_ASSIGN "local", localcmd },
8086 { BUILTIN_NOSPEC "pwd", pwdcmd },
8087 { BUILTIN_REGULAR "read", readcmd },
8088 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8089 { BUILTIN_SPEC_REG "return", returncmd },
8090 { BUILTIN_SPEC_REG "set", setcmd },
8091 { BUILTIN_SPEC_REG "shift", shiftcmd },
8092 { BUILTIN_SPEC_REG "source", dotcmd },
8093#if ENABLE_ASH_BUILTIN_TEST
8094 { BUILTIN_REGULAR "test", testcmd },
8095#endif
8096 { BUILTIN_SPEC_REG "times", timescmd },
8097 { BUILTIN_SPEC_REG "trap", trapcmd },
8098 { BUILTIN_REGULAR "true", truecmd },
8099 { BUILTIN_NOSPEC "type", typecmd },
8100 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8101 { BUILTIN_REGULAR "umask", umaskcmd },
8102#if ENABLE_ASH_ALIAS
8103 { BUILTIN_REGULAR "unalias", unaliascmd },
8104#endif
8105 { BUILTIN_SPEC_REG "unset", unsetcmd },
8106 { BUILTIN_REGULAR "wait", waitcmd },
8107};
8108
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008109
8110#define COMMANDCMD (builtintab + 5 + \
8111 2 * ENABLE_ASH_BUILTIN_TEST + \
8112 ENABLE_ASH_ALIAS + \
8113 ENABLE_ASH_JOB_CONTROL)
8114#define EXECCMD (builtintab + 7 + \
8115 2 * ENABLE_ASH_BUILTIN_TEST + \
8116 ENABLE_ASH_ALIAS + \
8117 ENABLE_ASH_JOB_CONTROL + \
8118 ENABLE_ASH_CMDCMD + \
8119 ENABLE_ASH_BUILTIN_ECHO)
8120
8121/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008122 * Search the table of builtin commands.
8123 */
8124static struct builtincmd *
8125find_builtin(const char *name)
8126{
8127 struct builtincmd *bp;
8128
8129 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008130 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008131 pstrcmp
8132 );
8133 return bp;
8134}
8135
8136/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008137 * Execute a simple command.
8138 */
8139static int back_exitstatus; /* exit status of backquoted command */
8140static int
8141isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008142{
8143 const char *q = endofname(p);
8144 if (p == q)
8145 return 0;
8146 return *q == '=';
8147}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008148static int
8149bltincmd(int argc, char **argv)
8150{
8151 /* Preserve exitstatus of a previous possible redirection
8152 * as POSIX mandates */
8153 return back_exitstatus;
8154}
Eric Andersenc470f442003-07-28 09:56:35 +00008155static void
8156evalcommand(union node *cmd, int flags)
8157{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008158 static const struct builtincmd bltin = {
8159 "\0\0", bltincmd
8160 };
Eric Andersenc470f442003-07-28 09:56:35 +00008161 struct stackmark smark;
8162 union node *argp;
8163 struct arglist arglist;
8164 struct arglist varlist;
8165 char **argv;
8166 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008167 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008168 struct cmdentry cmdentry;
8169 struct job *jp;
8170 char *lastarg;
8171 const char *path;
8172 int spclbltin;
8173 int cmd_is_exec;
8174 int status;
8175 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008176 struct builtincmd *bcmd;
8177 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008178
8179 /* First expand the arguments. */
8180 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8181 setstackmark(&smark);
8182 back_exitstatus = 0;
8183
8184 cmdentry.cmdtype = CMDBUILTIN;
8185 cmdentry.u.cmd = &bltin;
8186 varlist.lastp = &varlist.list;
8187 *varlist.lastp = NULL;
8188 arglist.lastp = &arglist.list;
8189 *arglist.lastp = NULL;
8190
8191 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008192 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008193 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8194 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8195 }
8196
Eric Andersenc470f442003-07-28 09:56:35 +00008197 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8198 struct strlist **spp;
8199
8200 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008201 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008202 expandarg(argp, &arglist, EXP_VARTILDE);
8203 else
8204 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8205
Eric Andersenc470f442003-07-28 09:56:35 +00008206 for (sp = *spp; sp; sp = sp->next)
8207 argc++;
8208 }
8209
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008210 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008211 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008212 TRACE(("evalcommand arg: %s\n", sp->text));
8213 *nargv++ = sp->text;
8214 }
8215 *nargv = NULL;
8216
8217 lastarg = NULL;
8218 if (iflag && funcnest == 0 && argc > 0)
8219 lastarg = nargv[-1];
8220
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008221 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008222 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008223 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008224
8225 path = vpath.text;
8226 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8227 struct strlist **spp;
8228 char *p;
8229
8230 spp = varlist.lastp;
8231 expandarg(argp, &varlist, EXP_VARTILDE);
8232
8233 /*
8234 * Modify the command lookup path, if a PATH= assignment
8235 * is present
8236 */
8237 p = (*spp)->text;
8238 if (varequal(p, path))
8239 path = p;
8240 }
8241
8242 /* Print the command if xflag is set. */
8243 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008244 int n;
8245 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008246
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008247 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008248 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008249
8250 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008251 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008252 while (sp) {
8253 dprintf(preverrout_fd, p, sp->text);
8254 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008255 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008256 p--;
8257 }
8258 }
8259 sp = arglist.list;
8260 }
Rob Landley53437472006-07-16 08:14:35 +00008261 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008262 }
8263
8264 cmd_is_exec = 0;
8265 spclbltin = -1;
8266
8267 /* Now locate the command. */
8268 if (argc) {
8269 const char *oldpath;
8270 int cmd_flag = DO_ERR;
8271
8272 path += 5;
8273 oldpath = path;
8274 for (;;) {
8275 find_command(argv[0], &cmdentry, cmd_flag, path);
8276 if (cmdentry.cmdtype == CMDUNKNOWN) {
8277 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008278 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008279 goto bail;
8280 }
8281
8282 /* implement bltin and command here */
8283 if (cmdentry.cmdtype != CMDBUILTIN)
8284 break;
8285 if (spclbltin < 0)
8286 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8287 if (cmdentry.u.cmd == EXECCMD)
8288 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008289#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008290 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008291 path = oldpath;
8292 nargv = parse_command_args(argv, &path);
8293 if (!nargv)
8294 break;
8295 argc -= nargv - argv;
8296 argv = nargv;
8297 cmd_flag |= DO_NOFUNC;
8298 } else
8299#endif
8300 break;
8301 }
8302 }
8303
8304 if (status) {
8305 /* We have a redirection error. */
8306 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008307 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008308 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008309 exitstatus = status;
8310 goto out;
8311 }
8312
8313 /* Execute the command. */
8314 switch (cmdentry.cmdtype) {
8315 default:
8316 /* Fork off a child process if necessary. */
8317 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008318 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008319 jp = makejob(cmd, 1);
8320 if (forkshell(jp, cmd, FORK_FG) != 0) {
8321 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008322 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008323 break;
8324 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008325 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008326 }
8327 listsetvar(varlist.list, VEXPORT|VSTACK);
8328 shellexec(argv, path, cmdentry.u.index);
8329 /* NOTREACHED */
8330
8331 case CMDBUILTIN:
8332 cmdenviron = varlist.list;
8333 if (cmdenviron) {
8334 struct strlist *list = cmdenviron;
8335 int i = VNOSET;
8336 if (spclbltin > 0 || argc == 0) {
8337 i = 0;
8338 if (cmd_is_exec && argc > 1)
8339 i = VEXPORT;
8340 }
8341 listsetvar(list, i);
8342 }
8343 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8344 int exit_status;
8345 int i, j;
8346
8347 i = exception;
8348 if (i == EXEXIT)
8349 goto raise;
8350
8351 exit_status = 2;
8352 j = 0;
8353 if (i == EXINT)
8354 j = SIGINT;
8355 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008356 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008357 if (j)
8358 exit_status = j + 128;
8359 exitstatus = exit_status;
8360
8361 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008362 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008363 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008364 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008365 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008366 }
8367 break;
8368
8369 case CMDFUNCTION:
8370 listsetvar(varlist.list, 0);
8371 if (evalfun(cmdentry.u.func, argc, argv, flags))
8372 goto raise;
8373 break;
8374 }
8375
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008376 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008377 popredir(cmd_is_exec);
8378 if (lastarg)
8379 /* dsl: I think this is intended to be used to support
8380 * '_' in 'vi' command mode during line editing...
8381 * However I implemented that within libedit itself.
8382 */
8383 setvar("_", lastarg, 0);
8384 popstackmark(&smark);
8385}
8386
8387static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008388evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8389{
Eric Andersenc470f442003-07-28 09:56:35 +00008390 char *volatile savecmdname;
8391 struct jmploc *volatile savehandler;
8392 struct jmploc jmploc;
8393 int i;
8394
8395 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008396 i = setjmp(jmploc.loc);
8397 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008398 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008399 savehandler = exception_handler;
8400 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008401 commandname = argv[0];
8402 argptr = argv + 1;
8403 optptr = NULL; /* initialize nextopt */
8404 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008405 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008406 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008407 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008408 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008409 commandname = savecmdname;
8410 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008411 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008412
8413 return i;
8414}
8415
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008416static int
8417goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008418{
8419 return !*endofname(p);
8420}
8421
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008422
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008423/*
8424 * Search for a command. This is called before we fork so that the
8425 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008426 * the child. The check for "goodname" is an overly conservative
8427 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008428 */
Eric Andersenc470f442003-07-28 09:56:35 +00008429static void
8430prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008431{
8432 struct cmdentry entry;
8433
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008434 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8435 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008436}
8437
Eric Andersencb57d552001-06-28 07:25:16 +00008438
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008439/* ============ Builtin commands
8440 *
8441 * Builtin commands whose functions are closely tied to evaluation
8442 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008443 */
8444
8445/*
Eric Andersencb57d552001-06-28 07:25:16 +00008446 * Handle break and continue commands. Break, continue, and return are
8447 * all handled by setting the evalskip flag. The evaluation routines
8448 * above all check this flag, and if it is set they start skipping
8449 * commands rather than executing them. The variable skipcount is
8450 * the number of loops to break/continue, or the number of function
8451 * levels to return. (The latter is always 1.) It should probably
8452 * be an error to break out of more loops than exist, but it isn't
8453 * in the standard shell so we don't make it one here.
8454 */
Eric Andersenc470f442003-07-28 09:56:35 +00008455static int
8456breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008457{
8458 int n = argc > 1 ? number(argv[1]) : 1;
8459
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008460 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008461 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008462 if (n > loopnest)
8463 n = loopnest;
8464 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008465 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008466 skipcount = n;
8467 }
8468 return 0;
8469}
8470
Eric Andersenc470f442003-07-28 09:56:35 +00008471
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008472/* ============ input.c
8473 *
Eric Andersen90898442003-08-06 11:20:52 +00008474 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008475 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008476
Eric Andersenc470f442003-07-28 09:56:35 +00008477#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008478
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008479enum {
8480 INPUT_PUSH_FILE = 1,
8481 INPUT_NOFILE_OK = 2,
8482};
Eric Andersencb57d552001-06-28 07:25:16 +00008483
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008484static int plinno = 1; /* input line number */
8485/* number of characters left in input buffer */
8486static int parsenleft; /* copy of parsefile->nleft */
8487static int parselleft; /* copy of parsefile->lleft */
8488/* next character in input buffer */
8489static char *parsenextc; /* copy of parsefile->nextc */
8490
8491static int checkkwd;
8492/* values of checkkwd variable */
8493#define CHKALIAS 0x1
8494#define CHKKWD 0x2
8495#define CHKNL 0x4
8496
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008497static void
8498popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008499{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008500 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008501
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008502 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008503#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008504 if (sp->ap) {
8505 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8506 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008507 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008508 if (sp->string != sp->ap->val) {
8509 free(sp->string);
8510 }
8511 sp->ap->flag &= ~ALIASINUSE;
8512 if (sp->ap->flag & ALIASDEAD) {
8513 unalias(sp->ap->name);
8514 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008515 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008516#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008517 parsenextc = sp->prevstring;
8518 parsenleft = sp->prevnleft;
8519/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8520 parsefile->strpush = sp->prev;
8521 if (sp != &(parsefile->basestrpush))
8522 free(sp);
8523 INT_ON;
8524}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008525
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008526static int
8527preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008528{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008529 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008530 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008531 parsenextc = buf;
8532
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008533 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008534#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008535 if (!iflag || parsefile->fd)
8536 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8537 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008538#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008539 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008540#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008541 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8542 if (nr == 0) {
8543 /* Ctrl+C pressed */
8544 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008545 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008546 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008547 raise(SIGINT);
8548 return 1;
8549 }
Eric Andersenc470f442003-07-28 09:56:35 +00008550 goto retry;
8551 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008552 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008553 /* Ctrl+D presend */
8554 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008555 }
Eric Andersencb57d552001-06-28 07:25:16 +00008556 }
8557#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008558 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008559#endif
8560
8561 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008562 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008563 int flags = fcntl(0, F_GETFL);
Eric Andersencb57d552001-06-28 07:25:16 +00008564 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008565 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008566 if (fcntl(0, F_SETFL, flags) >= 0) {
8567 out2str("sh: turning off NDELAY mode\n");
8568 goto retry;
8569 }
8570 }
8571 }
8572 }
8573 return nr;
8574}
8575
8576/*
8577 * Refill the input buffer and return the next input character:
8578 *
8579 * 1) If a string was pushed back on the input, pop it;
8580 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8581 * from a string so we can't refill the buffer, return EOF.
8582 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8583 * 4) Process input up to the next newline, deleting nul characters.
8584 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008585static int
Eric Andersenc470f442003-07-28 09:56:35 +00008586preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008587{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008588 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008589 int more;
8590 char savec;
8591
8592 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008593#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008594 if (parsenleft == -1 && parsefile->strpush->ap &&
8595 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008596 return PEOA;
8597 }
Eric Andersen2870d962001-07-02 17:27:21 +00008598#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008599 popstring();
8600 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008601 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008602 }
8603 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8604 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008605 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008606
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008607 more = parselleft;
8608 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008609 again:
8610 more = preadfd();
8611 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008612 parselleft = parsenleft = EOF_NLEFT;
8613 return PEOF;
8614 }
8615 }
8616
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008617 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008618
8619 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008620 for (;;) {
8621 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008622
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008623 more--;
8624 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008625
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008626 if (!c)
8627 memmove(q, q + 1, more);
8628 else {
8629 q++;
8630 if (c == '\n') {
8631 parsenleft = q - parsenextc - 1;
8632 break;
8633 }
Eric Andersencb57d552001-06-28 07:25:16 +00008634 }
8635
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008636 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008637 parsenleft = q - parsenextc - 1;
8638 if (parsenleft < 0)
8639 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008640 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008641 }
8642 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008643 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008644
8645 savec = *q;
8646 *q = '\0';
8647
8648 if (vflag) {
8649 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008650 }
8651
8652 *q = savec;
8653
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008654 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008655}
8656
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008657#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008658static int
8659pgetc(void)
8660{
8661 return pgetc_as_macro();
8662}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008663
8664#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8665#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008666#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008667#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008668#endif
8669
8670/*
8671 * Same as pgetc(), but ignores PEOA.
8672 */
8673#if ENABLE_ASH_ALIAS
8674static int
8675pgetc2(void)
8676{
8677 int c;
8678
8679 do {
8680 c = pgetc_macro();
8681 } while (c == PEOA);
8682 return c;
8683}
8684#else
8685static int
8686pgetc2(void)
8687{
8688 return pgetc_macro();
8689}
8690#endif
8691
8692/*
8693 * Read a line from the script.
8694 */
8695static char *
8696pfgets(char *line, int len)
8697{
8698 char *p = line;
8699 int nleft = len;
8700 int c;
8701
8702 while (--nleft > 0) {
8703 c = pgetc2();
8704 if (c == PEOF) {
8705 if (p == line)
8706 return NULL;
8707 break;
8708 }
8709 *p++ = c;
8710 if (c == '\n')
8711 break;
8712 }
8713 *p = '\0';
8714 return line;
8715}
8716
Eric Andersenc470f442003-07-28 09:56:35 +00008717/*
8718 * Undo the last call to pgetc. Only one character may be pushed back.
8719 * PEOF may be pushed back.
8720 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008721static void
Eric Andersenc470f442003-07-28 09:56:35 +00008722pungetc(void)
8723{
8724 parsenleft++;
8725 parsenextc--;
8726}
Eric Andersencb57d552001-06-28 07:25:16 +00008727
8728/*
8729 * Push a string back onto the input at this current parsefile level.
8730 * We handle aliases this way.
8731 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008732static void
Eric Andersenc470f442003-07-28 09:56:35 +00008733pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008734{
Eric Andersencb57d552001-06-28 07:25:16 +00008735 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008736 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008737
Eric Andersenc470f442003-07-28 09:56:35 +00008738 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008739 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008740/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8741 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008742 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008743 sp->prev = parsefile->strpush;
8744 parsefile->strpush = sp;
8745 } else
8746 sp = parsefile->strpush = &(parsefile->basestrpush);
8747 sp->prevstring = parsenextc;
8748 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008749#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008750 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008751 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008752 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008753 sp->string = s;
8754 }
Eric Andersen2870d962001-07-02 17:27:21 +00008755#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008756 parsenextc = s;
8757 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008758 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008759}
8760
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008761/*
8762 * To handle the "." command, a stack of input files is used. Pushfile
8763 * adds a new entry to the stack and popfile restores the previous level.
8764 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008765static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008766pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008767{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008768 struct parsefile *pf;
8769
8770 parsefile->nleft = parsenleft;
8771 parsefile->lleft = parselleft;
8772 parsefile->nextc = parsenextc;
8773 parsefile->linno = plinno;
8774 pf = ckmalloc(sizeof(*pf));
8775 pf->prev = parsefile;
8776 pf->fd = -1;
8777 pf->strpush = NULL;
8778 pf->basestrpush.prev = NULL;
8779 parsefile = pf;
8780}
8781
8782static void
8783popfile(void)
8784{
8785 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008786
Denis Vlasenkob012b102007-02-19 22:43:01 +00008787 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008788 if (pf->fd >= 0)
8789 close(pf->fd);
8790 if (pf->buf)
8791 free(pf->buf);
8792 while (pf->strpush)
8793 popstring();
8794 parsefile = pf->prev;
8795 free(pf);
8796 parsenleft = parsefile->nleft;
8797 parselleft = parsefile->lleft;
8798 parsenextc = parsefile->nextc;
8799 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008800 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008801}
8802
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008803/*
8804 * Return to top level.
8805 */
8806static void
8807popallfiles(void)
8808{
8809 while (parsefile != &basepf)
8810 popfile();
8811}
8812
8813/*
8814 * Close the file(s) that the shell is reading commands from. Called
8815 * after a fork is done.
8816 */
8817static void
8818closescript(void)
8819{
8820 popallfiles();
8821 if (parsefile->fd > 0) {
8822 close(parsefile->fd);
8823 parsefile->fd = 0;
8824 }
8825}
8826
8827/*
8828 * Like setinputfile, but takes an open file descriptor. Call this with
8829 * interrupts off.
8830 */
8831static void
8832setinputfd(int fd, int push)
8833{
8834 fcntl(fd, F_SETFD, FD_CLOEXEC);
8835 if (push) {
8836 pushfile();
8837 parsefile->buf = 0;
8838 }
8839 parsefile->fd = fd;
8840 if (parsefile->buf == NULL)
8841 parsefile->buf = ckmalloc(IBUFSIZ);
8842 parselleft = parsenleft = 0;
8843 plinno = 1;
8844}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008845
Eric Andersenc470f442003-07-28 09:56:35 +00008846/*
8847 * Set the input to take input from a file. If push is set, push the
8848 * old input onto the stack first.
8849 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008850static int
8851setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008852{
8853 int fd;
8854 int fd2;
8855
Denis Vlasenkob012b102007-02-19 22:43:01 +00008856 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008857 fd = open(fname, O_RDONLY);
8858 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008859 if (flags & INPUT_NOFILE_OK)
8860 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008861 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008862 }
Eric Andersenc470f442003-07-28 09:56:35 +00008863 if (fd < 10) {
8864 fd2 = copyfd(fd, 10);
8865 close(fd);
8866 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008867 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008868 fd = fd2;
8869 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008870 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008871 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008872 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008873 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008874}
8875
Eric Andersencb57d552001-06-28 07:25:16 +00008876/*
8877 * Like setinputfile, but takes input from a string.
8878 */
Eric Andersenc470f442003-07-28 09:56:35 +00008879static void
8880setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008881{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008883 pushfile();
8884 parsenextc = string;
8885 parsenleft = strlen(string);
8886 parsefile->buf = NULL;
8887 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008888 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008889}
8890
8891
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008892/* ============ mail.c
8893 *
8894 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008895 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008896
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008897#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008898
Eric Andersencb57d552001-06-28 07:25:16 +00008899#define MAXMBOXES 10
8900
Eric Andersenc470f442003-07-28 09:56:35 +00008901/* times of mailboxes */
8902static time_t mailtime[MAXMBOXES];
8903/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008904static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008905
Eric Andersencb57d552001-06-28 07:25:16 +00008906/*
Eric Andersenc470f442003-07-28 09:56:35 +00008907 * Print appropriate message(s) if mail has arrived.
8908 * If mail_var_path_changed is set,
8909 * then the value of MAIL has mail_var_path_changed,
8910 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008911 */
Eric Andersenc470f442003-07-28 09:56:35 +00008912static void
8913chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008914{
Eric Andersencb57d552001-06-28 07:25:16 +00008915 const char *mpath;
8916 char *p;
8917 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008918 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008919 struct stackmark smark;
8920 struct stat statb;
8921
Eric Andersencb57d552001-06-28 07:25:16 +00008922 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008923 mpath = mpathset() ? mpathval() : mailval();
8924 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008925 p = padvance(&mpath, nullstr);
8926 if (p == NULL)
8927 break;
8928 if (*p == '\0')
8929 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008930 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008931#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008932 if (q[-1] != '/')
8933 abort();
8934#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008935 q[-1] = '\0'; /* delete trailing '/' */
8936 if (stat(p, &statb) < 0) {
8937 *mtp = 0;
8938 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008939 }
Eric Andersenc470f442003-07-28 09:56:35 +00008940 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8941 fprintf(
8942 stderr, snlfmt,
8943 pathopt ? pathopt : "you have mail"
8944 );
8945 }
8946 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008947 }
Eric Andersenc470f442003-07-28 09:56:35 +00008948 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008949 popstackmark(&smark);
8950}
Eric Andersencb57d552001-06-28 07:25:16 +00008951
Eric Andersenc470f442003-07-28 09:56:35 +00008952static void
8953changemail(const char *val)
8954{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008955 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00008956}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008957
Denis Vlasenko131ae172007-02-18 13:00:19 +00008958#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008959
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008960
8961/* ============ ??? */
8962
Eric Andersencb57d552001-06-28 07:25:16 +00008963/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008964 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008965 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008966static void
8967setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008968{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008969 char **newparam;
8970 char **ap;
8971 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008972
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008973 for (nparam = 0; argv[nparam]; nparam++);
8974 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8975 while (*argv) {
8976 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008977 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008978 *ap = NULL;
8979 freeparam(&shellparam);
8980 shellparam.malloc = 1;
8981 shellparam.nparam = nparam;
8982 shellparam.p = newparam;
8983#if ENABLE_ASH_GETOPTS
8984 shellparam.optind = 1;
8985 shellparam.optoff = -1;
8986#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008987}
8988
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008989/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008990 * Process shell options. The global variable argptr contains a pointer
8991 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008992 */
8993static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008994minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008995{
8996 int i;
8997
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008998 if (name) {
8999 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009000 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009001 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009002 return;
9003 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009004 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009005 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009006 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009007 out1str("Current option settings\n");
9008 for (i = 0; i < NOPTS; i++)
9009 out1fmt("%-16s%s\n", optnames(i),
9010 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009011}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009012static void
9013setoption(int flag, int val)
9014{
9015 int i;
9016
9017 for (i = 0; i < NOPTS; i++) {
9018 if (optletters(i) == flag) {
9019 optlist[i] = val;
9020 return;
9021 }
9022 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009023 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009024 /* NOTREACHED */
9025}
Eric Andersenc470f442003-07-28 09:56:35 +00009026static void
9027options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009028{
9029 char *p;
9030 int val;
9031 int c;
9032
9033 if (cmdline)
9034 minusc = NULL;
9035 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009036 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009037 if (c != '-' && c != '+')
9038 break;
9039 argptr++;
9040 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009041 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009042 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009043 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009044 if (!cmdline) {
9045 /* "-" means turn off -x and -v */
9046 if (p[0] == '\0')
9047 xflag = vflag = 0;
9048 /* "--" means reset params */
9049 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009050 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009051 }
Eric Andersenc470f442003-07-28 09:56:35 +00009052 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009053 }
Eric Andersencb57d552001-06-28 07:25:16 +00009054 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009055 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009056 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009057 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009058 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009059 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009060 } else if (c == 'o') {
9061 minus_o(*argptr, val);
9062 if (*argptr)
9063 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009064 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9065 isloginsh = 1;
9066 /* bash does not accept +-login, we also won't */
9067 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009068 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009069 isloginsh = 1;
9070 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009071 } else {
9072 setoption(c, val);
9073 }
9074 }
9075 }
9076}
9077
Eric Andersencb57d552001-06-28 07:25:16 +00009078/*
Eric Andersencb57d552001-06-28 07:25:16 +00009079 * The shift builtin command.
9080 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009081static int
Eric Andersenc470f442003-07-28 09:56:35 +00009082shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009083{
9084 int n;
9085 char **ap1, **ap2;
9086
9087 n = 1;
9088 if (argc > 1)
9089 n = number(argv[1]);
9090 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009091 ash_msg_and_raise_error("can't shift that many");
9092 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009093 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009094 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009095 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009096 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009097 }
9098 ap2 = shellparam.p;
9099 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009100#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009101 shellparam.optind = 1;
9102 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009103#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009104 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009105 return 0;
9106}
9107
Eric Andersencb57d552001-06-28 07:25:16 +00009108/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009109 * POSIX requires that 'set' (but not export or readonly) output the
9110 * variables in lexicographic order - by the locale's collating order (sigh).
9111 * Maybe we could keep them in an ordered balanced binary tree
9112 * instead of hashed lists.
9113 * For now just roll 'em through qsort for printing...
9114 */
9115static int
9116showvars(const char *sep_prefix, int on, int off)
9117{
9118 const char *sep;
9119 char **ep, **epend;
9120
9121 ep = listvars(on, off, &epend);
9122 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9123
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009124 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009125
9126 for (; ep < epend; ep++) {
9127 const char *p;
9128 const char *q;
9129
9130 p = strchrnul(*ep, '=');
9131 q = nullstr;
9132 if (*p)
9133 q = single_quote(++p);
9134 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9135 }
9136 return 0;
9137}
9138
9139/*
Eric Andersencb57d552001-06-28 07:25:16 +00009140 * The set command builtin.
9141 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009142static int
Eric Andersenc470f442003-07-28 09:56:35 +00009143setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009144{
9145 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009146 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009147 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009148 options(0);
9149 optschanged();
9150 if (*argptr != NULL) {
9151 setparam(argptr);
9152 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009153 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009154 return 0;
9155}
9156
Denis Vlasenko131ae172007-02-18 13:00:19 +00009157#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009158/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009159static void
9160change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009161{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009162 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009163 /* "get", generate */
9164 char buf[16];
9165
9166 rseed = rseed * 1103515245 + 12345;
9167 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9168 /* set without recursion */
9169 setvar(vrandom.text, buf, VNOFUNC);
9170 vrandom.flags &= ~VNOFUNC;
9171 } else {
9172 /* set/reset */
9173 rseed = strtoul(value, (char **)NULL, 10);
9174 }
Eric Andersenef02f822004-03-11 13:34:24 +00009175}
Eric Andersen16767e22004-03-16 05:14:10 +00009176#endif
9177
Denis Vlasenko131ae172007-02-18 13:00:19 +00009178#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009179static int
Eric Andersenc470f442003-07-28 09:56:35 +00009180getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009181{
9182 char *p, *q;
9183 char c = '?';
9184 int done = 0;
9185 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009186 char s[12];
9187 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009188
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009189 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009190 return 1;
9191 optnext = optfirst + *param_optind - 1;
9192
9193 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009194 p = NULL;
9195 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009196 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 if (p == NULL || *p == '\0') {
9198 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009199 p = *optnext;
9200 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009201 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009202 p = NULL;
9203 done = 1;
9204 goto out;
9205 }
9206 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009207 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009208 goto atend;
9209 }
9210
9211 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009212 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009213 if (*q == '\0') {
9214 if (optstr[0] == ':') {
9215 s[0] = c;
9216 s[1] = '\0';
9217 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009218 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009219 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009220 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009221 }
9222 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009223 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009224 }
9225 if (*++q == ':')
9226 q++;
9227 }
9228
9229 if (*++q == ':') {
9230 if (*p == '\0' && (p = *optnext) == NULL) {
9231 if (optstr[0] == ':') {
9232 s[0] = c;
9233 s[1] = '\0';
9234 err |= setvarsafe("OPTARG", s, 0);
9235 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009236 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009237 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009238 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009239 c = '?';
9240 }
Eric Andersenc470f442003-07-28 09:56:35 +00009241 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009242 }
9243
9244 if (p == *optnext)
9245 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009246 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009247 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009248 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009249 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009250 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009251 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009252 *param_optind = optnext - optfirst + 1;
9253 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009254 err |= setvarsafe("OPTIND", s, VNOFUNC);
9255 s[0] = c;
9256 s[1] = '\0';
9257 err |= setvarsafe(optvar, s, 0);
9258 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009259 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009260 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009261 flush_stdout_stderr();
9262 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009263 }
9264 return done;
9265}
Eric Andersenc470f442003-07-28 09:56:35 +00009266
9267/*
9268 * The getopts builtin. Shellparam.optnext points to the next argument
9269 * to be processed. Shellparam.optptr points to the next character to
9270 * be processed in the current argument. If shellparam.optnext is NULL,
9271 * then it's the first time getopts has been called.
9272 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009273static int
Eric Andersenc470f442003-07-28 09:56:35 +00009274getoptscmd(int argc, char **argv)
9275{
9276 char **optbase;
9277
9278 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009279 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009280 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009281 optbase = shellparam.p;
9282 if (shellparam.optind > shellparam.nparam + 1) {
9283 shellparam.optind = 1;
9284 shellparam.optoff = -1;
9285 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009286 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009287 optbase = &argv[3];
9288 if (shellparam.optind > argc - 2) {
9289 shellparam.optind = 1;
9290 shellparam.optoff = -1;
9291 }
9292 }
9293
9294 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009295 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009296}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009297#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009298
Eric Andersencb57d552001-06-28 07:25:16 +00009299
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009300/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009301
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009302/*
9303 * NEOF is returned by parsecmd when it encounters an end of file. It
9304 * must be distinct from NULL, so we use the address of a variable that
9305 * happens to be handy.
9306 */
9307static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009308#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009309static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009310static int lasttoken; /* last token read */
9311static char *wordtext; /* text of last word returned by readtoken */
9312static struct nodelist *backquotelist;
9313static union node *redirnode;
9314static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009315static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009316
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009317static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9318static void
9319raise_error_syntax(const char *msg)
9320{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009321 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009322 /* NOTREACHED */
9323}
9324
9325/*
9326 * Called when an unexpected token is read during the parse. The argument
9327 * is the token that is expected, or -1 if more than one type of token can
9328 * occur at this point.
9329 */
9330static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9331static void
9332raise_error_unexpected_syntax(int token)
9333{
9334 char msg[64];
9335 int l;
9336
9337 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9338 if (token >= 0)
9339 sprintf(msg + l, " (expecting %s)", tokname(token));
9340 raise_error_syntax(msg);
9341 /* NOTREACHED */
9342}
Eric Andersencb57d552001-06-28 07:25:16 +00009343
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009344#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009345
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009346struct heredoc {
9347 struct heredoc *next; /* next here document in list */
9348 union node *here; /* redirection node */
9349 char *eofmark; /* string indicating end of input */
9350 int striptabs; /* if set, strip leading tabs */
9351};
Eric Andersencb57d552001-06-28 07:25:16 +00009352
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009353static struct heredoc *heredoclist; /* list of here documents to read */
9354
9355/* parsing is heavily cross-recursive, need these forward decls */
9356static union node *andor(void);
9357static union node *pipeline(void);
9358static union node *parse_command(void);
9359static void parseheredoc(void);
9360static char peektoken(void);
9361static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009362
Eric Andersenc470f442003-07-28 09:56:35 +00009363static union node *
9364list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009365{
9366 union node *n1, *n2, *n3;
9367 int tok;
9368
Eric Andersenc470f442003-07-28 09:56:35 +00009369 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9370 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009371 return NULL;
9372 n1 = NULL;
9373 for (;;) {
9374 n2 = andor();
9375 tok = readtoken();
9376 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009377 if (n2->type == NPIPE) {
9378 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009379 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009380 if (n2->type != NREDIR) {
9381 n3 = stalloc(sizeof(struct nredir));
9382 n3->nredir.n = n2;
9383 n3->nredir.redirect = NULL;
9384 n2 = n3;
9385 }
9386 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009387 }
9388 }
9389 if (n1 == NULL) {
9390 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009391 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009392 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009393 n3->type = NSEMI;
9394 n3->nbinary.ch1 = n1;
9395 n3->nbinary.ch2 = n2;
9396 n1 = n3;
9397 }
9398 switch (tok) {
9399 case TBACKGND:
9400 case TSEMI:
9401 tok = readtoken();
9402 /* fall through */
9403 case TNL:
9404 if (tok == TNL) {
9405 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009406 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009407 return n1;
9408 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009409 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009410 }
Eric Andersenc470f442003-07-28 09:56:35 +00009411 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009412 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009413 return n1;
9414 break;
9415 case TEOF:
9416 if (heredoclist)
9417 parseheredoc();
9418 else
Eric Andersenc470f442003-07-28 09:56:35 +00009419 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009420 return n1;
9421 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009422 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009423 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009424 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009425 return n1;
9426 }
9427 }
9428}
9429
Eric Andersenc470f442003-07-28 09:56:35 +00009430static union node *
9431andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009432{
Eric Andersencb57d552001-06-28 07:25:16 +00009433 union node *n1, *n2, *n3;
9434 int t;
9435
Eric Andersencb57d552001-06-28 07:25:16 +00009436 n1 = pipeline();
9437 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009438 t = readtoken();
9439 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009440 t = NAND;
9441 } else if (t == TOR) {
9442 t = NOR;
9443 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009444 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009445 return n1;
9446 }
Eric Andersenc470f442003-07-28 09:56:35 +00009447 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009448 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009449 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009450 n3->type = t;
9451 n3->nbinary.ch1 = n1;
9452 n3->nbinary.ch2 = n2;
9453 n1 = n3;
9454 }
9455}
9456
Eric Andersenc470f442003-07-28 09:56:35 +00009457static union node *
9458pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009459{
Eric Andersencb57d552001-06-28 07:25:16 +00009460 union node *n1, *n2, *pipenode;
9461 struct nodelist *lp, *prev;
9462 int negate;
9463
9464 negate = 0;
9465 TRACE(("pipeline: entered\n"));
9466 if (readtoken() == TNOT) {
9467 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009468 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009469 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009470 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009471 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009472 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009473 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009474 pipenode->type = NPIPE;
9475 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009476 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009477 pipenode->npipe.cmdlist = lp;
9478 lp->n = n1;
9479 do {
9480 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009481 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009482 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009483 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009484 prev->next = lp;
9485 } while (readtoken() == TPIPE);
9486 lp->next = NULL;
9487 n1 = pipenode;
9488 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009489 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009490 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009491 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009492 n2->type = NNOT;
9493 n2->nnot.com = n1;
9494 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009495 }
9496 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009497}
9498
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009499static union node *
9500makename(void)
9501{
9502 union node *n;
9503
9504 n = stalloc(sizeof(struct narg));
9505 n->type = NARG;
9506 n->narg.next = NULL;
9507 n->narg.text = wordtext;
9508 n->narg.backquote = backquotelist;
9509 return n;
9510}
9511
9512static void
9513fixredir(union node *n, const char *text, int err)
9514{
9515 TRACE(("Fix redir %s %d\n", text, err));
9516 if (!err)
9517 n->ndup.vname = NULL;
9518
9519 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009520 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009521 else if (LONE_DASH(text))
9522 n->ndup.dupfd = -1;
9523 else {
9524 if (err)
9525 raise_error_syntax("Bad fd number");
9526 n->ndup.vname = makename();
9527 }
9528}
9529
9530/*
9531 * Returns true if the text contains nothing to expand (no dollar signs
9532 * or backquotes).
9533 */
9534static int
9535noexpand(char *text)
9536{
9537 char *p;
9538 char c;
9539
9540 p = text;
9541 while ((c = *p++) != '\0') {
9542 if (c == CTLQUOTEMARK)
9543 continue;
9544 if (c == CTLESC)
9545 p++;
9546 else if (SIT(c, BASESYNTAX) == CCTL)
9547 return 0;
9548 }
9549 return 1;
9550}
9551
9552static void
9553parsefname(void)
9554{
9555 union node *n = redirnode;
9556
9557 if (readtoken() != TWORD)
9558 raise_error_unexpected_syntax(-1);
9559 if (n->type == NHERE) {
9560 struct heredoc *here = heredoc;
9561 struct heredoc *p;
9562 int i;
9563
9564 if (quoteflag == 0)
9565 n->type = NXHERE;
9566 TRACE(("Here document %d\n", n->type));
9567 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9568 raise_error_syntax("Illegal eof marker for << redirection");
9569 rmescapes(wordtext);
9570 here->eofmark = wordtext;
9571 here->next = NULL;
9572 if (heredoclist == NULL)
9573 heredoclist = here;
9574 else {
9575 for (p = heredoclist; p->next; p = p->next);
9576 p->next = here;
9577 }
9578 } else if (n->type == NTOFD || n->type == NFROMFD) {
9579 fixredir(n, wordtext, 0);
9580 } else {
9581 n->nfile.fname = makename();
9582 }
9583}
Eric Andersencb57d552001-06-28 07:25:16 +00009584
Eric Andersenc470f442003-07-28 09:56:35 +00009585static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009586simplecmd(void)
9587{
9588 union node *args, **app;
9589 union node *n = NULL;
9590 union node *vars, **vpp;
9591 union node **rpp, *redir;
9592 int savecheckkwd;
9593
9594 args = NULL;
9595 app = &args;
9596 vars = NULL;
9597 vpp = &vars;
9598 redir = NULL;
9599 rpp = &redir;
9600
9601 savecheckkwd = CHKALIAS;
9602 for (;;) {
9603 checkkwd = savecheckkwd;
9604 switch (readtoken()) {
9605 case TWORD:
9606 n = stalloc(sizeof(struct narg));
9607 n->type = NARG;
9608 n->narg.text = wordtext;
9609 n->narg.backquote = backquotelist;
9610 if (savecheckkwd && isassignment(wordtext)) {
9611 *vpp = n;
9612 vpp = &n->narg.next;
9613 } else {
9614 *app = n;
9615 app = &n->narg.next;
9616 savecheckkwd = 0;
9617 }
9618 break;
9619 case TREDIR:
9620 *rpp = n = redirnode;
9621 rpp = &n->nfile.next;
9622 parsefname(); /* read name of redirection file */
9623 break;
9624 case TLP:
9625 if (args && app == &args->narg.next
9626 && !vars && !redir
9627 ) {
9628 struct builtincmd *bcmd;
9629 const char *name;
9630
9631 /* We have a function */
9632 if (readtoken() != TRP)
9633 raise_error_unexpected_syntax(TRP);
9634 name = n->narg.text;
9635 if (!goodname(name)
9636 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9637 ) {
9638 raise_error_syntax("Bad function name");
9639 }
9640 n->type = NDEFUN;
9641 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9642 n->narg.next = parse_command();
9643 return n;
9644 }
9645 /* fall through */
9646 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009647 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009648 goto out;
9649 }
9650 }
9651 out:
9652 *app = NULL;
9653 *vpp = NULL;
9654 *rpp = NULL;
9655 n = stalloc(sizeof(struct ncmd));
9656 n->type = NCMD;
9657 n->ncmd.args = args;
9658 n->ncmd.assign = vars;
9659 n->ncmd.redirect = redir;
9660 return n;
9661}
9662
9663static union node *
9664parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009665{
Eric Andersencb57d552001-06-28 07:25:16 +00009666 union node *n1, *n2;
9667 union node *ap, **app;
9668 union node *cp, **cpp;
9669 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009670 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009671 int t;
9672
9673 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009674 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009675
Eric Andersencb57d552001-06-28 07:25:16 +00009676 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009677 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009678 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009679 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009680 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009681 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009682 n1->type = NIF;
9683 n1->nif.test = list(0);
9684 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009685 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009686 n1->nif.ifpart = list(0);
9687 n2 = n1;
9688 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009689 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009690 n2 = n2->nif.elsepart;
9691 n2->type = NIF;
9692 n2->nif.test = list(0);
9693 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009694 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009695 n2->nif.ifpart = list(0);
9696 }
9697 if (lasttoken == TELSE)
9698 n2->nif.elsepart = list(0);
9699 else {
9700 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009701 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009702 }
Eric Andersenc470f442003-07-28 09:56:35 +00009703 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009704 break;
9705 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009706 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009707 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009708 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009709 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009710 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009711 got = readtoken();
9712 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009713 TRACE(("expecting DO got %s %s\n", tokname(got),
9714 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009715 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009716 }
9717 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009718 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009719 break;
9720 }
9721 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009722 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009723 raise_error_syntax("Bad for loop variable");
9724 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009725 n1->type = NFOR;
9726 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009727 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009728 if (readtoken() == TIN) {
9729 app = &ap;
9730 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009731 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009732 n2->type = NARG;
9733 n2->narg.text = wordtext;
9734 n2->narg.backquote = backquotelist;
9735 *app = n2;
9736 app = &n2->narg.next;
9737 }
9738 *app = NULL;
9739 n1->nfor.args = ap;
9740 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009741 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009742 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009743 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009744 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009745 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009746 n2->narg.backquote = NULL;
9747 n2->narg.next = NULL;
9748 n1->nfor.args = n2;
9749 /*
9750 * Newline or semicolon here is optional (but note
9751 * that the original Bourne shell only allowed NL).
9752 */
9753 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009754 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009755 }
Eric Andersenc470f442003-07-28 09:56:35 +00009756 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009757 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009758 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009759 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009760 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009761 break;
9762 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009763 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009764 n1->type = NCASE;
9765 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009766 raise_error_unexpected_syntax(TWORD);
9767 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009768 n2->type = NARG;
9769 n2->narg.text = wordtext;
9770 n2->narg.backquote = backquotelist;
9771 n2->narg.next = NULL;
9772 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009773 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009774 } while (readtoken() == TNL);
9775 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009776 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009777 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009778 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009779 checkkwd = CHKNL | CHKKWD;
9780 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009781 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009782 if (lasttoken == TLP)
9783 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009784 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009785 cp->type = NCLIST;
9786 app = &cp->nclist.pattern;
9787 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009788 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009789 ap->type = NARG;
9790 ap->narg.text = wordtext;
9791 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009792 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009793 break;
9794 app = &ap->narg.next;
9795 readtoken();
9796 }
9797 ap->narg.next = NULL;
9798 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009799 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009800 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009801
Eric Andersenc470f442003-07-28 09:56:35 +00009802 cpp = &cp->nclist.next;
9803
9804 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009805 t = readtoken();
9806 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009807 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009808 raise_error_unexpected_syntax(TENDCASE);
9809 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009810 }
Eric Andersenc470f442003-07-28 09:56:35 +00009811 }
Eric Andersencb57d552001-06-28 07:25:16 +00009812 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009813 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009814 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009815 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009816 n1->type = NSUBSHELL;
9817 n1->nredir.n = list(0);
9818 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009819 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009820 break;
9821 case TBEGIN:
9822 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009823 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009824 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009825 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009826 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009827 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009828 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009829 }
9830
Eric Andersenc470f442003-07-28 09:56:35 +00009831 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009832 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009833
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009834 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009835 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009836 checkkwd = CHKKWD | CHKALIAS;
9837 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009838 while (readtoken() == TREDIR) {
9839 *rpp = n2 = redirnode;
9840 rpp = &n2->nfile.next;
9841 parsefname();
9842 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009843 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009844 *rpp = NULL;
9845 if (redir) {
9846 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009847 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009848 n2->type = NREDIR;
9849 n2->nredir.n = n1;
9850 n1 = n2;
9851 }
9852 n1->nredir.redirect = redir;
9853 }
Eric Andersencb57d552001-06-28 07:25:16 +00009854 return n1;
9855}
9856
Eric Andersencb57d552001-06-28 07:25:16 +00009857/*
9858 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9859 * is not NULL, read a here document. In the latter case, eofmark is the
9860 * word which marks the end of the document and striptabs is true if
9861 * leading tabs should be stripped from the document. The argument firstc
9862 * is the first character of the input token or document.
9863 *
9864 * Because C does not have internal subroutines, I have simulated them
9865 * using goto's to implement the subroutine linkage. The following macros
9866 * will run code that appears at the end of readtoken1.
9867 */
9868
Eric Andersen2870d962001-07-02 17:27:21 +00009869#define CHECKEND() {goto checkend; checkend_return:;}
9870#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9871#define PARSESUB() {goto parsesub; parsesub_return:;}
9872#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9873#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9874#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009875
9876static int
Eric Andersenc470f442003-07-28 09:56:35 +00009877readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009878{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009879 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009880 int c = firstc;
9881 char *out;
9882 int len;
9883 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009884 struct nodelist *bqlist;
9885 smallint quotef;
9886 smallint dblquote;
9887 smallint oldstyle;
9888 smallint prevsyntax; /* syntax before arithmetic */
9889 int varnest; /* levels of variables expansion */
9890 int arinest; /* levels of arithmetic expansion */
9891 int parenlevel; /* levels of parens in arithmetic */
9892 int dqvarnest; /* levels of variables expansion within double quotes */
9893
Eric Andersencb57d552001-06-28 07:25:16 +00009894#if __GNUC__
9895 /* Avoid longjmp clobbering */
9896 (void) &out;
9897 (void) &quotef;
9898 (void) &dblquote;
9899 (void) &varnest;
9900 (void) &arinest;
9901 (void) &parenlevel;
9902 (void) &dqvarnest;
9903 (void) &oldstyle;
9904 (void) &prevsyntax;
9905 (void) &syntax;
9906#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009907 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009908 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009909 quotef = 0;
9910 dblquote = (syntax == DQSYNTAX);
9911 oldstyle = 0;
9912 prevsyntax = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009913 varnest = 0;
9914 arinest = 0;
9915 parenlevel = 0;
9916 dqvarnest = 0;
9917
9918 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009919 loop: { /* for each line, until end of word */
9920 CHECKEND(); /* set c to PEOF if at end of here document */
9921 for (;;) { /* until end of line or end of word */
9922 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009923 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009924 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009925 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009926 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009927 USTPUTC(c, out);
9928 plinno++;
9929 if (doprompt)
9930 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009931 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009932 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009933 case CWORD:
9934 USTPUTC(c, out);
9935 break;
9936 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009937 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009938 USTPUTC(CTLESC, out);
9939 USTPUTC(c, out);
9940 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009941 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009942 c = pgetc2();
9943 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009944 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009945 USTPUTC('\\', out);
9946 pungetc();
9947 } else if (c == '\n') {
9948 if (doprompt)
9949 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009950 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009951 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009952 c != '\\' && c != '`' &&
9953 c != '$' && (
9954 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009955 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009956 ) {
9957 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009958 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009959 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009960 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009961 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009962 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009963 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009964 }
9965 break;
9966 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009967 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009968 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009969 if (eofmark == NULL) {
9970 USTPUTC(CTLQUOTEMARK, out);
9971 }
Eric Andersencb57d552001-06-28 07:25:16 +00009972 break;
9973 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009974 syntax = DQSYNTAX;
9975 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009976 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009977 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009978 if (eofmark != NULL && arinest == 0
9979 && varnest == 0
9980 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009981 USTPUTC(c, out);
9982 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009983 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009984 syntax = BASESYNTAX;
9985 dblquote = 0;
9986 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009987 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009988 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009989 }
9990 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009991 case CVAR: /* '$' */
9992 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009993 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009994 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009995 if (varnest > 0) {
9996 varnest--;
9997 if (dqvarnest > 0) {
9998 dqvarnest--;
9999 }
10000 USTPUTC(CTLENDVAR, out);
10001 } else {
10002 USTPUTC(c, out);
10003 }
10004 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010005#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010006 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010007 parenlevel++;
10008 USTPUTC(c, out);
10009 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010010 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010011 if (parenlevel > 0) {
10012 USTPUTC(c, out);
10013 --parenlevel;
10014 } else {
10015 if (pgetc() == ')') {
10016 if (--arinest == 0) {
10017 USTPUTC(CTLENDARI, out);
10018 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010019 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010020 } else
10021 USTPUTC(')', out);
10022 } else {
10023 /*
10024 * unbalanced parens
10025 * (don't 2nd guess - no error)
10026 */
10027 pungetc();
10028 USTPUTC(')', out);
10029 }
10030 }
10031 break;
10032#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010033 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010034 PARSEBACKQOLD();
10035 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010036 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010037 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010038 case CIGN:
10039 break;
10040 default:
10041 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010042 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010043#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010044 if (c != PEOA)
10045#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010046 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010047
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
10049 c = pgetc_macro();
10050 }
10051 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010052 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010053#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010054 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010055 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010056#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010057 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010058 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010059 if (varnest != 0) {
10060 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010061 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010062 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010063 }
10064 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010065 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010066 out = stackblock();
10067 if (eofmark == NULL) {
10068 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010069 && quotef == 0
10070 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010071 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010072 PARSEREDIR();
10073 return lasttoken = TREDIR;
10074 } else {
10075 pungetc();
10076 }
10077 }
10078 quoteflag = quotef;
10079 backquotelist = bqlist;
10080 grabstackblock(len);
10081 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010082 lasttoken = TWORD;
10083 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010084/* end of readtoken routine */
10085
Eric Andersencb57d552001-06-28 07:25:16 +000010086/*
10087 * Check to see whether we are at the end of the here document. When this
10088 * is called, c is set to the first character of the next input line. If
10089 * we are at the end of the here document, this routine sets the c to PEOF.
10090 */
Eric Andersenc470f442003-07-28 09:56:35 +000010091checkend: {
10092 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010093#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010094 if (c == PEOA) {
10095 c = pgetc2();
10096 }
10097#endif
10098 if (striptabs) {
10099 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010100 c = pgetc2();
10101 }
Eric Andersenc470f442003-07-28 09:56:35 +000010102 }
10103 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010104 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010105 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010106
Eric Andersenc470f442003-07-28 09:56:35 +000010107 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010108 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010109 if (*p == '\n' && *q == '\0') {
10110 c = PEOF;
10111 plinno++;
10112 needprompt = doprompt;
10113 } else {
10114 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010115 }
10116 }
10117 }
10118 }
Eric Andersenc470f442003-07-28 09:56:35 +000010119 goto checkend_return;
10120}
Eric Andersencb57d552001-06-28 07:25:16 +000010121
Eric Andersencb57d552001-06-28 07:25:16 +000010122/*
10123 * Parse a redirection operator. The variable "out" points to a string
10124 * specifying the fd to be redirected. The variable "c" contains the
10125 * first character of the redirection operator.
10126 */
Eric Andersenc470f442003-07-28 09:56:35 +000010127parseredir: {
10128 char fd = *out;
10129 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010130
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010131 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010132 if (c == '>') {
10133 np->nfile.fd = 1;
10134 c = pgetc();
10135 if (c == '>')
10136 np->type = NAPPEND;
10137 else if (c == '|')
10138 np->type = NCLOBBER;
10139 else if (c == '&')
10140 np->type = NTOFD;
10141 else {
10142 np->type = NTO;
10143 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010144 }
Eric Andersenc470f442003-07-28 09:56:35 +000010145 } else { /* c == '<' */
10146 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010147 c = pgetc();
10148 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010149 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010150 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010151 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010152 np->nfile.fd = 0;
10153 }
10154 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010155 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010156 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010157 c = pgetc();
10158 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010159 heredoc->striptabs = 1;
10160 } else {
10161 heredoc->striptabs = 0;
10162 pungetc();
10163 }
10164 break;
10165
10166 case '&':
10167 np->type = NFROMFD;
10168 break;
10169
10170 case '>':
10171 np->type = NFROMTO;
10172 break;
10173
10174 default:
10175 np->type = NFROM;
10176 pungetc();
10177 break;
10178 }
Eric Andersencb57d552001-06-28 07:25:16 +000010179 }
Eric Andersenc470f442003-07-28 09:56:35 +000010180 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010181 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010182 redirnode = np;
10183 goto parseredir_return;
10184}
Eric Andersencb57d552001-06-28 07:25:16 +000010185
Eric Andersencb57d552001-06-28 07:25:16 +000010186/*
10187 * Parse a substitution. At this point, we have read the dollar sign
10188 * and nothing else.
10189 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010190
10191/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10192 * (assuming ascii char codes, as the original implementation did) */
10193#define is_special(c) \
10194 ((((unsigned int)c) - 33 < 32) \
10195 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010196parsesub: {
10197 int subtype;
10198 int typeloc;
10199 int flags;
10200 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010201 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010202
Eric Andersenc470f442003-07-28 09:56:35 +000010203 c = pgetc();
10204 if (
10205 c <= PEOA_OR_PEOF ||
10206 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10207 ) {
10208 USTPUTC('$', out);
10209 pungetc();
10210 } else if (c == '(') { /* $(command) or $((arith)) */
10211 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010212#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010213 PARSEARITH();
10214#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010215 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010216#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010217 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010218 pungetc();
10219 PARSEBACKQNEW();
10220 }
10221 } else {
10222 USTPUTC(CTLVAR, out);
10223 typeloc = out - (char *)stackblock();
10224 USTPUTC(VSNORMAL, out);
10225 subtype = VSNORMAL;
10226 if (c == '{') {
10227 c = pgetc();
10228 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010229 c = pgetc();
10230 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010231 c = '#';
10232 else
10233 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010234 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010235 subtype = 0;
10236 }
10237 if (c > PEOA_OR_PEOF && is_name(c)) {
10238 do {
10239 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010240 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010241 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010242 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010243 do {
10244 STPUTC(c, out);
10245 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010247 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010248 USTPUTC(c, out);
10249 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010250 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010251 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010252
Eric Andersenc470f442003-07-28 09:56:35 +000010253 STPUTC('=', out);
10254 flags = 0;
10255 if (subtype == 0) {
10256 switch (c) {
10257 case ':':
10258 flags = VSNUL;
10259 c = pgetc();
10260 /*FALLTHROUGH*/
10261 default:
10262 p = strchr(types, c);
10263 if (p == NULL)
10264 goto badsub;
10265 subtype = p - types + VSNORMAL;
10266 break;
10267 case '%':
10268 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010269 {
10270 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010271 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010272 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010273 c = pgetc();
10274 if (c == cc)
10275 subtype++;
10276 else
10277 pungetc();
10278 break;
10279 }
10280 }
Eric Andersenc470f442003-07-28 09:56:35 +000010281 } else {
10282 pungetc();
10283 }
10284 if (dblquote || arinest)
10285 flags |= VSQUOTE;
10286 *((char *)stackblock() + typeloc) = subtype | flags;
10287 if (subtype != VSNORMAL) {
10288 varnest++;
10289 if (dblquote || arinest) {
10290 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010291 }
10292 }
10293 }
Eric Andersenc470f442003-07-28 09:56:35 +000010294 goto parsesub_return;
10295}
Eric Andersencb57d552001-06-28 07:25:16 +000010296
Eric Andersencb57d552001-06-28 07:25:16 +000010297/*
10298 * Called to parse command substitutions. Newstyle is set if the command
10299 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10300 * list of commands (passed by reference), and savelen is the number of
10301 * characters on the top of the stack which must be preserved.
10302 */
Eric Andersenc470f442003-07-28 09:56:35 +000010303parsebackq: {
10304 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010305 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010306 union node *n;
10307 char *volatile str;
10308 struct jmploc jmploc;
10309 struct jmploc *volatile savehandler;
10310 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010311 smallint saveprompt = 0;
10312
Eric Andersencb57d552001-06-28 07:25:16 +000010313#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010314 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010315#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010316 savepbq = parsebackquote;
10317 if (setjmp(jmploc.loc)) {
10318 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010319 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010320 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010321 exception_handler = savehandler;
10322 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010323 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010324 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010325 str = NULL;
10326 savelen = out - (char *)stackblock();
10327 if (savelen > 0) {
10328 str = ckmalloc(savelen);
10329 memcpy(str, stackblock(), savelen);
10330 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010331 savehandler = exception_handler;
10332 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010333 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010334 if (oldstyle) {
10335 /* We must read until the closing backquote, giving special
10336 treatment to some slashes, and then push the string and
10337 reread it as input, interpreting it normally. */
10338 char *pout;
10339 int pc;
10340 size_t psavelen;
10341 char *pstr;
10342
10343
10344 STARTSTACKSTR(pout);
10345 for (;;) {
10346 if (needprompt) {
10347 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010348 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010349 pc = pgetc();
10350 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010351 case '`':
10352 goto done;
10353
10354 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010355 pc = pgetc();
10356 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010357 plinno++;
10358 if (doprompt)
10359 setprompt(2);
10360 /*
10361 * If eating a newline, avoid putting
10362 * the newline into the new character
10363 * stream (via the STPUTC after the
10364 * switch).
10365 */
10366 continue;
10367 }
10368 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010369 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010370 STPUTC('\\', pout);
10371 if (pc > PEOA_OR_PEOF) {
10372 break;
10373 }
10374 /* fall through */
10375
10376 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010377#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010378 case PEOA:
10379#endif
10380 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010381 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010382
10383 case '\n':
10384 plinno++;
10385 needprompt = doprompt;
10386 break;
10387
10388 default:
10389 break;
10390 }
10391 STPUTC(pc, pout);
10392 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010393 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010394 STPUTC('\0', pout);
10395 psavelen = pout - (char *)stackblock();
10396 if (psavelen > 0) {
10397 pstr = grabstackstr(pout);
10398 setinputstring(pstr);
10399 }
10400 }
10401 nlpp = &bqlist;
10402 while (*nlpp)
10403 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010404 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010405 (*nlpp)->next = NULL;
10406 parsebackquote = oldstyle;
10407
10408 if (oldstyle) {
10409 saveprompt = doprompt;
10410 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010411 }
10412
Eric Andersenc470f442003-07-28 09:56:35 +000010413 n = list(2);
10414
10415 if (oldstyle)
10416 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010417 else if (readtoken() != TRP)
10418 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010419
10420 (*nlpp)->n = n;
10421 if (oldstyle) {
10422 /*
10423 * Start reading from old file again, ignoring any pushed back
10424 * tokens left from the backquote parsing
10425 */
10426 popfile();
10427 tokpushback = 0;
10428 }
10429 while (stackblocksize() <= savelen)
10430 growstackblock();
10431 STARTSTACKSTR(out);
10432 if (str) {
10433 memcpy(out, str, savelen);
10434 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010435 INT_OFF;
10436 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010437 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010438 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010439 }
10440 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010441 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010442 if (arinest || dblquote)
10443 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10444 else
10445 USTPUTC(CTLBACKQ, out);
10446 if (oldstyle)
10447 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010448 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010449}
10450
Denis Vlasenko131ae172007-02-18 13:00:19 +000010451#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010452/*
10453 * Parse an arithmetic expansion (indicate start of one and set state)
10454 */
Eric Andersenc470f442003-07-28 09:56:35 +000010455parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010456 if (++arinest == 1) {
10457 prevsyntax = syntax;
10458 syntax = ARISYNTAX;
10459 USTPUTC(CTLARI, out);
10460 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010461 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010462 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010463 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010464 } else {
10465 /*
10466 * we collapse embedded arithmetic expansion to
10467 * parenthesis, which should be equivalent
10468 */
10469 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010470 }
Eric Andersenc470f442003-07-28 09:56:35 +000010471 goto parsearith_return;
10472}
10473#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010474
Eric Andersenc470f442003-07-28 09:56:35 +000010475} /* end of readtoken */
10476
Eric Andersencb57d552001-06-28 07:25:16 +000010477/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010478 * Read the next input token.
10479 * If the token is a word, we set backquotelist to the list of cmds in
10480 * backquotes. We set quoteflag to true if any part of the word was
10481 * quoted.
10482 * If the token is TREDIR, then we set redirnode to a structure containing
10483 * the redirection.
10484 * In all cases, the variable startlinno is set to the number of the line
10485 * on which the token starts.
10486 *
10487 * [Change comment: here documents and internal procedures]
10488 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10489 * word parsing code into a separate routine. In this case, readtoken
10490 * doesn't need to have any internal procedures, but parseword does.
10491 * We could also make parseoperator in essence the main routine, and
10492 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010493 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010494#define NEW_xxreadtoken
10495#ifdef NEW_xxreadtoken
10496/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010497static const char xxreadtoken_chars[7] ALIGN1 = {
10498 '\n', '(', ')', '&', '|', ';', 0
10499};
Eric Andersencb57d552001-06-28 07:25:16 +000010500
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010501static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010502 TNL, TLP, TRP, /* only single occurrence allowed */
10503 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10504 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010505 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010506};
10507
10508#define xxreadtoken_doubles \
10509 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10510#define xxreadtoken_singles \
10511 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10512
10513static int
10514xxreadtoken(void)
10515{
10516 int c;
10517
10518 if (tokpushback) {
10519 tokpushback = 0;
10520 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010521 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010522 if (needprompt) {
10523 setprompt(2);
10524 }
10525 startlinno = plinno;
10526 for (;;) { /* until token or start of word found */
10527 c = pgetc_macro();
10528
10529 if ((c != ' ') && (c != '\t')
10530#if ENABLE_ASH_ALIAS
10531 && (c != PEOA)
10532#endif
10533 ) {
10534 if (c == '#') {
10535 while ((c = pgetc()) != '\n' && c != PEOF);
10536 pungetc();
10537 } else if (c == '\\') {
10538 if (pgetc() != '\n') {
10539 pungetc();
10540 goto READTOKEN1;
10541 }
10542 startlinno = ++plinno;
10543 if (doprompt)
10544 setprompt(2);
10545 } else {
10546 const char *p
10547 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10548
10549 if (c != PEOF) {
10550 if (c == '\n') {
10551 plinno++;
10552 needprompt = doprompt;
10553 }
10554
10555 p = strchr(xxreadtoken_chars, c);
10556 if (p == NULL) {
10557 READTOKEN1:
10558 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10559 }
10560
10561 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10562 if (pgetc() == *p) { /* double occurrence? */
10563 p += xxreadtoken_doubles + 1;
10564 } else {
10565 pungetc();
10566 }
10567 }
10568 }
10569 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10570 }
10571 }
10572 } /* for */
10573}
10574#else
10575#define RETURN(token) return lasttoken = token
10576static int
10577xxreadtoken(void)
10578{
10579 int c;
10580
10581 if (tokpushback) {
10582 tokpushback = 0;
10583 return lasttoken;
10584 }
10585 if (needprompt) {
10586 setprompt(2);
10587 }
10588 startlinno = plinno;
10589 for (;;) { /* until token or start of word found */
10590 c = pgetc_macro();
10591 switch (c) {
10592 case ' ': case '\t':
10593#if ENABLE_ASH_ALIAS
10594 case PEOA:
10595#endif
10596 continue;
10597 case '#':
10598 while ((c = pgetc()) != '\n' && c != PEOF);
10599 pungetc();
10600 continue;
10601 case '\\':
10602 if (pgetc() == '\n') {
10603 startlinno = ++plinno;
10604 if (doprompt)
10605 setprompt(2);
10606 continue;
10607 }
10608 pungetc();
10609 goto breakloop;
10610 case '\n':
10611 plinno++;
10612 needprompt = doprompt;
10613 RETURN(TNL);
10614 case PEOF:
10615 RETURN(TEOF);
10616 case '&':
10617 if (pgetc() == '&')
10618 RETURN(TAND);
10619 pungetc();
10620 RETURN(TBACKGND);
10621 case '|':
10622 if (pgetc() == '|')
10623 RETURN(TOR);
10624 pungetc();
10625 RETURN(TPIPE);
10626 case ';':
10627 if (pgetc() == ';')
10628 RETURN(TENDCASE);
10629 pungetc();
10630 RETURN(TSEMI);
10631 case '(':
10632 RETURN(TLP);
10633 case ')':
10634 RETURN(TRP);
10635 default:
10636 goto breakloop;
10637 }
10638 }
10639 breakloop:
10640 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10641#undef RETURN
10642}
10643#endif /* NEW_xxreadtoken */
10644
10645static int
10646readtoken(void)
10647{
10648 int t;
10649#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010650 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010651#endif
10652
10653#if ENABLE_ASH_ALIAS
10654 top:
10655#endif
10656
10657 t = xxreadtoken();
10658
10659 /*
10660 * eat newlines
10661 */
10662 if (checkkwd & CHKNL) {
10663 while (t == TNL) {
10664 parseheredoc();
10665 t = xxreadtoken();
10666 }
10667 }
10668
10669 if (t != TWORD || quoteflag) {
10670 goto out;
10671 }
10672
10673 /*
10674 * check for keywords
10675 */
10676 if (checkkwd & CHKKWD) {
10677 const char *const *pp;
10678
10679 pp = findkwd(wordtext);
10680 if (pp) {
10681 lasttoken = t = pp - tokname_array;
10682 TRACE(("keyword %s recognized\n", tokname(t)));
10683 goto out;
10684 }
10685 }
10686
10687 if (checkkwd & CHKALIAS) {
10688#if ENABLE_ASH_ALIAS
10689 struct alias *ap;
10690 ap = lookupalias(wordtext, 1);
10691 if (ap != NULL) {
10692 if (*ap->val) {
10693 pushstring(ap->val, ap);
10694 }
10695 goto top;
10696 }
10697#endif
10698 }
10699 out:
10700 checkkwd = 0;
10701#if DEBUG
10702 if (!alreadyseen)
10703 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10704 else
10705 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10706#endif
10707 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010708}
10709
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010710static char
10711peektoken(void)
10712{
10713 int t;
10714
10715 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010716 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010717 return tokname_array[t][0];
10718}
Eric Andersencb57d552001-06-28 07:25:16 +000010719
10720/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010721 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10722 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010723 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010724static union node *
10725parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010726{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010727 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010728
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010729 tokpushback = 0;
10730 doprompt = interact;
10731 if (doprompt)
10732 setprompt(doprompt);
10733 needprompt = 0;
10734 t = readtoken();
10735 if (t == TEOF)
10736 return NEOF;
10737 if (t == TNL)
10738 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010739 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010740 return list(1);
10741}
10742
10743/*
10744 * Input any here documents.
10745 */
10746static void
10747parseheredoc(void)
10748{
10749 struct heredoc *here;
10750 union node *n;
10751
10752 here = heredoclist;
10753 heredoclist = 0;
10754
10755 while (here) {
10756 if (needprompt) {
10757 setprompt(2);
10758 }
10759 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10760 here->eofmark, here->striptabs);
10761 n = stalloc(sizeof(struct narg));
10762 n->narg.type = NARG;
10763 n->narg.next = NULL;
10764 n->narg.text = wordtext;
10765 n->narg.backquote = backquotelist;
10766 here->here->nhere.doc = n;
10767 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010768 }
Eric Andersencb57d552001-06-28 07:25:16 +000010769}
10770
10771
10772/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010773 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010774 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010775#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010776static const char *
10777expandstr(const char *ps)
10778{
10779 union node n;
10780
10781 /* XXX Fix (char *) cast. */
10782 setinputstring((char *)ps);
Denis Vlasenko7b70d782007-09-21 22:48:02 +000010783 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010784 popfile();
10785
10786 n.narg.type = NARG;
10787 n.narg.next = NULL;
10788 n.narg.text = wordtext;
10789 n.narg.backquote = backquotelist;
10790
10791 expandarg(&n, NULL, 0);
10792 return stackblock();
10793}
10794#endif
10795
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010796/*
10797 * Execute a command or commands contained in a string.
10798 */
10799static int
10800evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010801{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010802 union node *n;
10803 struct stackmark smark;
10804 int skip;
10805
10806 setinputstring(s);
10807 setstackmark(&smark);
10808
10809 skip = 0;
10810 while ((n = parsecmd(0)) != NEOF) {
10811 evaltree(n, 0);
10812 popstackmark(&smark);
10813 skip = evalskip;
10814 if (skip)
10815 break;
10816 }
10817 popfile();
10818
10819 skip &= mask;
10820 evalskip = skip;
10821 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010822}
10823
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010824/*
10825 * The eval command.
10826 */
10827static int
10828evalcmd(int argc, char **argv)
10829{
10830 char *p;
10831 char *concat;
10832 char **ap;
10833
10834 if (argc > 1) {
10835 p = argv[1];
10836 if (argc > 2) {
10837 STARTSTACKSTR(concat);
10838 ap = argv + 2;
10839 for (;;) {
10840 concat = stack_putstr(p, concat);
10841 p = *ap++;
10842 if (p == NULL)
10843 break;
10844 STPUTC(' ', concat);
10845 }
10846 STPUTC('\0', concat);
10847 p = grabstackstr(concat);
10848 }
10849 evalstring(p, ~SKIPEVAL);
10850
10851 }
10852 return exitstatus;
10853}
10854
10855/*
10856 * Read and execute commands. "Top" is nonzero for the top level command
10857 * loop; it turns on prompting if the shell is interactive.
10858 */
10859static int
10860cmdloop(int top)
10861{
10862 union node *n;
10863 struct stackmark smark;
10864 int inter;
10865 int numeof = 0;
10866
10867 TRACE(("cmdloop(%d) called\n", top));
10868 for (;;) {
10869 int skip;
10870
10871 setstackmark(&smark);
10872#if JOBS
10873 if (jobctl)
10874 showjobs(stderr, SHOW_CHANGED);
10875#endif
10876 inter = 0;
10877 if (iflag && top) {
10878 inter++;
10879#if ENABLE_ASH_MAIL
10880 chkmail();
10881#endif
10882 }
10883 n = parsecmd(inter);
10884 /* showtree(n); DEBUG */
10885 if (n == NEOF) {
10886 if (!top || numeof >= 50)
10887 break;
10888 if (!stoppedjobs()) {
10889 if (!Iflag)
10890 break;
10891 out2str("\nUse \"exit\" to leave shell.\n");
10892 }
10893 numeof++;
10894 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010895 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10896 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010897 numeof = 0;
10898 evaltree(n, 0);
10899 }
10900 popstackmark(&smark);
10901 skip = evalskip;
10902
10903 if (skip) {
10904 evalskip = 0;
10905 return skip & SKIPEVAL;
10906 }
10907 }
10908 return 0;
10909}
10910
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010911/*
10912 * Take commands from a file. To be compatible we should do a path
10913 * search for the file, which is necessary to find sub-commands.
10914 */
10915static char *
10916find_dot_file(char *name)
10917{
10918 char *fullname;
10919 const char *path = pathval();
10920 struct stat statb;
10921
10922 /* don't try this for absolute or relative paths */
10923 if (strchr(name, '/'))
10924 return name;
10925
10926 while ((fullname = padvance(&path, name)) != NULL) {
10927 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10928 /*
10929 * Don't bother freeing here, since it will
10930 * be freed by the caller.
10931 */
10932 return fullname;
10933 }
10934 stunalloc(fullname);
10935 }
10936
10937 /* not found in the PATH */
10938 ash_msg_and_raise_error("%s: not found", name);
10939 /* NOTREACHED */
10940}
10941
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010942static int
10943dotcmd(int argc, char **argv)
10944{
10945 struct strlist *sp;
10946 volatile struct shparam saveparam;
10947 int status = 0;
10948
10949 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010950 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010951
10952 if (argc >= 2) { /* That's what SVR2 does */
10953 char *fullname;
10954
10955 fullname = find_dot_file(argv[1]);
10956
10957 if (argc > 2) {
10958 saveparam = shellparam;
10959 shellparam.malloc = 0;
10960 shellparam.nparam = argc - 2;
10961 shellparam.p = argv + 2;
10962 };
10963
10964 setinputfile(fullname, INPUT_PUSH_FILE);
10965 commandname = fullname;
10966 cmdloop(0);
10967 popfile();
10968
10969 if (argc > 2) {
10970 freeparam(&shellparam);
10971 shellparam = saveparam;
10972 };
10973 status = exitstatus;
10974 }
10975 return status;
10976}
10977
10978static int
10979exitcmd(int argc, char **argv)
10980{
10981 if (stoppedjobs())
10982 return 0;
10983 if (argc > 1)
10984 exitstatus = number(argv[1]);
10985 raise_exception(EXEXIT);
10986 /* NOTREACHED */
10987}
10988
10989#if ENABLE_ASH_BUILTIN_ECHO
10990static int
10991echocmd(int argc, char **argv)
10992{
10993 return bb_echo(argv);
10994}
10995#endif
10996
10997#if ENABLE_ASH_BUILTIN_TEST
10998static int
10999testcmd(int argc, char **argv)
11000{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011001 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011002}
11003#endif
11004
11005/*
11006 * Read a file containing shell functions.
11007 */
11008static void
11009readcmdfile(char *name)
11010{
11011 setinputfile(name, INPUT_PUSH_FILE);
11012 cmdloop(0);
11013 popfile();
11014}
11015
11016
Denis Vlasenkocc571512007-02-23 21:10:35 +000011017/* ============ find_command inplementation */
11018
11019/*
11020 * Resolve a command name. If you change this routine, you may have to
11021 * change the shellexec routine as well.
11022 */
11023static void
11024find_command(char *name, struct cmdentry *entry, int act, const char *path)
11025{
11026 struct tblentry *cmdp;
11027 int idx;
11028 int prev;
11029 char *fullname;
11030 struct stat statb;
11031 int e;
11032 int updatetbl;
11033 struct builtincmd *bcmd;
11034
11035 /* If name contains a slash, don't use PATH or hash table */
11036 if (strchr(name, '/') != NULL) {
11037 entry->u.index = -1;
11038 if (act & DO_ABS) {
11039 while (stat(name, &statb) < 0) {
11040#ifdef SYSV
11041 if (errno == EINTR)
11042 continue;
11043#endif
11044 entry->cmdtype = CMDUNKNOWN;
11045 return;
11046 }
11047 }
11048 entry->cmdtype = CMDNORMAL;
11049 return;
11050 }
11051
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011052/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011053
11054 updatetbl = (path == pathval());
11055 if (!updatetbl) {
11056 act |= DO_ALTPATH;
11057 if (strstr(path, "%builtin") != NULL)
11058 act |= DO_ALTBLTIN;
11059 }
11060
11061 /* If name is in the table, check answer will be ok */
11062 cmdp = cmdlookup(name, 0);
11063 if (cmdp != NULL) {
11064 int bit;
11065
11066 switch (cmdp->cmdtype) {
11067 default:
11068#if DEBUG
11069 abort();
11070#endif
11071 case CMDNORMAL:
11072 bit = DO_ALTPATH;
11073 break;
11074 case CMDFUNCTION:
11075 bit = DO_NOFUNC;
11076 break;
11077 case CMDBUILTIN:
11078 bit = DO_ALTBLTIN;
11079 break;
11080 }
11081 if (act & bit) {
11082 updatetbl = 0;
11083 cmdp = NULL;
11084 } else if (cmdp->rehash == 0)
11085 /* if not invalidated by cd, we're done */
11086 goto success;
11087 }
11088
11089 /* If %builtin not in path, check for builtin next */
11090 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011091 if (bcmd) {
11092 if (IS_BUILTIN_REGULAR(bcmd))
11093 goto builtin_success;
11094 if (act & DO_ALTPATH) {
11095 if (!(act & DO_ALTBLTIN))
11096 goto builtin_success;
11097 } else if (builtinloc <= 0) {
11098 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011099 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011100 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011101
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011102#if ENABLE_FEATURE_SH_STANDALONE
11103 if (find_applet_by_name(name)) {
11104 entry->cmdtype = CMDNORMAL;
11105 entry->u.index = -1;
11106 return;
11107 }
11108#endif
11109
Denis Vlasenkocc571512007-02-23 21:10:35 +000011110 /* We have to search path. */
11111 prev = -1; /* where to start */
11112 if (cmdp && cmdp->rehash) { /* doing a rehash */
11113 if (cmdp->cmdtype == CMDBUILTIN)
11114 prev = builtinloc;
11115 else
11116 prev = cmdp->param.index;
11117 }
11118
11119 e = ENOENT;
11120 idx = -1;
11121 loop:
11122 while ((fullname = padvance(&path, name)) != NULL) {
11123 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011124 /* NB: code below will still use fullname
11125 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011126 idx++;
11127 if (pathopt) {
11128 if (prefix(pathopt, "builtin")) {
11129 if (bcmd)
11130 goto builtin_success;
11131 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011132 } else if (!(act & DO_NOFUNC)
11133 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011134 /* handled below */
11135 } else {
11136 /* ignore unimplemented options */
11137 continue;
11138 }
11139 }
11140 /* if rehash, don't redo absolute path names */
11141 if (fullname[0] == '/' && idx <= prev) {
11142 if (idx < prev)
11143 continue;
11144 TRACE(("searchexec \"%s\": no change\n", name));
11145 goto success;
11146 }
11147 while (stat(fullname, &statb) < 0) {
11148#ifdef SYSV
11149 if (errno == EINTR)
11150 continue;
11151#endif
11152 if (errno != ENOENT && errno != ENOTDIR)
11153 e = errno;
11154 goto loop;
11155 }
11156 e = EACCES; /* if we fail, this will be the error */
11157 if (!S_ISREG(statb.st_mode))
11158 continue;
11159 if (pathopt) { /* this is a %func directory */
11160 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011161 /* NB: stalloc will return space pointed by fullname
11162 * (because we don't have any intervening allocations
11163 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011164 readcmdfile(fullname);
11165 cmdp = cmdlookup(name, 0);
11166 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11167 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11168 stunalloc(fullname);
11169 goto success;
11170 }
11171 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11172 if (!updatetbl) {
11173 entry->cmdtype = CMDNORMAL;
11174 entry->u.index = idx;
11175 return;
11176 }
11177 INT_OFF;
11178 cmdp = cmdlookup(name, 1);
11179 cmdp->cmdtype = CMDNORMAL;
11180 cmdp->param.index = idx;
11181 INT_ON;
11182 goto success;
11183 }
11184
11185 /* We failed. If there was an entry for this command, delete it */
11186 if (cmdp && updatetbl)
11187 delete_cmd_entry();
11188 if (act & DO_ERR)
11189 ash_msg("%s: %s", name, errmsg(e, "not found"));
11190 entry->cmdtype = CMDUNKNOWN;
11191 return;
11192
11193 builtin_success:
11194 if (!updatetbl) {
11195 entry->cmdtype = CMDBUILTIN;
11196 entry->u.cmd = bcmd;
11197 return;
11198 }
11199 INT_OFF;
11200 cmdp = cmdlookup(name, 1);
11201 cmdp->cmdtype = CMDBUILTIN;
11202 cmdp->param.cmd = bcmd;
11203 INT_ON;
11204 success:
11205 cmdp->rehash = 0;
11206 entry->cmdtype = cmdp->cmdtype;
11207 entry->u = cmdp->param;
11208}
11209
11210
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011211/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011212
Eric Andersencb57d552001-06-28 07:25:16 +000011213/*
Eric Andersencb57d552001-06-28 07:25:16 +000011214 * The trap builtin.
11215 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011216static int
Eric Andersenc470f442003-07-28 09:56:35 +000011217trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011218{
11219 char *action;
11220 char **ap;
11221 int signo;
11222
Eric Andersenc470f442003-07-28 09:56:35 +000011223 nextopt(nullstr);
11224 ap = argptr;
11225 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011226 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011227 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011228 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011229
Rob Landleyc9c1a412006-07-12 19:17:55 +000011230 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011231 out1fmt("trap -- %s %s\n",
11232 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011233 }
11234 }
11235 return 0;
11236 }
Eric Andersenc470f442003-07-28 09:56:35 +000011237 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011238 action = NULL;
11239 else
11240 action = *ap++;
11241 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011242 signo = get_signum(*ap);
11243 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011244 ash_msg_and_raise_error("%s: bad trap", *ap);
11245 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011246 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011247 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011248 action = NULL;
11249 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011250 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011251 }
Eric Andersenc470f442003-07-28 09:56:35 +000011252 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011253 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011254 trap[signo] = action;
11255 if (signo != 0)
11256 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011257 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011258 ap++;
11259 }
11260 return 0;
11261}
11262
Eric Andersenc470f442003-07-28 09:56:35 +000011263
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011264/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011265
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011266#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011267/*
11268 * Lists available builtins
11269 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011270static int
11271helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011272{
11273 int col, i;
11274
11275 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011276 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011277 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011278 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011279 if (col > 60) {
11280 out1fmt("\n");
11281 col = 0;
11282 }
11283 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011284#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011285 for (i = 0; i < NUM_APPLETS; i++) {
11286 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11287 if (col > 60) {
11288 out1fmt("\n");
11289 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011290 }
11291 }
11292#endif
11293 out1fmt("\n\n");
11294 return EXIT_SUCCESS;
11295}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011296#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011297
Eric Andersencb57d552001-06-28 07:25:16 +000011298/*
Eric Andersencb57d552001-06-28 07:25:16 +000011299 * The export and readonly commands.
11300 */
Eric Andersenc470f442003-07-28 09:56:35 +000011301static int
11302exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011303{
11304 struct var *vp;
11305 char *name;
11306 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011307 char **aptr;
11308 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011309
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011310 if (nextopt("p") != 'p') {
11311 aptr = argptr;
11312 name = *aptr;
11313 if (name) {
11314 do {
11315 p = strchr(name, '=');
11316 if (p != NULL) {
11317 p++;
11318 } else {
11319 vp = *findvar(hashvar(name), name);
11320 if (vp) {
11321 vp->flags |= flag;
11322 continue;
11323 }
Eric Andersencb57d552001-06-28 07:25:16 +000011324 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011325 setvar(name, p, flag);
11326 } while ((name = *++aptr) != NULL);
11327 return 0;
11328 }
Eric Andersencb57d552001-06-28 07:25:16 +000011329 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011330 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011331 return 0;
11332}
11333
Eric Andersencb57d552001-06-28 07:25:16 +000011334/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011335 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011336 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011337static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011338unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011339{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011340 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011341
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011342 cmdp = cmdlookup(name, 0);
11343 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11344 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011345}
11346
Eric Andersencb57d552001-06-28 07:25:16 +000011347/*
Eric Andersencb57d552001-06-28 07:25:16 +000011348 * The unset builtin command. We unset the function before we unset the
11349 * variable to allow a function to be unset when there is a readonly variable
11350 * with the same name.
11351 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011352static int
Eric Andersenc470f442003-07-28 09:56:35 +000011353unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011354{
11355 char **ap;
11356 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011357 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011358 int ret = 0;
11359
11360 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011361 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011362 }
Eric Andersencb57d552001-06-28 07:25:16 +000011363
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011364 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011365 if (flag != 'f') {
11366 i = unsetvar(*ap);
11367 ret |= i;
11368 if (!(i & 2))
11369 continue;
11370 }
11371 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011372 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011373 }
Eric Andersenc470f442003-07-28 09:56:35 +000011374 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011375}
11376
11377
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011378/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011379
Eric Andersenc470f442003-07-28 09:56:35 +000011380#include <sys/times.h>
11381
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011382static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011383 ' ', offsetof(struct tms, tms_utime),
11384 '\n', offsetof(struct tms, tms_stime),
11385 ' ', offsetof(struct tms, tms_cutime),
11386 '\n', offsetof(struct tms, tms_cstime),
11387 0
11388};
Eric Andersencb57d552001-06-28 07:25:16 +000011389
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011390static int
11391timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011392{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011393 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011394 const unsigned char *p;
11395 struct tms buf;
11396
11397 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011398 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011399
11400 p = timescmd_str;
11401 do {
11402 t = *(clock_t *)(((char *) &buf) + p[1]);
11403 s = t / clk_tck;
11404 out1fmt("%ldm%ld.%.3lds%c",
11405 s/60, s%60,
11406 ((t - s * clk_tck) * 1000) / clk_tck,
11407 p[0]);
11408 } while (*(p += 2));
11409
Eric Andersencb57d552001-06-28 07:25:16 +000011410 return 0;
11411}
11412
Denis Vlasenko131ae172007-02-18 13:00:19 +000011413#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011414static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011415dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011416{
Eric Andersened9ecf72004-06-22 08:29:45 +000011417 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011418 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011419
Denis Vlasenkob012b102007-02-19 22:43:01 +000011420 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011421 result = arith(s, &errcode);
11422 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011423 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011424 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011425 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011426 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011427 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011428 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011429 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011430 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011431 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011432
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011433 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011434}
Eric Andersenc470f442003-07-28 09:56:35 +000011435
Eric Andersenc470f442003-07-28 09:56:35 +000011436/*
Eric Andersen90898442003-08-06 11:20:52 +000011437 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11438 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11439 *
11440 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011441 */
11442static int
Eric Andersen90898442003-08-06 11:20:52 +000011443letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011444{
Eric Andersenc470f442003-07-28 09:56:35 +000011445 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011446 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011447
Eric Andersen90898442003-08-06 11:20:52 +000011448 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011449 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011450 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011451 for (ap = argv + 1; *ap; ap++) {
11452 i = dash_arith(*ap);
11453 }
Eric Andersenc470f442003-07-28 09:56:35 +000011454
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011455 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011456}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011457#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011458
Eric Andersenc470f442003-07-28 09:56:35 +000011459
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011460/* ============ miscbltin.c
11461 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011462 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011463 */
11464
11465#undef rflag
11466
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011467#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011468typedef enum __rlimit_resource rlim_t;
11469#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011470
Eric Andersenc470f442003-07-28 09:56:35 +000011471/*
11472 * The read builtin. The -e option causes backslashes to escape the
11473 * following character.
11474 *
11475 * This uses unbuffered input, which may be avoidable in some cases.
11476 */
Eric Andersenc470f442003-07-28 09:56:35 +000011477static int
11478readcmd(int argc, char **argv)
11479{
11480 char **ap;
11481 int backslash;
11482 char c;
11483 int rflag;
11484 char *prompt;
11485 const char *ifs;
11486 char *p;
11487 int startword;
11488 int status;
11489 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011490#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011491 int nch_flag = 0;
11492 int nchars = 0;
11493 int silent = 0;
11494 struct termios tty, old_tty;
11495#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011496#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011497 fd_set set;
11498 struct timeval ts;
11499
11500 ts.tv_sec = ts.tv_usec = 0;
11501#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011502
11503 rflag = 0;
11504 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011505#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011506 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011507#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011508 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011509#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011510 while ((i = nextopt("p:rt:")) != '\0')
11511#else
11512 while ((i = nextopt("p:r")) != '\0')
11513#endif
11514 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011515 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011516 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011517 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011518 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011519#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011520 case 'n':
11521 nchars = strtol(optionarg, &p, 10);
11522 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011523 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011524 nch_flag = (nchars > 0);
11525 break;
11526 case 's':
11527 silent = 1;
11528 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011529#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011530#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011531 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011532 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011533 ts.tv_usec = 0;
11534 if (*p == '.') {
11535 char *p2;
11536 if (*++p) {
11537 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011538 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011539 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011540 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011541 scale = p2 - p;
11542 /* normalize to usec */
11543 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011544 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011545 while (scale++ < 6)
11546 ts.tv_usec *= 10;
11547 }
11548 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011549 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011550 }
11551 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011552 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011553 break;
11554#endif
11555 case 'r':
11556 rflag = 1;
11557 break;
11558 default:
11559 break;
11560 }
Eric Andersenc470f442003-07-28 09:56:35 +000011561 }
11562 if (prompt && isatty(0)) {
11563 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011564 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011565 ap = argptr;
11566 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011567 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011568 ifs = bltinlookup("IFS");
11569 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011570 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011571#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011572 if (nch_flag || silent) {
11573 tcgetattr(0, &tty);
11574 old_tty = tty;
11575 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011576 tty.c_lflag &= ~ICANON;
11577 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011578 }
11579 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011580 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011581
11582 }
11583 tcsetattr(0, TCSANOW, &tty);
11584 }
11585#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011586#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011587 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenko87f3b262007-09-07 13:43:28 +000011588// TODO: replace with poll, it is smaller
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011589 FD_ZERO(&set);
11590 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011591
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011592 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011593 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011594#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011595 if (nch_flag)
11596 tcsetattr(0, TCSANOW, &old_tty);
11597#endif
11598 return 1;
11599 }
11600 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011601#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011602 status = 0;
11603 startword = 1;
11604 backslash = 0;
11605 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011606#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011607 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011608#else
11609 for (;;)
11610#endif
11611 {
Eric Andersenc470f442003-07-28 09:56:35 +000011612 if (read(0, &c, 1) != 1) {
11613 status = 1;
11614 break;
11615 }
11616 if (c == '\0')
11617 continue;
11618 if (backslash) {
11619 backslash = 0;
11620 if (c != '\n')
11621 goto put;
11622 continue;
11623 }
11624 if (!rflag && c == '\\') {
11625 backslash++;
11626 continue;
11627 }
11628 if (c == '\n')
11629 break;
11630 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11631 continue;
11632 }
11633 startword = 0;
11634 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11635 STACKSTRNUL(p);
11636 setvar(*ap, stackblock(), 0);
11637 ap++;
11638 startword = 1;
11639 STARTSTACKSTR(p);
11640 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011641 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011642 STPUTC(c, p);
11643 }
11644 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011645#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011646 if (nch_flag || silent)
11647 tcsetattr(0, TCSANOW, &old_tty);
11648#endif
11649
Eric Andersenc470f442003-07-28 09:56:35 +000011650 STACKSTRNUL(p);
11651 /* Remove trailing blanks */
11652 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11653 *p = '\0';
11654 setvar(*ap, stackblock(), 0);
11655 while (*++ap != NULL)
11656 setvar(*ap, nullstr, 0);
11657 return status;
11658}
11659
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011660static int
11661umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011662{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011663 static const char permuser[3] ALIGN1 = "ugo";
11664 static const char permmode[3] ALIGN1 = "rwx";
11665 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011666 S_IRUSR, S_IWUSR, S_IXUSR,
11667 S_IRGRP, S_IWGRP, S_IXGRP,
11668 S_IROTH, S_IWOTH, S_IXOTH
11669 };
11670
11671 char *ap;
11672 mode_t mask;
11673 int i;
11674 int symbolic_mode = 0;
11675
11676 while (nextopt("S") != '\0') {
11677 symbolic_mode = 1;
11678 }
11679
Denis Vlasenkob012b102007-02-19 22:43:01 +000011680 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011681 mask = umask(0);
11682 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011683 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011684
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011685 ap = *argptr;
11686 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011687 if (symbolic_mode) {
11688 char buf[18];
11689 char *p = buf;
11690
11691 for (i = 0; i < 3; i++) {
11692 int j;
11693
11694 *p++ = permuser[i];
11695 *p++ = '=';
11696 for (j = 0; j < 3; j++) {
11697 if ((mask & permmask[3 * i + j]) == 0) {
11698 *p++ = permmode[j];
11699 }
11700 }
11701 *p++ = ',';
11702 }
11703 *--p = 0;
11704 puts(buf);
11705 } else {
11706 out1fmt("%.4o\n", mask);
11707 }
11708 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011709 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011710 mask = 0;
11711 do {
11712 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011713 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011714 mask = (mask << 3) + (*ap - '0');
11715 } while (*++ap != '\0');
11716 umask(mask);
11717 } else {
11718 mask = ~mask & 0777;
11719 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011720 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011721 }
11722 umask(~mask & 0777);
11723 }
11724 }
11725 return 0;
11726}
11727
11728/*
11729 * ulimit builtin
11730 *
11731 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11732 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11733 * ash by J.T. Conklin.
11734 *
11735 * Public domain.
11736 */
11737
11738struct limits {
11739 const char *name;
11740 int cmd;
11741 int factor; /* multiply by to get rlim_{cur,max} values */
11742 char option;
11743};
11744
11745static const struct limits limits[] = {
11746#ifdef RLIMIT_CPU
11747 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11748#endif
11749#ifdef RLIMIT_FSIZE
11750 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11751#endif
11752#ifdef RLIMIT_DATA
11753 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11754#endif
11755#ifdef RLIMIT_STACK
11756 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11757#endif
11758#ifdef RLIMIT_CORE
11759 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11760#endif
11761#ifdef RLIMIT_RSS
11762 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11763#endif
11764#ifdef RLIMIT_MEMLOCK
11765 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11766#endif
11767#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011768 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011769#endif
11770#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011771 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011772#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011773#ifdef RLIMIT_AS
11774 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011775#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011776#ifdef RLIMIT_LOCKS
11777 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011778#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011779 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011780};
11781
Glenn L McGrath76620622004-01-13 10:19:37 +000011782enum limtype { SOFT = 0x1, HARD = 0x2 };
11783
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011784static void
11785printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011786 const struct limits *l)
11787{
11788 rlim_t val;
11789
11790 val = limit->rlim_max;
11791 if (how & SOFT)
11792 val = limit->rlim_cur;
11793
11794 if (val == RLIM_INFINITY)
11795 out1fmt("unlimited\n");
11796 else {
11797 val /= l->factor;
11798 out1fmt("%lld\n", (long long) val);
11799 }
11800}
11801
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011802static int
Eric Andersenc470f442003-07-28 09:56:35 +000011803ulimitcmd(int argc, char **argv)
11804{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011805 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011806 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011807 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011808 const struct limits *l;
11809 int set, all = 0;
11810 int optc, what;
11811 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011812
11813 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011814 while ((optc = nextopt("HSa"
11815#ifdef RLIMIT_CPU
11816 "t"
11817#endif
11818#ifdef RLIMIT_FSIZE
11819 "f"
11820#endif
11821#ifdef RLIMIT_DATA
11822 "d"
11823#endif
11824#ifdef RLIMIT_STACK
11825 "s"
11826#endif
11827#ifdef RLIMIT_CORE
11828 "c"
11829#endif
11830#ifdef RLIMIT_RSS
11831 "m"
11832#endif
11833#ifdef RLIMIT_MEMLOCK
11834 "l"
11835#endif
11836#ifdef RLIMIT_NPROC
11837 "p"
11838#endif
11839#ifdef RLIMIT_NOFILE
11840 "n"
11841#endif
11842#ifdef RLIMIT_AS
11843 "v"
11844#endif
11845#ifdef RLIMIT_LOCKS
11846 "w"
11847#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011848 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011849 switch (optc) {
11850 case 'H':
11851 how = HARD;
11852 break;
11853 case 'S':
11854 how = SOFT;
11855 break;
11856 case 'a':
11857 all = 1;
11858 break;
11859 default:
11860 what = optc;
11861 }
11862
Glenn L McGrath76620622004-01-13 10:19:37 +000011863 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011864 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011865
11866 set = *argptr ? 1 : 0;
11867 if (set) {
11868 char *p = *argptr;
11869
11870 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011871 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011872 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011873 val = RLIM_INFINITY;
11874 else {
11875 val = (rlim_t) 0;
11876
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011877 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011878 val = (val * 10) + (long)(c - '0');
11879 if (val < (rlim_t) 0)
11880 break;
11881 }
11882 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011883 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011884 val *= l->factor;
11885 }
11886 }
11887 if (all) {
11888 for (l = limits; l->name; l++) {
11889 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011890 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011891 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011892 }
11893 return 0;
11894 }
11895
11896 getrlimit(l->cmd, &limit);
11897 if (set) {
11898 if (how & HARD)
11899 limit.rlim_max = val;
11900 if (how & SOFT)
11901 limit.rlim_cur = val;
11902 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011903 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011904 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011905 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011906 }
11907 return 0;
11908}
11909
Eric Andersen90898442003-08-06 11:20:52 +000011910
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011911/* ============ Math support */
11912
Denis Vlasenko131ae172007-02-18 13:00:19 +000011913#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011914
11915/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11916
11917 Permission is hereby granted, free of charge, to any person obtaining
11918 a copy of this software and associated documentation files (the
11919 "Software"), to deal in the Software without restriction, including
11920 without limitation the rights to use, copy, modify, merge, publish,
11921 distribute, sublicense, and/or sell copies of the Software, and to
11922 permit persons to whom the Software is furnished to do so, subject to
11923 the following conditions:
11924
11925 The above copyright notice and this permission notice shall be
11926 included in all copies or substantial portions of the Software.
11927
11928 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11929 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11930 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11931 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11932 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11933 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11934 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11935*/
11936
11937/* This is my infix parser/evaluator. It is optimized for size, intended
11938 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011939 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011940 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011941 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011942 * be that which POSIX specifies for shells. */
11943
11944/* The code uses a simple two-stack algorithm. See
11945 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011946 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011947 * this is based (this code differs in that it applies operators immediately
11948 * to the stack instead of adding them to a queue to end up with an
11949 * expression). */
11950
11951/* To use the routine, call it with an expression string and error return
11952 * pointer */
11953
11954/*
11955 * Aug 24, 2001 Manuel Novoa III
11956 *
11957 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11958 *
11959 * 1) In arith_apply():
11960 * a) Cached values of *numptr and &(numptr[-1]).
11961 * b) Removed redundant test for zero denominator.
11962 *
11963 * 2) In arith():
11964 * a) Eliminated redundant code for processing operator tokens by moving
11965 * to a table-based implementation. Also folded handling of parens
11966 * into the table.
11967 * b) Combined all 3 loops which called arith_apply to reduce generated
11968 * code size at the cost of speed.
11969 *
11970 * 3) The following expressions were treated as valid by the original code:
11971 * 1() , 0! , 1 ( *3 ) .
11972 * These bugs have been fixed by internally enclosing the expression in
11973 * parens and then checking that all binary ops and right parens are
11974 * preceded by a valid expression (NUM_TOKEN).
11975 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011976 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011977 * ctype's isspace() if it is used by another busybox applet or if additional
11978 * whitespace chars should be considered. Look below the "#include"s for a
11979 * precompiler test.
11980 */
11981
11982/*
11983 * Aug 26, 2001 Manuel Novoa III
11984 *
11985 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11986 *
11987 * Merge in Aaron's comments previously posted to the busybox list,
11988 * modified slightly to take account of my changes to the code.
11989 *
11990 */
11991
11992/*
11993 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
11994 *
11995 * - allow access to variable,
11996 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
11997 * - realize assign syntax (VAR=expr, +=, *= etc)
11998 * - realize exponentiation (** operator)
11999 * - realize comma separated - expr, expr
12000 * - realise ++expr --expr expr++ expr--
12001 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012002 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012003 * - was restored loses XOR operator
12004 * - remove one goto label, added three ;-)
12005 * - protect $((num num)) as true zero expr (Manuel`s error)
12006 * - always use special isspace(), see comment from bash ;-)
12007 */
12008
Eric Andersen90898442003-08-06 11:20:52 +000012009#define arith_isspace(arithval) \
12010 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12011
Eric Andersen90898442003-08-06 11:20:52 +000012012typedef unsigned char operator;
12013
12014/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012015 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012016 * precedence. The ID portion is so that multiple operators can have the
12017 * same precedence, ensuring that the leftmost one is evaluated first.
12018 * Consider * and /. */
12019
12020#define tok_decl(prec,id) (((id)<<5)|(prec))
12021#define PREC(op) ((op) & 0x1F)
12022
12023#define TOK_LPAREN tok_decl(0,0)
12024
12025#define TOK_COMMA tok_decl(1,0)
12026
12027#define TOK_ASSIGN tok_decl(2,0)
12028#define TOK_AND_ASSIGN tok_decl(2,1)
12029#define TOK_OR_ASSIGN tok_decl(2,2)
12030#define TOK_XOR_ASSIGN tok_decl(2,3)
12031#define TOK_PLUS_ASSIGN tok_decl(2,4)
12032#define TOK_MINUS_ASSIGN tok_decl(2,5)
12033#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12034#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12035
12036#define TOK_MUL_ASSIGN tok_decl(3,0)
12037#define TOK_DIV_ASSIGN tok_decl(3,1)
12038#define TOK_REM_ASSIGN tok_decl(3,2)
12039
12040/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012041#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012042
12043/* conditional is right associativity too */
12044#define TOK_CONDITIONAL tok_decl(4,0)
12045#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12046
12047#define TOK_OR tok_decl(5,0)
12048
12049#define TOK_AND tok_decl(6,0)
12050
12051#define TOK_BOR tok_decl(7,0)
12052
12053#define TOK_BXOR tok_decl(8,0)
12054
12055#define TOK_BAND tok_decl(9,0)
12056
12057#define TOK_EQ tok_decl(10,0)
12058#define TOK_NE tok_decl(10,1)
12059
12060#define TOK_LT tok_decl(11,0)
12061#define TOK_GT tok_decl(11,1)
12062#define TOK_GE tok_decl(11,2)
12063#define TOK_LE tok_decl(11,3)
12064
12065#define TOK_LSHIFT tok_decl(12,0)
12066#define TOK_RSHIFT tok_decl(12,1)
12067
12068#define TOK_ADD tok_decl(13,0)
12069#define TOK_SUB tok_decl(13,1)
12070
12071#define TOK_MUL tok_decl(14,0)
12072#define TOK_DIV tok_decl(14,1)
12073#define TOK_REM tok_decl(14,2)
12074
12075/* exponent is right associativity */
12076#define TOK_EXPONENT tok_decl(15,1)
12077
12078/* For now unary operators. */
12079#define UNARYPREC 16
12080#define TOK_BNOT tok_decl(UNARYPREC,0)
12081#define TOK_NOT tok_decl(UNARYPREC,1)
12082
12083#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12084#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12085
12086#define PREC_PRE (UNARYPREC+2)
12087
12088#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12089#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12090
12091#define PREC_POST (UNARYPREC+3)
12092
12093#define TOK_POST_INC tok_decl(PREC_POST, 0)
12094#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12095
12096#define SPEC_PREC (UNARYPREC+4)
12097
12098#define TOK_NUM tok_decl(SPEC_PREC, 0)
12099#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12100
12101#define NUMPTR (*numstackptr)
12102
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012103static int
12104tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012105{
12106 operator prec = PREC(op);
12107
12108 convert_prec_is_assing(prec);
12109 return (prec == PREC(TOK_ASSIGN) ||
12110 prec == PREC_PRE || prec == PREC_POST);
12111}
12112
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012113static int
12114is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012115{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012116 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12117 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012118}
12119
Eric Andersen90898442003-08-06 11:20:52 +000012120typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012121 arith_t val;
12122 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012123 char contidional_second_val_initialized;
12124 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012125 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012126} v_n_t;
12127
Eric Andersen90898442003-08-06 11:20:52 +000012128typedef struct CHK_VAR_RECURSIVE_LOOPED {
12129 const char *var;
12130 struct CHK_VAR_RECURSIVE_LOOPED *next;
12131} chk_var_recursive_looped_t;
12132
12133static chk_var_recursive_looped_t *prev_chk_var_recursive;
12134
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012135static int
12136arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012137{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012138 if (t->var) {
12139 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012140
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012141 if (p) {
12142 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012143
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012144 /* recursive try as expression */
12145 chk_var_recursive_looped_t *cur;
12146 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012147
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012148 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12149 if (strcmp(cur->var, t->var) == 0) {
12150 /* expression recursion loop detected */
12151 return -5;
12152 }
12153 }
12154 /* save current lookuped var name */
12155 cur = prev_chk_var_recursive;
12156 cur_save.var = t->var;
12157 cur_save.next = cur;
12158 prev_chk_var_recursive = &cur_save;
12159
12160 t->val = arith (p, &errcode);
12161 /* restore previous ptr after recursiving */
12162 prev_chk_var_recursive = cur;
12163 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012164 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012165 /* allow undefined var as 0 */
12166 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012167 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012168 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012169}
12170
12171/* "applying" a token means performing it on the top elements on the integer
12172 * stack. For a unary operator it will only change the top element, but a
12173 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012174static int
12175arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012176{
Eric Andersen90898442003-08-06 11:20:52 +000012177 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012178 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012179 int ret_arith_lookup_val;
12180
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012181 /* There is no operator that can work without arguments */
12182 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012183 numptr_m1 = NUMPTR - 1;
12184
12185 /* check operand is var with noninteger value */
12186 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012187 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012188 return ret_arith_lookup_val;
12189
12190 rez = numptr_m1->val;
12191 if (op == TOK_UMINUS)
12192 rez *= -1;
12193 else if (op == TOK_NOT)
12194 rez = !rez;
12195 else if (op == TOK_BNOT)
12196 rez = ~rez;
12197 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12198 rez++;
12199 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12200 rez--;
12201 else if (op != TOK_UPLUS) {
12202 /* Binary operators */
12203
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012204 /* check and binary operators need two arguments */
12205 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012206
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012207 /* ... and they pop one */
12208 --NUMPTR;
12209 numptr_val = rez;
12210 if (op == TOK_CONDITIONAL) {
12211 if (! numptr_m1->contidional_second_val_initialized) {
12212 /* protect $((expr1 ? expr2)) without ": expr" */
12213 goto err;
12214 }
12215 rez = numptr_m1->contidional_second_val;
12216 } else if (numptr_m1->contidional_second_val_initialized) {
12217 /* protect $((expr1 : expr2)) without "expr ? " */
12218 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012219 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012220 numptr_m1 = NUMPTR - 1;
12221 if (op != TOK_ASSIGN) {
12222 /* check operand is var with noninteger value for not '=' */
12223 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12224 if (ret_arith_lookup_val)
12225 return ret_arith_lookup_val;
12226 }
12227 if (op == TOK_CONDITIONAL) {
12228 numptr_m1->contidional_second_val = rez;
12229 }
12230 rez = numptr_m1->val;
12231 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012232 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012233 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012234 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012235 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012236 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012237 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012238 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012239 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012240 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012241 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012242 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012243 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012244 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012245 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012246 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012247 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012248 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012249 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012250 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012251 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012252 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012253 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012254 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012255 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012256 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012257 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012258 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012259 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012260 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012261 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012262 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012263 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012264 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012265 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012266 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012267 /* protect $((expr : expr)) without "expr ? " */
12268 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012269 }
12270 numptr_m1->contidional_second_val_initialized = op;
12271 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012272 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012273 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012274 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012275 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012276 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012277 return -3; /* exponent less than 0 */
12278 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012279 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012280
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012281 if (numptr_val)
12282 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012283 c *= rez;
12284 rez = c;
12285 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012286 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012287 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012288 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012289 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012290 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012291 rez %= numptr_val;
12292 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012294 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012295
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012296 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012297 /* Hmm, 1=2 ? */
12298 goto err;
12299 }
12300 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012301#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012302 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012303#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012304 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012305#endif
Eric Andersen90898442003-08-06 11:20:52 +000012306 setvar(numptr_m1->var, buf, 0);
12307 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012308 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012309 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012311 rez++;
12312 }
12313 numptr_m1->val = rez;
12314 /* protect geting var value, is number now */
12315 numptr_m1->var = NULL;
12316 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012317 err:
12318 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012319}
12320
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012321/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012322static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012323 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12324 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12325 '<','<', 0, TOK_LSHIFT,
12326 '>','>', 0, TOK_RSHIFT,
12327 '|','|', 0, TOK_OR,
12328 '&','&', 0, TOK_AND,
12329 '!','=', 0, TOK_NE,
12330 '<','=', 0, TOK_LE,
12331 '>','=', 0, TOK_GE,
12332 '=','=', 0, TOK_EQ,
12333 '|','=', 0, TOK_OR_ASSIGN,
12334 '&','=', 0, TOK_AND_ASSIGN,
12335 '*','=', 0, TOK_MUL_ASSIGN,
12336 '/','=', 0, TOK_DIV_ASSIGN,
12337 '%','=', 0, TOK_REM_ASSIGN,
12338 '+','=', 0, TOK_PLUS_ASSIGN,
12339 '-','=', 0, TOK_MINUS_ASSIGN,
12340 '-','-', 0, TOK_POST_DEC,
12341 '^','=', 0, TOK_XOR_ASSIGN,
12342 '+','+', 0, TOK_POST_INC,
12343 '*','*', 0, TOK_EXPONENT,
12344 '!', 0, TOK_NOT,
12345 '<', 0, TOK_LT,
12346 '>', 0, TOK_GT,
12347 '=', 0, TOK_ASSIGN,
12348 '|', 0, TOK_BOR,
12349 '&', 0, TOK_BAND,
12350 '*', 0, TOK_MUL,
12351 '/', 0, TOK_DIV,
12352 '%', 0, TOK_REM,
12353 '+', 0, TOK_ADD,
12354 '-', 0, TOK_SUB,
12355 '^', 0, TOK_BXOR,
12356 /* uniq */
12357 '~', 0, TOK_BNOT,
12358 ',', 0, TOK_COMMA,
12359 '?', 0, TOK_CONDITIONAL,
12360 ':', 0, TOK_CONDITIONAL_SEP,
12361 ')', 0, TOK_RPAREN,
12362 '(', 0, TOK_LPAREN,
12363 0
12364};
12365/* ptr to ")" */
12366#define endexpression &op_tokens[sizeof(op_tokens)-7]
12367
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012368static arith_t
12369arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012370{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012371 char arithval; /* Current character under analysis */
12372 operator lasttok, op;
12373 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012374
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012375 const char *p = endexpression;
12376 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012377
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012378 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012379
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 /* Stack of integers */
12381 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12382 * in any given correct or incorrect expression is left as an exercise to
12383 * the reader. */
12384 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12385 *numstackptr = numstack;
12386 /* Stack of operator tokens */
12387 operator *stack = alloca((datasizes) * sizeof(operator)),
12388 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012389
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012390 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12391 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012392
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012393 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012394 arithval = *expr;
12395 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 if (p == endexpression) {
12397 /* Null expression. */
12398 return 0;
12399 }
12400
12401 /* This is only reached after all tokens have been extracted from the
12402 * input stream. If there are still tokens on the operator stack, they
12403 * are to be applied in order. At the end, there should be a final
12404 * result on the integer stack */
12405
12406 if (expr != endexpression + 1) {
12407 /* If we haven't done so already, */
12408 /* append a closing right paren */
12409 expr = endexpression;
12410 /* and let the loop process it. */
12411 continue;
12412 }
12413 /* At this point, we're done with the expression. */
12414 if (numstackptr != numstack+1) {
12415 /* ... but if there isn't, it's bad */
12416 err:
12417 return (*perrcode = -1);
12418 }
12419 if (numstack->var) {
12420 /* expression is $((var)) only, lookup now */
12421 errcode = arith_lookup_val(numstack);
12422 }
12423 ret:
12424 *perrcode = errcode;
12425 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012426 }
12427
Eric Andersen90898442003-08-06 11:20:52 +000012428 /* Continue processing the expression. */
12429 if (arith_isspace(arithval)) {
12430 /* Skip whitespace */
12431 goto prologue;
12432 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012433 p = endofname(expr);
12434 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012435 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012436
12437 numstackptr->var = alloca(var_name_size);
12438 safe_strncpy(numstackptr->var, expr, var_name_size);
12439 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012440 num:
Eric Andersen90898442003-08-06 11:20:52 +000012441 numstackptr->contidional_second_val_initialized = 0;
12442 numstackptr++;
12443 lasttok = TOK_NUM;
12444 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012445 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012446 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012447 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012448#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012449 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012450#else
12451 numstackptr->val = strtol(expr, (char **) &expr, 0);
12452#endif
Eric Andersen90898442003-08-06 11:20:52 +000012453 goto num;
12454 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012456 const char *o;
12457
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012458 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012459 /* strange operator not found */
12460 goto err;
12461 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012462 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012463 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012464 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012465 /* found */
12466 expr = o - 1;
12467 break;
12468 }
12469 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012470 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012471 p++;
12472 /* skip zero delim */
12473 p++;
12474 }
12475 op = p[1];
12476
12477 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012478 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12479 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012480
12481 /* Plus and minus are binary (not unary) _only_ if the last
12482 * token was as number, or a right paren (which pretends to be
12483 * a number, since it evaluates to one). Think about it.
12484 * It makes sense. */
12485 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012486 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012487 case TOK_ADD:
12488 op = TOK_UPLUS;
12489 break;
12490 case TOK_SUB:
12491 op = TOK_UMINUS;
12492 break;
12493 case TOK_POST_INC:
12494 op = TOK_PRE_INC;
12495 break;
12496 case TOK_POST_DEC:
12497 op = TOK_PRE_DEC;
12498 break;
Eric Andersen90898442003-08-06 11:20:52 +000012499 }
12500 }
12501 /* We don't want a unary operator to cause recursive descent on the
12502 * stack, because there can be many in a row and it could cause an
12503 * operator to be evaluated before its argument is pushed onto the
12504 * integer stack. */
12505 /* But for binary operators, "apply" everything on the operator
12506 * stack until we find an operator with a lesser priority than the
12507 * one we have just extracted. */
12508 /* Left paren is given the lowest priority so it will never be
12509 * "applied" in this way.
12510 * if associativity is right and priority eq, applied also skip
12511 */
12512 prec = PREC(op);
12513 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12514 /* not left paren or unary */
12515 if (lasttok != TOK_NUM) {
12516 /* binary op must be preceded by a num */
12517 goto err;
12518 }
12519 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012520 if (op == TOK_RPAREN) {
12521 /* The algorithm employed here is simple: while we don't
12522 * hit an open paren nor the bottom of the stack, pop
12523 * tokens and apply them */
12524 if (stackptr[-1] == TOK_LPAREN) {
12525 --stackptr;
12526 /* Any operator directly after a */
12527 lasttok = TOK_NUM;
12528 /* close paren should consider itself binary */
12529 goto prologue;
12530 }
12531 } else {
12532 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012533
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012534 convert_prec_is_assing(prec);
12535 convert_prec_is_assing(prev_prec);
12536 if (prev_prec < prec)
12537 break;
12538 /* check right assoc */
12539 if (prev_prec == prec && is_right_associativity(prec))
12540 break;
12541 }
12542 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12543 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012544 }
12545 if (op == TOK_RPAREN) {
12546 goto err;
12547 }
12548 }
12549
12550 /* Push this operator to the stack and remember it. */
12551 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012552 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012553 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012554 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012555}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012556#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012557
12558
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012559/* ============ main() and helpers */
12560
12561/*
12562 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012563 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012564static void exitshell(void) ATTRIBUTE_NORETURN;
12565static void
12566exitshell(void)
12567{
12568 struct jmploc loc;
12569 char *p;
12570 int status;
12571
12572 status = exitstatus;
12573 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12574 if (setjmp(loc.loc)) {
12575 if (exception == EXEXIT)
12576/* dash bug: it just does _exit(exitstatus) here
12577 * but we have to do setjobctl(0) first!
12578 * (bug is still not fixed in dash-0.5.3 - if you run dash
12579 * under Midnight Commander, on exit from dash MC is backgrounded) */
12580 status = exitstatus;
12581 goto out;
12582 }
12583 exception_handler = &loc;
12584 p = trap[0];
12585 if (p) {
12586 trap[0] = NULL;
12587 evalstring(p, 0);
12588 }
12589 flush_stdout_stderr();
12590 out:
12591 setjobctl(0);
12592 _exit(status);
12593 /* NOTREACHED */
12594}
12595
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012596static void
12597init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012598{
12599 /* from input.c: */
12600 basepf.nextc = basepf.buf = basebuf;
12601
12602 /* from trap.c: */
12603 signal(SIGCHLD, SIG_DFL);
12604
12605 /* from var.c: */
12606 {
12607 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012608 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012609 const char *p;
12610 struct stat st1, st2;
12611
12612 initvar();
12613 for (envp = environ; envp && *envp; envp++) {
12614 if (strchr(*envp, '=')) {
12615 setvareq(*envp, VEXPORT|VTEXTFIXED);
12616 }
12617 }
12618
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012619 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012620 setvar("PPID", ppid, 0);
12621
12622 p = lookupvar("PWD");
12623 if (p)
12624 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12625 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12626 p = '\0';
12627 setpwd(p, 0);
12628 }
12629}
12630
12631/*
12632 * Process the shell command line arguments.
12633 */
12634static void
12635procargs(int argc, char **argv)
12636{
12637 int i;
12638 const char *xminusc;
12639 char **xargv;
12640
12641 xargv = argv;
12642 arg0 = xargv[0];
12643 if (argc > 0)
12644 xargv++;
12645 for (i = 0; i < NOPTS; i++)
12646 optlist[i] = 2;
12647 argptr = xargv;
12648 options(1);
12649 xargv = argptr;
12650 xminusc = minusc;
12651 if (*xargv == NULL) {
12652 if (xminusc)
12653 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12654 sflag = 1;
12655 }
12656 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12657 iflag = 1;
12658 if (mflag == 2)
12659 mflag = iflag;
12660 for (i = 0; i < NOPTS; i++)
12661 if (optlist[i] == 2)
12662 optlist[i] = 0;
12663#if DEBUG == 2
12664 debug = 1;
12665#endif
12666 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12667 if (xminusc) {
12668 minusc = *xargv++;
12669 if (*xargv)
12670 goto setarg0;
12671 } else if (!sflag) {
12672 setinputfile(*xargv, 0);
12673 setarg0:
12674 arg0 = *xargv++;
12675 commandname = arg0;
12676 }
12677
12678 shellparam.p = xargv;
12679#if ENABLE_ASH_GETOPTS
12680 shellparam.optind = 1;
12681 shellparam.optoff = -1;
12682#endif
12683 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12684 while (*xargv) {
12685 shellparam.nparam++;
12686 xargv++;
12687 }
12688 optschanged();
12689}
12690
12691/*
12692 * Read /etc/profile or .profile.
12693 */
12694static void
12695read_profile(const char *name)
12696{
12697 int skip;
12698
12699 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12700 return;
12701 skip = cmdloop(0);
12702 popfile();
12703 if (skip)
12704 exitshell();
12705}
12706
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012707/*
12708 * This routine is called when an error or an interrupt occurs in an
12709 * interactive shell and control is returned to the main command loop.
12710 */
12711static void
12712reset(void)
12713{
12714 /* from eval.c: */
12715 evalskip = 0;
12716 loopnest = 0;
12717 /* from input.c: */
12718 parselleft = parsenleft = 0; /* clear input buffer */
12719 popallfiles();
12720 /* from parser.c: */
12721 tokpushback = 0;
12722 checkkwd = 0;
12723 /* from redir.c: */
12724 clearredir(0);
12725}
12726
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012727#if PROFILE
12728static short profile_buf[16384];
12729extern int etext();
12730#endif
12731
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012732/*
12733 * Main routine. We initialize things, parse the arguments, execute
12734 * profiles if we're a login shell, and then call cmdloop to execute
12735 * commands. The setjmp call sets up the location to jump to when an
12736 * exception occurs. When an exception occurs the variable "state"
12737 * is used to figure out how far we had gotten.
12738 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012739int ash_main(int argc, char **argv);
12740int ash_main(int argc, char **argv)
12741{
12742 char *shinit;
12743 volatile int state;
12744 struct jmploc jmploc;
12745 struct stackmark smark;
12746
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012747#if PROFILE
12748 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12749#endif
12750
12751#if ENABLE_FEATURE_EDITING
12752 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12753#endif
12754 state = 0;
12755 if (setjmp(jmploc.loc)) {
12756 int e;
12757 int s;
12758
12759 reset();
12760
12761 e = exception;
12762 if (e == EXERROR)
12763 exitstatus = 2;
12764 s = state;
12765 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12766 exitshell();
12767
12768 if (e == EXINT) {
12769 outcslow('\n', stderr);
12770 }
12771 popstackmark(&smark);
12772 FORCE_INT_ON; /* enable interrupts */
12773 if (s == 1)
12774 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012775 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012776 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012777 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012778 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012779 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012780 }
12781 exception_handler = &jmploc;
12782#if DEBUG
12783 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012784 trace_puts("Shell args: ");
12785 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012786#endif
12787 rootpid = getpid();
12788
12789#if ENABLE_ASH_RANDOM_SUPPORT
12790 rseed = rootpid + time(NULL);
12791#endif
12792 init();
12793 setstackmark(&smark);
12794 procargs(argc, argv);
12795#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12796 if (iflag) {
12797 const char *hp = lookupvar("HISTFILE");
12798
12799 if (hp == NULL) {
12800 hp = lookupvar("HOME");
12801 if (hp != NULL) {
12802 char *defhp = concat_path_file(hp, ".ash_history");
12803 setvar("HISTFILE", defhp, 0);
12804 free(defhp);
12805 }
12806 }
12807 }
12808#endif
12809 if (argv[0] && argv[0][0] == '-')
12810 isloginsh = 1;
12811 if (isloginsh) {
12812 state = 1;
12813 read_profile("/etc/profile");
12814 state1:
12815 state = 2;
12816 read_profile(".profile");
12817 }
12818 state2:
12819 state = 3;
12820 if (
12821#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012822 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012823#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012824 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012825 ) {
12826 shinit = lookupvar("ENV");
12827 if (shinit != NULL && *shinit != '\0') {
12828 read_profile(shinit);
12829 }
12830 }
12831 state3:
12832 state = 4;
12833 if (minusc)
12834 evalstring(minusc, 0);
12835
12836 if (sflag || minusc == NULL) {
12837#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12838 if ( iflag ) {
12839 const char *hp = lookupvar("HISTFILE");
12840
12841 if (hp != NULL)
12842 line_input_state->hist_file = hp;
12843 }
12844#endif
12845 state4: /* XXX ??? - why isn't this before the "if" statement */
12846 cmdloop(1);
12847 }
12848#if PROFILE
12849 monitor(0);
12850#endif
12851#ifdef GPROF
12852 {
12853 extern void _mcleanup(void);
12854 _mcleanup();
12855 }
12856#endif
12857 exitshell();
12858 /* NOTREACHED */
12859}
12860
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012861#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012862const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012863int main(int argc, char **argv)
12864{
12865 return ash_main(argc, argv);
12866}
12867#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012868
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012869
Eric Andersendf82f612001-06-28 07:46:40 +000012870/*-
12871 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012872 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012873 *
12874 * This code is derived from software contributed to Berkeley by
12875 * Kenneth Almquist.
12876 *
12877 * Redistribution and use in source and binary forms, with or without
12878 * modification, are permitted provided that the following conditions
12879 * are met:
12880 * 1. Redistributions of source code must retain the above copyright
12881 * notice, this list of conditions and the following disclaimer.
12882 * 2. Redistributions in binary form must reproduce the above copyright
12883 * notice, this list of conditions and the following disclaimer in the
12884 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012885 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012886 * may be used to endorse or promote products derived from this software
12887 * without specific prior written permission.
12888 *
12889 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12890 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12891 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12892 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12893 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12894 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12895 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12896 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12897 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12898 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12899 * SUCH DAMAGE.
12900 */