blob: 9d8b83c1844b79c34f09d7a8c0ebd18526529299 [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 */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002497#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002498
Denis Vlasenko131ae172007-02-18 13:00:19 +00002499#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002500#define USE_SIT_FUNCTION
2501#endif
2502
Denis Vlasenko131ae172007-02-18 13:00:19 +00002503#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002504static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002505#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002506 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002507#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002508 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2509 { CNL, CNL, CNL, CNL }, /* 2, \n */
2510 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2511 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2512 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2513 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2514 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2515 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2516 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2517 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2518 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002519#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002520 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2521 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2522 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002523#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002524};
Eric Andersenc470f442003-07-28 09:56:35 +00002525#else
2526static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002527#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002528 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002529#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002530 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2531 { CNL, CNL, CNL }, /* 2, \n */
2532 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2533 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2534 { CVAR, CVAR, CWORD }, /* 5, $ */
2535 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2536 { CSPCL, CWORD, CWORD }, /* 7, ( */
2537 { CSPCL, CWORD, CWORD }, /* 8, ) */
2538 { CBACK, CBACK, CCTL }, /* 9, \ */
2539 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2540 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002541#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002542 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2543 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2544 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002545#endif
2546};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002547#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002548
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002549#ifdef USE_SIT_FUNCTION
2550
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002551static int
2552SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002553{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002554 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002555#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002556 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002557 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2558 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2559 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2560 11, 3 /* "}~" */
2561 };
2562#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002563 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002564 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2565 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2566 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2567 10, 2 /* "}~" */
2568 };
2569#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002570 const char *s;
2571 int indx;
2572
Eric Andersenc470f442003-07-28 09:56:35 +00002573 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002574 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002575#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002576 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002577 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002578 else
2579#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002580#define U_C(c) ((unsigned char)(c))
2581
2582 if ((unsigned char)c >= (unsigned char)(CTLESC)
2583 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2584 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002585 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002586 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002587 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002588 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002589 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002590 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002591 }
2592 return S_I_T[indx][syntax];
2593}
2594
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002595#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596
Denis Vlasenko131ae172007-02-18 13:00:19 +00002597#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002598#define CSPCL_CIGN_CIGN_CIGN 0
2599#define CSPCL_CWORD_CWORD_CWORD 1
2600#define CNL_CNL_CNL_CNL 2
2601#define CWORD_CCTL_CCTL_CWORD 3
2602#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2603#define CVAR_CVAR_CWORD_CVAR 5
2604#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2605#define CSPCL_CWORD_CWORD_CLP 7
2606#define CSPCL_CWORD_CWORD_CRP 8
2607#define CBACK_CBACK_CCTL_CBACK 9
2608#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2609#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2610#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2611#define CWORD_CWORD_CWORD_CWORD 13
2612#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002613#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002614#define CSPCL_CWORD_CWORD_CWORD 0
2615#define CNL_CNL_CNL_CNL 1
2616#define CWORD_CCTL_CCTL_CWORD 2
2617#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2618#define CVAR_CVAR_CWORD_CVAR 4
2619#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2620#define CSPCL_CWORD_CWORD_CLP 6
2621#define CSPCL_CWORD_CWORD_CRP 7
2622#define CBACK_CBACK_CCTL_CBACK 8
2623#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2624#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2625#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2626#define CWORD_CWORD_CWORD_CWORD 12
2627#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002628#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002629
2630static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002631 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002632 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002633#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002634 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2635#endif
2636 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2637 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2638 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2639 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2640 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2641 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2642 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2643 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2644 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002645 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2646 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2647 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2648 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2649 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2774 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2775 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2797 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002798 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002799 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2800 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2801 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2802 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002803 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002804 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2805 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2806 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2807 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2808 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2809 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2810 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2811 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2812 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2814 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2815 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2816 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2821 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2822 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2823 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2824 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2825 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2826 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2827 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2828 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2829 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2830 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2831 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2832 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2856 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2857 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2858 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2861 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2862 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2889 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2890 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2891 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002892};
2893
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002894#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2895
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002896#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002897
Eric Andersen2870d962001-07-02 17:27:21 +00002898
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002899/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002900
Denis Vlasenko131ae172007-02-18 13:00:19 +00002901#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002902
2903#define ALIASINUSE 1
2904#define ALIASDEAD 2
2905
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002906#define ATABSIZE 39
2907
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002908struct alias {
2909 struct alias *next;
2910 char *name;
2911 char *val;
2912 int flag;
2913};
2914
Eric Andersen2870d962001-07-02 17:27:21 +00002915static struct alias *atab[ATABSIZE];
2916
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002917static struct alias **
2918__lookupalias(const char *name) {
2919 unsigned int hashval;
2920 struct alias **app;
2921 const char *p;
2922 unsigned int ch;
2923
2924 p = name;
2925
2926 ch = (unsigned char)*p;
2927 hashval = ch << 4;
2928 while (ch) {
2929 hashval += ch;
2930 ch = (unsigned char)*++p;
2931 }
2932 app = &atab[hashval % ATABSIZE];
2933
2934 for (; *app; app = &(*app)->next) {
2935 if (strcmp(name, (*app)->name) == 0) {
2936 break;
2937 }
2938 }
2939
2940 return app;
2941}
2942
2943static struct alias *
2944lookupalias(const char *name, int check)
2945{
2946 struct alias *ap = *__lookupalias(name);
2947
2948 if (check && ap && (ap->flag & ALIASINUSE))
2949 return NULL;
2950 return ap;
2951}
2952
2953static struct alias *
2954freealias(struct alias *ap)
2955{
2956 struct alias *next;
2957
2958 if (ap->flag & ALIASINUSE) {
2959 ap->flag |= ALIASDEAD;
2960 return ap;
2961 }
2962
2963 next = ap->next;
2964 free(ap->name);
2965 free(ap->val);
2966 free(ap);
2967 return next;
2968}
Eric Andersencb57d552001-06-28 07:25:16 +00002969
Eric Andersenc470f442003-07-28 09:56:35 +00002970static void
2971setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002972{
2973 struct alias *ap, **app;
2974
2975 app = __lookupalias(name);
2976 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002977 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002978 if (ap) {
2979 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002980 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002981 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002982 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002983 ap->flag &= ~ALIASDEAD;
2984 } else {
2985 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002986 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002987 ap->name = ckstrdup(name);
2988 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002989 ap->flag = 0;
2990 ap->next = 0;
2991 *app = ap;
2992 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002993 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002994}
2995
Eric Andersenc470f442003-07-28 09:56:35 +00002996static int
2997unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00002998{
Eric Andersencb57d552001-06-28 07:25:16 +00002999 struct alias **app;
3000
3001 app = __lookupalias(name);
3002
3003 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003004 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003005 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003006 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003007 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003008 }
3009
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003010 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003011}
3012
Eric Andersenc470f442003-07-28 09:56:35 +00003013static void
3014rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003015{
Eric Andersencb57d552001-06-28 07:25:16 +00003016 struct alias *ap, **app;
3017 int i;
3018
Denis Vlasenkob012b102007-02-19 22:43:01 +00003019 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003020 for (i = 0; i < ATABSIZE; i++) {
3021 app = &atab[i];
3022 for (ap = *app; ap; ap = *app) {
3023 *app = freealias(*app);
3024 if (ap == *app) {
3025 app = &ap->next;
3026 }
3027 }
3028 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003029 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003030}
3031
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003032static void
3033printalias(const struct alias *ap)
3034{
3035 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3036}
3037
Eric Andersencb57d552001-06-28 07:25:16 +00003038/*
3039 * TODO - sort output
3040 */
Eric Andersenc470f442003-07-28 09:56:35 +00003041static int
3042aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003043{
3044 char *n, *v;
3045 int ret = 0;
3046 struct alias *ap;
3047
3048 if (argc == 1) {
3049 int i;
3050
3051 for (i = 0; i < ATABSIZE; i++)
3052 for (ap = atab[i]; ap; ap = ap->next) {
3053 printalias(ap);
3054 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003055 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003056 }
3057 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003058 v = strchr(n+1, '=');
3059 if (v == NULL) { /* n+1: funny ksh stuff */
3060 ap = *__lookupalias(n);
3061 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003062 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003063 ret = 1;
3064 } else
3065 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003066 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003067 *v++ = '\0';
3068 setalias(n, v);
3069 }
3070 }
3071
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003072 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003073}
3074
Eric Andersenc470f442003-07-28 09:56:35 +00003075static int
3076unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003077{
3078 int i;
3079
3080 while ((i = nextopt("a")) != '\0') {
3081 if (i == 'a') {
3082 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003083 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003084 }
3085 }
3086 for (i = 0; *argptr; argptr++) {
3087 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003088 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003089 i = 1;
3090 }
3091 }
3092
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003093 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003094}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003095
Denis Vlasenko131ae172007-02-18 13:00:19 +00003096#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003097
Eric Andersenc470f442003-07-28 09:56:35 +00003098
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003099/* ============ jobs.c */
3100
3101/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3102#define FORK_FG 0
3103#define FORK_BG 1
3104#define FORK_NOJOB 2
3105
3106/* mode flags for showjob(s) */
3107#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3108#define SHOW_PID 0x04 /* include process pid */
3109#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3110
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003111/*
3112 * A job structure contains information about a job. A job is either a
3113 * single process or a set of processes contained in a pipeline. In the
3114 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3115 * array of pids.
3116 */
3117
3118struct procstat {
3119 pid_t pid; /* process id */
3120 int status; /* last process status from wait() */
3121 char *cmd; /* text of command being run */
3122};
3123
3124struct job {
3125 struct procstat ps0; /* status of process */
3126 struct procstat *ps; /* status or processes when more than one */
3127#if JOBS
3128 int stopstatus; /* status of a stopped job */
3129#endif
3130 uint32_t
3131 nprocs: 16, /* number of processes */
3132 state: 8,
3133#define JOBRUNNING 0 /* at least one proc running */
3134#define JOBSTOPPED 1 /* all procs are stopped */
3135#define JOBDONE 2 /* all procs are completed */
3136#if JOBS
3137 sigint: 1, /* job was killed by SIGINT */
3138 jobctl: 1, /* job running under job control */
3139#endif
3140 waited: 1, /* true if this entry has been waited for */
3141 used: 1, /* true if this entry is in used */
3142 changed: 1; /* true if status has changed */
3143 struct job *prev_job; /* previous job */
3144};
3145
3146static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003147static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003148
3149static struct job *makejob(union node *, int);
3150static int forkshell(struct job *, union node *, int);
3151static int waitforjob(struct job *);
3152
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003153#if !JOBS
3154enum { jobctl = 0 };
3155#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003156#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003157static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003158static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003159#endif
3160
3161/*
3162 * Set the signal handler for the specified signal. The routine figures
3163 * out what it should be set to.
3164 */
3165static void
3166setsignal(int signo)
3167{
3168 int action;
3169 char *t, tsig;
3170 struct sigaction act;
3171
3172 t = trap[signo];
3173 if (t == NULL)
3174 action = S_DFL;
3175 else if (*t != '\0')
3176 action = S_CATCH;
3177 else
3178 action = S_IGN;
3179 if (rootshell && action == S_DFL) {
3180 switch (signo) {
3181 case SIGINT:
3182 if (iflag || minusc || sflag == 0)
3183 action = S_CATCH;
3184 break;
3185 case SIGQUIT:
3186#if DEBUG
3187 if (debug)
3188 break;
3189#endif
3190 /* FALLTHROUGH */
3191 case SIGTERM:
3192 if (iflag)
3193 action = S_IGN;
3194 break;
3195#if JOBS
3196 case SIGTSTP:
3197 case SIGTTOU:
3198 if (mflag)
3199 action = S_IGN;
3200 break;
3201#endif
3202 }
3203 }
3204
3205 t = &sigmode[signo - 1];
3206 tsig = *t;
3207 if (tsig == 0) {
3208 /*
3209 * current setting unknown
3210 */
3211 if (sigaction(signo, 0, &act) == -1) {
3212 /*
3213 * Pretend it worked; maybe we should give a warning
3214 * here, but other shells don't. We don't alter
3215 * sigmode, so that we retry every time.
3216 */
3217 return;
3218 }
3219 if (act.sa_handler == SIG_IGN) {
3220 if (mflag
3221 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3222 ) {
3223 tsig = S_IGN; /* don't hard ignore these */
3224 } else
3225 tsig = S_HARD_IGN;
3226 } else {
3227 tsig = S_RESET; /* force to be set */
3228 }
3229 }
3230 if (tsig == S_HARD_IGN || tsig == action)
3231 return;
3232 switch (action) {
3233 case S_CATCH:
3234 act.sa_handler = onsig;
3235 break;
3236 case S_IGN:
3237 act.sa_handler = SIG_IGN;
3238 break;
3239 default:
3240 act.sa_handler = SIG_DFL;
3241 }
3242 *t = action;
3243 act.sa_flags = 0;
3244 sigfillset(&act.sa_mask);
3245 sigaction(signo, &act, 0);
3246}
3247
3248/* mode flags for set_curjob */
3249#define CUR_DELETE 2
3250#define CUR_RUNNING 1
3251#define CUR_STOPPED 0
3252
3253/* mode flags for dowait */
3254#define DOWAIT_NORMAL 0
3255#define DOWAIT_BLOCK 1
3256
3257#if JOBS
3258/* pgrp of shell on invocation */
3259static int initialpgrp;
3260static int ttyfd = -1;
3261#endif
3262/* array of jobs */
3263static struct job *jobtab;
3264/* size of array */
3265static unsigned njobs;
3266/* current job */
3267static struct job *curjob;
3268/* number of presumed living untracked jobs */
3269static int jobless;
3270
3271static void
3272set_curjob(struct job *jp, unsigned mode)
3273{
3274 struct job *jp1;
3275 struct job **jpp, **curp;
3276
3277 /* first remove from list */
3278 jpp = curp = &curjob;
3279 do {
3280 jp1 = *jpp;
3281 if (jp1 == jp)
3282 break;
3283 jpp = &jp1->prev_job;
3284 } while (1);
3285 *jpp = jp1->prev_job;
3286
3287 /* Then re-insert in correct position */
3288 jpp = curp;
3289 switch (mode) {
3290 default:
3291#if DEBUG
3292 abort();
3293#endif
3294 case CUR_DELETE:
3295 /* job being deleted */
3296 break;
3297 case CUR_RUNNING:
3298 /* newly created job or backgrounded job,
3299 put after all stopped jobs. */
3300 do {
3301 jp1 = *jpp;
3302#if JOBS
3303 if (!jp1 || jp1->state != JOBSTOPPED)
3304#endif
3305 break;
3306 jpp = &jp1->prev_job;
3307 } while (1);
3308 /* FALLTHROUGH */
3309#if JOBS
3310 case CUR_STOPPED:
3311#endif
3312 /* newly stopped job - becomes curjob */
3313 jp->prev_job = *jpp;
3314 *jpp = jp;
3315 break;
3316 }
3317}
3318
3319#if JOBS || DEBUG
3320static int
3321jobno(const struct job *jp)
3322{
3323 return jp - jobtab + 1;
3324}
3325#endif
3326
3327/*
3328 * Convert a job name to a job structure.
3329 */
3330static struct job *
3331getjob(const char *name, int getctl)
3332{
3333 struct job *jp;
3334 struct job *found;
3335 const char *err_msg = "No such job: %s";
3336 unsigned num;
3337 int c;
3338 const char *p;
3339 char *(*match)(const char *, const char *);
3340
3341 jp = curjob;
3342 p = name;
3343 if (!p)
3344 goto currentjob;
3345
3346 if (*p != '%')
3347 goto err;
3348
3349 c = *++p;
3350 if (!c)
3351 goto currentjob;
3352
3353 if (!p[1]) {
3354 if (c == '+' || c == '%') {
3355 currentjob:
3356 err_msg = "No current job";
3357 goto check;
3358 }
3359 if (c == '-') {
3360 if (jp)
3361 jp = jp->prev_job;
3362 err_msg = "No previous job";
3363 check:
3364 if (!jp)
3365 goto err;
3366 goto gotit;
3367 }
3368 }
3369
3370 if (is_number(p)) {
3371 num = atoi(p);
3372 if (num < njobs) {
3373 jp = jobtab + num - 1;
3374 if (jp->used)
3375 goto gotit;
3376 goto err;
3377 }
3378 }
3379
3380 match = prefix;
3381 if (*p == '?') {
3382 match = strstr;
3383 p++;
3384 }
3385
3386 found = 0;
3387 while (1) {
3388 if (!jp)
3389 goto err;
3390 if (match(jp->ps[0].cmd, p)) {
3391 if (found)
3392 goto err;
3393 found = jp;
3394 err_msg = "%s: ambiguous";
3395 }
3396 jp = jp->prev_job;
3397 }
3398
3399 gotit:
3400#if JOBS
3401 err_msg = "job %s not created under job control";
3402 if (getctl && jp->jobctl == 0)
3403 goto err;
3404#endif
3405 return jp;
3406 err:
3407 ash_msg_and_raise_error(err_msg, name);
3408}
3409
3410/*
3411 * Mark a job structure as unused.
3412 */
3413static void
3414freejob(struct job *jp)
3415{
3416 struct procstat *ps;
3417 int i;
3418
3419 INT_OFF;
3420 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3421 if (ps->cmd != nullstr)
3422 free(ps->cmd);
3423 }
3424 if (jp->ps != &jp->ps0)
3425 free(jp->ps);
3426 jp->used = 0;
3427 set_curjob(jp, CUR_DELETE);
3428 INT_ON;
3429}
3430
3431#if JOBS
3432static void
3433xtcsetpgrp(int fd, pid_t pgrp)
3434{
3435 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003436 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437}
3438
3439/*
3440 * Turn job control on and off.
3441 *
3442 * Note: This code assumes that the third arg to ioctl is a character
3443 * pointer, which is true on Berkeley systems but not System V. Since
3444 * System V doesn't have job control yet, this isn't a problem now.
3445 *
3446 * Called with interrupts off.
3447 */
3448static void
3449setjobctl(int on)
3450{
3451 int fd;
3452 int pgrp;
3453
3454 if (on == jobctl || rootshell == 0)
3455 return;
3456 if (on) {
3457 int ofd;
3458 ofd = fd = open(_PATH_TTY, O_RDWR);
3459 if (fd < 0) {
3460 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3461 * That sometimes helps to acquire controlling tty.
3462 * Obviously, a workaround for bugs when someone
3463 * failed to provide a controlling tty to bash! :) */
3464 fd += 3;
3465 while (!isatty(fd) && --fd >= 0)
3466 ;
3467 }
3468 fd = fcntl(fd, F_DUPFD, 10);
3469 close(ofd);
3470 if (fd < 0)
3471 goto out;
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003472 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003473 do { /* while we are in the background */
3474 pgrp = tcgetpgrp(fd);
3475 if (pgrp < 0) {
3476 out:
3477 ash_msg("can't access tty; job control turned off");
3478 mflag = on = 0;
3479 goto close;
3480 }
3481 if (pgrp == getpgrp())
3482 break;
3483 killpg(0, SIGTTIN);
3484 } while (1);
3485 initialpgrp = pgrp;
3486
3487 setsignal(SIGTSTP);
3488 setsignal(SIGTTOU);
3489 setsignal(SIGTTIN);
3490 pgrp = rootpid;
3491 setpgid(0, pgrp);
3492 xtcsetpgrp(fd, pgrp);
3493 } else {
3494 /* turning job control off */
3495 fd = ttyfd;
3496 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003497 /* was xtcsetpgrp, but this can make exiting ash
3498 * with pty already deleted loop forever */
3499 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003500 setpgid(0, pgrp);
3501 setsignal(SIGTSTP);
3502 setsignal(SIGTTOU);
3503 setsignal(SIGTTIN);
3504 close:
3505 close(fd);
3506 fd = -1;
3507 }
3508 ttyfd = fd;
3509 jobctl = on;
3510}
3511
3512static int
3513killcmd(int argc, char **argv)
3514{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003515 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3516 int i = 1;
3517 do {
3518 if (argv[i][0] == '%') {
3519 struct job *jp = getjob(argv[i], 0);
3520 unsigned pid = jp->ps[0].pid;
3521 /* Enough space for ' -NNN<nul>' */
3522 argv[i] = alloca(sizeof(int)*3 + 3);
3523 /* kill_main has matching code to expect
3524 * leading space. Needed to not confuse
3525 * negative pids with "kill -SIGNAL_NO" syntax */
3526 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003528 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003530 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531}
3532
3533static void
3534showpipe(struct job *jp, FILE *out)
3535{
3536 struct procstat *sp;
3537 struct procstat *spend;
3538
3539 spend = jp->ps + jp->nprocs;
3540 for (sp = jp->ps + 1; sp < spend; sp++)
3541 fprintf(out, " | %s", sp->cmd);
3542 outcslow('\n', out);
3543 flush_stdout_stderr();
3544}
3545
3546
3547static int
3548restartjob(struct job *jp, int mode)
3549{
3550 struct procstat *ps;
3551 int i;
3552 int status;
3553 pid_t pgid;
3554
3555 INT_OFF;
3556 if (jp->state == JOBDONE)
3557 goto out;
3558 jp->state = JOBRUNNING;
3559 pgid = jp->ps->pid;
3560 if (mode == FORK_FG)
3561 xtcsetpgrp(ttyfd, pgid);
3562 killpg(pgid, SIGCONT);
3563 ps = jp->ps;
3564 i = jp->nprocs;
3565 do {
3566 if (WIFSTOPPED(ps->status)) {
3567 ps->status = -1;
3568 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003569 ps++;
3570 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003571 out:
3572 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3573 INT_ON;
3574 return status;
3575}
3576
3577static int
3578fg_bgcmd(int argc, char **argv)
3579{
3580 struct job *jp;
3581 FILE *out;
3582 int mode;
3583 int retval;
3584
3585 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3586 nextopt(nullstr);
3587 argv = argptr;
3588 out = stdout;
3589 do {
3590 jp = getjob(*argv, 1);
3591 if (mode == FORK_BG) {
3592 set_curjob(jp, CUR_RUNNING);
3593 fprintf(out, "[%d] ", jobno(jp));
3594 }
3595 outstr(jp->ps->cmd, out);
3596 showpipe(jp, out);
3597 retval = restartjob(jp, mode);
3598 } while (*argv && *++argv);
3599 return retval;
3600}
3601#endif
3602
3603static int
3604sprint_status(char *s, int status, int sigonly)
3605{
3606 int col;
3607 int st;
3608
3609 col = 0;
3610 if (!WIFEXITED(status)) {
3611#if JOBS
3612 if (WIFSTOPPED(status))
3613 st = WSTOPSIG(status);
3614 else
3615#endif
3616 st = WTERMSIG(status);
3617 if (sigonly) {
3618 if (st == SIGINT || st == SIGPIPE)
3619 goto out;
3620#if JOBS
3621 if (WIFSTOPPED(status))
3622 goto out;
3623#endif
3624 }
3625 st &= 0x7f;
3626 col = fmtstr(s, 32, strsignal(st));
3627 if (WCOREDUMP(status)) {
3628 col += fmtstr(s + col, 16, " (core dumped)");
3629 }
3630 } else if (!sigonly) {
3631 st = WEXITSTATUS(status);
3632 if (st)
3633 col = fmtstr(s, 16, "Done(%d)", st);
3634 else
3635 col = fmtstr(s, 16, "Done");
3636 }
3637 out:
3638 return col;
3639}
3640
3641/*
3642 * Do a wait system call. If job control is compiled in, we accept
3643 * stopped processes. If block is zero, we return a value of zero
3644 * rather than blocking.
3645 *
3646 * System V doesn't have a non-blocking wait system call. It does
3647 * have a SIGCLD signal that is sent to a process when one of it's
3648 * children dies. The obvious way to use SIGCLD would be to install
3649 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3650 * was received, and have waitproc bump another counter when it got
3651 * the status of a process. Waitproc would then know that a wait
3652 * system call would not block if the two counters were different.
3653 * This approach doesn't work because if a process has children that
3654 * have not been waited for, System V will send it a SIGCLD when it
3655 * installs a signal handler for SIGCLD. What this means is that when
3656 * a child exits, the shell will be sent SIGCLD signals continuously
3657 * until is runs out of stack space, unless it does a wait call before
3658 * restoring the signal handler. The code below takes advantage of
3659 * this (mis)feature by installing a signal handler for SIGCLD and
3660 * then checking to see whether it was called. If there are any
3661 * children to be waited for, it will be.
3662 *
3663 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3664 * waits at all. In this case, the user will not be informed when
3665 * a background process until the next time she runs a real program
3666 * (as opposed to running a builtin command or just typing return),
3667 * and the jobs command may give out of date information.
3668 */
3669static int
3670waitproc(int block, int *status)
3671{
3672 int flags = 0;
3673
3674#if JOBS
3675 if (jobctl)
3676 flags |= WUNTRACED;
3677#endif
3678 if (block == 0)
3679 flags |= WNOHANG;
3680 return wait3(status, flags, (struct rusage *)NULL);
3681}
3682
3683/*
3684 * Wait for a process to terminate.
3685 */
3686static int
3687dowait(int block, struct job *job)
3688{
3689 int pid;
3690 int status;
3691 struct job *jp;
3692 struct job *thisjob;
3693 int state;
3694
3695 TRACE(("dowait(%d) called\n", block));
3696 pid = waitproc(block, &status);
3697 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3698 if (pid <= 0)
3699 return pid;
3700 INT_OFF;
3701 thisjob = NULL;
3702 for (jp = curjob; jp; jp = jp->prev_job) {
3703 struct procstat *sp;
3704 struct procstat *spend;
3705 if (jp->state == JOBDONE)
3706 continue;
3707 state = JOBDONE;
3708 spend = jp->ps + jp->nprocs;
3709 sp = jp->ps;
3710 do {
3711 if (sp->pid == pid) {
3712 TRACE(("Job %d: changing status of proc %d "
3713 "from 0x%x to 0x%x\n",
3714 jobno(jp), pid, sp->status, status));
3715 sp->status = status;
3716 thisjob = jp;
3717 }
3718 if (sp->status == -1)
3719 state = JOBRUNNING;
3720#if JOBS
3721 if (state == JOBRUNNING)
3722 continue;
3723 if (WIFSTOPPED(sp->status)) {
3724 jp->stopstatus = sp->status;
3725 state = JOBSTOPPED;
3726 }
3727#endif
3728 } while (++sp < spend);
3729 if (thisjob)
3730 goto gotjob;
3731 }
3732#if JOBS
3733 if (!WIFSTOPPED(status))
3734#endif
3735
3736 jobless--;
3737 goto out;
3738
3739 gotjob:
3740 if (state != JOBRUNNING) {
3741 thisjob->changed = 1;
3742
3743 if (thisjob->state != state) {
3744 TRACE(("Job %d: changing state from %d to %d\n",
3745 jobno(thisjob), thisjob->state, state));
3746 thisjob->state = state;
3747#if JOBS
3748 if (state == JOBSTOPPED) {
3749 set_curjob(thisjob, CUR_STOPPED);
3750 }
3751#endif
3752 }
3753 }
3754
3755 out:
3756 INT_ON;
3757
3758 if (thisjob && thisjob == job) {
3759 char s[48 + 1];
3760 int len;
3761
3762 len = sprint_status(s, status, 1);
3763 if (len) {
3764 s[len] = '\n';
3765 s[len + 1] = 0;
3766 out2str(s);
3767 }
3768 }
3769 return pid;
3770}
3771
3772#if JOBS
3773static void
3774showjob(FILE *out, struct job *jp, int mode)
3775{
3776 struct procstat *ps;
3777 struct procstat *psend;
3778 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003779 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780 char s[80];
3781
3782 ps = jp->ps;
3783
3784 if (mode & SHOW_PGID) {
3785 /* just output process (group) id of pipeline */
3786 fprintf(out, "%d\n", ps->pid);
3787 return;
3788 }
3789
3790 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003791 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792
3793 if (jp == curjob)
3794 s[col - 2] = '+';
3795 else if (curjob && jp == curjob->prev_job)
3796 s[col - 2] = '-';
3797
3798 if (mode & SHOW_PID)
3799 col += fmtstr(s + col, 16, "%d ", ps->pid);
3800
3801 psend = ps + jp->nprocs;
3802
3803 if (jp->state == JOBRUNNING) {
3804 strcpy(s + col, "Running");
3805 col += sizeof("Running") - 1;
3806 } else {
3807 int status = psend[-1].status;
3808 if (jp->state == JOBSTOPPED)
3809 status = jp->stopstatus;
3810 col += sprint_status(s + col, status, 0);
3811 }
3812
3813 goto start;
3814
3815 do {
3816 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003817 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003818 start:
3819 fprintf(out, "%s%*c%s",
3820 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3821 );
3822 if (!(mode & SHOW_PID)) {
3823 showpipe(jp, out);
3824 break;
3825 }
3826 if (++ps == psend) {
3827 outcslow('\n', out);
3828 break;
3829 }
3830 } while (1);
3831
3832 jp->changed = 0;
3833
3834 if (jp->state == JOBDONE) {
3835 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3836 freejob(jp);
3837 }
3838}
3839
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840/*
3841 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3842 * statuses have changed since the last call to showjobs.
3843 */
3844static void
3845showjobs(FILE *out, int mode)
3846{
3847 struct job *jp;
3848
3849 TRACE(("showjobs(%x) called\n", mode));
3850
3851 /* If not even one one job changed, there is nothing to do */
3852 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3853 continue;
3854
3855 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003856 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003857 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003858 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003859 }
3860}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003861
3862static int
3863jobscmd(int argc, char **argv)
3864{
3865 int mode, m;
3866
3867 mode = 0;
3868 while ((m = nextopt("lp"))) {
3869 if (m == 'l')
3870 mode = SHOW_PID;
3871 else
3872 mode = SHOW_PGID;
3873 }
3874
3875 argv = argptr;
3876 if (*argv) {
3877 do
3878 showjob(stdout, getjob(*argv,0), mode);
3879 while (*++argv);
3880 } else
3881 showjobs(stdout, mode);
3882
3883 return 0;
3884}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003885#endif /* JOBS */
3886
3887static int
3888getstatus(struct job *job)
3889{
3890 int status;
3891 int retval;
3892
3893 status = job->ps[job->nprocs - 1].status;
3894 retval = WEXITSTATUS(status);
3895 if (!WIFEXITED(status)) {
3896#if JOBS
3897 retval = WSTOPSIG(status);
3898 if (!WIFSTOPPED(status))
3899#endif
3900 {
3901 /* XXX: limits number of signals */
3902 retval = WTERMSIG(status);
3903#if JOBS
3904 if (retval == SIGINT)
3905 job->sigint = 1;
3906#endif
3907 }
3908 retval += 128;
3909 }
3910 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3911 jobno(job), job->nprocs, status, retval));
3912 return retval;
3913}
3914
3915static int
3916waitcmd(int argc, char **argv)
3917{
3918 struct job *job;
3919 int retval;
3920 struct job *jp;
3921
3922 EXSIGON;
3923
3924 nextopt(nullstr);
3925 retval = 0;
3926
3927 argv = argptr;
3928 if (!*argv) {
3929 /* wait for all jobs */
3930 for (;;) {
3931 jp = curjob;
3932 while (1) {
3933 if (!jp) {
3934 /* no running procs */
3935 goto out;
3936 }
3937 if (jp->state == JOBRUNNING)
3938 break;
3939 jp->waited = 1;
3940 jp = jp->prev_job;
3941 }
3942 dowait(DOWAIT_BLOCK, 0);
3943 }
3944 }
3945
3946 retval = 127;
3947 do {
3948 if (**argv != '%') {
3949 pid_t pid = number(*argv);
3950 job = curjob;
3951 goto start;
3952 do {
3953 if (job->ps[job->nprocs - 1].pid == pid)
3954 break;
3955 job = job->prev_job;
3956 start:
3957 if (!job)
3958 goto repeat;
3959 } while (1);
3960 } else
3961 job = getjob(*argv, 0);
3962 /* loop until process terminated or stopped */
3963 while (job->state == JOBRUNNING)
3964 dowait(DOWAIT_BLOCK, 0);
3965 job->waited = 1;
3966 retval = getstatus(job);
3967 repeat:
3968 ;
3969 } while (*++argv);
3970
3971 out:
3972 return retval;
3973}
3974
3975static struct job *
3976growjobtab(void)
3977{
3978 size_t len;
3979 ptrdiff_t offset;
3980 struct job *jp, *jq;
3981
3982 len = njobs * sizeof(*jp);
3983 jq = jobtab;
3984 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3985
3986 offset = (char *)jp - (char *)jq;
3987 if (offset) {
3988 /* Relocate pointers */
3989 size_t l = len;
3990
3991 jq = (struct job *)((char *)jq + l);
3992 while (l) {
3993 l -= sizeof(*jp);
3994 jq--;
3995#define joff(p) ((struct job *)((char *)(p) + l))
3996#define jmove(p) (p) = (void *)((char *)(p) + offset)
3997 if (joff(jp)->ps == &jq->ps0)
3998 jmove(joff(jp)->ps);
3999 if (joff(jp)->prev_job)
4000 jmove(joff(jp)->prev_job);
4001 }
4002 if (curjob)
4003 jmove(curjob);
4004#undef joff
4005#undef jmove
4006 }
4007
4008 njobs += 4;
4009 jobtab = jp;
4010 jp = (struct job *)((char *)jp + len);
4011 jq = jp + 3;
4012 do {
4013 jq->used = 0;
4014 } while (--jq >= jp);
4015 return jp;
4016}
4017
4018/*
4019 * Return a new job structure.
4020 * Called with interrupts off.
4021 */
4022static struct job *
4023makejob(union node *node, int nprocs)
4024{
4025 int i;
4026 struct job *jp;
4027
4028 for (i = njobs, jp = jobtab; ; jp++) {
4029 if (--i < 0) {
4030 jp = growjobtab();
4031 break;
4032 }
4033 if (jp->used == 0)
4034 break;
4035 if (jp->state != JOBDONE || !jp->waited)
4036 continue;
4037#if JOBS
4038 if (jobctl)
4039 continue;
4040#endif
4041 freejob(jp);
4042 break;
4043 }
4044 memset(jp, 0, sizeof(*jp));
4045#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004046 /* jp->jobctl is a bitfield.
4047 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004048 if (jobctl)
4049 jp->jobctl = 1;
4050#endif
4051 jp->prev_job = curjob;
4052 curjob = jp;
4053 jp->used = 1;
4054 jp->ps = &jp->ps0;
4055 if (nprocs > 1) {
4056 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4057 }
4058 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4059 jobno(jp)));
4060 return jp;
4061}
4062
4063#if JOBS
4064/*
4065 * Return a string identifying a command (to be printed by the
4066 * jobs command).
4067 */
4068static char *cmdnextc;
4069
4070static void
4071cmdputs(const char *s)
4072{
4073 const char *p, *str;
4074 char c, cc[2] = " ";
4075 char *nextc;
4076 int subtype = 0;
4077 int quoted = 0;
4078 static const char vstype[VSTYPE + 1][4] = {
4079 "", "}", "-", "+", "?", "=",
4080 "%", "%%", "#", "##"
4081 };
4082
4083 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4084 p = s;
4085 while ((c = *p++) != 0) {
4086 str = 0;
4087 switch (c) {
4088 case CTLESC:
4089 c = *p++;
4090 break;
4091 case CTLVAR:
4092 subtype = *p++;
4093 if ((subtype & VSTYPE) == VSLENGTH)
4094 str = "${#";
4095 else
4096 str = "${";
4097 if (!(subtype & VSQUOTE) == !(quoted & 1))
4098 goto dostr;
4099 quoted ^= 1;
4100 c = '"';
4101 break;
4102 case CTLENDVAR:
4103 str = "\"}" + !(quoted & 1);
4104 quoted >>= 1;
4105 subtype = 0;
4106 goto dostr;
4107 case CTLBACKQ:
4108 str = "$(...)";
4109 goto dostr;
4110 case CTLBACKQ+CTLQUOTE:
4111 str = "\"$(...)\"";
4112 goto dostr;
4113#if ENABLE_ASH_MATH_SUPPORT
4114 case CTLARI:
4115 str = "$((";
4116 goto dostr;
4117 case CTLENDARI:
4118 str = "))";
4119 goto dostr;
4120#endif
4121 case CTLQUOTEMARK:
4122 quoted ^= 1;
4123 c = '"';
4124 break;
4125 case '=':
4126 if (subtype == 0)
4127 break;
4128 if ((subtype & VSTYPE) != VSNORMAL)
4129 quoted <<= 1;
4130 str = vstype[subtype & VSTYPE];
4131 if (subtype & VSNUL)
4132 c = ':';
4133 else
4134 goto checkstr;
4135 break;
4136 case '\'':
4137 case '\\':
4138 case '"':
4139 case '$':
4140 /* These can only happen inside quotes */
4141 cc[0] = c;
4142 str = cc;
4143 c = '\\';
4144 break;
4145 default:
4146 break;
4147 }
4148 USTPUTC(c, nextc);
4149 checkstr:
4150 if (!str)
4151 continue;
4152 dostr:
4153 while ((c = *str++)) {
4154 USTPUTC(c, nextc);
4155 }
4156 }
4157 if (quoted & 1) {
4158 USTPUTC('"', nextc);
4159 }
4160 *nextc = 0;
4161 cmdnextc = nextc;
4162}
4163
4164/* cmdtxt() and cmdlist() call each other */
4165static void cmdtxt(union node *n);
4166
4167static void
4168cmdlist(union node *np, int sep)
4169{
4170 for (; np; np = np->narg.next) {
4171 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004172 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004173 cmdtxt(np);
4174 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004175 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176 }
4177}
4178
4179static void
4180cmdtxt(union node *n)
4181{
4182 union node *np;
4183 struct nodelist *lp;
4184 const char *p;
4185 char s[2];
4186
4187 if (!n)
4188 return;
4189 switch (n->type) {
4190 default:
4191#if DEBUG
4192 abort();
4193#endif
4194 case NPIPE:
4195 lp = n->npipe.cmdlist;
4196 for (;;) {
4197 cmdtxt(lp->n);
4198 lp = lp->next;
4199 if (!lp)
4200 break;
4201 cmdputs(" | ");
4202 }
4203 break;
4204 case NSEMI:
4205 p = "; ";
4206 goto binop;
4207 case NAND:
4208 p = " && ";
4209 goto binop;
4210 case NOR:
4211 p = " || ";
4212 binop:
4213 cmdtxt(n->nbinary.ch1);
4214 cmdputs(p);
4215 n = n->nbinary.ch2;
4216 goto donode;
4217 case NREDIR:
4218 case NBACKGND:
4219 n = n->nredir.n;
4220 goto donode;
4221 case NNOT:
4222 cmdputs("!");
4223 n = n->nnot.com;
4224 donode:
4225 cmdtxt(n);
4226 break;
4227 case NIF:
4228 cmdputs("if ");
4229 cmdtxt(n->nif.test);
4230 cmdputs("; then ");
4231 n = n->nif.ifpart;
4232 if (n->nif.elsepart) {
4233 cmdtxt(n);
4234 cmdputs("; else ");
4235 n = n->nif.elsepart;
4236 }
4237 p = "; fi";
4238 goto dotail;
4239 case NSUBSHELL:
4240 cmdputs("(");
4241 n = n->nredir.n;
4242 p = ")";
4243 goto dotail;
4244 case NWHILE:
4245 p = "while ";
4246 goto until;
4247 case NUNTIL:
4248 p = "until ";
4249 until:
4250 cmdputs(p);
4251 cmdtxt(n->nbinary.ch1);
4252 n = n->nbinary.ch2;
4253 p = "; done";
4254 dodo:
4255 cmdputs("; do ");
4256 dotail:
4257 cmdtxt(n);
4258 goto dotail2;
4259 case NFOR:
4260 cmdputs("for ");
4261 cmdputs(n->nfor.var);
4262 cmdputs(" in ");
4263 cmdlist(n->nfor.args, 1);
4264 n = n->nfor.body;
4265 p = "; done";
4266 goto dodo;
4267 case NDEFUN:
4268 cmdputs(n->narg.text);
4269 p = "() { ... }";
4270 goto dotail2;
4271 case NCMD:
4272 cmdlist(n->ncmd.args, 1);
4273 cmdlist(n->ncmd.redirect, 0);
4274 break;
4275 case NARG:
4276 p = n->narg.text;
4277 dotail2:
4278 cmdputs(p);
4279 break;
4280 case NHERE:
4281 case NXHERE:
4282 p = "<<...";
4283 goto dotail2;
4284 case NCASE:
4285 cmdputs("case ");
4286 cmdputs(n->ncase.expr->narg.text);
4287 cmdputs(" in ");
4288 for (np = n->ncase.cases; np; np = np->nclist.next) {
4289 cmdtxt(np->nclist.pattern);
4290 cmdputs(") ");
4291 cmdtxt(np->nclist.body);
4292 cmdputs(";; ");
4293 }
4294 p = "esac";
4295 goto dotail2;
4296 case NTO:
4297 p = ">";
4298 goto redir;
4299 case NCLOBBER:
4300 p = ">|";
4301 goto redir;
4302 case NAPPEND:
4303 p = ">>";
4304 goto redir;
4305 case NTOFD:
4306 p = ">&";
4307 goto redir;
4308 case NFROM:
4309 p = "<";
4310 goto redir;
4311 case NFROMFD:
4312 p = "<&";
4313 goto redir;
4314 case NFROMTO:
4315 p = "<>";
4316 redir:
4317 s[0] = n->nfile.fd + '0';
4318 s[1] = '\0';
4319 cmdputs(s);
4320 cmdputs(p);
4321 if (n->type == NTOFD || n->type == NFROMFD) {
4322 s[0] = n->ndup.dupfd + '0';
4323 p = s;
4324 goto dotail2;
4325 }
4326 n = n->nfile.fname;
4327 goto donode;
4328 }
4329}
4330
4331static char *
4332commandtext(union node *n)
4333{
4334 char *name;
4335
4336 STARTSTACKSTR(cmdnextc);
4337 cmdtxt(n);
4338 name = stackblock();
4339 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4340 name, cmdnextc, cmdnextc));
4341 return ckstrdup(name);
4342}
4343#endif /* JOBS */
4344
4345/*
4346 * Fork off a subshell. If we are doing job control, give the subshell its
4347 * own process group. Jp is a job structure that the job is to be added to.
4348 * N is the command that will be evaluated by the child. Both jp and n may
4349 * be NULL. The mode parameter can be one of the following:
4350 * FORK_FG - Fork off a foreground process.
4351 * FORK_BG - Fork off a background process.
4352 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4353 * process group even if job control is on.
4354 *
4355 * When job control is turned off, background processes have their standard
4356 * input redirected to /dev/null (except for the second and later processes
4357 * in a pipeline).
4358 *
4359 * Called with interrupts off.
4360 */
4361/*
4362 * Clear traps on a fork.
4363 */
4364static void
4365clear_traps(void)
4366{
4367 char **tp;
4368
4369 for (tp = trap; tp < &trap[NSIG]; tp++) {
4370 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4371 INT_OFF;
4372 free(*tp);
4373 *tp = NULL;
4374 if (tp != &trap[0])
4375 setsignal(tp - trap);
4376 INT_ON;
4377 }
4378 }
4379}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004380
4381/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382static void closescript(void);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004383/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004384static void
4385forkchild(struct job *jp, union node *n, int mode)
4386{
4387 int oldlvl;
4388
4389 TRACE(("Child shell %d\n", getpid()));
4390 oldlvl = shlvl;
4391 shlvl++;
4392
4393 closescript();
4394 clear_traps();
4395#if JOBS
4396 /* do job control only in root shell */
4397 jobctl = 0;
4398 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4399 pid_t pgrp;
4400
4401 if (jp->nprocs == 0)
4402 pgrp = getpid();
4403 else
4404 pgrp = jp->ps[0].pid;
4405 /* This can fail because we are doing it in the parent also */
4406 (void)setpgid(0, pgrp);
4407 if (mode == FORK_FG)
4408 xtcsetpgrp(ttyfd, pgrp);
4409 setsignal(SIGTSTP);
4410 setsignal(SIGTTOU);
4411 } else
4412#endif
4413 if (mode == FORK_BG) {
4414 ignoresig(SIGINT);
4415 ignoresig(SIGQUIT);
4416 if (jp->nprocs == 0) {
4417 close(0);
4418 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004419 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004420 }
4421 }
4422 if (!oldlvl && iflag) {
4423 setsignal(SIGINT);
4424 setsignal(SIGQUIT);
4425 setsignal(SIGTERM);
4426 }
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004427#if JOBS
4428 /* For "jobs | cat" to work like in bash, we must retain list of jobs
4429 * in child, but we do need to remove ourself */
Denis Vlasenkod4293c72007-07-18 21:35:43 +00004430 if (jp)
4431 freejob(jp);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004432#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004433 for (jp = curjob; jp; jp = jp->prev_job)
4434 freejob(jp);
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004435#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004436 jobless = 0;
4437}
4438
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004439/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004440static void
4441forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4442{
4443 TRACE(("In parent shell: child = %d\n", pid));
4444 if (!jp) {
4445 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4446 jobless++;
4447 return;
4448 }
4449#if JOBS
4450 if (mode != FORK_NOJOB && jp->jobctl) {
4451 int pgrp;
4452
4453 if (jp->nprocs == 0)
4454 pgrp = pid;
4455 else
4456 pgrp = jp->ps[0].pid;
4457 /* This can fail because we are doing it in the child also */
4458 setpgid(pid, pgrp);
4459 }
4460#endif
4461 if (mode == FORK_BG) {
4462 backgndpid = pid; /* set $! */
4463 set_curjob(jp, CUR_RUNNING);
4464 }
4465 if (jp) {
4466 struct procstat *ps = &jp->ps[jp->nprocs++];
4467 ps->pid = pid;
4468 ps->status = -1;
4469 ps->cmd = nullstr;
4470#if JOBS
4471 if (jobctl && n)
4472 ps->cmd = commandtext(n);
4473#endif
4474 }
4475}
4476
4477static int
4478forkshell(struct job *jp, union node *n, int mode)
4479{
4480 int pid;
4481
4482 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4483 pid = fork();
4484 if (pid < 0) {
4485 TRACE(("Fork failed, errno=%d", errno));
4486 if (jp)
4487 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004488 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 }
4490 if (pid == 0)
4491 forkchild(jp, n, mode);
4492 else
4493 forkparent(jp, n, mode, pid);
4494 return pid;
4495}
4496
4497/*
4498 * Wait for job to finish.
4499 *
4500 * Under job control we have the problem that while a child process is
4501 * running interrupts generated by the user are sent to the child but not
4502 * to the shell. This means that an infinite loop started by an inter-
4503 * active user may be hard to kill. With job control turned off, an
4504 * interactive user may place an interactive program inside a loop. If
4505 * the interactive program catches interrupts, the user doesn't want
4506 * these interrupts to also abort the loop. The approach we take here
4507 * is to have the shell ignore interrupt signals while waiting for a
4508 * foreground process to terminate, and then send itself an interrupt
4509 * signal if the child process was terminated by an interrupt signal.
4510 * Unfortunately, some programs want to do a bit of cleanup and then
4511 * exit on interrupt; unless these processes terminate themselves by
4512 * sending a signal to themselves (instead of calling exit) they will
4513 * confuse this approach.
4514 *
4515 * Called with interrupts off.
4516 */
4517static int
4518waitforjob(struct job *jp)
4519{
4520 int st;
4521
4522 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4523 while (jp->state == JOBRUNNING) {
4524 dowait(DOWAIT_BLOCK, jp);
4525 }
4526 st = getstatus(jp);
4527#if JOBS
4528 if (jp->jobctl) {
4529 xtcsetpgrp(ttyfd, rootpid);
4530 /*
4531 * This is truly gross.
4532 * If we're doing job control, then we did a TIOCSPGRP which
4533 * caused us (the shell) to no longer be in the controlling
4534 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4535 * intuit from the subprocess exit status whether a SIGINT
4536 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4537 */
4538 if (jp->sigint)
4539 raise(SIGINT);
4540 }
4541 if (jp->state == JOBDONE)
4542#endif
4543 freejob(jp);
4544 return st;
4545}
4546
4547/*
4548 * return 1 if there are stopped jobs, otherwise 0
4549 */
4550static int
4551stoppedjobs(void)
4552{
4553 struct job *jp;
4554 int retval;
4555
4556 retval = 0;
4557 if (job_warning)
4558 goto out;
4559 jp = curjob;
4560 if (jp && jp->state == JOBSTOPPED) {
4561 out2str("You have stopped jobs.\n");
4562 job_warning = 2;
4563 retval++;
4564 }
4565 out:
4566 return retval;
4567}
4568
4569
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004570/* ============ redir.c
4571 *
4572 * Code for dealing with input/output redirection.
4573 */
4574
4575#define EMPTY -2 /* marks an unused slot in redirtab */
4576#ifndef PIPE_BUF
4577# define PIPESIZE 4096 /* amount of buffering in a pipe */
4578#else
4579# define PIPESIZE PIPE_BUF
4580#endif
4581
4582/*
4583 * Open a file in noclobber mode.
4584 * The code was copied from bash.
4585 */
4586static int
4587noclobberopen(const char *fname)
4588{
4589 int r, fd;
4590 struct stat finfo, finfo2;
4591
4592 /*
4593 * If the file exists and is a regular file, return an error
4594 * immediately.
4595 */
4596 r = stat(fname, &finfo);
4597 if (r == 0 && S_ISREG(finfo.st_mode)) {
4598 errno = EEXIST;
4599 return -1;
4600 }
4601
4602 /*
4603 * If the file was not present (r != 0), make sure we open it
4604 * exclusively so that if it is created before we open it, our open
4605 * will fail. Make sure that we do not truncate an existing file.
4606 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4607 * file was not a regular file, we leave O_EXCL off.
4608 */
4609 if (r != 0)
4610 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4611 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4612
4613 /* If the open failed, return the file descriptor right away. */
4614 if (fd < 0)
4615 return fd;
4616
4617 /*
4618 * OK, the open succeeded, but the file may have been changed from a
4619 * non-regular file to a regular file between the stat and the open.
4620 * We are assuming that the O_EXCL open handles the case where FILENAME
4621 * did not exist and is symlinked to an existing file between the stat
4622 * and open.
4623 */
4624
4625 /*
4626 * If we can open it and fstat the file descriptor, and neither check
4627 * revealed that it was a regular file, and the file has not been
4628 * replaced, return the file descriptor.
4629 */
4630 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4631 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4632 return fd;
4633
4634 /* The file has been replaced. badness. */
4635 close(fd);
4636 errno = EEXIST;
4637 return -1;
4638}
4639
4640/*
4641 * Handle here documents. Normally we fork off a process to write the
4642 * data to a pipe. If the document is short, we can stuff the data in
4643 * the pipe without forking.
4644 */
4645/* openhere needs this forward reference */
4646static void expandhere(union node *arg, int fd);
4647static int
4648openhere(union node *redir)
4649{
4650 int pip[2];
4651 size_t len = 0;
4652
4653 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004654 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004655 if (redir->type == NHERE) {
4656 len = strlen(redir->nhere.doc->narg.text);
4657 if (len <= PIPESIZE) {
4658 full_write(pip[1], redir->nhere.doc->narg.text, len);
4659 goto out;
4660 }
4661 }
4662 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4663 close(pip[0]);
4664 signal(SIGINT, SIG_IGN);
4665 signal(SIGQUIT, SIG_IGN);
4666 signal(SIGHUP, SIG_IGN);
4667#ifdef SIGTSTP
4668 signal(SIGTSTP, SIG_IGN);
4669#endif
4670 signal(SIGPIPE, SIG_DFL);
4671 if (redir->type == NHERE)
4672 full_write(pip[1], redir->nhere.doc->narg.text, len);
4673 else
4674 expandhere(redir->nhere.doc, pip[1]);
4675 _exit(0);
4676 }
4677 out:
4678 close(pip[1]);
4679 return pip[0];
4680}
4681
4682static int
4683openredirect(union node *redir)
4684{
4685 char *fname;
4686 int f;
4687
4688 switch (redir->nfile.type) {
4689 case NFROM:
4690 fname = redir->nfile.expfname;
4691 f = open(fname, O_RDONLY);
4692 if (f < 0)
4693 goto eopen;
4694 break;
4695 case NFROMTO:
4696 fname = redir->nfile.expfname;
4697 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4698 if (f < 0)
4699 goto ecreate;
4700 break;
4701 case NTO:
4702 /* Take care of noclobber mode. */
4703 if (Cflag) {
4704 fname = redir->nfile.expfname;
4705 f = noclobberopen(fname);
4706 if (f < 0)
4707 goto ecreate;
4708 break;
4709 }
4710 /* FALLTHROUGH */
4711 case NCLOBBER:
4712 fname = redir->nfile.expfname;
4713 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4714 if (f < 0)
4715 goto ecreate;
4716 break;
4717 case NAPPEND:
4718 fname = redir->nfile.expfname;
4719 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4720 if (f < 0)
4721 goto ecreate;
4722 break;
4723 default:
4724#if DEBUG
4725 abort();
4726#endif
4727 /* Fall through to eliminate warning. */
4728 case NTOFD:
4729 case NFROMFD:
4730 f = -1;
4731 break;
4732 case NHERE:
4733 case NXHERE:
4734 f = openhere(redir);
4735 break;
4736 }
4737
4738 return f;
4739 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004740 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004741 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004742 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004743}
4744
4745/*
4746 * Copy a file descriptor to be >= to. Returns -1
4747 * if the source file descriptor is closed, EMPTY if there are no unused
4748 * file descriptors left.
4749 */
4750static int
4751copyfd(int from, int to)
4752{
4753 int newfd;
4754
4755 newfd = fcntl(from, F_DUPFD, to);
4756 if (newfd < 0) {
4757 if (errno == EMFILE)
4758 return EMPTY;
4759 ash_msg_and_raise_error("%d: %m", from);
4760 }
4761 return newfd;
4762}
4763
4764static void
4765dupredirect(union node *redir, int f)
4766{
4767 int fd = redir->nfile.fd;
4768
4769 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4770 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4771 copyfd(redir->ndup.dupfd, fd);
4772 }
4773 return;
4774 }
4775
4776 if (f != fd) {
4777 copyfd(f, fd);
4778 close(f);
4779 }
4780}
4781
4782/*
4783 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4784 * old file descriptors are stashed away so that the redirection can be
4785 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4786 * standard output, and the standard error if it becomes a duplicate of
4787 * stdout, is saved in memory.
4788 */
4789/* flags passed to redirect */
4790#define REDIR_PUSH 01 /* save previous values of file descriptors */
4791#define REDIR_SAVEFD2 03 /* set preverrout */
4792static void
4793redirect(union node *redir, int flags)
4794{
4795 union node *n;
4796 struct redirtab *sv;
4797 int i;
4798 int fd;
4799 int newfd;
4800 int *p;
4801 nullredirs++;
4802 if (!redir) {
4803 return;
4804 }
4805 sv = NULL;
4806 INT_OFF;
4807 if (flags & REDIR_PUSH) {
4808 struct redirtab *q;
4809 q = ckmalloc(sizeof(struct redirtab));
4810 q->next = redirlist;
4811 redirlist = q;
4812 q->nullredirs = nullredirs - 1;
4813 for (i = 0; i < 10; i++)
4814 q->renamed[i] = EMPTY;
4815 nullredirs = 0;
4816 sv = q;
4817 }
4818 n = redir;
4819 do {
4820 fd = n->nfile.fd;
4821 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4822 && n->ndup.dupfd == fd)
4823 continue; /* redirect from/to same file descriptor */
4824
4825 newfd = openredirect(n);
4826 if (fd == newfd)
4827 continue;
4828 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4829 i = fcntl(fd, F_DUPFD, 10);
4830
4831 if (i == -1) {
4832 i = errno;
4833 if (i != EBADF) {
4834 close(newfd);
4835 errno = i;
4836 ash_msg_and_raise_error("%d: %m", fd);
4837 /* NOTREACHED */
4838 }
4839 } else {
4840 *p = i;
4841 close(fd);
4842 }
4843 } else {
4844 close(fd);
4845 }
4846 dupredirect(n, newfd);
4847 } while ((n = n->nfile.next));
4848 INT_ON;
4849 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4850 preverrout_fd = sv->renamed[2];
4851}
4852
4853/*
4854 * Undo the effects of the last redirection.
4855 */
4856static void
4857popredir(int drop)
4858{
4859 struct redirtab *rp;
4860 int i;
4861
4862 if (--nullredirs >= 0)
4863 return;
4864 INT_OFF;
4865 rp = redirlist;
4866 for (i = 0; i < 10; i++) {
4867 if (rp->renamed[i] != EMPTY) {
4868 if (!drop) {
4869 close(i);
4870 copyfd(rp->renamed[i], i);
4871 }
4872 close(rp->renamed[i]);
4873 }
4874 }
4875 redirlist = rp->next;
4876 nullredirs = rp->nullredirs;
4877 free(rp);
4878 INT_ON;
4879}
4880
4881/*
4882 * Undo all redirections. Called on error or interrupt.
4883 */
4884
4885/*
4886 * Discard all saved file descriptors.
4887 */
4888static void
4889clearredir(int drop)
4890{
4891 for (;;) {
4892 nullredirs = 0;
4893 if (!redirlist)
4894 break;
4895 popredir(drop);
4896 }
4897}
4898
4899static int
4900redirectsafe(union node *redir, int flags)
4901{
4902 int err;
4903 volatile int saveint;
4904 struct jmploc *volatile savehandler = exception_handler;
4905 struct jmploc jmploc;
4906
4907 SAVE_INT(saveint);
4908 err = setjmp(jmploc.loc) * 2;
4909 if (!err) {
4910 exception_handler = &jmploc;
4911 redirect(redir, flags);
4912 }
4913 exception_handler = savehandler;
4914 if (err && exception != EXERROR)
4915 longjmp(exception_handler->loc, 1);
4916 RESTORE_INT(saveint);
4917 return err;
4918}
4919
4920
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004921/* ============ Routines to expand arguments to commands
4922 *
4923 * We have to deal with backquotes, shell variables, and file metacharacters.
4924 */
4925
4926/*
4927 * expandarg flags
4928 */
4929#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4930#define EXP_TILDE 0x2 /* do normal tilde expansion */
4931#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4932#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4933#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4934#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4935#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4936#define EXP_WORD 0x80 /* expand word in parameter expansion */
4937#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4938/*
4939 * _rmescape() flags
4940 */
4941#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4942#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4943#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4944#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4945#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4946
4947/*
4948 * Structure specifying which parts of the string should be searched
4949 * for IFS characters.
4950 */
4951struct ifsregion {
4952 struct ifsregion *next; /* next region in list */
4953 int begoff; /* offset of start of region */
4954 int endoff; /* offset of end of region */
4955 int nulonly; /* search for nul bytes only */
4956};
4957
4958struct arglist {
4959 struct strlist *list;
4960 struct strlist **lastp;
4961};
4962
4963/* output of current string */
4964static char *expdest;
4965/* list of back quote expressions */
4966static struct nodelist *argbackq;
4967/* first struct in list of ifs regions */
4968static struct ifsregion ifsfirst;
4969/* last struct in list */
4970static struct ifsregion *ifslastp;
4971/* holds expanded arg list */
4972static struct arglist exparg;
4973
4974/*
4975 * Our own itoa().
4976 */
4977static int
4978cvtnum(arith_t num)
4979{
4980 int len;
4981
4982 expdest = makestrspace(32, expdest);
4983#if ENABLE_ASH_MATH_SUPPORT_64
4984 len = fmtstr(expdest, 32, "%lld", (long long) num);
4985#else
4986 len = fmtstr(expdest, 32, "%ld", num);
4987#endif
4988 STADJUST(len, expdest);
4989 return len;
4990}
4991
4992static size_t
4993esclen(const char *start, const char *p)
4994{
4995 size_t esc = 0;
4996
4997 while (p > start && *--p == CTLESC) {
4998 esc++;
4999 }
5000 return esc;
5001}
5002
5003/*
5004 * Remove any CTLESC characters from a string.
5005 */
5006static char *
5007_rmescapes(char *str, int flag)
5008{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005009 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005010
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005011 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005012 unsigned inquotes;
5013 int notescaped;
5014 int globbing;
5015
5016 p = strpbrk(str, qchars);
5017 if (!p) {
5018 return str;
5019 }
5020 q = p;
5021 r = str;
5022 if (flag & RMESCAPE_ALLOC) {
5023 size_t len = p - str;
5024 size_t fulllen = len + strlen(p) + 1;
5025
5026 if (flag & RMESCAPE_GROW) {
5027 r = makestrspace(fulllen, expdest);
5028 } else if (flag & RMESCAPE_HEAP) {
5029 r = ckmalloc(fulllen);
5030 } else {
5031 r = stalloc(fulllen);
5032 }
5033 q = r;
5034 if (len > 0) {
5035 q = memcpy(q, str, len) + len;
5036 }
5037 }
5038 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5039 globbing = flag & RMESCAPE_GLOB;
5040 notescaped = globbing;
5041 while (*p) {
5042 if (*p == CTLQUOTEMARK) {
5043 inquotes = ~inquotes;
5044 p++;
5045 notescaped = globbing;
5046 continue;
5047 }
5048 if (*p == '\\') {
5049 /* naked back slash */
5050 notescaped = 0;
5051 goto copy;
5052 }
5053 if (*p == CTLESC) {
5054 p++;
5055 if (notescaped && inquotes && *p != '/') {
5056 *q++ = '\\';
5057 }
5058 }
5059 notescaped = globbing;
5060 copy:
5061 *q++ = *p++;
5062 }
5063 *q = '\0';
5064 if (flag & RMESCAPE_GROW) {
5065 expdest = r;
5066 STADJUST(q - r + 1, expdest);
5067 }
5068 return r;
5069}
5070#define rmescapes(p) _rmescapes((p), 0)
5071
5072#define pmatch(a, b) !fnmatch((a), (b), 0)
5073
5074/*
5075 * Prepare a pattern for a expmeta (internal glob(3)) call.
5076 *
5077 * Returns an stalloced string.
5078 */
5079static char *
5080preglob(const char *pattern, int quoted, int flag)
5081{
5082 flag |= RMESCAPE_GLOB;
5083 if (quoted) {
5084 flag |= RMESCAPE_QUOTED;
5085 }
5086 return _rmescapes((char *)pattern, flag);
5087}
5088
5089/*
5090 * Put a string on the stack.
5091 */
5092static void
5093memtodest(const char *p, size_t len, int syntax, int quotes)
5094{
5095 char *q = expdest;
5096
5097 q = makestrspace(len * 2, q);
5098
5099 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005100 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005101 if (!c)
5102 continue;
5103 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5104 USTPUTC(CTLESC, q);
5105 USTPUTC(c, q);
5106 }
5107
5108 expdest = q;
5109}
5110
5111static void
5112strtodest(const char *p, int syntax, int quotes)
5113{
5114 memtodest(p, strlen(p), syntax, quotes);
5115}
5116
5117/*
5118 * Record the fact that we have to scan this region of the
5119 * string for IFS characters.
5120 */
5121static void
5122recordregion(int start, int end, int nulonly)
5123{
5124 struct ifsregion *ifsp;
5125
5126 if (ifslastp == NULL) {
5127 ifsp = &ifsfirst;
5128 } else {
5129 INT_OFF;
5130 ifsp = ckmalloc(sizeof(*ifsp));
5131 ifsp->next = NULL;
5132 ifslastp->next = ifsp;
5133 INT_ON;
5134 }
5135 ifslastp = ifsp;
5136 ifslastp->begoff = start;
5137 ifslastp->endoff = end;
5138 ifslastp->nulonly = nulonly;
5139}
5140
5141static void
5142removerecordregions(int endoff)
5143{
5144 if (ifslastp == NULL)
5145 return;
5146
5147 if (ifsfirst.endoff > endoff) {
5148 while (ifsfirst.next != NULL) {
5149 struct ifsregion *ifsp;
5150 INT_OFF;
5151 ifsp = ifsfirst.next->next;
5152 free(ifsfirst.next);
5153 ifsfirst.next = ifsp;
5154 INT_ON;
5155 }
5156 if (ifsfirst.begoff > endoff)
5157 ifslastp = NULL;
5158 else {
5159 ifslastp = &ifsfirst;
5160 ifsfirst.endoff = endoff;
5161 }
5162 return;
5163 }
5164
5165 ifslastp = &ifsfirst;
5166 while (ifslastp->next && ifslastp->next->begoff < endoff)
5167 ifslastp=ifslastp->next;
5168 while (ifslastp->next != NULL) {
5169 struct ifsregion *ifsp;
5170 INT_OFF;
5171 ifsp = ifslastp->next->next;
5172 free(ifslastp->next);
5173 ifslastp->next = ifsp;
5174 INT_ON;
5175 }
5176 if (ifslastp->endoff > endoff)
5177 ifslastp->endoff = endoff;
5178}
5179
5180static char *
5181exptilde(char *startp, char *p, int flag)
5182{
5183 char c;
5184 char *name;
5185 struct passwd *pw;
5186 const char *home;
5187 int quotes = flag & (EXP_FULL | EXP_CASE);
5188 int startloc;
5189
5190 name = p + 1;
5191
5192 while ((c = *++p) != '\0') {
5193 switch (c) {
5194 case CTLESC:
5195 return startp;
5196 case CTLQUOTEMARK:
5197 return startp;
5198 case ':':
5199 if (flag & EXP_VARTILDE)
5200 goto done;
5201 break;
5202 case '/':
5203 case CTLENDVAR:
5204 goto done;
5205 }
5206 }
5207 done:
5208 *p = '\0';
5209 if (*name == '\0') {
5210 home = lookupvar(homestr);
5211 } else {
5212 pw = getpwnam(name);
5213 if (pw == NULL)
5214 goto lose;
5215 home = pw->pw_dir;
5216 }
5217 if (!home || !*home)
5218 goto lose;
5219 *p = c;
5220 startloc = expdest - (char *)stackblock();
5221 strtodest(home, SQSYNTAX, quotes);
5222 recordregion(startloc, expdest - (char *)stackblock(), 0);
5223 return p;
5224 lose:
5225 *p = c;
5226 return startp;
5227}
5228
5229/*
5230 * Execute a command inside back quotes. If it's a builtin command, we
5231 * want to save its output in a block obtained from malloc. Otherwise
5232 * we fork off a subprocess and get the output of the command via a pipe.
5233 * Should be called with interrupts off.
5234 */
5235struct backcmd { /* result of evalbackcmd */
5236 int fd; /* file descriptor to read from */
5237 char *buf; /* buffer */
5238 int nleft; /* number of chars in buffer */
5239 struct job *jp; /* job structure for command */
5240};
5241
5242/* These forward decls are needed to use "eval" code for backticks handling: */
5243static int back_exitstatus; /* exit status of backquoted command */
5244#define EV_EXIT 01 /* exit after evaluating tree */
5245static void evaltree(union node *, int);
5246
5247static void
5248evalbackcmd(union node *n, struct backcmd *result)
5249{
5250 int saveherefd;
5251
5252 result->fd = -1;
5253 result->buf = NULL;
5254 result->nleft = 0;
5255 result->jp = NULL;
5256 if (n == NULL) {
5257 goto out;
5258 }
5259
5260 saveherefd = herefd;
5261 herefd = -1;
5262
5263 {
5264 int pip[2];
5265 struct job *jp;
5266
5267 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005268 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005269 jp = makejob(n, 1);
5270 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5271 FORCE_INT_ON;
5272 close(pip[0]);
5273 if (pip[1] != 1) {
5274 close(1);
5275 copyfd(pip[1], 1);
5276 close(pip[1]);
5277 }
5278 eflag = 0;
5279 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5280 /* NOTREACHED */
5281 }
5282 close(pip[1]);
5283 result->fd = pip[0];
5284 result->jp = jp;
5285 }
5286 herefd = saveherefd;
5287 out:
5288 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5289 result->fd, result->buf, result->nleft, result->jp));
5290}
5291
5292/*
5293 * Expand stuff in backwards quotes.
5294 */
5295static void
5296expbackq(union node *cmd, int quoted, int quotes)
5297{
5298 struct backcmd in;
5299 int i;
5300 char buf[128];
5301 char *p;
5302 char *dest;
5303 int startloc;
5304 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5305 struct stackmark smark;
5306
5307 INT_OFF;
5308 setstackmark(&smark);
5309 dest = expdest;
5310 startloc = dest - (char *)stackblock();
5311 grabstackstr(dest);
5312 evalbackcmd(cmd, &in);
5313 popstackmark(&smark);
5314
5315 p = in.buf;
5316 i = in.nleft;
5317 if (i == 0)
5318 goto read;
5319 for (;;) {
5320 memtodest(p, i, syntax, quotes);
5321 read:
5322 if (in.fd < 0)
5323 break;
5324 i = safe_read(in.fd, buf, sizeof(buf));
5325 TRACE(("expbackq: read returns %d\n", i));
5326 if (i <= 0)
5327 break;
5328 p = buf;
5329 }
5330
Denis Vlasenko60818682007-09-28 22:07:23 +00005331 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005332 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);
Denis Vlasenko60818682007-09-28 22:07:23 +00008790 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008791 while (pf->strpush)
8792 popstring();
8793 parsefile = pf->prev;
8794 free(pf);
8795 parsenleft = parsefile->nleft;
8796 parselleft = parsefile->lleft;
8797 parsenextc = parsefile->nextc;
8798 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008799 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008800}
8801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008802/*
8803 * Return to top level.
8804 */
8805static void
8806popallfiles(void)
8807{
8808 while (parsefile != &basepf)
8809 popfile();
8810}
8811
8812/*
8813 * Close the file(s) that the shell is reading commands from. Called
8814 * after a fork is done.
8815 */
8816static void
8817closescript(void)
8818{
8819 popallfiles();
8820 if (parsefile->fd > 0) {
8821 close(parsefile->fd);
8822 parsefile->fd = 0;
8823 }
8824}
8825
8826/*
8827 * Like setinputfile, but takes an open file descriptor. Call this with
8828 * interrupts off.
8829 */
8830static void
8831setinputfd(int fd, int push)
8832{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008833 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008834 if (push) {
8835 pushfile();
8836 parsefile->buf = 0;
8837 }
8838 parsefile->fd = fd;
8839 if (parsefile->buf == NULL)
8840 parsefile->buf = ckmalloc(IBUFSIZ);
8841 parselleft = parsenleft = 0;
8842 plinno = 1;
8843}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008844
Eric Andersenc470f442003-07-28 09:56:35 +00008845/*
8846 * Set the input to take input from a file. If push is set, push the
8847 * old input onto the stack first.
8848 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008849static int
8850setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008851{
8852 int fd;
8853 int fd2;
8854
Denis Vlasenkob012b102007-02-19 22:43:01 +00008855 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008856 fd = open(fname, O_RDONLY);
8857 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008858 if (flags & INPUT_NOFILE_OK)
8859 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008860 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008861 }
Eric Andersenc470f442003-07-28 09:56:35 +00008862 if (fd < 10) {
8863 fd2 = copyfd(fd, 10);
8864 close(fd);
8865 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008866 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008867 fd = fd2;
8868 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008869 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008870 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008871 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008872 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008873}
8874
Eric Andersencb57d552001-06-28 07:25:16 +00008875/*
8876 * Like setinputfile, but takes input from a string.
8877 */
Eric Andersenc470f442003-07-28 09:56:35 +00008878static void
8879setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008880{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008881 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008882 pushfile();
8883 parsenextc = string;
8884 parsenleft = strlen(string);
8885 parsefile->buf = NULL;
8886 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008887 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008888}
8889
8890
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008891/* ============ mail.c
8892 *
8893 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008894 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008895
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008896#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008897
Eric Andersencb57d552001-06-28 07:25:16 +00008898#define MAXMBOXES 10
8899
Eric Andersenc470f442003-07-28 09:56:35 +00008900/* times of mailboxes */
8901static time_t mailtime[MAXMBOXES];
8902/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008903static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008904
Eric Andersencb57d552001-06-28 07:25:16 +00008905/*
Eric Andersenc470f442003-07-28 09:56:35 +00008906 * Print appropriate message(s) if mail has arrived.
8907 * If mail_var_path_changed is set,
8908 * then the value of MAIL has mail_var_path_changed,
8909 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008910 */
Eric Andersenc470f442003-07-28 09:56:35 +00008911static void
8912chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008913{
Eric Andersencb57d552001-06-28 07:25:16 +00008914 const char *mpath;
8915 char *p;
8916 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008917 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008918 struct stackmark smark;
8919 struct stat statb;
8920
Eric Andersencb57d552001-06-28 07:25:16 +00008921 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008922 mpath = mpathset() ? mpathval() : mailval();
8923 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008924 p = padvance(&mpath, nullstr);
8925 if (p == NULL)
8926 break;
8927 if (*p == '\0')
8928 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008929 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008930#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008931 if (q[-1] != '/')
8932 abort();
8933#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008934 q[-1] = '\0'; /* delete trailing '/' */
8935 if (stat(p, &statb) < 0) {
8936 *mtp = 0;
8937 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008938 }
Eric Andersenc470f442003-07-28 09:56:35 +00008939 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8940 fprintf(
8941 stderr, snlfmt,
8942 pathopt ? pathopt : "you have mail"
8943 );
8944 }
8945 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008946 }
Eric Andersenc470f442003-07-28 09:56:35 +00008947 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008948 popstackmark(&smark);
8949}
Eric Andersencb57d552001-06-28 07:25:16 +00008950
Eric Andersenc470f442003-07-28 09:56:35 +00008951static void
8952changemail(const char *val)
8953{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008954 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00008955}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008956
Denis Vlasenko131ae172007-02-18 13:00:19 +00008957#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008958
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008959
8960/* ============ ??? */
8961
Eric Andersencb57d552001-06-28 07:25:16 +00008962/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008963 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008964 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008965static void
8966setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008967{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008968 char **newparam;
8969 char **ap;
8970 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008971
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008972 for (nparam = 0; argv[nparam]; nparam++);
8973 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8974 while (*argv) {
8975 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008976 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008977 *ap = NULL;
8978 freeparam(&shellparam);
8979 shellparam.malloc = 1;
8980 shellparam.nparam = nparam;
8981 shellparam.p = newparam;
8982#if ENABLE_ASH_GETOPTS
8983 shellparam.optind = 1;
8984 shellparam.optoff = -1;
8985#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008986}
8987
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008988/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008989 * Process shell options. The global variable argptr contains a pointer
8990 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008991 */
8992static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008993minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008994{
8995 int i;
8996
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008997 if (name) {
8998 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008999 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009000 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009001 return;
9002 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009003 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009004 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009005 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009006 out1str("Current option settings\n");
9007 for (i = 0; i < NOPTS; i++)
9008 out1fmt("%-16s%s\n", optnames(i),
9009 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009010}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009011static void
9012setoption(int flag, int val)
9013{
9014 int i;
9015
9016 for (i = 0; i < NOPTS; i++) {
9017 if (optletters(i) == flag) {
9018 optlist[i] = val;
9019 return;
9020 }
9021 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009022 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009023 /* NOTREACHED */
9024}
Eric Andersenc470f442003-07-28 09:56:35 +00009025static void
9026options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009027{
9028 char *p;
9029 int val;
9030 int c;
9031
9032 if (cmdline)
9033 minusc = NULL;
9034 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009035 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009036 if (c != '-' && c != '+')
9037 break;
9038 argptr++;
9039 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009040 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009041 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009042 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009043 if (!cmdline) {
9044 /* "-" means turn off -x and -v */
9045 if (p[0] == '\0')
9046 xflag = vflag = 0;
9047 /* "--" means reset params */
9048 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009049 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009050 }
Eric Andersenc470f442003-07-28 09:56:35 +00009051 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009052 }
Eric Andersencb57d552001-06-28 07:25:16 +00009053 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009054 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009055 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009056 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009057 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009058 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009059 } else if (c == 'o') {
9060 minus_o(*argptr, val);
9061 if (*argptr)
9062 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009063 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9064 isloginsh = 1;
9065 /* bash does not accept +-login, we also won't */
9066 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009067 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009068 isloginsh = 1;
9069 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009070 } else {
9071 setoption(c, val);
9072 }
9073 }
9074 }
9075}
9076
Eric Andersencb57d552001-06-28 07:25:16 +00009077/*
Eric Andersencb57d552001-06-28 07:25:16 +00009078 * The shift builtin command.
9079 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009080static int
Eric Andersenc470f442003-07-28 09:56:35 +00009081shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009082{
9083 int n;
9084 char **ap1, **ap2;
9085
9086 n = 1;
9087 if (argc > 1)
9088 n = number(argv[1]);
9089 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009090 ash_msg_and_raise_error("can't shift that many");
9091 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009092 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009093 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009094 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009095 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009096 }
9097 ap2 = shellparam.p;
9098 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009099#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009100 shellparam.optind = 1;
9101 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009102#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009103 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009104 return 0;
9105}
9106
Eric Andersencb57d552001-06-28 07:25:16 +00009107/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009108 * POSIX requires that 'set' (but not export or readonly) output the
9109 * variables in lexicographic order - by the locale's collating order (sigh).
9110 * Maybe we could keep them in an ordered balanced binary tree
9111 * instead of hashed lists.
9112 * For now just roll 'em through qsort for printing...
9113 */
9114static int
9115showvars(const char *sep_prefix, int on, int off)
9116{
9117 const char *sep;
9118 char **ep, **epend;
9119
9120 ep = listvars(on, off, &epend);
9121 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9122
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009123 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009124
9125 for (; ep < epend; ep++) {
9126 const char *p;
9127 const char *q;
9128
9129 p = strchrnul(*ep, '=');
9130 q = nullstr;
9131 if (*p)
9132 q = single_quote(++p);
9133 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9134 }
9135 return 0;
9136}
9137
9138/*
Eric Andersencb57d552001-06-28 07:25:16 +00009139 * The set command builtin.
9140 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009141static int
Eric Andersenc470f442003-07-28 09:56:35 +00009142setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009143{
9144 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009145 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009146 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009147 options(0);
9148 optschanged();
9149 if (*argptr != NULL) {
9150 setparam(argptr);
9151 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009152 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009153 return 0;
9154}
9155
Denis Vlasenko131ae172007-02-18 13:00:19 +00009156#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009157/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009158static void
9159change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009160{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009161 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009162 /* "get", generate */
9163 char buf[16];
9164
9165 rseed = rseed * 1103515245 + 12345;
9166 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9167 /* set without recursion */
9168 setvar(vrandom.text, buf, VNOFUNC);
9169 vrandom.flags &= ~VNOFUNC;
9170 } else {
9171 /* set/reset */
9172 rseed = strtoul(value, (char **)NULL, 10);
9173 }
Eric Andersenef02f822004-03-11 13:34:24 +00009174}
Eric Andersen16767e22004-03-16 05:14:10 +00009175#endif
9176
Denis Vlasenko131ae172007-02-18 13:00:19 +00009177#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009178static int
Eric Andersenc470f442003-07-28 09:56:35 +00009179getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009180{
9181 char *p, *q;
9182 char c = '?';
9183 int done = 0;
9184 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009185 char s[12];
9186 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009187
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009188 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009189 return 1;
9190 optnext = optfirst + *param_optind - 1;
9191
9192 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009193 p = NULL;
9194 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009195 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009196 if (p == NULL || *p == '\0') {
9197 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009198 p = *optnext;
9199 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009200 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009201 p = NULL;
9202 done = 1;
9203 goto out;
9204 }
9205 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009206 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009207 goto atend;
9208 }
9209
9210 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009211 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009212 if (*q == '\0') {
9213 if (optstr[0] == ':') {
9214 s[0] = c;
9215 s[1] = '\0';
9216 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009217 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009218 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009219 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009220 }
9221 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009222 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009223 }
9224 if (*++q == ':')
9225 q++;
9226 }
9227
9228 if (*++q == ':') {
9229 if (*p == '\0' && (p = *optnext) == NULL) {
9230 if (optstr[0] == ':') {
9231 s[0] = c;
9232 s[1] = '\0';
9233 err |= setvarsafe("OPTARG", s, 0);
9234 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009235 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009236 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009237 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009238 c = '?';
9239 }
Eric Andersenc470f442003-07-28 09:56:35 +00009240 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009241 }
9242
9243 if (p == *optnext)
9244 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009245 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009246 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009247 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009248 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009249 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009250 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009251 *param_optind = optnext - optfirst + 1;
9252 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009253 err |= setvarsafe("OPTIND", s, VNOFUNC);
9254 s[0] = c;
9255 s[1] = '\0';
9256 err |= setvarsafe(optvar, s, 0);
9257 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009258 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009259 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009260 flush_stdout_stderr();
9261 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009262 }
9263 return done;
9264}
Eric Andersenc470f442003-07-28 09:56:35 +00009265
9266/*
9267 * The getopts builtin. Shellparam.optnext points to the next argument
9268 * to be processed. Shellparam.optptr points to the next character to
9269 * be processed in the current argument. If shellparam.optnext is NULL,
9270 * then it's the first time getopts has been called.
9271 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009272static int
Eric Andersenc470f442003-07-28 09:56:35 +00009273getoptscmd(int argc, char **argv)
9274{
9275 char **optbase;
9276
9277 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009278 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009279 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009280 optbase = shellparam.p;
9281 if (shellparam.optind > shellparam.nparam + 1) {
9282 shellparam.optind = 1;
9283 shellparam.optoff = -1;
9284 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009285 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009286 optbase = &argv[3];
9287 if (shellparam.optind > argc - 2) {
9288 shellparam.optind = 1;
9289 shellparam.optoff = -1;
9290 }
9291 }
9292
9293 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009294 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009295}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009296#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009297
Eric Andersencb57d552001-06-28 07:25:16 +00009298
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009299/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009300
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009301/*
9302 * NEOF is returned by parsecmd when it encounters an end of file. It
9303 * must be distinct from NULL, so we use the address of a variable that
9304 * happens to be handy.
9305 */
9306static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009307#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009308static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009309static int lasttoken; /* last token read */
9310static char *wordtext; /* text of last word returned by readtoken */
9311static struct nodelist *backquotelist;
9312static union node *redirnode;
9313static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009314static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009315
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009316static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9317static void
9318raise_error_syntax(const char *msg)
9319{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009320 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009321 /* NOTREACHED */
9322}
9323
9324/*
9325 * Called when an unexpected token is read during the parse. The argument
9326 * is the token that is expected, or -1 if more than one type of token can
9327 * occur at this point.
9328 */
9329static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9330static void
9331raise_error_unexpected_syntax(int token)
9332{
9333 char msg[64];
9334 int l;
9335
9336 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9337 if (token >= 0)
9338 sprintf(msg + l, " (expecting %s)", tokname(token));
9339 raise_error_syntax(msg);
9340 /* NOTREACHED */
9341}
Eric Andersencb57d552001-06-28 07:25:16 +00009342
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009343#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009344
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009345struct heredoc {
9346 struct heredoc *next; /* next here document in list */
9347 union node *here; /* redirection node */
9348 char *eofmark; /* string indicating end of input */
9349 int striptabs; /* if set, strip leading tabs */
9350};
Eric Andersencb57d552001-06-28 07:25:16 +00009351
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009352static struct heredoc *heredoclist; /* list of here documents to read */
9353
9354/* parsing is heavily cross-recursive, need these forward decls */
9355static union node *andor(void);
9356static union node *pipeline(void);
9357static union node *parse_command(void);
9358static void parseheredoc(void);
9359static char peektoken(void);
9360static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009361
Eric Andersenc470f442003-07-28 09:56:35 +00009362static union node *
9363list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009364{
9365 union node *n1, *n2, *n3;
9366 int tok;
9367
Eric Andersenc470f442003-07-28 09:56:35 +00009368 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9369 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009370 return NULL;
9371 n1 = NULL;
9372 for (;;) {
9373 n2 = andor();
9374 tok = readtoken();
9375 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009376 if (n2->type == NPIPE) {
9377 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009378 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009379 if (n2->type != NREDIR) {
9380 n3 = stalloc(sizeof(struct nredir));
9381 n3->nredir.n = n2;
9382 n3->nredir.redirect = NULL;
9383 n2 = n3;
9384 }
9385 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009386 }
9387 }
9388 if (n1 == NULL) {
9389 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009390 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009391 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009392 n3->type = NSEMI;
9393 n3->nbinary.ch1 = n1;
9394 n3->nbinary.ch2 = n2;
9395 n1 = n3;
9396 }
9397 switch (tok) {
9398 case TBACKGND:
9399 case TSEMI:
9400 tok = readtoken();
9401 /* fall through */
9402 case TNL:
9403 if (tok == TNL) {
9404 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009405 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009406 return n1;
9407 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009408 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009409 }
Eric Andersenc470f442003-07-28 09:56:35 +00009410 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009411 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009412 return n1;
9413 break;
9414 case TEOF:
9415 if (heredoclist)
9416 parseheredoc();
9417 else
Eric Andersenc470f442003-07-28 09:56:35 +00009418 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009419 return n1;
9420 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009421 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009422 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009423 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009424 return n1;
9425 }
9426 }
9427}
9428
Eric Andersenc470f442003-07-28 09:56:35 +00009429static union node *
9430andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009431{
Eric Andersencb57d552001-06-28 07:25:16 +00009432 union node *n1, *n2, *n3;
9433 int t;
9434
Eric Andersencb57d552001-06-28 07:25:16 +00009435 n1 = pipeline();
9436 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009437 t = readtoken();
9438 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009439 t = NAND;
9440 } else if (t == TOR) {
9441 t = NOR;
9442 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009443 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009444 return n1;
9445 }
Eric Andersenc470f442003-07-28 09:56:35 +00009446 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009447 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009448 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009449 n3->type = t;
9450 n3->nbinary.ch1 = n1;
9451 n3->nbinary.ch2 = n2;
9452 n1 = n3;
9453 }
9454}
9455
Eric Andersenc470f442003-07-28 09:56:35 +00009456static union node *
9457pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009458{
Eric Andersencb57d552001-06-28 07:25:16 +00009459 union node *n1, *n2, *pipenode;
9460 struct nodelist *lp, *prev;
9461 int negate;
9462
9463 negate = 0;
9464 TRACE(("pipeline: entered\n"));
9465 if (readtoken() == TNOT) {
9466 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009467 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009468 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009469 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009470 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009471 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009472 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009473 pipenode->type = NPIPE;
9474 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009475 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009476 pipenode->npipe.cmdlist = lp;
9477 lp->n = n1;
9478 do {
9479 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009480 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009481 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009482 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009483 prev->next = lp;
9484 } while (readtoken() == TPIPE);
9485 lp->next = NULL;
9486 n1 = pipenode;
9487 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009488 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009489 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009490 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009491 n2->type = NNOT;
9492 n2->nnot.com = n1;
9493 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009494 }
9495 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009496}
9497
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009498static union node *
9499makename(void)
9500{
9501 union node *n;
9502
9503 n = stalloc(sizeof(struct narg));
9504 n->type = NARG;
9505 n->narg.next = NULL;
9506 n->narg.text = wordtext;
9507 n->narg.backquote = backquotelist;
9508 return n;
9509}
9510
9511static void
9512fixredir(union node *n, const char *text, int err)
9513{
9514 TRACE(("Fix redir %s %d\n", text, err));
9515 if (!err)
9516 n->ndup.vname = NULL;
9517
9518 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009519 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009520 else if (LONE_DASH(text))
9521 n->ndup.dupfd = -1;
9522 else {
9523 if (err)
9524 raise_error_syntax("Bad fd number");
9525 n->ndup.vname = makename();
9526 }
9527}
9528
9529/*
9530 * Returns true if the text contains nothing to expand (no dollar signs
9531 * or backquotes).
9532 */
9533static int
9534noexpand(char *text)
9535{
9536 char *p;
9537 char c;
9538
9539 p = text;
9540 while ((c = *p++) != '\0') {
9541 if (c == CTLQUOTEMARK)
9542 continue;
9543 if (c == CTLESC)
9544 p++;
9545 else if (SIT(c, BASESYNTAX) == CCTL)
9546 return 0;
9547 }
9548 return 1;
9549}
9550
9551static void
9552parsefname(void)
9553{
9554 union node *n = redirnode;
9555
9556 if (readtoken() != TWORD)
9557 raise_error_unexpected_syntax(-1);
9558 if (n->type == NHERE) {
9559 struct heredoc *here = heredoc;
9560 struct heredoc *p;
9561 int i;
9562
9563 if (quoteflag == 0)
9564 n->type = NXHERE;
9565 TRACE(("Here document %d\n", n->type));
9566 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9567 raise_error_syntax("Illegal eof marker for << redirection");
9568 rmescapes(wordtext);
9569 here->eofmark = wordtext;
9570 here->next = NULL;
9571 if (heredoclist == NULL)
9572 heredoclist = here;
9573 else {
9574 for (p = heredoclist; p->next; p = p->next);
9575 p->next = here;
9576 }
9577 } else if (n->type == NTOFD || n->type == NFROMFD) {
9578 fixredir(n, wordtext, 0);
9579 } else {
9580 n->nfile.fname = makename();
9581 }
9582}
Eric Andersencb57d552001-06-28 07:25:16 +00009583
Eric Andersenc470f442003-07-28 09:56:35 +00009584static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009585simplecmd(void)
9586{
9587 union node *args, **app;
9588 union node *n = NULL;
9589 union node *vars, **vpp;
9590 union node **rpp, *redir;
9591 int savecheckkwd;
9592
9593 args = NULL;
9594 app = &args;
9595 vars = NULL;
9596 vpp = &vars;
9597 redir = NULL;
9598 rpp = &redir;
9599
9600 savecheckkwd = CHKALIAS;
9601 for (;;) {
9602 checkkwd = savecheckkwd;
9603 switch (readtoken()) {
9604 case TWORD:
9605 n = stalloc(sizeof(struct narg));
9606 n->type = NARG;
9607 n->narg.text = wordtext;
9608 n->narg.backquote = backquotelist;
9609 if (savecheckkwd && isassignment(wordtext)) {
9610 *vpp = n;
9611 vpp = &n->narg.next;
9612 } else {
9613 *app = n;
9614 app = &n->narg.next;
9615 savecheckkwd = 0;
9616 }
9617 break;
9618 case TREDIR:
9619 *rpp = n = redirnode;
9620 rpp = &n->nfile.next;
9621 parsefname(); /* read name of redirection file */
9622 break;
9623 case TLP:
9624 if (args && app == &args->narg.next
9625 && !vars && !redir
9626 ) {
9627 struct builtincmd *bcmd;
9628 const char *name;
9629
9630 /* We have a function */
9631 if (readtoken() != TRP)
9632 raise_error_unexpected_syntax(TRP);
9633 name = n->narg.text;
9634 if (!goodname(name)
9635 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9636 ) {
9637 raise_error_syntax("Bad function name");
9638 }
9639 n->type = NDEFUN;
9640 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9641 n->narg.next = parse_command();
9642 return n;
9643 }
9644 /* fall through */
9645 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009646 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009647 goto out;
9648 }
9649 }
9650 out:
9651 *app = NULL;
9652 *vpp = NULL;
9653 *rpp = NULL;
9654 n = stalloc(sizeof(struct ncmd));
9655 n->type = NCMD;
9656 n->ncmd.args = args;
9657 n->ncmd.assign = vars;
9658 n->ncmd.redirect = redir;
9659 return n;
9660}
9661
9662static union node *
9663parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009664{
Eric Andersencb57d552001-06-28 07:25:16 +00009665 union node *n1, *n2;
9666 union node *ap, **app;
9667 union node *cp, **cpp;
9668 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009669 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009670 int t;
9671
9672 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009673 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009674
Eric Andersencb57d552001-06-28 07:25:16 +00009675 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009676 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009677 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009678 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009679 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009680 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009681 n1->type = NIF;
9682 n1->nif.test = list(0);
9683 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009684 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009685 n1->nif.ifpart = list(0);
9686 n2 = n1;
9687 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009688 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009689 n2 = n2->nif.elsepart;
9690 n2->type = NIF;
9691 n2->nif.test = list(0);
9692 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009693 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009694 n2->nif.ifpart = list(0);
9695 }
9696 if (lasttoken == TELSE)
9697 n2->nif.elsepart = list(0);
9698 else {
9699 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009700 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009701 }
Eric Andersenc470f442003-07-28 09:56:35 +00009702 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009703 break;
9704 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009705 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009706 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009707 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009708 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009709 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009710 got = readtoken();
9711 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009712 TRACE(("expecting DO got %s %s\n", tokname(got),
9713 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009714 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009715 }
9716 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009717 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009718 break;
9719 }
9720 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009721 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009722 raise_error_syntax("Bad for loop variable");
9723 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009724 n1->type = NFOR;
9725 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009726 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009727 if (readtoken() == TIN) {
9728 app = &ap;
9729 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009730 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009731 n2->type = NARG;
9732 n2->narg.text = wordtext;
9733 n2->narg.backquote = backquotelist;
9734 *app = n2;
9735 app = &n2->narg.next;
9736 }
9737 *app = NULL;
9738 n1->nfor.args = ap;
9739 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009740 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009741 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009742 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009743 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009744 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009745 n2->narg.backquote = NULL;
9746 n2->narg.next = NULL;
9747 n1->nfor.args = n2;
9748 /*
9749 * Newline or semicolon here is optional (but note
9750 * that the original Bourne shell only allowed NL).
9751 */
9752 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009753 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009754 }
Eric Andersenc470f442003-07-28 09:56:35 +00009755 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009756 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009757 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009758 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009759 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009760 break;
9761 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009762 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009763 n1->type = NCASE;
9764 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009765 raise_error_unexpected_syntax(TWORD);
9766 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009767 n2->type = NARG;
9768 n2->narg.text = wordtext;
9769 n2->narg.backquote = backquotelist;
9770 n2->narg.next = NULL;
9771 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009772 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009773 } while (readtoken() == TNL);
9774 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009775 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009776 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009777 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009778 checkkwd = CHKNL | CHKKWD;
9779 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009780 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009781 if (lasttoken == TLP)
9782 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009783 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009784 cp->type = NCLIST;
9785 app = &cp->nclist.pattern;
9786 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009787 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009788 ap->type = NARG;
9789 ap->narg.text = wordtext;
9790 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009791 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009792 break;
9793 app = &ap->narg.next;
9794 readtoken();
9795 }
9796 ap->narg.next = NULL;
9797 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009798 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009799 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009800
Eric Andersenc470f442003-07-28 09:56:35 +00009801 cpp = &cp->nclist.next;
9802
9803 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009804 t = readtoken();
9805 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009806 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009807 raise_error_unexpected_syntax(TENDCASE);
9808 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009809 }
Eric Andersenc470f442003-07-28 09:56:35 +00009810 }
Eric Andersencb57d552001-06-28 07:25:16 +00009811 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009812 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009814 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009815 n1->type = NSUBSHELL;
9816 n1->nredir.n = list(0);
9817 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009818 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009819 break;
9820 case TBEGIN:
9821 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009822 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009823 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009824 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009825 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009826 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009827 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009828 }
9829
Eric Andersenc470f442003-07-28 09:56:35 +00009830 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009831 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009832
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009833 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009834 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009835 checkkwd = CHKKWD | CHKALIAS;
9836 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009837 while (readtoken() == TREDIR) {
9838 *rpp = n2 = redirnode;
9839 rpp = &n2->nfile.next;
9840 parsefname();
9841 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009842 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009843 *rpp = NULL;
9844 if (redir) {
9845 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009846 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009847 n2->type = NREDIR;
9848 n2->nredir.n = n1;
9849 n1 = n2;
9850 }
9851 n1->nredir.redirect = redir;
9852 }
Eric Andersencb57d552001-06-28 07:25:16 +00009853 return n1;
9854}
9855
Eric Andersencb57d552001-06-28 07:25:16 +00009856/*
9857 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9858 * is not NULL, read a here document. In the latter case, eofmark is the
9859 * word which marks the end of the document and striptabs is true if
9860 * leading tabs should be stripped from the document. The argument firstc
9861 * is the first character of the input token or document.
9862 *
9863 * Because C does not have internal subroutines, I have simulated them
9864 * using goto's to implement the subroutine linkage. The following macros
9865 * will run code that appears at the end of readtoken1.
9866 */
9867
Eric Andersen2870d962001-07-02 17:27:21 +00009868#define CHECKEND() {goto checkend; checkend_return:;}
9869#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9870#define PARSESUB() {goto parsesub; parsesub_return:;}
9871#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9872#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9873#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009874
9875static int
Eric Andersenc470f442003-07-28 09:56:35 +00009876readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009877{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009878 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009879 int c = firstc;
9880 char *out;
9881 int len;
9882 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009883 struct nodelist *bqlist;
9884 smallint quotef;
9885 smallint dblquote;
9886 smallint oldstyle;
9887 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009888#if ENABLE_ASH_EXPAND_PRMT
9889 smallint pssyntax; /* we are expanding a prompt string */
9890#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009891 int varnest; /* levels of variables expansion */
9892 int arinest; /* levels of arithmetic expansion */
9893 int parenlevel; /* levels of parens in arithmetic */
9894 int dqvarnest; /* levels of variables expansion within double quotes */
9895
Eric Andersencb57d552001-06-28 07:25:16 +00009896#if __GNUC__
9897 /* Avoid longjmp clobbering */
9898 (void) &out;
9899 (void) &quotef;
9900 (void) &dblquote;
9901 (void) &varnest;
9902 (void) &arinest;
9903 (void) &parenlevel;
9904 (void) &dqvarnest;
9905 (void) &oldstyle;
9906 (void) &prevsyntax;
9907 (void) &syntax;
9908#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009909 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009910 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009911 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009912 oldstyle = 0;
9913 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +00009914#if ENABLE_ASH_EXPAND_PRMT
9915 pssyntax = (syntax == PSSYNTAX);
9916 if (pssyntax)
9917 syntax = DQSYNTAX;
9918#endif
9919 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +00009920 varnest = 0;
9921 arinest = 0;
9922 parenlevel = 0;
9923 dqvarnest = 0;
9924
9925 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009926 loop: { /* for each line, until end of word */
9927 CHECKEND(); /* set c to PEOF if at end of here document */
9928 for (;;) { /* until end of line or end of word */
9929 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009930 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009931 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009932 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009933 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009934 USTPUTC(c, out);
9935 plinno++;
9936 if (doprompt)
9937 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009938 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009939 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009940 case CWORD:
9941 USTPUTC(c, out);
9942 break;
9943 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009944 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009945 USTPUTC(CTLESC, out);
9946 USTPUTC(c, out);
9947 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009948 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009949 c = pgetc2();
9950 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009951 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009952 USTPUTC('\\', out);
9953 pungetc();
9954 } else if (c == '\n') {
9955 if (doprompt)
9956 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009957 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +00009958#if ENABLE_ASH_EXPAND_PRMT
9959 if (c == '$' && pssyntax) {
9960 USTPUTC(CTLESC, out);
9961 USTPUTC('\\', out);
9962 }
9963#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009964 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009965 c != '\\' && c != '`' &&
9966 c != '$' && (
9967 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009968 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009969 ) {
9970 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009971 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009972 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009973 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009974 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009975 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009976 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009977 }
9978 break;
9979 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009980 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009981 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009982 if (eofmark == NULL) {
9983 USTPUTC(CTLQUOTEMARK, out);
9984 }
Eric Andersencb57d552001-06-28 07:25:16 +00009985 break;
9986 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009987 syntax = DQSYNTAX;
9988 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009989 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009990 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009991 if (eofmark != NULL && arinest == 0
9992 && varnest == 0
9993 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009994 USTPUTC(c, out);
9995 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009996 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009997 syntax = BASESYNTAX;
9998 dblquote = 0;
9999 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010000 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010001 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010002 }
10003 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010004 case CVAR: /* '$' */
10005 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010006 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010007 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010008 if (varnest > 0) {
10009 varnest--;
10010 if (dqvarnest > 0) {
10011 dqvarnest--;
10012 }
10013 USTPUTC(CTLENDVAR, out);
10014 } else {
10015 USTPUTC(c, out);
10016 }
10017 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010018#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010019 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010020 parenlevel++;
10021 USTPUTC(c, out);
10022 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010023 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010024 if (parenlevel > 0) {
10025 USTPUTC(c, out);
10026 --parenlevel;
10027 } else {
10028 if (pgetc() == ')') {
10029 if (--arinest == 0) {
10030 USTPUTC(CTLENDARI, out);
10031 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010032 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010033 } else
10034 USTPUTC(')', out);
10035 } else {
10036 /*
10037 * unbalanced parens
10038 * (don't 2nd guess - no error)
10039 */
10040 pungetc();
10041 USTPUTC(')', out);
10042 }
10043 }
10044 break;
10045#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010046 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010047 PARSEBACKQOLD();
10048 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010049 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010050 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010051 case CIGN:
10052 break;
10053 default:
10054 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010055 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010056#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010057 if (c != PEOA)
10058#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010059 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010060
Eric Andersencb57d552001-06-28 07:25:16 +000010061 }
10062 c = pgetc_macro();
10063 }
10064 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010065 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010066#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010067 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010068 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010069#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010070 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010071 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010072 if (varnest != 0) {
10073 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010074 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010075 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010076 }
10077 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010078 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010079 out = stackblock();
10080 if (eofmark == NULL) {
10081 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010082 && quotef == 0
10083 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010084 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010085 PARSEREDIR();
10086 return lasttoken = TREDIR;
10087 } else {
10088 pungetc();
10089 }
10090 }
10091 quoteflag = quotef;
10092 backquotelist = bqlist;
10093 grabstackblock(len);
10094 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010095 lasttoken = TWORD;
10096 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010097/* end of readtoken routine */
10098
Eric Andersencb57d552001-06-28 07:25:16 +000010099/*
10100 * Check to see whether we are at the end of the here document. When this
10101 * is called, c is set to the first character of the next input line. If
10102 * we are at the end of the here document, this routine sets the c to PEOF.
10103 */
Eric Andersenc470f442003-07-28 09:56:35 +000010104checkend: {
10105 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010106#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010107 if (c == PEOA) {
10108 c = pgetc2();
10109 }
10110#endif
10111 if (striptabs) {
10112 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010113 c = pgetc2();
10114 }
Eric Andersenc470f442003-07-28 09:56:35 +000010115 }
10116 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010117 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010118 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010119
Eric Andersenc470f442003-07-28 09:56:35 +000010120 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010121 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010122 if (*p == '\n' && *q == '\0') {
10123 c = PEOF;
10124 plinno++;
10125 needprompt = doprompt;
10126 } else {
10127 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010128 }
10129 }
10130 }
10131 }
Eric Andersenc470f442003-07-28 09:56:35 +000010132 goto checkend_return;
10133}
Eric Andersencb57d552001-06-28 07:25:16 +000010134
Eric Andersencb57d552001-06-28 07:25:16 +000010135/*
10136 * Parse a redirection operator. The variable "out" points to a string
10137 * specifying the fd to be redirected. The variable "c" contains the
10138 * first character of the redirection operator.
10139 */
Eric Andersenc470f442003-07-28 09:56:35 +000010140parseredir: {
10141 char fd = *out;
10142 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010143
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010144 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010145 if (c == '>') {
10146 np->nfile.fd = 1;
10147 c = pgetc();
10148 if (c == '>')
10149 np->type = NAPPEND;
10150 else if (c == '|')
10151 np->type = NCLOBBER;
10152 else if (c == '&')
10153 np->type = NTOFD;
10154 else {
10155 np->type = NTO;
10156 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010157 }
Eric Andersenc470f442003-07-28 09:56:35 +000010158 } else { /* c == '<' */
10159 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010160 c = pgetc();
10161 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010162 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010163 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010164 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010165 np->nfile.fd = 0;
10166 }
10167 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010168 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010169 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010170 c = pgetc();
10171 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010172 heredoc->striptabs = 1;
10173 } else {
10174 heredoc->striptabs = 0;
10175 pungetc();
10176 }
10177 break;
10178
10179 case '&':
10180 np->type = NFROMFD;
10181 break;
10182
10183 case '>':
10184 np->type = NFROMTO;
10185 break;
10186
10187 default:
10188 np->type = NFROM;
10189 pungetc();
10190 break;
10191 }
Eric Andersencb57d552001-06-28 07:25:16 +000010192 }
Eric Andersenc470f442003-07-28 09:56:35 +000010193 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010194 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010195 redirnode = np;
10196 goto parseredir_return;
10197}
Eric Andersencb57d552001-06-28 07:25:16 +000010198
Eric Andersencb57d552001-06-28 07:25:16 +000010199/*
10200 * Parse a substitution. At this point, we have read the dollar sign
10201 * and nothing else.
10202 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010203
10204/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10205 * (assuming ascii char codes, as the original implementation did) */
10206#define is_special(c) \
10207 ((((unsigned int)c) - 33 < 32) \
10208 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010209parsesub: {
10210 int subtype;
10211 int typeloc;
10212 int flags;
10213 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010214 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010215
Eric Andersenc470f442003-07-28 09:56:35 +000010216 c = pgetc();
10217 if (
10218 c <= PEOA_OR_PEOF ||
10219 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10220 ) {
10221 USTPUTC('$', out);
10222 pungetc();
10223 } else if (c == '(') { /* $(command) or $((arith)) */
10224 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010225#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010226 PARSEARITH();
10227#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010228 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010229#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010230 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010231 pungetc();
10232 PARSEBACKQNEW();
10233 }
10234 } else {
10235 USTPUTC(CTLVAR, out);
10236 typeloc = out - (char *)stackblock();
10237 USTPUTC(VSNORMAL, out);
10238 subtype = VSNORMAL;
10239 if (c == '{') {
10240 c = pgetc();
10241 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010242 c = pgetc();
10243 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010244 c = '#';
10245 else
10246 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010247 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010248 subtype = 0;
10249 }
10250 if (c > PEOA_OR_PEOF && is_name(c)) {
10251 do {
10252 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010253 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010254 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010255 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010256 do {
10257 STPUTC(c, out);
10258 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010259 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010260 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010261 USTPUTC(c, out);
10262 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010263 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010264 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010265
Eric Andersenc470f442003-07-28 09:56:35 +000010266 STPUTC('=', out);
10267 flags = 0;
10268 if (subtype == 0) {
10269 switch (c) {
10270 case ':':
10271 flags = VSNUL;
10272 c = pgetc();
10273 /*FALLTHROUGH*/
10274 default:
10275 p = strchr(types, c);
10276 if (p == NULL)
10277 goto badsub;
10278 subtype = p - types + VSNORMAL;
10279 break;
10280 case '%':
10281 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010282 {
10283 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010284 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010285 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010286 c = pgetc();
10287 if (c == cc)
10288 subtype++;
10289 else
10290 pungetc();
10291 break;
10292 }
10293 }
Eric Andersenc470f442003-07-28 09:56:35 +000010294 } else {
10295 pungetc();
10296 }
10297 if (dblquote || arinest)
10298 flags |= VSQUOTE;
10299 *((char *)stackblock() + typeloc) = subtype | flags;
10300 if (subtype != VSNORMAL) {
10301 varnest++;
10302 if (dblquote || arinest) {
10303 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010304 }
10305 }
10306 }
Eric Andersenc470f442003-07-28 09:56:35 +000010307 goto parsesub_return;
10308}
Eric Andersencb57d552001-06-28 07:25:16 +000010309
Eric Andersencb57d552001-06-28 07:25:16 +000010310/*
10311 * Called to parse command substitutions. Newstyle is set if the command
10312 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10313 * list of commands (passed by reference), and savelen is the number of
10314 * characters on the top of the stack which must be preserved.
10315 */
Eric Andersenc470f442003-07-28 09:56:35 +000010316parsebackq: {
10317 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010318 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010319 union node *n;
10320 char *volatile str;
10321 struct jmploc jmploc;
10322 struct jmploc *volatile savehandler;
10323 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010324 smallint saveprompt = 0;
10325
Eric Andersencb57d552001-06-28 07:25:16 +000010326#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010327 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010328#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010329 savepbq = parsebackquote;
10330 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010331 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010332 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010333 exception_handler = savehandler;
10334 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010335 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010336 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010337 str = NULL;
10338 savelen = out - (char *)stackblock();
10339 if (savelen > 0) {
10340 str = ckmalloc(savelen);
10341 memcpy(str, stackblock(), savelen);
10342 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010343 savehandler = exception_handler;
10344 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010345 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010346 if (oldstyle) {
10347 /* We must read until the closing backquote, giving special
10348 treatment to some slashes, and then push the string and
10349 reread it as input, interpreting it normally. */
10350 char *pout;
10351 int pc;
10352 size_t psavelen;
10353 char *pstr;
10354
10355
10356 STARTSTACKSTR(pout);
10357 for (;;) {
10358 if (needprompt) {
10359 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010360 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010361 pc = pgetc();
10362 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010363 case '`':
10364 goto done;
10365
10366 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010367 pc = pgetc();
10368 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010369 plinno++;
10370 if (doprompt)
10371 setprompt(2);
10372 /*
10373 * If eating a newline, avoid putting
10374 * the newline into the new character
10375 * stream (via the STPUTC after the
10376 * switch).
10377 */
10378 continue;
10379 }
10380 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010381 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010382 STPUTC('\\', pout);
10383 if (pc > PEOA_OR_PEOF) {
10384 break;
10385 }
10386 /* fall through */
10387
10388 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010389#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010390 case PEOA:
10391#endif
10392 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010393 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010394
10395 case '\n':
10396 plinno++;
10397 needprompt = doprompt;
10398 break;
10399
10400 default:
10401 break;
10402 }
10403 STPUTC(pc, pout);
10404 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010405 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010406 STPUTC('\0', pout);
10407 psavelen = pout - (char *)stackblock();
10408 if (psavelen > 0) {
10409 pstr = grabstackstr(pout);
10410 setinputstring(pstr);
10411 }
10412 }
10413 nlpp = &bqlist;
10414 while (*nlpp)
10415 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010416 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010417 (*nlpp)->next = NULL;
10418 parsebackquote = oldstyle;
10419
10420 if (oldstyle) {
10421 saveprompt = doprompt;
10422 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010423 }
10424
Eric Andersenc470f442003-07-28 09:56:35 +000010425 n = list(2);
10426
10427 if (oldstyle)
10428 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010429 else if (readtoken() != TRP)
10430 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010431
10432 (*nlpp)->n = n;
10433 if (oldstyle) {
10434 /*
10435 * Start reading from old file again, ignoring any pushed back
10436 * tokens left from the backquote parsing
10437 */
10438 popfile();
10439 tokpushback = 0;
10440 }
10441 while (stackblocksize() <= savelen)
10442 growstackblock();
10443 STARTSTACKSTR(out);
10444 if (str) {
10445 memcpy(out, str, savelen);
10446 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010447 INT_OFF;
10448 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010449 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010450 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010451 }
10452 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010453 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010454 if (arinest || dblquote)
10455 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10456 else
10457 USTPUTC(CTLBACKQ, out);
10458 if (oldstyle)
10459 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010460 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010461}
10462
Denis Vlasenko131ae172007-02-18 13:00:19 +000010463#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010464/*
10465 * Parse an arithmetic expansion (indicate start of one and set state)
10466 */
Eric Andersenc470f442003-07-28 09:56:35 +000010467parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010468 if (++arinest == 1) {
10469 prevsyntax = syntax;
10470 syntax = ARISYNTAX;
10471 USTPUTC(CTLARI, out);
10472 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010474 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010475 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010476 } else {
10477 /*
10478 * we collapse embedded arithmetic expansion to
10479 * parenthesis, which should be equivalent
10480 */
10481 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010482 }
Eric Andersenc470f442003-07-28 09:56:35 +000010483 goto parsearith_return;
10484}
10485#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010486
Eric Andersenc470f442003-07-28 09:56:35 +000010487} /* end of readtoken */
10488
Eric Andersencb57d552001-06-28 07:25:16 +000010489/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010490 * Read the next input token.
10491 * If the token is a word, we set backquotelist to the list of cmds in
10492 * backquotes. We set quoteflag to true if any part of the word was
10493 * quoted.
10494 * If the token is TREDIR, then we set redirnode to a structure containing
10495 * the redirection.
10496 * In all cases, the variable startlinno is set to the number of the line
10497 * on which the token starts.
10498 *
10499 * [Change comment: here documents and internal procedures]
10500 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10501 * word parsing code into a separate routine. In this case, readtoken
10502 * doesn't need to have any internal procedures, but parseword does.
10503 * We could also make parseoperator in essence the main routine, and
10504 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010505 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010506#define NEW_xxreadtoken
10507#ifdef NEW_xxreadtoken
10508/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010509static const char xxreadtoken_chars[7] ALIGN1 = {
10510 '\n', '(', ')', '&', '|', ';', 0
10511};
Eric Andersencb57d552001-06-28 07:25:16 +000010512
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010513static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010514 TNL, TLP, TRP, /* only single occurrence allowed */
10515 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10516 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010517 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010518};
10519
10520#define xxreadtoken_doubles \
10521 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10522#define xxreadtoken_singles \
10523 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10524
10525static int
10526xxreadtoken(void)
10527{
10528 int c;
10529
10530 if (tokpushback) {
10531 tokpushback = 0;
10532 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010533 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010534 if (needprompt) {
10535 setprompt(2);
10536 }
10537 startlinno = plinno;
10538 for (;;) { /* until token or start of word found */
10539 c = pgetc_macro();
10540
10541 if ((c != ' ') && (c != '\t')
10542#if ENABLE_ASH_ALIAS
10543 && (c != PEOA)
10544#endif
10545 ) {
10546 if (c == '#') {
10547 while ((c = pgetc()) != '\n' && c != PEOF);
10548 pungetc();
10549 } else if (c == '\\') {
10550 if (pgetc() != '\n') {
10551 pungetc();
10552 goto READTOKEN1;
10553 }
10554 startlinno = ++plinno;
10555 if (doprompt)
10556 setprompt(2);
10557 } else {
10558 const char *p
10559 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10560
10561 if (c != PEOF) {
10562 if (c == '\n') {
10563 plinno++;
10564 needprompt = doprompt;
10565 }
10566
10567 p = strchr(xxreadtoken_chars, c);
10568 if (p == NULL) {
10569 READTOKEN1:
10570 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10571 }
10572
10573 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10574 if (pgetc() == *p) { /* double occurrence? */
10575 p += xxreadtoken_doubles + 1;
10576 } else {
10577 pungetc();
10578 }
10579 }
10580 }
10581 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10582 }
10583 }
10584 } /* for */
10585}
10586#else
10587#define RETURN(token) return lasttoken = token
10588static int
10589xxreadtoken(void)
10590{
10591 int c;
10592
10593 if (tokpushback) {
10594 tokpushback = 0;
10595 return lasttoken;
10596 }
10597 if (needprompt) {
10598 setprompt(2);
10599 }
10600 startlinno = plinno;
10601 for (;;) { /* until token or start of word found */
10602 c = pgetc_macro();
10603 switch (c) {
10604 case ' ': case '\t':
10605#if ENABLE_ASH_ALIAS
10606 case PEOA:
10607#endif
10608 continue;
10609 case '#':
10610 while ((c = pgetc()) != '\n' && c != PEOF);
10611 pungetc();
10612 continue;
10613 case '\\':
10614 if (pgetc() == '\n') {
10615 startlinno = ++plinno;
10616 if (doprompt)
10617 setprompt(2);
10618 continue;
10619 }
10620 pungetc();
10621 goto breakloop;
10622 case '\n':
10623 plinno++;
10624 needprompt = doprompt;
10625 RETURN(TNL);
10626 case PEOF:
10627 RETURN(TEOF);
10628 case '&':
10629 if (pgetc() == '&')
10630 RETURN(TAND);
10631 pungetc();
10632 RETURN(TBACKGND);
10633 case '|':
10634 if (pgetc() == '|')
10635 RETURN(TOR);
10636 pungetc();
10637 RETURN(TPIPE);
10638 case ';':
10639 if (pgetc() == ';')
10640 RETURN(TENDCASE);
10641 pungetc();
10642 RETURN(TSEMI);
10643 case '(':
10644 RETURN(TLP);
10645 case ')':
10646 RETURN(TRP);
10647 default:
10648 goto breakloop;
10649 }
10650 }
10651 breakloop:
10652 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10653#undef RETURN
10654}
10655#endif /* NEW_xxreadtoken */
10656
10657static int
10658readtoken(void)
10659{
10660 int t;
10661#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010662 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010663#endif
10664
10665#if ENABLE_ASH_ALIAS
10666 top:
10667#endif
10668
10669 t = xxreadtoken();
10670
10671 /*
10672 * eat newlines
10673 */
10674 if (checkkwd & CHKNL) {
10675 while (t == TNL) {
10676 parseheredoc();
10677 t = xxreadtoken();
10678 }
10679 }
10680
10681 if (t != TWORD || quoteflag) {
10682 goto out;
10683 }
10684
10685 /*
10686 * check for keywords
10687 */
10688 if (checkkwd & CHKKWD) {
10689 const char *const *pp;
10690
10691 pp = findkwd(wordtext);
10692 if (pp) {
10693 lasttoken = t = pp - tokname_array;
10694 TRACE(("keyword %s recognized\n", tokname(t)));
10695 goto out;
10696 }
10697 }
10698
10699 if (checkkwd & CHKALIAS) {
10700#if ENABLE_ASH_ALIAS
10701 struct alias *ap;
10702 ap = lookupalias(wordtext, 1);
10703 if (ap != NULL) {
10704 if (*ap->val) {
10705 pushstring(ap->val, ap);
10706 }
10707 goto top;
10708 }
10709#endif
10710 }
10711 out:
10712 checkkwd = 0;
10713#if DEBUG
10714 if (!alreadyseen)
10715 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10716 else
10717 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10718#endif
10719 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010720}
10721
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010722static char
10723peektoken(void)
10724{
10725 int t;
10726
10727 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010728 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010729 return tokname_array[t][0];
10730}
Eric Andersencb57d552001-06-28 07:25:16 +000010731
10732/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10734 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010735 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010736static union node *
10737parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010738{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010739 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010740
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010741 tokpushback = 0;
10742 doprompt = interact;
10743 if (doprompt)
10744 setprompt(doprompt);
10745 needprompt = 0;
10746 t = readtoken();
10747 if (t == TEOF)
10748 return NEOF;
10749 if (t == TNL)
10750 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010751 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010752 return list(1);
10753}
10754
10755/*
10756 * Input any here documents.
10757 */
10758static void
10759parseheredoc(void)
10760{
10761 struct heredoc *here;
10762 union node *n;
10763
10764 here = heredoclist;
10765 heredoclist = 0;
10766
10767 while (here) {
10768 if (needprompt) {
10769 setprompt(2);
10770 }
10771 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10772 here->eofmark, here->striptabs);
10773 n = stalloc(sizeof(struct narg));
10774 n->narg.type = NARG;
10775 n->narg.next = NULL;
10776 n->narg.text = wordtext;
10777 n->narg.backquote = backquotelist;
10778 here->here->nhere.doc = n;
10779 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010780 }
Eric Andersencb57d552001-06-28 07:25:16 +000010781}
10782
10783
10784/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010785 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010786 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010787#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010788static const char *
10789expandstr(const char *ps)
10790{
10791 union node n;
10792
10793 /* XXX Fix (char *) cast. */
10794 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010795 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010796 popfile();
10797
10798 n.narg.type = NARG;
10799 n.narg.next = NULL;
10800 n.narg.text = wordtext;
10801 n.narg.backquote = backquotelist;
10802
10803 expandarg(&n, NULL, 0);
10804 return stackblock();
10805}
10806#endif
10807
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010808/*
10809 * Execute a command or commands contained in a string.
10810 */
10811static int
10812evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010813{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010814 union node *n;
10815 struct stackmark smark;
10816 int skip;
10817
10818 setinputstring(s);
10819 setstackmark(&smark);
10820
10821 skip = 0;
10822 while ((n = parsecmd(0)) != NEOF) {
10823 evaltree(n, 0);
10824 popstackmark(&smark);
10825 skip = evalskip;
10826 if (skip)
10827 break;
10828 }
10829 popfile();
10830
10831 skip &= mask;
10832 evalskip = skip;
10833 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010834}
10835
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010836/*
10837 * The eval command.
10838 */
10839static int
10840evalcmd(int argc, char **argv)
10841{
10842 char *p;
10843 char *concat;
10844 char **ap;
10845
10846 if (argc > 1) {
10847 p = argv[1];
10848 if (argc > 2) {
10849 STARTSTACKSTR(concat);
10850 ap = argv + 2;
10851 for (;;) {
10852 concat = stack_putstr(p, concat);
10853 p = *ap++;
10854 if (p == NULL)
10855 break;
10856 STPUTC(' ', concat);
10857 }
10858 STPUTC('\0', concat);
10859 p = grabstackstr(concat);
10860 }
10861 evalstring(p, ~SKIPEVAL);
10862
10863 }
10864 return exitstatus;
10865}
10866
10867/*
10868 * Read and execute commands. "Top" is nonzero for the top level command
10869 * loop; it turns on prompting if the shell is interactive.
10870 */
10871static int
10872cmdloop(int top)
10873{
10874 union node *n;
10875 struct stackmark smark;
10876 int inter;
10877 int numeof = 0;
10878
10879 TRACE(("cmdloop(%d) called\n", top));
10880 for (;;) {
10881 int skip;
10882
10883 setstackmark(&smark);
10884#if JOBS
10885 if (jobctl)
10886 showjobs(stderr, SHOW_CHANGED);
10887#endif
10888 inter = 0;
10889 if (iflag && top) {
10890 inter++;
10891#if ENABLE_ASH_MAIL
10892 chkmail();
10893#endif
10894 }
10895 n = parsecmd(inter);
10896 /* showtree(n); DEBUG */
10897 if (n == NEOF) {
10898 if (!top || numeof >= 50)
10899 break;
10900 if (!stoppedjobs()) {
10901 if (!Iflag)
10902 break;
10903 out2str("\nUse \"exit\" to leave shell.\n");
10904 }
10905 numeof++;
10906 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010907 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10908 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010909 numeof = 0;
10910 evaltree(n, 0);
10911 }
10912 popstackmark(&smark);
10913 skip = evalskip;
10914
10915 if (skip) {
10916 evalskip = 0;
10917 return skip & SKIPEVAL;
10918 }
10919 }
10920 return 0;
10921}
10922
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010923/*
10924 * Take commands from a file. To be compatible we should do a path
10925 * search for the file, which is necessary to find sub-commands.
10926 */
10927static char *
10928find_dot_file(char *name)
10929{
10930 char *fullname;
10931 const char *path = pathval();
10932 struct stat statb;
10933
10934 /* don't try this for absolute or relative paths */
10935 if (strchr(name, '/'))
10936 return name;
10937
10938 while ((fullname = padvance(&path, name)) != NULL) {
10939 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10940 /*
10941 * Don't bother freeing here, since it will
10942 * be freed by the caller.
10943 */
10944 return fullname;
10945 }
10946 stunalloc(fullname);
10947 }
10948
10949 /* not found in the PATH */
10950 ash_msg_and_raise_error("%s: not found", name);
10951 /* NOTREACHED */
10952}
10953
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010954static int
10955dotcmd(int argc, char **argv)
10956{
10957 struct strlist *sp;
10958 volatile struct shparam saveparam;
10959 int status = 0;
10960
10961 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010962 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010963
10964 if (argc >= 2) { /* That's what SVR2 does */
10965 char *fullname;
10966
10967 fullname = find_dot_file(argv[1]);
10968
10969 if (argc > 2) {
10970 saveparam = shellparam;
10971 shellparam.malloc = 0;
10972 shellparam.nparam = argc - 2;
10973 shellparam.p = argv + 2;
10974 };
10975
10976 setinputfile(fullname, INPUT_PUSH_FILE);
10977 commandname = fullname;
10978 cmdloop(0);
10979 popfile();
10980
10981 if (argc > 2) {
10982 freeparam(&shellparam);
10983 shellparam = saveparam;
10984 };
10985 status = exitstatus;
10986 }
10987 return status;
10988}
10989
10990static int
10991exitcmd(int argc, char **argv)
10992{
10993 if (stoppedjobs())
10994 return 0;
10995 if (argc > 1)
10996 exitstatus = number(argv[1]);
10997 raise_exception(EXEXIT);
10998 /* NOTREACHED */
10999}
11000
11001#if ENABLE_ASH_BUILTIN_ECHO
11002static int
11003echocmd(int argc, char **argv)
11004{
11005 return bb_echo(argv);
11006}
11007#endif
11008
11009#if ENABLE_ASH_BUILTIN_TEST
11010static int
11011testcmd(int argc, char **argv)
11012{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011013 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011014}
11015#endif
11016
11017/*
11018 * Read a file containing shell functions.
11019 */
11020static void
11021readcmdfile(char *name)
11022{
11023 setinputfile(name, INPUT_PUSH_FILE);
11024 cmdloop(0);
11025 popfile();
11026}
11027
11028
Denis Vlasenkocc571512007-02-23 21:10:35 +000011029/* ============ find_command inplementation */
11030
11031/*
11032 * Resolve a command name. If you change this routine, you may have to
11033 * change the shellexec routine as well.
11034 */
11035static void
11036find_command(char *name, struct cmdentry *entry, int act, const char *path)
11037{
11038 struct tblentry *cmdp;
11039 int idx;
11040 int prev;
11041 char *fullname;
11042 struct stat statb;
11043 int e;
11044 int updatetbl;
11045 struct builtincmd *bcmd;
11046
11047 /* If name contains a slash, don't use PATH or hash table */
11048 if (strchr(name, '/') != NULL) {
11049 entry->u.index = -1;
11050 if (act & DO_ABS) {
11051 while (stat(name, &statb) < 0) {
11052#ifdef SYSV
11053 if (errno == EINTR)
11054 continue;
11055#endif
11056 entry->cmdtype = CMDUNKNOWN;
11057 return;
11058 }
11059 }
11060 entry->cmdtype = CMDNORMAL;
11061 return;
11062 }
11063
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011064/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011065
11066 updatetbl = (path == pathval());
11067 if (!updatetbl) {
11068 act |= DO_ALTPATH;
11069 if (strstr(path, "%builtin") != NULL)
11070 act |= DO_ALTBLTIN;
11071 }
11072
11073 /* If name is in the table, check answer will be ok */
11074 cmdp = cmdlookup(name, 0);
11075 if (cmdp != NULL) {
11076 int bit;
11077
11078 switch (cmdp->cmdtype) {
11079 default:
11080#if DEBUG
11081 abort();
11082#endif
11083 case CMDNORMAL:
11084 bit = DO_ALTPATH;
11085 break;
11086 case CMDFUNCTION:
11087 bit = DO_NOFUNC;
11088 break;
11089 case CMDBUILTIN:
11090 bit = DO_ALTBLTIN;
11091 break;
11092 }
11093 if (act & bit) {
11094 updatetbl = 0;
11095 cmdp = NULL;
11096 } else if (cmdp->rehash == 0)
11097 /* if not invalidated by cd, we're done */
11098 goto success;
11099 }
11100
11101 /* If %builtin not in path, check for builtin next */
11102 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011103 if (bcmd) {
11104 if (IS_BUILTIN_REGULAR(bcmd))
11105 goto builtin_success;
11106 if (act & DO_ALTPATH) {
11107 if (!(act & DO_ALTBLTIN))
11108 goto builtin_success;
11109 } else if (builtinloc <= 0) {
11110 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011111 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011112 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011113
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011114#if ENABLE_FEATURE_SH_STANDALONE
11115 if (find_applet_by_name(name)) {
11116 entry->cmdtype = CMDNORMAL;
11117 entry->u.index = -1;
11118 return;
11119 }
11120#endif
11121
Denis Vlasenkocc571512007-02-23 21:10:35 +000011122 /* We have to search path. */
11123 prev = -1; /* where to start */
11124 if (cmdp && cmdp->rehash) { /* doing a rehash */
11125 if (cmdp->cmdtype == CMDBUILTIN)
11126 prev = builtinloc;
11127 else
11128 prev = cmdp->param.index;
11129 }
11130
11131 e = ENOENT;
11132 idx = -1;
11133 loop:
11134 while ((fullname = padvance(&path, name)) != NULL) {
11135 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011136 /* NB: code below will still use fullname
11137 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011138 idx++;
11139 if (pathopt) {
11140 if (prefix(pathopt, "builtin")) {
11141 if (bcmd)
11142 goto builtin_success;
11143 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011144 } else if (!(act & DO_NOFUNC)
11145 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011146 /* handled below */
11147 } else {
11148 /* ignore unimplemented options */
11149 continue;
11150 }
11151 }
11152 /* if rehash, don't redo absolute path names */
11153 if (fullname[0] == '/' && idx <= prev) {
11154 if (idx < prev)
11155 continue;
11156 TRACE(("searchexec \"%s\": no change\n", name));
11157 goto success;
11158 }
11159 while (stat(fullname, &statb) < 0) {
11160#ifdef SYSV
11161 if (errno == EINTR)
11162 continue;
11163#endif
11164 if (errno != ENOENT && errno != ENOTDIR)
11165 e = errno;
11166 goto loop;
11167 }
11168 e = EACCES; /* if we fail, this will be the error */
11169 if (!S_ISREG(statb.st_mode))
11170 continue;
11171 if (pathopt) { /* this is a %func directory */
11172 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011173 /* NB: stalloc will return space pointed by fullname
11174 * (because we don't have any intervening allocations
11175 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011176 readcmdfile(fullname);
11177 cmdp = cmdlookup(name, 0);
11178 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11179 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11180 stunalloc(fullname);
11181 goto success;
11182 }
11183 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11184 if (!updatetbl) {
11185 entry->cmdtype = CMDNORMAL;
11186 entry->u.index = idx;
11187 return;
11188 }
11189 INT_OFF;
11190 cmdp = cmdlookup(name, 1);
11191 cmdp->cmdtype = CMDNORMAL;
11192 cmdp->param.index = idx;
11193 INT_ON;
11194 goto success;
11195 }
11196
11197 /* We failed. If there was an entry for this command, delete it */
11198 if (cmdp && updatetbl)
11199 delete_cmd_entry();
11200 if (act & DO_ERR)
11201 ash_msg("%s: %s", name, errmsg(e, "not found"));
11202 entry->cmdtype = CMDUNKNOWN;
11203 return;
11204
11205 builtin_success:
11206 if (!updatetbl) {
11207 entry->cmdtype = CMDBUILTIN;
11208 entry->u.cmd = bcmd;
11209 return;
11210 }
11211 INT_OFF;
11212 cmdp = cmdlookup(name, 1);
11213 cmdp->cmdtype = CMDBUILTIN;
11214 cmdp->param.cmd = bcmd;
11215 INT_ON;
11216 success:
11217 cmdp->rehash = 0;
11218 entry->cmdtype = cmdp->cmdtype;
11219 entry->u = cmdp->param;
11220}
11221
11222
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011223/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011224
Eric Andersencb57d552001-06-28 07:25:16 +000011225/*
Eric Andersencb57d552001-06-28 07:25:16 +000011226 * The trap builtin.
11227 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011228static int
Eric Andersenc470f442003-07-28 09:56:35 +000011229trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011230{
11231 char *action;
11232 char **ap;
11233 int signo;
11234
Eric Andersenc470f442003-07-28 09:56:35 +000011235 nextopt(nullstr);
11236 ap = argptr;
11237 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011238 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011239 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011240 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011241
Rob Landleyc9c1a412006-07-12 19:17:55 +000011242 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011243 out1fmt("trap -- %s %s\n",
11244 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011245 }
11246 }
11247 return 0;
11248 }
Eric Andersenc470f442003-07-28 09:56:35 +000011249 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011250 action = NULL;
11251 else
11252 action = *ap++;
11253 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011254 signo = get_signum(*ap);
11255 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011256 ash_msg_and_raise_error("%s: bad trap", *ap);
11257 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011258 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011259 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011260 action = NULL;
11261 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011262 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011263 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011264 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011265 trap[signo] = action;
11266 if (signo != 0)
11267 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011268 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011269 ap++;
11270 }
11271 return 0;
11272}
11273
Eric Andersenc470f442003-07-28 09:56:35 +000011274
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011275/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011276
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011277#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011278/*
11279 * Lists available builtins
11280 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011281static int
11282helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011283{
11284 int col, i;
11285
11286 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011287 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011288 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011289 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011290 if (col > 60) {
11291 out1fmt("\n");
11292 col = 0;
11293 }
11294 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011295#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011296 for (i = 0; i < NUM_APPLETS; i++) {
11297 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11298 if (col > 60) {
11299 out1fmt("\n");
11300 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011301 }
11302 }
11303#endif
11304 out1fmt("\n\n");
11305 return EXIT_SUCCESS;
11306}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011307#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011308
Eric Andersencb57d552001-06-28 07:25:16 +000011309/*
Eric Andersencb57d552001-06-28 07:25:16 +000011310 * The export and readonly commands.
11311 */
Eric Andersenc470f442003-07-28 09:56:35 +000011312static int
11313exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011314{
11315 struct var *vp;
11316 char *name;
11317 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011318 char **aptr;
11319 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011320
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011321 if (nextopt("p") != 'p') {
11322 aptr = argptr;
11323 name = *aptr;
11324 if (name) {
11325 do {
11326 p = strchr(name, '=');
11327 if (p != NULL) {
11328 p++;
11329 } else {
11330 vp = *findvar(hashvar(name), name);
11331 if (vp) {
11332 vp->flags |= flag;
11333 continue;
11334 }
Eric Andersencb57d552001-06-28 07:25:16 +000011335 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011336 setvar(name, p, flag);
11337 } while ((name = *++aptr) != NULL);
11338 return 0;
11339 }
Eric Andersencb57d552001-06-28 07:25:16 +000011340 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011341 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011342 return 0;
11343}
11344
Eric Andersencb57d552001-06-28 07:25:16 +000011345/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011346 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011347 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011348static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011349unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011350{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011351 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011352
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011353 cmdp = cmdlookup(name, 0);
11354 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11355 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011356}
11357
Eric Andersencb57d552001-06-28 07:25:16 +000011358/*
Eric Andersencb57d552001-06-28 07:25:16 +000011359 * The unset builtin command. We unset the function before we unset the
11360 * variable to allow a function to be unset when there is a readonly variable
11361 * with the same name.
11362 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011363static int
Eric Andersenc470f442003-07-28 09:56:35 +000011364unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011365{
11366 char **ap;
11367 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011368 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011369 int ret = 0;
11370
11371 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011372 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011373 }
Eric Andersencb57d552001-06-28 07:25:16 +000011374
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011375 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011376 if (flag != 'f') {
11377 i = unsetvar(*ap);
11378 ret |= i;
11379 if (!(i & 2))
11380 continue;
11381 }
11382 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011383 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011384 }
Eric Andersenc470f442003-07-28 09:56:35 +000011385 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011386}
11387
11388
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011389/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011390
Eric Andersenc470f442003-07-28 09:56:35 +000011391#include <sys/times.h>
11392
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011393static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011394 ' ', offsetof(struct tms, tms_utime),
11395 '\n', offsetof(struct tms, tms_stime),
11396 ' ', offsetof(struct tms, tms_cutime),
11397 '\n', offsetof(struct tms, tms_cstime),
11398 0
11399};
Eric Andersencb57d552001-06-28 07:25:16 +000011400
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011401static int
11402timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011403{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011404 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011405 const unsigned char *p;
11406 struct tms buf;
11407
11408 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011409 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011410
11411 p = timescmd_str;
11412 do {
11413 t = *(clock_t *)(((char *) &buf) + p[1]);
11414 s = t / clk_tck;
11415 out1fmt("%ldm%ld.%.3lds%c",
11416 s/60, s%60,
11417 ((t - s * clk_tck) * 1000) / clk_tck,
11418 p[0]);
11419 } while (*(p += 2));
11420
Eric Andersencb57d552001-06-28 07:25:16 +000011421 return 0;
11422}
11423
Denis Vlasenko131ae172007-02-18 13:00:19 +000011424#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011425static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011426dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011427{
Eric Andersened9ecf72004-06-22 08:29:45 +000011428 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011429 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011430
Denis Vlasenkob012b102007-02-19 22:43:01 +000011431 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011432 result = arith(s, &errcode);
11433 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011434 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011435 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011436 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011437 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011438 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011439 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011440 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011441 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011442 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011443
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011444 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011445}
Eric Andersenc470f442003-07-28 09:56:35 +000011446
Eric Andersenc470f442003-07-28 09:56:35 +000011447/*
Eric Andersen90898442003-08-06 11:20:52 +000011448 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11449 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11450 *
11451 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011452 */
11453static int
Eric Andersen90898442003-08-06 11:20:52 +000011454letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011455{
Eric Andersenc470f442003-07-28 09:56:35 +000011456 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011457 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011458
Eric Andersen90898442003-08-06 11:20:52 +000011459 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011460 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011461 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011462 for (ap = argv + 1; *ap; ap++) {
11463 i = dash_arith(*ap);
11464 }
Eric Andersenc470f442003-07-28 09:56:35 +000011465
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011466 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011467}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011468#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011469
Eric Andersenc470f442003-07-28 09:56:35 +000011470
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011471/* ============ miscbltin.c
11472 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011473 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011474 */
11475
11476#undef rflag
11477
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011478#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011479typedef enum __rlimit_resource rlim_t;
11480#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011481
Eric Andersenc470f442003-07-28 09:56:35 +000011482/*
11483 * The read builtin. The -e option causes backslashes to escape the
11484 * following character.
11485 *
11486 * This uses unbuffered input, which may be avoidable in some cases.
11487 */
Eric Andersenc470f442003-07-28 09:56:35 +000011488static int
11489readcmd(int argc, char **argv)
11490{
11491 char **ap;
11492 int backslash;
11493 char c;
11494 int rflag;
11495 char *prompt;
11496 const char *ifs;
11497 char *p;
11498 int startword;
11499 int status;
11500 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011501#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011502 int nch_flag = 0;
11503 int nchars = 0;
11504 int silent = 0;
11505 struct termios tty, old_tty;
11506#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011507#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011508 fd_set set;
11509 struct timeval ts;
11510
11511 ts.tv_sec = ts.tv_usec = 0;
11512#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011513
11514 rflag = 0;
11515 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011516#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011517 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011518#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011519 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011520#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011521 while ((i = nextopt("p:rt:")) != '\0')
11522#else
11523 while ((i = nextopt("p:r")) != '\0')
11524#endif
11525 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011526 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011527 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011528 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011529 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011530#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011531 case 'n':
11532 nchars = strtol(optionarg, &p, 10);
11533 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011534 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011535 nch_flag = (nchars > 0);
11536 break;
11537 case 's':
11538 silent = 1;
11539 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011540#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011541#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011542 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011543 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011544 ts.tv_usec = 0;
11545 if (*p == '.') {
11546 char *p2;
11547 if (*++p) {
11548 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011549 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011550 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011551 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011552 scale = p2 - p;
11553 /* normalize to usec */
11554 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011555 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011556 while (scale++ < 6)
11557 ts.tv_usec *= 10;
11558 }
11559 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011560 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011561 }
11562 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011563 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011564 break;
11565#endif
11566 case 'r':
11567 rflag = 1;
11568 break;
11569 default:
11570 break;
11571 }
Eric Andersenc470f442003-07-28 09:56:35 +000011572 }
11573 if (prompt && isatty(0)) {
11574 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011575 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011576 ap = argptr;
11577 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011578 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011579 ifs = bltinlookup("IFS");
11580 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011581 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011582#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011583 if (nch_flag || silent) {
11584 tcgetattr(0, &tty);
11585 old_tty = tty;
11586 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011587 tty.c_lflag &= ~ICANON;
11588 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011589 }
11590 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011591 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011592
11593 }
11594 tcsetattr(0, TCSANOW, &tty);
11595 }
11596#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011597#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011598 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenko87f3b262007-09-07 13:43:28 +000011599// TODO: replace with poll, it is smaller
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011600 FD_ZERO(&set);
11601 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011602
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011603 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011604 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011605#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011606 if (nch_flag)
11607 tcsetattr(0, TCSANOW, &old_tty);
11608#endif
11609 return 1;
11610 }
11611 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011612#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011613 status = 0;
11614 startword = 1;
11615 backslash = 0;
11616 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011617#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011618 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011619#else
11620 for (;;)
11621#endif
11622 {
Eric Andersenc470f442003-07-28 09:56:35 +000011623 if (read(0, &c, 1) != 1) {
11624 status = 1;
11625 break;
11626 }
11627 if (c == '\0')
11628 continue;
11629 if (backslash) {
11630 backslash = 0;
11631 if (c != '\n')
11632 goto put;
11633 continue;
11634 }
11635 if (!rflag && c == '\\') {
11636 backslash++;
11637 continue;
11638 }
11639 if (c == '\n')
11640 break;
11641 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11642 continue;
11643 }
11644 startword = 0;
11645 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11646 STACKSTRNUL(p);
11647 setvar(*ap, stackblock(), 0);
11648 ap++;
11649 startword = 1;
11650 STARTSTACKSTR(p);
11651 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011652 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011653 STPUTC(c, p);
11654 }
11655 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011656#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011657 if (nch_flag || silent)
11658 tcsetattr(0, TCSANOW, &old_tty);
11659#endif
11660
Eric Andersenc470f442003-07-28 09:56:35 +000011661 STACKSTRNUL(p);
11662 /* Remove trailing blanks */
11663 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11664 *p = '\0';
11665 setvar(*ap, stackblock(), 0);
11666 while (*++ap != NULL)
11667 setvar(*ap, nullstr, 0);
11668 return status;
11669}
11670
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011671static int
11672umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011673{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011674 static const char permuser[3] ALIGN1 = "ugo";
11675 static const char permmode[3] ALIGN1 = "rwx";
11676 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011677 S_IRUSR, S_IWUSR, S_IXUSR,
11678 S_IRGRP, S_IWGRP, S_IXGRP,
11679 S_IROTH, S_IWOTH, S_IXOTH
11680 };
11681
11682 char *ap;
11683 mode_t mask;
11684 int i;
11685 int symbolic_mode = 0;
11686
11687 while (nextopt("S") != '\0') {
11688 symbolic_mode = 1;
11689 }
11690
Denis Vlasenkob012b102007-02-19 22:43:01 +000011691 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011692 mask = umask(0);
11693 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011694 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011695
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011696 ap = *argptr;
11697 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011698 if (symbolic_mode) {
11699 char buf[18];
11700 char *p = buf;
11701
11702 for (i = 0; i < 3; i++) {
11703 int j;
11704
11705 *p++ = permuser[i];
11706 *p++ = '=';
11707 for (j = 0; j < 3; j++) {
11708 if ((mask & permmask[3 * i + j]) == 0) {
11709 *p++ = permmode[j];
11710 }
11711 }
11712 *p++ = ',';
11713 }
11714 *--p = 0;
11715 puts(buf);
11716 } else {
11717 out1fmt("%.4o\n", mask);
11718 }
11719 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011720 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011721 mask = 0;
11722 do {
11723 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011724 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011725 mask = (mask << 3) + (*ap - '0');
11726 } while (*++ap != '\0');
11727 umask(mask);
11728 } else {
11729 mask = ~mask & 0777;
11730 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011731 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011732 }
11733 umask(~mask & 0777);
11734 }
11735 }
11736 return 0;
11737}
11738
11739/*
11740 * ulimit builtin
11741 *
11742 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11743 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11744 * ash by J.T. Conklin.
11745 *
11746 * Public domain.
11747 */
11748
11749struct limits {
11750 const char *name;
11751 int cmd;
11752 int factor; /* multiply by to get rlim_{cur,max} values */
11753 char option;
11754};
11755
11756static const struct limits limits[] = {
11757#ifdef RLIMIT_CPU
11758 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11759#endif
11760#ifdef RLIMIT_FSIZE
11761 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11762#endif
11763#ifdef RLIMIT_DATA
11764 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11765#endif
11766#ifdef RLIMIT_STACK
11767 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11768#endif
11769#ifdef RLIMIT_CORE
11770 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11771#endif
11772#ifdef RLIMIT_RSS
11773 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11774#endif
11775#ifdef RLIMIT_MEMLOCK
11776 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11777#endif
11778#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011779 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011780#endif
11781#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011782 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011783#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011784#ifdef RLIMIT_AS
11785 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011786#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011787#ifdef RLIMIT_LOCKS
11788 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011789#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011790 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011791};
11792
Glenn L McGrath76620622004-01-13 10:19:37 +000011793enum limtype { SOFT = 0x1, HARD = 0x2 };
11794
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011795static void
11796printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011797 const struct limits *l)
11798{
11799 rlim_t val;
11800
11801 val = limit->rlim_max;
11802 if (how & SOFT)
11803 val = limit->rlim_cur;
11804
11805 if (val == RLIM_INFINITY)
11806 out1fmt("unlimited\n");
11807 else {
11808 val /= l->factor;
11809 out1fmt("%lld\n", (long long) val);
11810 }
11811}
11812
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011813static int
Eric Andersenc470f442003-07-28 09:56:35 +000011814ulimitcmd(int argc, char **argv)
11815{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011816 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011817 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011818 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011819 const struct limits *l;
11820 int set, all = 0;
11821 int optc, what;
11822 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011823
11824 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011825 while ((optc = nextopt("HSa"
11826#ifdef RLIMIT_CPU
11827 "t"
11828#endif
11829#ifdef RLIMIT_FSIZE
11830 "f"
11831#endif
11832#ifdef RLIMIT_DATA
11833 "d"
11834#endif
11835#ifdef RLIMIT_STACK
11836 "s"
11837#endif
11838#ifdef RLIMIT_CORE
11839 "c"
11840#endif
11841#ifdef RLIMIT_RSS
11842 "m"
11843#endif
11844#ifdef RLIMIT_MEMLOCK
11845 "l"
11846#endif
11847#ifdef RLIMIT_NPROC
11848 "p"
11849#endif
11850#ifdef RLIMIT_NOFILE
11851 "n"
11852#endif
11853#ifdef RLIMIT_AS
11854 "v"
11855#endif
11856#ifdef RLIMIT_LOCKS
11857 "w"
11858#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011859 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011860 switch (optc) {
11861 case 'H':
11862 how = HARD;
11863 break;
11864 case 'S':
11865 how = SOFT;
11866 break;
11867 case 'a':
11868 all = 1;
11869 break;
11870 default:
11871 what = optc;
11872 }
11873
Glenn L McGrath76620622004-01-13 10:19:37 +000011874 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011875 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011876
11877 set = *argptr ? 1 : 0;
11878 if (set) {
11879 char *p = *argptr;
11880
11881 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011882 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011883 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011884 val = RLIM_INFINITY;
11885 else {
11886 val = (rlim_t) 0;
11887
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011888 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011889 val = (val * 10) + (long)(c - '0');
11890 if (val < (rlim_t) 0)
11891 break;
11892 }
11893 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011894 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011895 val *= l->factor;
11896 }
11897 }
11898 if (all) {
11899 for (l = limits; l->name; l++) {
11900 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011901 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011902 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011903 }
11904 return 0;
11905 }
11906
11907 getrlimit(l->cmd, &limit);
11908 if (set) {
11909 if (how & HARD)
11910 limit.rlim_max = val;
11911 if (how & SOFT)
11912 limit.rlim_cur = val;
11913 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011914 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011915 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011916 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011917 }
11918 return 0;
11919}
11920
Eric Andersen90898442003-08-06 11:20:52 +000011921
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011922/* ============ Math support */
11923
Denis Vlasenko131ae172007-02-18 13:00:19 +000011924#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011925
11926/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11927
11928 Permission is hereby granted, free of charge, to any person obtaining
11929 a copy of this software and associated documentation files (the
11930 "Software"), to deal in the Software without restriction, including
11931 without limitation the rights to use, copy, modify, merge, publish,
11932 distribute, sublicense, and/or sell copies of the Software, and to
11933 permit persons to whom the Software is furnished to do so, subject to
11934 the following conditions:
11935
11936 The above copyright notice and this permission notice shall be
11937 included in all copies or substantial portions of the Software.
11938
11939 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11940 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11941 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11942 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11943 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11944 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11945 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11946*/
11947
11948/* This is my infix parser/evaluator. It is optimized for size, intended
11949 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011950 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011951 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011952 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011953 * be that which POSIX specifies for shells. */
11954
11955/* The code uses a simple two-stack algorithm. See
11956 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011957 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011958 * this is based (this code differs in that it applies operators immediately
11959 * to the stack instead of adding them to a queue to end up with an
11960 * expression). */
11961
11962/* To use the routine, call it with an expression string and error return
11963 * pointer */
11964
11965/*
11966 * Aug 24, 2001 Manuel Novoa III
11967 *
11968 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11969 *
11970 * 1) In arith_apply():
11971 * a) Cached values of *numptr and &(numptr[-1]).
11972 * b) Removed redundant test for zero denominator.
11973 *
11974 * 2) In arith():
11975 * a) Eliminated redundant code for processing operator tokens by moving
11976 * to a table-based implementation. Also folded handling of parens
11977 * into the table.
11978 * b) Combined all 3 loops which called arith_apply to reduce generated
11979 * code size at the cost of speed.
11980 *
11981 * 3) The following expressions were treated as valid by the original code:
11982 * 1() , 0! , 1 ( *3 ) .
11983 * These bugs have been fixed by internally enclosing the expression in
11984 * parens and then checking that all binary ops and right parens are
11985 * preceded by a valid expression (NUM_TOKEN).
11986 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011987 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011988 * ctype's isspace() if it is used by another busybox applet or if additional
11989 * whitespace chars should be considered. Look below the "#include"s for a
11990 * precompiler test.
11991 */
11992
11993/*
11994 * Aug 26, 2001 Manuel Novoa III
11995 *
11996 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11997 *
11998 * Merge in Aaron's comments previously posted to the busybox list,
11999 * modified slightly to take account of my changes to the code.
12000 *
12001 */
12002
12003/*
12004 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12005 *
12006 * - allow access to variable,
12007 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12008 * - realize assign syntax (VAR=expr, +=, *= etc)
12009 * - realize exponentiation (** operator)
12010 * - realize comma separated - expr, expr
12011 * - realise ++expr --expr expr++ expr--
12012 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012013 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012014 * - was restored loses XOR operator
12015 * - remove one goto label, added three ;-)
12016 * - protect $((num num)) as true zero expr (Manuel`s error)
12017 * - always use special isspace(), see comment from bash ;-)
12018 */
12019
Eric Andersen90898442003-08-06 11:20:52 +000012020#define arith_isspace(arithval) \
12021 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12022
Eric Andersen90898442003-08-06 11:20:52 +000012023typedef unsigned char operator;
12024
12025/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012026 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012027 * precedence. The ID portion is so that multiple operators can have the
12028 * same precedence, ensuring that the leftmost one is evaluated first.
12029 * Consider * and /. */
12030
12031#define tok_decl(prec,id) (((id)<<5)|(prec))
12032#define PREC(op) ((op) & 0x1F)
12033
12034#define TOK_LPAREN tok_decl(0,0)
12035
12036#define TOK_COMMA tok_decl(1,0)
12037
12038#define TOK_ASSIGN tok_decl(2,0)
12039#define TOK_AND_ASSIGN tok_decl(2,1)
12040#define TOK_OR_ASSIGN tok_decl(2,2)
12041#define TOK_XOR_ASSIGN tok_decl(2,3)
12042#define TOK_PLUS_ASSIGN tok_decl(2,4)
12043#define TOK_MINUS_ASSIGN tok_decl(2,5)
12044#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12045#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12046
12047#define TOK_MUL_ASSIGN tok_decl(3,0)
12048#define TOK_DIV_ASSIGN tok_decl(3,1)
12049#define TOK_REM_ASSIGN tok_decl(3,2)
12050
12051/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012052#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012053
12054/* conditional is right associativity too */
12055#define TOK_CONDITIONAL tok_decl(4,0)
12056#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12057
12058#define TOK_OR tok_decl(5,0)
12059
12060#define TOK_AND tok_decl(6,0)
12061
12062#define TOK_BOR tok_decl(7,0)
12063
12064#define TOK_BXOR tok_decl(8,0)
12065
12066#define TOK_BAND tok_decl(9,0)
12067
12068#define TOK_EQ tok_decl(10,0)
12069#define TOK_NE tok_decl(10,1)
12070
12071#define TOK_LT tok_decl(11,0)
12072#define TOK_GT tok_decl(11,1)
12073#define TOK_GE tok_decl(11,2)
12074#define TOK_LE tok_decl(11,3)
12075
12076#define TOK_LSHIFT tok_decl(12,0)
12077#define TOK_RSHIFT tok_decl(12,1)
12078
12079#define TOK_ADD tok_decl(13,0)
12080#define TOK_SUB tok_decl(13,1)
12081
12082#define TOK_MUL tok_decl(14,0)
12083#define TOK_DIV tok_decl(14,1)
12084#define TOK_REM tok_decl(14,2)
12085
12086/* exponent is right associativity */
12087#define TOK_EXPONENT tok_decl(15,1)
12088
12089/* For now unary operators. */
12090#define UNARYPREC 16
12091#define TOK_BNOT tok_decl(UNARYPREC,0)
12092#define TOK_NOT tok_decl(UNARYPREC,1)
12093
12094#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12095#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12096
12097#define PREC_PRE (UNARYPREC+2)
12098
12099#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12100#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12101
12102#define PREC_POST (UNARYPREC+3)
12103
12104#define TOK_POST_INC tok_decl(PREC_POST, 0)
12105#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12106
12107#define SPEC_PREC (UNARYPREC+4)
12108
12109#define TOK_NUM tok_decl(SPEC_PREC, 0)
12110#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12111
12112#define NUMPTR (*numstackptr)
12113
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012114static int
12115tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012116{
12117 operator prec = PREC(op);
12118
12119 convert_prec_is_assing(prec);
12120 return (prec == PREC(TOK_ASSIGN) ||
12121 prec == PREC_PRE || prec == PREC_POST);
12122}
12123
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012124static int
12125is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012126{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012127 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12128 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012129}
12130
Eric Andersen90898442003-08-06 11:20:52 +000012131typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012132 arith_t val;
12133 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012134 char contidional_second_val_initialized;
12135 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012136 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012137} v_n_t;
12138
Eric Andersen90898442003-08-06 11:20:52 +000012139typedef struct CHK_VAR_RECURSIVE_LOOPED {
12140 const char *var;
12141 struct CHK_VAR_RECURSIVE_LOOPED *next;
12142} chk_var_recursive_looped_t;
12143
12144static chk_var_recursive_looped_t *prev_chk_var_recursive;
12145
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012146static int
12147arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012148{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012149 if (t->var) {
12150 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012151
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012152 if (p) {
12153 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012154
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012155 /* recursive try as expression */
12156 chk_var_recursive_looped_t *cur;
12157 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012158
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012159 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12160 if (strcmp(cur->var, t->var) == 0) {
12161 /* expression recursion loop detected */
12162 return -5;
12163 }
12164 }
12165 /* save current lookuped var name */
12166 cur = prev_chk_var_recursive;
12167 cur_save.var = t->var;
12168 cur_save.next = cur;
12169 prev_chk_var_recursive = &cur_save;
12170
12171 t->val = arith (p, &errcode);
12172 /* restore previous ptr after recursiving */
12173 prev_chk_var_recursive = cur;
12174 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012175 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012176 /* allow undefined var as 0 */
12177 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012178 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012179 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012180}
12181
12182/* "applying" a token means performing it on the top elements on the integer
12183 * stack. For a unary operator it will only change the top element, but a
12184 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012185static int
12186arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012187{
Eric Andersen90898442003-08-06 11:20:52 +000012188 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012189 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012190 int ret_arith_lookup_val;
12191
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012192 /* There is no operator that can work without arguments */
12193 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012194 numptr_m1 = NUMPTR - 1;
12195
12196 /* check operand is var with noninteger value */
12197 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012198 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012199 return ret_arith_lookup_val;
12200
12201 rez = numptr_m1->val;
12202 if (op == TOK_UMINUS)
12203 rez *= -1;
12204 else if (op == TOK_NOT)
12205 rez = !rez;
12206 else if (op == TOK_BNOT)
12207 rez = ~rez;
12208 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12209 rez++;
12210 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12211 rez--;
12212 else if (op != TOK_UPLUS) {
12213 /* Binary operators */
12214
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012215 /* check and binary operators need two arguments */
12216 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012217
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012218 /* ... and they pop one */
12219 --NUMPTR;
12220 numptr_val = rez;
12221 if (op == TOK_CONDITIONAL) {
12222 if (! numptr_m1->contidional_second_val_initialized) {
12223 /* protect $((expr1 ? expr2)) without ": expr" */
12224 goto err;
12225 }
12226 rez = numptr_m1->contidional_second_val;
12227 } else if (numptr_m1->contidional_second_val_initialized) {
12228 /* protect $((expr1 : expr2)) without "expr ? " */
12229 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012230 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012231 numptr_m1 = NUMPTR - 1;
12232 if (op != TOK_ASSIGN) {
12233 /* check operand is var with noninteger value for not '=' */
12234 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12235 if (ret_arith_lookup_val)
12236 return ret_arith_lookup_val;
12237 }
12238 if (op == TOK_CONDITIONAL) {
12239 numptr_m1->contidional_second_val = rez;
12240 }
12241 rez = numptr_m1->val;
12242 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012243 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012244 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012245 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012246 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012247 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012248 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012249 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012250 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012251 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012252 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012253 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012254 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012255 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012256 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012257 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012258 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012259 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012260 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012261 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012262 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012263 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012264 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012265 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012266 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012267 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012268 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012269 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012270 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012271 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012272 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012273 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012274 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012275 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012276 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012277 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012278 /* protect $((expr : expr)) without "expr ? " */
12279 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012280 }
12281 numptr_m1->contidional_second_val_initialized = op;
12282 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012283 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012284 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012285 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012286 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012287 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012288 return -3; /* exponent less than 0 */
12289 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012290 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012291
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012292 if (numptr_val)
12293 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012294 c *= rez;
12295 rez = c;
12296 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012297 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012298 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012299 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012300 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012301 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012302 rez %= numptr_val;
12303 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012304 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012305 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012306
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012307 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012308 /* Hmm, 1=2 ? */
12309 goto err;
12310 }
12311 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012312#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012313 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012314#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012315 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012316#endif
Eric Andersen90898442003-08-06 11:20:52 +000012317 setvar(numptr_m1->var, buf, 0);
12318 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012319 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012320 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012322 rez++;
12323 }
12324 numptr_m1->val = rez;
12325 /* protect geting var value, is number now */
12326 numptr_m1->var = NULL;
12327 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012328 err:
12329 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012330}
12331
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012332/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012333static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012334 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12335 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12336 '<','<', 0, TOK_LSHIFT,
12337 '>','>', 0, TOK_RSHIFT,
12338 '|','|', 0, TOK_OR,
12339 '&','&', 0, TOK_AND,
12340 '!','=', 0, TOK_NE,
12341 '<','=', 0, TOK_LE,
12342 '>','=', 0, TOK_GE,
12343 '=','=', 0, TOK_EQ,
12344 '|','=', 0, TOK_OR_ASSIGN,
12345 '&','=', 0, TOK_AND_ASSIGN,
12346 '*','=', 0, TOK_MUL_ASSIGN,
12347 '/','=', 0, TOK_DIV_ASSIGN,
12348 '%','=', 0, TOK_REM_ASSIGN,
12349 '+','=', 0, TOK_PLUS_ASSIGN,
12350 '-','=', 0, TOK_MINUS_ASSIGN,
12351 '-','-', 0, TOK_POST_DEC,
12352 '^','=', 0, TOK_XOR_ASSIGN,
12353 '+','+', 0, TOK_POST_INC,
12354 '*','*', 0, TOK_EXPONENT,
12355 '!', 0, TOK_NOT,
12356 '<', 0, TOK_LT,
12357 '>', 0, TOK_GT,
12358 '=', 0, TOK_ASSIGN,
12359 '|', 0, TOK_BOR,
12360 '&', 0, TOK_BAND,
12361 '*', 0, TOK_MUL,
12362 '/', 0, TOK_DIV,
12363 '%', 0, TOK_REM,
12364 '+', 0, TOK_ADD,
12365 '-', 0, TOK_SUB,
12366 '^', 0, TOK_BXOR,
12367 /* uniq */
12368 '~', 0, TOK_BNOT,
12369 ',', 0, TOK_COMMA,
12370 '?', 0, TOK_CONDITIONAL,
12371 ':', 0, TOK_CONDITIONAL_SEP,
12372 ')', 0, TOK_RPAREN,
12373 '(', 0, TOK_LPAREN,
12374 0
12375};
12376/* ptr to ")" */
12377#define endexpression &op_tokens[sizeof(op_tokens)-7]
12378
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012379static arith_t
12380arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012381{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012382 char arithval; /* Current character under analysis */
12383 operator lasttok, op;
12384 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012385
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 const char *p = endexpression;
12387 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012388
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012389 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012390
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012391 /* Stack of integers */
12392 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12393 * in any given correct or incorrect expression is left as an exercise to
12394 * the reader. */
12395 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12396 *numstackptr = numstack;
12397 /* Stack of operator tokens */
12398 operator *stack = alloca((datasizes) * sizeof(operator)),
12399 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012400
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012401 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12402 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012403
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012405 arithval = *expr;
12406 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012407 if (p == endexpression) {
12408 /* Null expression. */
12409 return 0;
12410 }
12411
12412 /* This is only reached after all tokens have been extracted from the
12413 * input stream. If there are still tokens on the operator stack, they
12414 * are to be applied in order. At the end, there should be a final
12415 * result on the integer stack */
12416
12417 if (expr != endexpression + 1) {
12418 /* If we haven't done so already, */
12419 /* append a closing right paren */
12420 expr = endexpression;
12421 /* and let the loop process it. */
12422 continue;
12423 }
12424 /* At this point, we're done with the expression. */
12425 if (numstackptr != numstack+1) {
12426 /* ... but if there isn't, it's bad */
12427 err:
12428 return (*perrcode = -1);
12429 }
12430 if (numstack->var) {
12431 /* expression is $((var)) only, lookup now */
12432 errcode = arith_lookup_val(numstack);
12433 }
12434 ret:
12435 *perrcode = errcode;
12436 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012437 }
12438
Eric Andersen90898442003-08-06 11:20:52 +000012439 /* Continue processing the expression. */
12440 if (arith_isspace(arithval)) {
12441 /* Skip whitespace */
12442 goto prologue;
12443 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012444 p = endofname(expr);
12445 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012446 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012447
12448 numstackptr->var = alloca(var_name_size);
12449 safe_strncpy(numstackptr->var, expr, var_name_size);
12450 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 num:
Eric Andersen90898442003-08-06 11:20:52 +000012452 numstackptr->contidional_second_val_initialized = 0;
12453 numstackptr++;
12454 lasttok = TOK_NUM;
12455 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012456 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012457 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012458 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012459#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012460 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012461#else
12462 numstackptr->val = strtol(expr, (char **) &expr, 0);
12463#endif
Eric Andersen90898442003-08-06 11:20:52 +000012464 goto num;
12465 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012466 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012467 const char *o;
12468
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012469 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012470 /* strange operator not found */
12471 goto err;
12472 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012473 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012474 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012475 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012476 /* found */
12477 expr = o - 1;
12478 break;
12479 }
12480 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012481 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012482 p++;
12483 /* skip zero delim */
12484 p++;
12485 }
12486 op = p[1];
12487
12488 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012489 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12490 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012491
12492 /* Plus and minus are binary (not unary) _only_ if the last
12493 * token was as number, or a right paren (which pretends to be
12494 * a number, since it evaluates to one). Think about it.
12495 * It makes sense. */
12496 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012497 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012498 case TOK_ADD:
12499 op = TOK_UPLUS;
12500 break;
12501 case TOK_SUB:
12502 op = TOK_UMINUS;
12503 break;
12504 case TOK_POST_INC:
12505 op = TOK_PRE_INC;
12506 break;
12507 case TOK_POST_DEC:
12508 op = TOK_PRE_DEC;
12509 break;
Eric Andersen90898442003-08-06 11:20:52 +000012510 }
12511 }
12512 /* We don't want a unary operator to cause recursive descent on the
12513 * stack, because there can be many in a row and it could cause an
12514 * operator to be evaluated before its argument is pushed onto the
12515 * integer stack. */
12516 /* But for binary operators, "apply" everything on the operator
12517 * stack until we find an operator with a lesser priority than the
12518 * one we have just extracted. */
12519 /* Left paren is given the lowest priority so it will never be
12520 * "applied" in this way.
12521 * if associativity is right and priority eq, applied also skip
12522 */
12523 prec = PREC(op);
12524 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12525 /* not left paren or unary */
12526 if (lasttok != TOK_NUM) {
12527 /* binary op must be preceded by a num */
12528 goto err;
12529 }
12530 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 if (op == TOK_RPAREN) {
12532 /* The algorithm employed here is simple: while we don't
12533 * hit an open paren nor the bottom of the stack, pop
12534 * tokens and apply them */
12535 if (stackptr[-1] == TOK_LPAREN) {
12536 --stackptr;
12537 /* Any operator directly after a */
12538 lasttok = TOK_NUM;
12539 /* close paren should consider itself binary */
12540 goto prologue;
12541 }
12542 } else {
12543 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012544
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012545 convert_prec_is_assing(prec);
12546 convert_prec_is_assing(prev_prec);
12547 if (prev_prec < prec)
12548 break;
12549 /* check right assoc */
12550 if (prev_prec == prec && is_right_associativity(prec))
12551 break;
12552 }
12553 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12554 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012555 }
12556 if (op == TOK_RPAREN) {
12557 goto err;
12558 }
12559 }
12560
12561 /* Push this operator to the stack and remember it. */
12562 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012563 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012564 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012565 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012566}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012567#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012568
12569
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012570/* ============ main() and helpers */
12571
12572/*
12573 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012574 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012575static void exitshell(void) ATTRIBUTE_NORETURN;
12576static void
12577exitshell(void)
12578{
12579 struct jmploc loc;
12580 char *p;
12581 int status;
12582
12583 status = exitstatus;
12584 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12585 if (setjmp(loc.loc)) {
12586 if (exception == EXEXIT)
12587/* dash bug: it just does _exit(exitstatus) here
12588 * but we have to do setjobctl(0) first!
12589 * (bug is still not fixed in dash-0.5.3 - if you run dash
12590 * under Midnight Commander, on exit from dash MC is backgrounded) */
12591 status = exitstatus;
12592 goto out;
12593 }
12594 exception_handler = &loc;
12595 p = trap[0];
12596 if (p) {
12597 trap[0] = NULL;
12598 evalstring(p, 0);
12599 }
12600 flush_stdout_stderr();
12601 out:
12602 setjobctl(0);
12603 _exit(status);
12604 /* NOTREACHED */
12605}
12606
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012607static void
12608init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012609{
12610 /* from input.c: */
12611 basepf.nextc = basepf.buf = basebuf;
12612
12613 /* from trap.c: */
12614 signal(SIGCHLD, SIG_DFL);
12615
12616 /* from var.c: */
12617 {
12618 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012619 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012620 const char *p;
12621 struct stat st1, st2;
12622
12623 initvar();
12624 for (envp = environ; envp && *envp; envp++) {
12625 if (strchr(*envp, '=')) {
12626 setvareq(*envp, VEXPORT|VTEXTFIXED);
12627 }
12628 }
12629
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012630 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012631 setvar("PPID", ppid, 0);
12632
12633 p = lookupvar("PWD");
12634 if (p)
12635 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12636 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12637 p = '\0';
12638 setpwd(p, 0);
12639 }
12640}
12641
12642/*
12643 * Process the shell command line arguments.
12644 */
12645static void
12646procargs(int argc, char **argv)
12647{
12648 int i;
12649 const char *xminusc;
12650 char **xargv;
12651
12652 xargv = argv;
12653 arg0 = xargv[0];
12654 if (argc > 0)
12655 xargv++;
12656 for (i = 0; i < NOPTS; i++)
12657 optlist[i] = 2;
12658 argptr = xargv;
12659 options(1);
12660 xargv = argptr;
12661 xminusc = minusc;
12662 if (*xargv == NULL) {
12663 if (xminusc)
12664 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12665 sflag = 1;
12666 }
12667 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12668 iflag = 1;
12669 if (mflag == 2)
12670 mflag = iflag;
12671 for (i = 0; i < NOPTS; i++)
12672 if (optlist[i] == 2)
12673 optlist[i] = 0;
12674#if DEBUG == 2
12675 debug = 1;
12676#endif
12677 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12678 if (xminusc) {
12679 minusc = *xargv++;
12680 if (*xargv)
12681 goto setarg0;
12682 } else if (!sflag) {
12683 setinputfile(*xargv, 0);
12684 setarg0:
12685 arg0 = *xargv++;
12686 commandname = arg0;
12687 }
12688
12689 shellparam.p = xargv;
12690#if ENABLE_ASH_GETOPTS
12691 shellparam.optind = 1;
12692 shellparam.optoff = -1;
12693#endif
12694 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12695 while (*xargv) {
12696 shellparam.nparam++;
12697 xargv++;
12698 }
12699 optschanged();
12700}
12701
12702/*
12703 * Read /etc/profile or .profile.
12704 */
12705static void
12706read_profile(const char *name)
12707{
12708 int skip;
12709
12710 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12711 return;
12712 skip = cmdloop(0);
12713 popfile();
12714 if (skip)
12715 exitshell();
12716}
12717
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012718/*
12719 * This routine is called when an error or an interrupt occurs in an
12720 * interactive shell and control is returned to the main command loop.
12721 */
12722static void
12723reset(void)
12724{
12725 /* from eval.c: */
12726 evalskip = 0;
12727 loopnest = 0;
12728 /* from input.c: */
12729 parselleft = parsenleft = 0; /* clear input buffer */
12730 popallfiles();
12731 /* from parser.c: */
12732 tokpushback = 0;
12733 checkkwd = 0;
12734 /* from redir.c: */
12735 clearredir(0);
12736}
12737
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012738#if PROFILE
12739static short profile_buf[16384];
12740extern int etext();
12741#endif
12742
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012743/*
12744 * Main routine. We initialize things, parse the arguments, execute
12745 * profiles if we're a login shell, and then call cmdloop to execute
12746 * commands. The setjmp call sets up the location to jump to when an
12747 * exception occurs. When an exception occurs the variable "state"
12748 * is used to figure out how far we had gotten.
12749 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012750int ash_main(int argc, char **argv);
12751int ash_main(int argc, char **argv)
12752{
12753 char *shinit;
12754 volatile int state;
12755 struct jmploc jmploc;
12756 struct stackmark smark;
12757
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012758#if PROFILE
12759 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12760#endif
12761
12762#if ENABLE_FEATURE_EDITING
12763 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12764#endif
12765 state = 0;
12766 if (setjmp(jmploc.loc)) {
12767 int e;
12768 int s;
12769
12770 reset();
12771
12772 e = exception;
12773 if (e == EXERROR)
12774 exitstatus = 2;
12775 s = state;
12776 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12777 exitshell();
12778
12779 if (e == EXINT) {
12780 outcslow('\n', stderr);
12781 }
12782 popstackmark(&smark);
12783 FORCE_INT_ON; /* enable interrupts */
12784 if (s == 1)
12785 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012786 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012787 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012788 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012789 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012790 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012791 }
12792 exception_handler = &jmploc;
12793#if DEBUG
12794 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012795 trace_puts("Shell args: ");
12796 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012797#endif
12798 rootpid = getpid();
12799
12800#if ENABLE_ASH_RANDOM_SUPPORT
12801 rseed = rootpid + time(NULL);
12802#endif
12803 init();
12804 setstackmark(&smark);
12805 procargs(argc, argv);
12806#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12807 if (iflag) {
12808 const char *hp = lookupvar("HISTFILE");
12809
12810 if (hp == NULL) {
12811 hp = lookupvar("HOME");
12812 if (hp != NULL) {
12813 char *defhp = concat_path_file(hp, ".ash_history");
12814 setvar("HISTFILE", defhp, 0);
12815 free(defhp);
12816 }
12817 }
12818 }
12819#endif
12820 if (argv[0] && argv[0][0] == '-')
12821 isloginsh = 1;
12822 if (isloginsh) {
12823 state = 1;
12824 read_profile("/etc/profile");
12825 state1:
12826 state = 2;
12827 read_profile(".profile");
12828 }
12829 state2:
12830 state = 3;
12831 if (
12832#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012833 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012834#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012835 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012836 ) {
12837 shinit = lookupvar("ENV");
12838 if (shinit != NULL && *shinit != '\0') {
12839 read_profile(shinit);
12840 }
12841 }
12842 state3:
12843 state = 4;
12844 if (minusc)
12845 evalstring(minusc, 0);
12846
12847 if (sflag || minusc == NULL) {
12848#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12849 if ( iflag ) {
12850 const char *hp = lookupvar("HISTFILE");
12851
12852 if (hp != NULL)
12853 line_input_state->hist_file = hp;
12854 }
12855#endif
12856 state4: /* XXX ??? - why isn't this before the "if" statement */
12857 cmdloop(1);
12858 }
12859#if PROFILE
12860 monitor(0);
12861#endif
12862#ifdef GPROF
12863 {
12864 extern void _mcleanup(void);
12865 _mcleanup();
12866 }
12867#endif
12868 exitshell();
12869 /* NOTREACHED */
12870}
12871
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012872#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012873const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012874int main(int argc, char **argv)
12875{
12876 return ash_main(argc, argv);
12877}
12878#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012879
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012880
Eric Andersendf82f612001-06-28 07:46:40 +000012881/*-
12882 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012883 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012884 *
12885 * This code is derived from software contributed to Berkeley by
12886 * Kenneth Almquist.
12887 *
12888 * Redistribution and use in source and binary forms, with or without
12889 * modification, are permitted provided that the following conditions
12890 * are met:
12891 * 1. Redistributions of source code must retain the above copyright
12892 * notice, this list of conditions and the following disclaimer.
12893 * 2. Redistributions in binary form must reproduce the above copyright
12894 * notice, this list of conditions and the following disclaimer in the
12895 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012896 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012897 * may be used to endorse or promote products derived from this software
12898 * without specific prior written permission.
12899 *
12900 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12901 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12902 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12903 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12904 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12905 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12906 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12907 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12908 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12909 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12910 * SUCH DAMAGE.
12911 */