blob: 8b21e655cfdff86fe66b945158ae3be598db75b3 [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 Vlasenko41770222007-10-07 18:02:52 +00004383
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004384/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004385static void
4386forkchild(struct job *jp, union node *n, int mode)
4387{
4388 int oldlvl;
4389
4390 TRACE(("Child shell %d\n", getpid()));
4391 oldlvl = shlvl;
4392 shlvl++;
4393
4394 closescript();
4395 clear_traps();
4396#if JOBS
4397 /* do job control only in root shell */
4398 jobctl = 0;
4399 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4400 pid_t pgrp;
4401
4402 if (jp->nprocs == 0)
4403 pgrp = getpid();
4404 else
4405 pgrp = jp->ps[0].pid;
4406 /* This can fail because we are doing it in the parent also */
4407 (void)setpgid(0, pgrp);
4408 if (mode == FORK_FG)
4409 xtcsetpgrp(ttyfd, pgrp);
4410 setsignal(SIGTSTP);
4411 setsignal(SIGTTOU);
4412 } else
4413#endif
4414 if (mode == FORK_BG) {
4415 ignoresig(SIGINT);
4416 ignoresig(SIGQUIT);
4417 if (jp->nprocs == 0) {
4418 close(0);
4419 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004420 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004421 }
4422 }
4423 if (!oldlvl && iflag) {
4424 setsignal(SIGINT);
4425 setsignal(SIGQUIT);
4426 setsignal(SIGTERM);
4427 }
4428 for (jp = curjob; jp; jp = jp->prev_job)
4429 freejob(jp);
4430 jobless = 0;
4431}
4432
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004433/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004434static void
4435forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4436{
4437 TRACE(("In parent shell: child = %d\n", pid));
4438 if (!jp) {
4439 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4440 jobless++;
4441 return;
4442 }
4443#if JOBS
4444 if (mode != FORK_NOJOB && jp->jobctl) {
4445 int pgrp;
4446
4447 if (jp->nprocs == 0)
4448 pgrp = pid;
4449 else
4450 pgrp = jp->ps[0].pid;
4451 /* This can fail because we are doing it in the child also */
4452 setpgid(pid, pgrp);
4453 }
4454#endif
4455 if (mode == FORK_BG) {
4456 backgndpid = pid; /* set $! */
4457 set_curjob(jp, CUR_RUNNING);
4458 }
4459 if (jp) {
4460 struct procstat *ps = &jp->ps[jp->nprocs++];
4461 ps->pid = pid;
4462 ps->status = -1;
4463 ps->cmd = nullstr;
4464#if JOBS
4465 if (jobctl && n)
4466 ps->cmd = commandtext(n);
4467#endif
4468 }
4469}
4470
4471static int
4472forkshell(struct job *jp, union node *n, int mode)
4473{
4474 int pid;
4475
4476 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4477 pid = fork();
4478 if (pid < 0) {
4479 TRACE(("Fork failed, errno=%d", errno));
4480 if (jp)
4481 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004482 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004483 }
4484 if (pid == 0)
4485 forkchild(jp, n, mode);
4486 else
4487 forkparent(jp, n, mode, pid);
4488 return pid;
4489}
4490
4491/*
4492 * Wait for job to finish.
4493 *
4494 * Under job control we have the problem that while a child process is
4495 * running interrupts generated by the user are sent to the child but not
4496 * to the shell. This means that an infinite loop started by an inter-
4497 * active user may be hard to kill. With job control turned off, an
4498 * interactive user may place an interactive program inside a loop. If
4499 * the interactive program catches interrupts, the user doesn't want
4500 * these interrupts to also abort the loop. The approach we take here
4501 * is to have the shell ignore interrupt signals while waiting for a
4502 * foreground process to terminate, and then send itself an interrupt
4503 * signal if the child process was terminated by an interrupt signal.
4504 * Unfortunately, some programs want to do a bit of cleanup and then
4505 * exit on interrupt; unless these processes terminate themselves by
4506 * sending a signal to themselves (instead of calling exit) they will
4507 * confuse this approach.
4508 *
4509 * Called with interrupts off.
4510 */
4511static int
4512waitforjob(struct job *jp)
4513{
4514 int st;
4515
4516 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4517 while (jp->state == JOBRUNNING) {
4518 dowait(DOWAIT_BLOCK, jp);
4519 }
4520 st = getstatus(jp);
4521#if JOBS
4522 if (jp->jobctl) {
4523 xtcsetpgrp(ttyfd, rootpid);
4524 /*
4525 * This is truly gross.
4526 * If we're doing job control, then we did a TIOCSPGRP which
4527 * caused us (the shell) to no longer be in the controlling
4528 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4529 * intuit from the subprocess exit status whether a SIGINT
4530 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4531 */
4532 if (jp->sigint)
4533 raise(SIGINT);
4534 }
4535 if (jp->state == JOBDONE)
4536#endif
4537 freejob(jp);
4538 return st;
4539}
4540
4541/*
4542 * return 1 if there are stopped jobs, otherwise 0
4543 */
4544static int
4545stoppedjobs(void)
4546{
4547 struct job *jp;
4548 int retval;
4549
4550 retval = 0;
4551 if (job_warning)
4552 goto out;
4553 jp = curjob;
4554 if (jp && jp->state == JOBSTOPPED) {
4555 out2str("You have stopped jobs.\n");
4556 job_warning = 2;
4557 retval++;
4558 }
4559 out:
4560 return retval;
4561}
4562
4563
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004564/* ============ redir.c
4565 *
4566 * Code for dealing with input/output redirection.
4567 */
4568
4569#define EMPTY -2 /* marks an unused slot in redirtab */
4570#ifndef PIPE_BUF
4571# define PIPESIZE 4096 /* amount of buffering in a pipe */
4572#else
4573# define PIPESIZE PIPE_BUF
4574#endif
4575
4576/*
4577 * Open a file in noclobber mode.
4578 * The code was copied from bash.
4579 */
4580static int
4581noclobberopen(const char *fname)
4582{
4583 int r, fd;
4584 struct stat finfo, finfo2;
4585
4586 /*
4587 * If the file exists and is a regular file, return an error
4588 * immediately.
4589 */
4590 r = stat(fname, &finfo);
4591 if (r == 0 && S_ISREG(finfo.st_mode)) {
4592 errno = EEXIST;
4593 return -1;
4594 }
4595
4596 /*
4597 * If the file was not present (r != 0), make sure we open it
4598 * exclusively so that if it is created before we open it, our open
4599 * will fail. Make sure that we do not truncate an existing file.
4600 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4601 * file was not a regular file, we leave O_EXCL off.
4602 */
4603 if (r != 0)
4604 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4605 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4606
4607 /* If the open failed, return the file descriptor right away. */
4608 if (fd < 0)
4609 return fd;
4610
4611 /*
4612 * OK, the open succeeded, but the file may have been changed from a
4613 * non-regular file to a regular file between the stat and the open.
4614 * We are assuming that the O_EXCL open handles the case where FILENAME
4615 * did not exist and is symlinked to an existing file between the stat
4616 * and open.
4617 */
4618
4619 /*
4620 * If we can open it and fstat the file descriptor, and neither check
4621 * revealed that it was a regular file, and the file has not been
4622 * replaced, return the file descriptor.
4623 */
4624 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4625 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4626 return fd;
4627
4628 /* The file has been replaced. badness. */
4629 close(fd);
4630 errno = EEXIST;
4631 return -1;
4632}
4633
4634/*
4635 * Handle here documents. Normally we fork off a process to write the
4636 * data to a pipe. If the document is short, we can stuff the data in
4637 * the pipe without forking.
4638 */
4639/* openhere needs this forward reference */
4640static void expandhere(union node *arg, int fd);
4641static int
4642openhere(union node *redir)
4643{
4644 int pip[2];
4645 size_t len = 0;
4646
4647 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004648 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004649 if (redir->type == NHERE) {
4650 len = strlen(redir->nhere.doc->narg.text);
4651 if (len <= PIPESIZE) {
4652 full_write(pip[1], redir->nhere.doc->narg.text, len);
4653 goto out;
4654 }
4655 }
4656 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4657 close(pip[0]);
4658 signal(SIGINT, SIG_IGN);
4659 signal(SIGQUIT, SIG_IGN);
4660 signal(SIGHUP, SIG_IGN);
4661#ifdef SIGTSTP
4662 signal(SIGTSTP, SIG_IGN);
4663#endif
4664 signal(SIGPIPE, SIG_DFL);
4665 if (redir->type == NHERE)
4666 full_write(pip[1], redir->nhere.doc->narg.text, len);
4667 else
4668 expandhere(redir->nhere.doc, pip[1]);
4669 _exit(0);
4670 }
4671 out:
4672 close(pip[1]);
4673 return pip[0];
4674}
4675
4676static int
4677openredirect(union node *redir)
4678{
4679 char *fname;
4680 int f;
4681
4682 switch (redir->nfile.type) {
4683 case NFROM:
4684 fname = redir->nfile.expfname;
4685 f = open(fname, O_RDONLY);
4686 if (f < 0)
4687 goto eopen;
4688 break;
4689 case NFROMTO:
4690 fname = redir->nfile.expfname;
4691 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4692 if (f < 0)
4693 goto ecreate;
4694 break;
4695 case NTO:
4696 /* Take care of noclobber mode. */
4697 if (Cflag) {
4698 fname = redir->nfile.expfname;
4699 f = noclobberopen(fname);
4700 if (f < 0)
4701 goto ecreate;
4702 break;
4703 }
4704 /* FALLTHROUGH */
4705 case NCLOBBER:
4706 fname = redir->nfile.expfname;
4707 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4708 if (f < 0)
4709 goto ecreate;
4710 break;
4711 case NAPPEND:
4712 fname = redir->nfile.expfname;
4713 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4714 if (f < 0)
4715 goto ecreate;
4716 break;
4717 default:
4718#if DEBUG
4719 abort();
4720#endif
4721 /* Fall through to eliminate warning. */
4722 case NTOFD:
4723 case NFROMFD:
4724 f = -1;
4725 break;
4726 case NHERE:
4727 case NXHERE:
4728 f = openhere(redir);
4729 break;
4730 }
4731
4732 return f;
4733 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004734 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004735 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004736 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004737}
4738
4739/*
4740 * Copy a file descriptor to be >= to. Returns -1
4741 * if the source file descriptor is closed, EMPTY if there are no unused
4742 * file descriptors left.
4743 */
4744static int
4745copyfd(int from, int to)
4746{
4747 int newfd;
4748
4749 newfd = fcntl(from, F_DUPFD, to);
4750 if (newfd < 0) {
4751 if (errno == EMFILE)
4752 return EMPTY;
4753 ash_msg_and_raise_error("%d: %m", from);
4754 }
4755 return newfd;
4756}
4757
4758static void
4759dupredirect(union node *redir, int f)
4760{
4761 int fd = redir->nfile.fd;
4762
4763 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4764 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4765 copyfd(redir->ndup.dupfd, fd);
4766 }
4767 return;
4768 }
4769
4770 if (f != fd) {
4771 copyfd(f, fd);
4772 close(f);
4773 }
4774}
4775
4776/*
4777 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4778 * old file descriptors are stashed away so that the redirection can be
4779 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4780 * standard output, and the standard error if it becomes a duplicate of
4781 * stdout, is saved in memory.
4782 */
4783/* flags passed to redirect */
4784#define REDIR_PUSH 01 /* save previous values of file descriptors */
4785#define REDIR_SAVEFD2 03 /* set preverrout */
4786static void
4787redirect(union node *redir, int flags)
4788{
4789 union node *n;
4790 struct redirtab *sv;
4791 int i;
4792 int fd;
4793 int newfd;
4794 int *p;
4795 nullredirs++;
4796 if (!redir) {
4797 return;
4798 }
4799 sv = NULL;
4800 INT_OFF;
4801 if (flags & REDIR_PUSH) {
4802 struct redirtab *q;
4803 q = ckmalloc(sizeof(struct redirtab));
4804 q->next = redirlist;
4805 redirlist = q;
4806 q->nullredirs = nullredirs - 1;
4807 for (i = 0; i < 10; i++)
4808 q->renamed[i] = EMPTY;
4809 nullredirs = 0;
4810 sv = q;
4811 }
4812 n = redir;
4813 do {
4814 fd = n->nfile.fd;
4815 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4816 && n->ndup.dupfd == fd)
4817 continue; /* redirect from/to same file descriptor */
4818
4819 newfd = openredirect(n);
4820 if (fd == newfd)
4821 continue;
4822 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4823 i = fcntl(fd, F_DUPFD, 10);
4824
4825 if (i == -1) {
4826 i = errno;
4827 if (i != EBADF) {
4828 close(newfd);
4829 errno = i;
4830 ash_msg_and_raise_error("%d: %m", fd);
4831 /* NOTREACHED */
4832 }
4833 } else {
4834 *p = i;
4835 close(fd);
4836 }
4837 } else {
4838 close(fd);
4839 }
4840 dupredirect(n, newfd);
4841 } while ((n = n->nfile.next));
4842 INT_ON;
4843 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4844 preverrout_fd = sv->renamed[2];
4845}
4846
4847/*
4848 * Undo the effects of the last redirection.
4849 */
4850static void
4851popredir(int drop)
4852{
4853 struct redirtab *rp;
4854 int i;
4855
4856 if (--nullredirs >= 0)
4857 return;
4858 INT_OFF;
4859 rp = redirlist;
4860 for (i = 0; i < 10; i++) {
4861 if (rp->renamed[i] != EMPTY) {
4862 if (!drop) {
4863 close(i);
4864 copyfd(rp->renamed[i], i);
4865 }
4866 close(rp->renamed[i]);
4867 }
4868 }
4869 redirlist = rp->next;
4870 nullredirs = rp->nullredirs;
4871 free(rp);
4872 INT_ON;
4873}
4874
4875/*
4876 * Undo all redirections. Called on error or interrupt.
4877 */
4878
4879/*
4880 * Discard all saved file descriptors.
4881 */
4882static void
4883clearredir(int drop)
4884{
4885 for (;;) {
4886 nullredirs = 0;
4887 if (!redirlist)
4888 break;
4889 popredir(drop);
4890 }
4891}
4892
4893static int
4894redirectsafe(union node *redir, int flags)
4895{
4896 int err;
4897 volatile int saveint;
4898 struct jmploc *volatile savehandler = exception_handler;
4899 struct jmploc jmploc;
4900
4901 SAVE_INT(saveint);
4902 err = setjmp(jmploc.loc) * 2;
4903 if (!err) {
4904 exception_handler = &jmploc;
4905 redirect(redir, flags);
4906 }
4907 exception_handler = savehandler;
4908 if (err && exception != EXERROR)
4909 longjmp(exception_handler->loc, 1);
4910 RESTORE_INT(saveint);
4911 return err;
4912}
4913
4914
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004915/* ============ Routines to expand arguments to commands
4916 *
4917 * We have to deal with backquotes, shell variables, and file metacharacters.
4918 */
4919
4920/*
4921 * expandarg flags
4922 */
4923#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4924#define EXP_TILDE 0x2 /* do normal tilde expansion */
4925#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4926#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4927#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4928#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4929#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4930#define EXP_WORD 0x80 /* expand word in parameter expansion */
4931#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4932/*
4933 * _rmescape() flags
4934 */
4935#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4936#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4937#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4938#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4939#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4940
4941/*
4942 * Structure specifying which parts of the string should be searched
4943 * for IFS characters.
4944 */
4945struct ifsregion {
4946 struct ifsregion *next; /* next region in list */
4947 int begoff; /* offset of start of region */
4948 int endoff; /* offset of end of region */
4949 int nulonly; /* search for nul bytes only */
4950};
4951
4952struct arglist {
4953 struct strlist *list;
4954 struct strlist **lastp;
4955};
4956
4957/* output of current string */
4958static char *expdest;
4959/* list of back quote expressions */
4960static struct nodelist *argbackq;
4961/* first struct in list of ifs regions */
4962static struct ifsregion ifsfirst;
4963/* last struct in list */
4964static struct ifsregion *ifslastp;
4965/* holds expanded arg list */
4966static struct arglist exparg;
4967
4968/*
4969 * Our own itoa().
4970 */
4971static int
4972cvtnum(arith_t num)
4973{
4974 int len;
4975
4976 expdest = makestrspace(32, expdest);
4977#if ENABLE_ASH_MATH_SUPPORT_64
4978 len = fmtstr(expdest, 32, "%lld", (long long) num);
4979#else
4980 len = fmtstr(expdest, 32, "%ld", num);
4981#endif
4982 STADJUST(len, expdest);
4983 return len;
4984}
4985
4986static size_t
4987esclen(const char *start, const char *p)
4988{
4989 size_t esc = 0;
4990
4991 while (p > start && *--p == CTLESC) {
4992 esc++;
4993 }
4994 return esc;
4995}
4996
4997/*
4998 * Remove any CTLESC characters from a string.
4999 */
5000static char *
5001_rmescapes(char *str, int flag)
5002{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005003 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005004
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005005 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005006 unsigned inquotes;
5007 int notescaped;
5008 int globbing;
5009
5010 p = strpbrk(str, qchars);
5011 if (!p) {
5012 return str;
5013 }
5014 q = p;
5015 r = str;
5016 if (flag & RMESCAPE_ALLOC) {
5017 size_t len = p - str;
5018 size_t fulllen = len + strlen(p) + 1;
5019
5020 if (flag & RMESCAPE_GROW) {
5021 r = makestrspace(fulllen, expdest);
5022 } else if (flag & RMESCAPE_HEAP) {
5023 r = ckmalloc(fulllen);
5024 } else {
5025 r = stalloc(fulllen);
5026 }
5027 q = r;
5028 if (len > 0) {
5029 q = memcpy(q, str, len) + len;
5030 }
5031 }
5032 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5033 globbing = flag & RMESCAPE_GLOB;
5034 notescaped = globbing;
5035 while (*p) {
5036 if (*p == CTLQUOTEMARK) {
5037 inquotes = ~inquotes;
5038 p++;
5039 notescaped = globbing;
5040 continue;
5041 }
5042 if (*p == '\\') {
5043 /* naked back slash */
5044 notescaped = 0;
5045 goto copy;
5046 }
5047 if (*p == CTLESC) {
5048 p++;
5049 if (notescaped && inquotes && *p != '/') {
5050 *q++ = '\\';
5051 }
5052 }
5053 notescaped = globbing;
5054 copy:
5055 *q++ = *p++;
5056 }
5057 *q = '\0';
5058 if (flag & RMESCAPE_GROW) {
5059 expdest = r;
5060 STADJUST(q - r + 1, expdest);
5061 }
5062 return r;
5063}
5064#define rmescapes(p) _rmescapes((p), 0)
5065
5066#define pmatch(a, b) !fnmatch((a), (b), 0)
5067
5068/*
5069 * Prepare a pattern for a expmeta (internal glob(3)) call.
5070 *
5071 * Returns an stalloced string.
5072 */
5073static char *
5074preglob(const char *pattern, int quoted, int flag)
5075{
5076 flag |= RMESCAPE_GLOB;
5077 if (quoted) {
5078 flag |= RMESCAPE_QUOTED;
5079 }
5080 return _rmescapes((char *)pattern, flag);
5081}
5082
5083/*
5084 * Put a string on the stack.
5085 */
5086static void
5087memtodest(const char *p, size_t len, int syntax, int quotes)
5088{
5089 char *q = expdest;
5090
5091 q = makestrspace(len * 2, q);
5092
5093 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005094 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005095 if (!c)
5096 continue;
5097 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5098 USTPUTC(CTLESC, q);
5099 USTPUTC(c, q);
5100 }
5101
5102 expdest = q;
5103}
5104
5105static void
5106strtodest(const char *p, int syntax, int quotes)
5107{
5108 memtodest(p, strlen(p), syntax, quotes);
5109}
5110
5111/*
5112 * Record the fact that we have to scan this region of the
5113 * string for IFS characters.
5114 */
5115static void
5116recordregion(int start, int end, int nulonly)
5117{
5118 struct ifsregion *ifsp;
5119
5120 if (ifslastp == NULL) {
5121 ifsp = &ifsfirst;
5122 } else {
5123 INT_OFF;
5124 ifsp = ckmalloc(sizeof(*ifsp));
5125 ifsp->next = NULL;
5126 ifslastp->next = ifsp;
5127 INT_ON;
5128 }
5129 ifslastp = ifsp;
5130 ifslastp->begoff = start;
5131 ifslastp->endoff = end;
5132 ifslastp->nulonly = nulonly;
5133}
5134
5135static void
5136removerecordregions(int endoff)
5137{
5138 if (ifslastp == NULL)
5139 return;
5140
5141 if (ifsfirst.endoff > endoff) {
5142 while (ifsfirst.next != NULL) {
5143 struct ifsregion *ifsp;
5144 INT_OFF;
5145 ifsp = ifsfirst.next->next;
5146 free(ifsfirst.next);
5147 ifsfirst.next = ifsp;
5148 INT_ON;
5149 }
5150 if (ifsfirst.begoff > endoff)
5151 ifslastp = NULL;
5152 else {
5153 ifslastp = &ifsfirst;
5154 ifsfirst.endoff = endoff;
5155 }
5156 return;
5157 }
5158
5159 ifslastp = &ifsfirst;
5160 while (ifslastp->next && ifslastp->next->begoff < endoff)
5161 ifslastp=ifslastp->next;
5162 while (ifslastp->next != NULL) {
5163 struct ifsregion *ifsp;
5164 INT_OFF;
5165 ifsp = ifslastp->next->next;
5166 free(ifslastp->next);
5167 ifslastp->next = ifsp;
5168 INT_ON;
5169 }
5170 if (ifslastp->endoff > endoff)
5171 ifslastp->endoff = endoff;
5172}
5173
5174static char *
5175exptilde(char *startp, char *p, int flag)
5176{
5177 char c;
5178 char *name;
5179 struct passwd *pw;
5180 const char *home;
5181 int quotes = flag & (EXP_FULL | EXP_CASE);
5182 int startloc;
5183
5184 name = p + 1;
5185
5186 while ((c = *++p) != '\0') {
5187 switch (c) {
5188 case CTLESC:
5189 return startp;
5190 case CTLQUOTEMARK:
5191 return startp;
5192 case ':':
5193 if (flag & EXP_VARTILDE)
5194 goto done;
5195 break;
5196 case '/':
5197 case CTLENDVAR:
5198 goto done;
5199 }
5200 }
5201 done:
5202 *p = '\0';
5203 if (*name == '\0') {
5204 home = lookupvar(homestr);
5205 } else {
5206 pw = getpwnam(name);
5207 if (pw == NULL)
5208 goto lose;
5209 home = pw->pw_dir;
5210 }
5211 if (!home || !*home)
5212 goto lose;
5213 *p = c;
5214 startloc = expdest - (char *)stackblock();
5215 strtodest(home, SQSYNTAX, quotes);
5216 recordregion(startloc, expdest - (char *)stackblock(), 0);
5217 return p;
5218 lose:
5219 *p = c;
5220 return startp;
5221}
5222
5223/*
5224 * Execute a command inside back quotes. If it's a builtin command, we
5225 * want to save its output in a block obtained from malloc. Otherwise
5226 * we fork off a subprocess and get the output of the command via a pipe.
5227 * Should be called with interrupts off.
5228 */
5229struct backcmd { /* result of evalbackcmd */
5230 int fd; /* file descriptor to read from */
5231 char *buf; /* buffer */
5232 int nleft; /* number of chars in buffer */
5233 struct job *jp; /* job structure for command */
5234};
5235
5236/* These forward decls are needed to use "eval" code for backticks handling: */
5237static int back_exitstatus; /* exit status of backquoted command */
5238#define EV_EXIT 01 /* exit after evaluating tree */
5239static void evaltree(union node *, int);
5240
5241static void
5242evalbackcmd(union node *n, struct backcmd *result)
5243{
5244 int saveherefd;
5245
5246 result->fd = -1;
5247 result->buf = NULL;
5248 result->nleft = 0;
5249 result->jp = NULL;
5250 if (n == NULL) {
5251 goto out;
5252 }
5253
5254 saveherefd = herefd;
5255 herefd = -1;
5256
5257 {
5258 int pip[2];
5259 struct job *jp;
5260
5261 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005262 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005263 jp = makejob(n, 1);
5264 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5265 FORCE_INT_ON;
5266 close(pip[0]);
5267 if (pip[1] != 1) {
5268 close(1);
5269 copyfd(pip[1], 1);
5270 close(pip[1]);
5271 }
5272 eflag = 0;
5273 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5274 /* NOTREACHED */
5275 }
5276 close(pip[1]);
5277 result->fd = pip[0];
5278 result->jp = jp;
5279 }
5280 herefd = saveherefd;
5281 out:
5282 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5283 result->fd, result->buf, result->nleft, result->jp));
5284}
5285
5286/*
5287 * Expand stuff in backwards quotes.
5288 */
5289static void
5290expbackq(union node *cmd, int quoted, int quotes)
5291{
5292 struct backcmd in;
5293 int i;
5294 char buf[128];
5295 char *p;
5296 char *dest;
5297 int startloc;
5298 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5299 struct stackmark smark;
5300
5301 INT_OFF;
5302 setstackmark(&smark);
5303 dest = expdest;
5304 startloc = dest - (char *)stackblock();
5305 grabstackstr(dest);
5306 evalbackcmd(cmd, &in);
5307 popstackmark(&smark);
5308
5309 p = in.buf;
5310 i = in.nleft;
5311 if (i == 0)
5312 goto read;
5313 for (;;) {
5314 memtodest(p, i, syntax, quotes);
5315 read:
5316 if (in.fd < 0)
5317 break;
5318 i = safe_read(in.fd, buf, sizeof(buf));
5319 TRACE(("expbackq: read returns %d\n", i));
5320 if (i <= 0)
5321 break;
5322 p = buf;
5323 }
5324
Denis Vlasenko60818682007-09-28 22:07:23 +00005325 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005326 if (in.fd >= 0) {
5327 close(in.fd);
5328 back_exitstatus = waitforjob(in.jp);
5329 }
5330 INT_ON;
5331
5332 /* Eat all trailing newlines */
5333 dest = expdest;
5334 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5335 STUNPUTC(dest);
5336 expdest = dest;
5337
5338 if (quoted == 0)
5339 recordregion(startloc, dest - (char *)stackblock(), 0);
5340 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5341 (dest - (char *)stackblock()) - startloc,
5342 (dest - (char *)stackblock()) - startloc,
5343 stackblock() + startloc));
5344}
5345
5346#if ENABLE_ASH_MATH_SUPPORT
5347/*
5348 * Expand arithmetic expression. Backup to start of expression,
5349 * evaluate, place result in (backed up) result, adjust string position.
5350 */
5351static void
5352expari(int quotes)
5353{
5354 char *p, *start;
5355 int begoff;
5356 int flag;
5357 int len;
5358
5359 /* ifsfree(); */
5360
5361 /*
5362 * This routine is slightly over-complicated for
5363 * efficiency. Next we scan backwards looking for the
5364 * start of arithmetic.
5365 */
5366 start = stackblock();
5367 p = expdest - 1;
5368 *p = '\0';
5369 p--;
5370 do {
5371 int esc;
5372
5373 while (*p != CTLARI) {
5374 p--;
5375#if DEBUG
5376 if (p < start) {
5377 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5378 }
5379#endif
5380 }
5381
5382 esc = esclen(start, p);
5383 if (!(esc % 2)) {
5384 break;
5385 }
5386
5387 p -= esc + 1;
5388 } while (1);
5389
5390 begoff = p - start;
5391
5392 removerecordregions(begoff);
5393
5394 flag = p[1];
5395
5396 expdest = p;
5397
5398 if (quotes)
5399 rmescapes(p + 2);
5400
5401 len = cvtnum(dash_arith(p + 2));
5402
5403 if (flag != '"')
5404 recordregion(begoff, begoff + len, 0);
5405}
5406#endif
5407
5408/* argstr needs it */
5409static char *evalvar(char *p, int flag);
5410
5411/*
5412 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5413 * characters to allow for further processing. Otherwise treat
5414 * $@ like $* since no splitting will be performed.
5415 */
5416static void
5417argstr(char *p, int flag)
5418{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005419 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005420 '=',
5421 ':',
5422 CTLQUOTEMARK,
5423 CTLENDVAR,
5424 CTLESC,
5425 CTLVAR,
5426 CTLBACKQ,
5427 CTLBACKQ | CTLQUOTE,
5428#if ENABLE_ASH_MATH_SUPPORT
5429 CTLENDARI,
5430#endif
5431 0
5432 };
5433 const char *reject = spclchars;
5434 int c;
5435 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5436 int breakall = flag & EXP_WORD;
5437 int inquotes;
5438 size_t length;
5439 int startloc;
5440
5441 if (!(flag & EXP_VARTILDE)) {
5442 reject += 2;
5443 } else if (flag & EXP_VARTILDE2) {
5444 reject++;
5445 }
5446 inquotes = 0;
5447 length = 0;
5448 if (flag & EXP_TILDE) {
5449 char *q;
5450
5451 flag &= ~EXP_TILDE;
5452 tilde:
5453 q = p;
5454 if (*q == CTLESC && (flag & EXP_QWORD))
5455 q++;
5456 if (*q == '~')
5457 p = exptilde(p, q, flag);
5458 }
5459 start:
5460 startloc = expdest - (char *)stackblock();
5461 for (;;) {
5462 length += strcspn(p + length, reject);
5463 c = p[length];
5464 if (c && (!(c & 0x80)
5465#if ENABLE_ASH_MATH_SUPPORT
5466 || c == CTLENDARI
5467#endif
5468 )) {
5469 /* c == '=' || c == ':' || c == CTLENDARI */
5470 length++;
5471 }
5472 if (length > 0) {
5473 int newloc;
5474 expdest = stack_nputstr(p, length, expdest);
5475 newloc = expdest - (char *)stackblock();
5476 if (breakall && !inquotes && newloc > startloc) {
5477 recordregion(startloc, newloc, 0);
5478 }
5479 startloc = newloc;
5480 }
5481 p += length + 1;
5482 length = 0;
5483
5484 switch (c) {
5485 case '\0':
5486 goto breakloop;
5487 case '=':
5488 if (flag & EXP_VARTILDE2) {
5489 p--;
5490 continue;
5491 }
5492 flag |= EXP_VARTILDE2;
5493 reject++;
5494 /* fall through */
5495 case ':':
5496 /*
5497 * sort of a hack - expand tildes in variable
5498 * assignments (after the first '=' and after ':'s).
5499 */
5500 if (*--p == '~') {
5501 goto tilde;
5502 }
5503 continue;
5504 }
5505
5506 switch (c) {
5507 case CTLENDVAR: /* ??? */
5508 goto breakloop;
5509 case CTLQUOTEMARK:
5510 /* "$@" syntax adherence hack */
5511 if (
5512 !inquotes &&
5513 !memcmp(p, dolatstr, 4) &&
5514 (p[4] == CTLQUOTEMARK || (
5515 p[4] == CTLENDVAR &&
5516 p[5] == CTLQUOTEMARK
5517 ))
5518 ) {
5519 p = evalvar(p + 1, flag) + 1;
5520 goto start;
5521 }
5522 inquotes = !inquotes;
5523 addquote:
5524 if (quotes) {
5525 p--;
5526 length++;
5527 startloc++;
5528 }
5529 break;
5530 case CTLESC:
5531 startloc++;
5532 length++;
5533 goto addquote;
5534 case CTLVAR:
5535 p = evalvar(p, flag);
5536 goto start;
5537 case CTLBACKQ:
5538 c = 0;
5539 case CTLBACKQ|CTLQUOTE:
5540 expbackq(argbackq->n, c, quotes);
5541 argbackq = argbackq->next;
5542 goto start;
5543#if ENABLE_ASH_MATH_SUPPORT
5544 case CTLENDARI:
5545 p--;
5546 expari(quotes);
5547 goto start;
5548#endif
5549 }
5550 }
5551 breakloop:
5552 ;
5553}
5554
5555static char *
5556scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5557 int zero)
5558{
5559 char *loc;
5560 char *loc2;
5561 char c;
5562
5563 loc = startp;
5564 loc2 = rmesc;
5565 do {
5566 int match;
5567 const char *s = loc2;
5568 c = *loc2;
5569 if (zero) {
5570 *loc2 = '\0';
5571 s = rmesc;
5572 }
5573 match = pmatch(str, s);
5574 *loc2 = c;
5575 if (match)
5576 return loc;
5577 if (quotes && *loc == CTLESC)
5578 loc++;
5579 loc++;
5580 loc2++;
5581 } while (c);
5582 return 0;
5583}
5584
5585static char *
5586scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5587 int zero)
5588{
5589 int esc = 0;
5590 char *loc;
5591 char *loc2;
5592
5593 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5594 int match;
5595 char c = *loc2;
5596 const char *s = loc2;
5597 if (zero) {
5598 *loc2 = '\0';
5599 s = rmesc;
5600 }
5601 match = pmatch(str, s);
5602 *loc2 = c;
5603 if (match)
5604 return loc;
5605 loc--;
5606 if (quotes) {
5607 if (--esc < 0) {
5608 esc = esclen(startp, loc);
5609 }
5610 if (esc % 2) {
5611 esc--;
5612 loc--;
5613 }
5614 }
5615 }
5616 return 0;
5617}
5618
5619static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5620static void
5621varunset(const char *end, const char *var, const char *umsg, int varflags)
5622{
5623 const char *msg;
5624 const char *tail;
5625
5626 tail = nullstr;
5627 msg = "parameter not set";
5628 if (umsg) {
5629 if (*end == CTLENDVAR) {
5630 if (varflags & VSNUL)
5631 tail = " or null";
5632 } else
5633 msg = umsg;
5634 }
5635 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5636}
5637
5638static const char *
5639subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5640{
5641 char *startp;
5642 char *loc;
5643 int saveherefd = herefd;
5644 struct nodelist *saveargbackq = argbackq;
5645 int amount;
5646 char *rmesc, *rmescend;
5647 int zero;
5648 char *(*scan)(char *, char *, char *, char *, int , int);
5649
5650 herefd = -1;
5651 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5652 STPUTC('\0', expdest);
5653 herefd = saveherefd;
5654 argbackq = saveargbackq;
5655 startp = stackblock() + startloc;
5656
5657 switch (subtype) {
5658 case VSASSIGN:
5659 setvar(str, startp, 0);
5660 amount = startp - expdest;
5661 STADJUST(amount, expdest);
5662 return startp;
5663
5664 case VSQUESTION:
5665 varunset(p, str, startp, varflags);
5666 /* NOTREACHED */
5667 }
5668
5669 subtype -= VSTRIMRIGHT;
5670#if DEBUG
5671 if (subtype < 0 || subtype > 3)
5672 abort();
5673#endif
5674
5675 rmesc = startp;
5676 rmescend = stackblock() + strloc;
5677 if (quotes) {
5678 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5679 if (rmesc != startp) {
5680 rmescend = expdest;
5681 startp = stackblock() + startloc;
5682 }
5683 }
5684 rmescend--;
5685 str = stackblock() + strloc;
5686 preglob(str, varflags & VSQUOTE, 0);
5687
5688 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5689 zero = subtype >> 1;
5690 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5691 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5692
5693 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5694 if (loc) {
5695 if (zero) {
5696 memmove(startp, loc, str - loc);
5697 loc = startp + (str - loc) - 1;
5698 }
5699 *loc = '\0';
5700 amount = loc - expdest;
5701 STADJUST(amount, expdest);
5702 }
5703 return loc;
5704}
5705
5706/*
5707 * Add the value of a specialized variable to the stack string.
5708 */
5709static ssize_t
5710varvalue(char *name, int varflags, int flags)
5711{
5712 int num;
5713 char *p;
5714 int i;
5715 int sep = 0;
5716 int sepq = 0;
5717 ssize_t len = 0;
5718 char **ap;
5719 int syntax;
5720 int quoted = varflags & VSQUOTE;
5721 int subtype = varflags & VSTYPE;
5722 int quotes = flags & (EXP_FULL | EXP_CASE);
5723
5724 if (quoted && (flags & EXP_FULL))
5725 sep = 1 << CHAR_BIT;
5726
5727 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5728 switch (*name) {
5729 case '$':
5730 num = rootpid;
5731 goto numvar;
5732 case '?':
5733 num = exitstatus;
5734 goto numvar;
5735 case '#':
5736 num = shellparam.nparam;
5737 goto numvar;
5738 case '!':
5739 num = backgndpid;
5740 if (num == 0)
5741 return -1;
5742 numvar:
5743 len = cvtnum(num);
5744 break;
5745 case '-':
5746 p = makestrspace(NOPTS, expdest);
5747 for (i = NOPTS - 1; i >= 0; i--) {
5748 if (optlist[i]) {
5749 USTPUTC(optletters(i), p);
5750 len++;
5751 }
5752 }
5753 expdest = p;
5754 break;
5755 case '@':
5756 if (sep)
5757 goto param;
5758 /* fall through */
5759 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005760 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005761 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5762 sepq = 1;
5763 param:
5764 ap = shellparam.p;
5765 if (!ap)
5766 return -1;
5767 while ((p = *ap++)) {
5768 size_t partlen;
5769
5770 partlen = strlen(p);
5771 len += partlen;
5772
5773 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5774 memtodest(p, partlen, syntax, quotes);
5775
5776 if (*ap && sep) {
5777 char *q;
5778
5779 len++;
5780 if (subtype == VSPLUS || subtype == VSLENGTH) {
5781 continue;
5782 }
5783 q = expdest;
5784 if (sepq)
5785 STPUTC(CTLESC, q);
5786 STPUTC(sep, q);
5787 expdest = q;
5788 }
5789 }
5790 return len;
5791 case '0':
5792 case '1':
5793 case '2':
5794 case '3':
5795 case '4':
5796 case '5':
5797 case '6':
5798 case '7':
5799 case '8':
5800 case '9':
5801 num = atoi(name);
5802 if (num < 0 || num > shellparam.nparam)
5803 return -1;
5804 p = num ? shellparam.p[num - 1] : arg0;
5805 goto value;
5806 default:
5807 p = lookupvar(name);
5808 value:
5809 if (!p)
5810 return -1;
5811
5812 len = strlen(p);
5813 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5814 memtodest(p, len, syntax, quotes);
5815 return len;
5816 }
5817
5818 if (subtype == VSPLUS || subtype == VSLENGTH)
5819 STADJUST(-len, expdest);
5820 return len;
5821}
5822
5823/*
5824 * Expand a variable, and return a pointer to the next character in the
5825 * input string.
5826 */
5827static char *
5828evalvar(char *p, int flag)
5829{
5830 int subtype;
5831 int varflags;
5832 char *var;
5833 int patloc;
5834 int c;
5835 int startloc;
5836 ssize_t varlen;
5837 int easy;
5838 int quotes;
5839 int quoted;
5840
5841 quotes = flag & (EXP_FULL | EXP_CASE);
5842 varflags = *p++;
5843 subtype = varflags & VSTYPE;
5844 quoted = varflags & VSQUOTE;
5845 var = p;
5846 easy = (!quoted || (*var == '@' && shellparam.nparam));
5847 startloc = expdest - (char *)stackblock();
5848 p = strchr(p, '=') + 1;
5849
5850 again:
5851 varlen = varvalue(var, varflags, flag);
5852 if (varflags & VSNUL)
5853 varlen--;
5854
5855 if (subtype == VSPLUS) {
5856 varlen = -1 - varlen;
5857 goto vsplus;
5858 }
5859
5860 if (subtype == VSMINUS) {
5861 vsplus:
5862 if (varlen < 0) {
5863 argstr(
5864 p, flag | EXP_TILDE |
5865 (quoted ? EXP_QWORD : EXP_WORD)
5866 );
5867 goto end;
5868 }
5869 if (easy)
5870 goto record;
5871 goto end;
5872 }
5873
5874 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5875 if (varlen < 0) {
5876 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5877 varflags &= ~VSNUL;
5878 /*
5879 * Remove any recorded regions beyond
5880 * start of variable
5881 */
5882 removerecordregions(startloc);
5883 goto again;
5884 }
5885 goto end;
5886 }
5887 if (easy)
5888 goto record;
5889 goto end;
5890 }
5891
5892 if (varlen < 0 && uflag)
5893 varunset(p, var, 0, 0);
5894
5895 if (subtype == VSLENGTH) {
5896 cvtnum(varlen > 0 ? varlen : 0);
5897 goto record;
5898 }
5899
5900 if (subtype == VSNORMAL) {
5901 if (!easy)
5902 goto end;
5903 record:
5904 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5905 goto end;
5906 }
5907
5908#if DEBUG
5909 switch (subtype) {
5910 case VSTRIMLEFT:
5911 case VSTRIMLEFTMAX:
5912 case VSTRIMRIGHT:
5913 case VSTRIMRIGHTMAX:
5914 break;
5915 default:
5916 abort();
5917 }
5918#endif
5919
5920 if (varlen >= 0) {
5921 /*
5922 * Terminate the string and start recording the pattern
5923 * right after it
5924 */
5925 STPUTC('\0', expdest);
5926 patloc = expdest - (char *)stackblock();
5927 if (subevalvar(p, NULL, patloc, subtype,
5928 startloc, varflags, quotes) == 0) {
5929 int amount = expdest - (
5930 (char *)stackblock() + patloc - 1
5931 );
5932 STADJUST(-amount, expdest);
5933 }
5934 /* Remove any recorded regions beyond start of variable */
5935 removerecordregions(startloc);
5936 goto record;
5937 }
5938
5939 end:
5940 if (subtype != VSNORMAL) { /* skip to end of alternative */
5941 int nesting = 1;
5942 for (;;) {
5943 c = *p++;
5944 if (c == CTLESC)
5945 p++;
5946 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5947 if (varlen >= 0)
5948 argbackq = argbackq->next;
5949 } else if (c == CTLVAR) {
5950 if ((*p++ & VSTYPE) != VSNORMAL)
5951 nesting++;
5952 } else if (c == CTLENDVAR) {
5953 if (--nesting == 0)
5954 break;
5955 }
5956 }
5957 }
5958 return p;
5959}
5960
5961/*
5962 * Break the argument string into pieces based upon IFS and add the
5963 * strings to the argument list. The regions of the string to be
5964 * searched for IFS characters have been stored by recordregion.
5965 */
5966static void
5967ifsbreakup(char *string, struct arglist *arglist)
5968{
5969 struct ifsregion *ifsp;
5970 struct strlist *sp;
5971 char *start;
5972 char *p;
5973 char *q;
5974 const char *ifs, *realifs;
5975 int ifsspc;
5976 int nulonly;
5977
5978 start = string;
5979 if (ifslastp != NULL) {
5980 ifsspc = 0;
5981 nulonly = 0;
5982 realifs = ifsset() ? ifsval() : defifs;
5983 ifsp = &ifsfirst;
5984 do {
5985 p = string + ifsp->begoff;
5986 nulonly = ifsp->nulonly;
5987 ifs = nulonly ? nullstr : realifs;
5988 ifsspc = 0;
5989 while (p < string + ifsp->endoff) {
5990 q = p;
5991 if (*p == CTLESC)
5992 p++;
5993 if (!strchr(ifs, *p)) {
5994 p++;
5995 continue;
5996 }
5997 if (!nulonly)
5998 ifsspc = (strchr(defifs, *p) != NULL);
5999 /* Ignore IFS whitespace at start */
6000 if (q == start && ifsspc) {
6001 p++;
6002 start = p;
6003 continue;
6004 }
6005 *q = '\0';
6006 sp = stalloc(sizeof(*sp));
6007 sp->text = start;
6008 *arglist->lastp = sp;
6009 arglist->lastp = &sp->next;
6010 p++;
6011 if (!nulonly) {
6012 for (;;) {
6013 if (p >= string + ifsp->endoff) {
6014 break;
6015 }
6016 q = p;
6017 if (*p == CTLESC)
6018 p++;
6019 if (strchr(ifs, *p) == NULL ) {
6020 p = q;
6021 break;
6022 } else if (strchr(defifs, *p) == NULL) {
6023 if (ifsspc) {
6024 p++;
6025 ifsspc = 0;
6026 } else {
6027 p = q;
6028 break;
6029 }
6030 } else
6031 p++;
6032 }
6033 }
6034 start = p;
6035 } /* while */
6036 ifsp = ifsp->next;
6037 } while (ifsp != NULL);
6038 if (nulonly)
6039 goto add;
6040 }
6041
6042 if (!*start)
6043 return;
6044
6045 add:
6046 sp = stalloc(sizeof(*sp));
6047 sp->text = start;
6048 *arglist->lastp = sp;
6049 arglist->lastp = &sp->next;
6050}
6051
6052static void
6053ifsfree(void)
6054{
6055 struct ifsregion *p;
6056
6057 INT_OFF;
6058 p = ifsfirst.next;
6059 do {
6060 struct ifsregion *ifsp;
6061 ifsp = p->next;
6062 free(p);
6063 p = ifsp;
6064 } while (p);
6065 ifslastp = NULL;
6066 ifsfirst.next = NULL;
6067 INT_ON;
6068}
6069
6070/*
6071 * Add a file name to the list.
6072 */
6073static void
6074addfname(const char *name)
6075{
6076 struct strlist *sp;
6077
6078 sp = stalloc(sizeof(*sp));
6079 sp->text = ststrdup(name);
6080 *exparg.lastp = sp;
6081 exparg.lastp = &sp->next;
6082}
6083
6084static char *expdir;
6085
6086/*
6087 * Do metacharacter (i.e. *, ?, [...]) expansion.
6088 */
6089static void
6090expmeta(char *enddir, char *name)
6091{
6092 char *p;
6093 const char *cp;
6094 char *start;
6095 char *endname;
6096 int metaflag;
6097 struct stat statb;
6098 DIR *dirp;
6099 struct dirent *dp;
6100 int atend;
6101 int matchdot;
6102
6103 metaflag = 0;
6104 start = name;
6105 for (p = name; *p; p++) {
6106 if (*p == '*' || *p == '?')
6107 metaflag = 1;
6108 else if (*p == '[') {
6109 char *q = p + 1;
6110 if (*q == '!')
6111 q++;
6112 for (;;) {
6113 if (*q == '\\')
6114 q++;
6115 if (*q == '/' || *q == '\0')
6116 break;
6117 if (*++q == ']') {
6118 metaflag = 1;
6119 break;
6120 }
6121 }
6122 } else if (*p == '\\')
6123 p++;
6124 else if (*p == '/') {
6125 if (metaflag)
6126 goto out;
6127 start = p + 1;
6128 }
6129 }
6130 out:
6131 if (metaflag == 0) { /* we've reached the end of the file name */
6132 if (enddir != expdir)
6133 metaflag++;
6134 p = name;
6135 do {
6136 if (*p == '\\')
6137 p++;
6138 *enddir++ = *p;
6139 } while (*p++);
6140 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6141 addfname(expdir);
6142 return;
6143 }
6144 endname = p;
6145 if (name < start) {
6146 p = name;
6147 do {
6148 if (*p == '\\')
6149 p++;
6150 *enddir++ = *p++;
6151 } while (p < start);
6152 }
6153 if (enddir == expdir) {
6154 cp = ".";
6155 } else if (enddir == expdir + 1 && *expdir == '/') {
6156 cp = "/";
6157 } else {
6158 cp = expdir;
6159 enddir[-1] = '\0';
6160 }
6161 dirp = opendir(cp);
6162 if (dirp == NULL)
6163 return;
6164 if (enddir != expdir)
6165 enddir[-1] = '/';
6166 if (*endname == 0) {
6167 atend = 1;
6168 } else {
6169 atend = 0;
6170 *endname++ = '\0';
6171 }
6172 matchdot = 0;
6173 p = start;
6174 if (*p == '\\')
6175 p++;
6176 if (*p == '.')
6177 matchdot++;
6178 while (! intpending && (dp = readdir(dirp)) != NULL) {
6179 if (dp->d_name[0] == '.' && ! matchdot)
6180 continue;
6181 if (pmatch(start, dp->d_name)) {
6182 if (atend) {
6183 strcpy(enddir, dp->d_name);
6184 addfname(expdir);
6185 } else {
6186 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6187 continue;
6188 p[-1] = '/';
6189 expmeta(p, endname);
6190 }
6191 }
6192 }
6193 closedir(dirp);
6194 if (! atend)
6195 endname[-1] = '/';
6196}
6197
6198static struct strlist *
6199msort(struct strlist *list, int len)
6200{
6201 struct strlist *p, *q = NULL;
6202 struct strlist **lpp;
6203 int half;
6204 int n;
6205
6206 if (len <= 1)
6207 return list;
6208 half = len >> 1;
6209 p = list;
6210 for (n = half; --n >= 0; ) {
6211 q = p;
6212 p = p->next;
6213 }
6214 q->next = NULL; /* terminate first half of list */
6215 q = msort(list, half); /* sort first half of list */
6216 p = msort(p, len - half); /* sort second half */
6217 lpp = &list;
6218 for (;;) {
6219#if ENABLE_LOCALE_SUPPORT
6220 if (strcoll(p->text, q->text) < 0)
6221#else
6222 if (strcmp(p->text, q->text) < 0)
6223#endif
6224 {
6225 *lpp = p;
6226 lpp = &p->next;
6227 p = *lpp;
6228 if (p == NULL) {
6229 *lpp = q;
6230 break;
6231 }
6232 } else {
6233 *lpp = q;
6234 lpp = &q->next;
6235 q = *lpp;
6236 if (q == NULL) {
6237 *lpp = p;
6238 break;
6239 }
6240 }
6241 }
6242 return list;
6243}
6244
6245/*
6246 * Sort the results of file name expansion. It calculates the number of
6247 * strings to sort and then calls msort (short for merge sort) to do the
6248 * work.
6249 */
6250static struct strlist *
6251expsort(struct strlist *str)
6252{
6253 int len;
6254 struct strlist *sp;
6255
6256 len = 0;
6257 for (sp = str; sp; sp = sp->next)
6258 len++;
6259 return msort(str, len);
6260}
6261
6262static void
6263expandmeta(struct strlist *str, int flag)
6264{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006265 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266 '*', '?', '[', 0
6267 };
6268 /* TODO - EXP_REDIR */
6269
6270 while (str) {
6271 struct strlist **savelastp;
6272 struct strlist *sp;
6273 char *p;
6274
6275 if (fflag)
6276 goto nometa;
6277 if (!strpbrk(str->text, metachars))
6278 goto nometa;
6279 savelastp = exparg.lastp;
6280
6281 INT_OFF;
6282 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6283 {
6284 int i = strlen(str->text);
6285 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6286 }
6287
6288 expmeta(expdir, p);
6289 free(expdir);
6290 if (p != str->text)
6291 free(p);
6292 INT_ON;
6293 if (exparg.lastp == savelastp) {
6294 /*
6295 * no matches
6296 */
6297 nometa:
6298 *exparg.lastp = str;
6299 rmescapes(str->text);
6300 exparg.lastp = &str->next;
6301 } else {
6302 *exparg.lastp = NULL;
6303 *savelastp = sp = expsort(*savelastp);
6304 while (sp->next != NULL)
6305 sp = sp->next;
6306 exparg.lastp = &sp->next;
6307 }
6308 str = str->next;
6309 }
6310}
6311
6312/*
6313 * Perform variable substitution and command substitution on an argument,
6314 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6315 * perform splitting and file name expansion. When arglist is NULL, perform
6316 * here document expansion.
6317 */
6318static void
6319expandarg(union node *arg, struct arglist *arglist, int flag)
6320{
6321 struct strlist *sp;
6322 char *p;
6323
6324 argbackq = arg->narg.backquote;
6325 STARTSTACKSTR(expdest);
6326 ifsfirst.next = NULL;
6327 ifslastp = NULL;
6328 argstr(arg->narg.text, flag);
6329 p = _STPUTC('\0', expdest);
6330 expdest = p - 1;
6331 if (arglist == NULL) {
6332 return; /* here document expanded */
6333 }
6334 p = grabstackstr(p);
6335 exparg.lastp = &exparg.list;
6336 /*
6337 * TODO - EXP_REDIR
6338 */
6339 if (flag & EXP_FULL) {
6340 ifsbreakup(p, &exparg);
6341 *exparg.lastp = NULL;
6342 exparg.lastp = &exparg.list;
6343 expandmeta(exparg.list, flag);
6344 } else {
6345 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6346 rmescapes(p);
6347 sp = stalloc(sizeof(*sp));
6348 sp->text = p;
6349 *exparg.lastp = sp;
6350 exparg.lastp = &sp->next;
6351 }
6352 if (ifsfirst.next)
6353 ifsfree();
6354 *exparg.lastp = NULL;
6355 if (exparg.list) {
6356 *arglist->lastp = exparg.list;
6357 arglist->lastp = exparg.lastp;
6358 }
6359}
6360
6361/*
6362 * Expand shell variables and backquotes inside a here document.
6363 */
6364static void
6365expandhere(union node *arg, int fd)
6366{
6367 herefd = fd;
6368 expandarg(arg, (struct arglist *)NULL, 0);
6369 full_write(fd, stackblock(), expdest - (char *)stackblock());
6370}
6371
6372/*
6373 * Returns true if the pattern matches the string.
6374 */
6375static int
6376patmatch(char *pattern, const char *string)
6377{
6378 return pmatch(preglob(pattern, 0, 0), string);
6379}
6380
6381/*
6382 * See if a pattern matches in a case statement.
6383 */
6384static int
6385casematch(union node *pattern, char *val)
6386{
6387 struct stackmark smark;
6388 int result;
6389
6390 setstackmark(&smark);
6391 argbackq = pattern->narg.backquote;
6392 STARTSTACKSTR(expdest);
6393 ifslastp = NULL;
6394 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6395 STACKSTRNUL(expdest);
6396 result = patmatch(stackblock(), val);
6397 popstackmark(&smark);
6398 return result;
6399}
6400
6401
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006402/* ============ find_command */
6403
6404struct builtincmd {
6405 const char *name;
6406 int (*builtin)(int, char **);
6407 /* unsigned flags; */
6408};
6409#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6410#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6411#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6412
6413struct cmdentry {
6414 int cmdtype;
6415 union param {
6416 int index;
6417 const struct builtincmd *cmd;
6418 struct funcnode *func;
6419 } u;
6420};
6421/* values of cmdtype */
6422#define CMDUNKNOWN -1 /* no entry in table for command */
6423#define CMDNORMAL 0 /* command is an executable program */
6424#define CMDFUNCTION 1 /* command is a shell function */
6425#define CMDBUILTIN 2 /* command is a shell builtin */
6426
6427/* action to find_command() */
6428#define DO_ERR 0x01 /* prints errors */
6429#define DO_ABS 0x02 /* checks absolute paths */
6430#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6431#define DO_ALTPATH 0x08 /* using alternate path */
6432#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6433
6434static void find_command(char *, struct cmdentry *, int, const char *);
6435
6436
6437/* ============ Hashing commands */
6438
6439/*
6440 * When commands are first encountered, they are entered in a hash table.
6441 * This ensures that a full path search will not have to be done for them
6442 * on each invocation.
6443 *
6444 * We should investigate converting to a linear search, even though that
6445 * would make the command name "hash" a misnomer.
6446 */
6447
6448#define CMDTABLESIZE 31 /* should be prime */
6449#define ARB 1 /* actual size determined at run time */
6450
6451struct tblentry {
6452 struct tblentry *next; /* next entry in hash chain */
6453 union param param; /* definition of builtin function */
6454 short cmdtype; /* index identifying command */
6455 char rehash; /* if set, cd done since entry created */
6456 char cmdname[ARB]; /* name of command */
6457};
6458
6459static struct tblentry *cmdtable[CMDTABLESIZE];
6460static int builtinloc = -1; /* index in path of %builtin, or -1 */
6461
6462static void
6463tryexec(char *cmd, char **argv, char **envp)
6464{
6465 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006466
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006467#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006468 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006469 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006470
6471 a = find_applet_by_name(cmd);
6472 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006473 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006474 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006475 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006476 }
6477 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006478 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006479 /* If they called chroot or otherwise made the binary no longer
6480 * executable, fall through */
6481 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006482 }
6483#endif
6484
6485 repeat:
6486#ifdef SYSV
6487 do {
6488 execve(cmd, argv, envp);
6489 } while (errno == EINTR);
6490#else
6491 execve(cmd, argv, envp);
6492#endif
6493 if (repeated++) {
6494 free(argv);
6495 } else if (errno == ENOEXEC) {
6496 char **ap;
6497 char **new;
6498
6499 for (ap = argv; *ap; ap++)
6500 ;
6501 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6502 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006503 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006504 ap += 2;
6505 argv++;
6506 while ((*ap++ = *argv++))
6507 ;
6508 argv = new;
6509 goto repeat;
6510 }
6511}
6512
6513/*
6514 * Exec a program. Never returns. If you change this routine, you may
6515 * have to change the find_command routine as well.
6516 */
6517#define environment() listvars(VEXPORT, VUNSET, 0)
6518static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6519static void
6520shellexec(char **argv, const char *path, int idx)
6521{
6522 char *cmdname;
6523 int e;
6524 char **envp;
6525 int exerrno;
6526
6527 clearredir(1);
6528 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006529 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006530#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006531 || find_applet_by_name(argv[0])
6532#endif
6533 ) {
6534 tryexec(argv[0], argv, envp);
6535 e = errno;
6536 } else {
6537 e = ENOENT;
6538 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6539 if (--idx < 0 && pathopt == NULL) {
6540 tryexec(cmdname, argv, envp);
6541 if (errno != ENOENT && errno != ENOTDIR)
6542 e = errno;
6543 }
6544 stunalloc(cmdname);
6545 }
6546 }
6547
6548 /* Map to POSIX errors */
6549 switch (e) {
6550 case EACCES:
6551 exerrno = 126;
6552 break;
6553 case ENOENT:
6554 exerrno = 127;
6555 break;
6556 default:
6557 exerrno = 2;
6558 break;
6559 }
6560 exitstatus = exerrno;
6561 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6562 argv[0], e, suppressint ));
6563 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6564 /* NOTREACHED */
6565}
6566
6567static void
6568printentry(struct tblentry *cmdp)
6569{
6570 int idx;
6571 const char *path;
6572 char *name;
6573
6574 idx = cmdp->param.index;
6575 path = pathval();
6576 do {
6577 name = padvance(&path, cmdp->cmdname);
6578 stunalloc(name);
6579 } while (--idx >= 0);
6580 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6581}
6582
6583/*
6584 * Clear out command entries. The argument specifies the first entry in
6585 * PATH which has changed.
6586 */
6587static void
6588clearcmdentry(int firstchange)
6589{
6590 struct tblentry **tblp;
6591 struct tblentry **pp;
6592 struct tblentry *cmdp;
6593
6594 INT_OFF;
6595 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6596 pp = tblp;
6597 while ((cmdp = *pp) != NULL) {
6598 if ((cmdp->cmdtype == CMDNORMAL &&
6599 cmdp->param.index >= firstchange)
6600 || (cmdp->cmdtype == CMDBUILTIN &&
6601 builtinloc >= firstchange)
6602 ) {
6603 *pp = cmdp->next;
6604 free(cmdp);
6605 } else {
6606 pp = &cmdp->next;
6607 }
6608 }
6609 }
6610 INT_ON;
6611}
6612
6613/*
6614 * Locate a command in the command hash table. If "add" is nonzero,
6615 * add the command to the table if it is not already present. The
6616 * variable "lastcmdentry" is set to point to the address of the link
6617 * pointing to the entry, so that delete_cmd_entry can delete the
6618 * entry.
6619 *
6620 * Interrupts must be off if called with add != 0.
6621 */
6622static struct tblentry **lastcmdentry;
6623
6624static struct tblentry *
6625cmdlookup(const char *name, int add)
6626{
6627 unsigned int hashval;
6628 const char *p;
6629 struct tblentry *cmdp;
6630 struct tblentry **pp;
6631
6632 p = name;
6633 hashval = (unsigned char)*p << 4;
6634 while (*p)
6635 hashval += (unsigned char)*p++;
6636 hashval &= 0x7FFF;
6637 pp = &cmdtable[hashval % CMDTABLESIZE];
6638 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6639 if (strcmp(cmdp->cmdname, name) == 0)
6640 break;
6641 pp = &cmdp->next;
6642 }
6643 if (add && cmdp == NULL) {
6644 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6645 + strlen(name) + 1);
6646 cmdp->next = NULL;
6647 cmdp->cmdtype = CMDUNKNOWN;
6648 strcpy(cmdp->cmdname, name);
6649 }
6650 lastcmdentry = pp;
6651 return cmdp;
6652}
6653
6654/*
6655 * Delete the command entry returned on the last lookup.
6656 */
6657static void
6658delete_cmd_entry(void)
6659{
6660 struct tblentry *cmdp;
6661
6662 INT_OFF;
6663 cmdp = *lastcmdentry;
6664 *lastcmdentry = cmdp->next;
6665 if (cmdp->cmdtype == CMDFUNCTION)
6666 freefunc(cmdp->param.func);
6667 free(cmdp);
6668 INT_ON;
6669}
6670
6671/*
6672 * Add a new command entry, replacing any existing command entry for
6673 * the same name - except special builtins.
6674 */
6675static void
6676addcmdentry(char *name, struct cmdentry *entry)
6677{
6678 struct tblentry *cmdp;
6679
6680 cmdp = cmdlookup(name, 1);
6681 if (cmdp->cmdtype == CMDFUNCTION) {
6682 freefunc(cmdp->param.func);
6683 }
6684 cmdp->cmdtype = entry->cmdtype;
6685 cmdp->param = entry->u;
6686 cmdp->rehash = 0;
6687}
6688
6689static int
6690hashcmd(int argc, char **argv)
6691{
6692 struct tblentry **pp;
6693 struct tblentry *cmdp;
6694 int c;
6695 struct cmdentry entry;
6696 char *name;
6697
6698 while ((c = nextopt("r")) != '\0') {
6699 clearcmdentry(0);
6700 return 0;
6701 }
6702 if (*argptr == NULL) {
6703 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6704 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6705 if (cmdp->cmdtype == CMDNORMAL)
6706 printentry(cmdp);
6707 }
6708 }
6709 return 0;
6710 }
6711 c = 0;
6712 while ((name = *argptr) != NULL) {
6713 cmdp = cmdlookup(name, 0);
6714 if (cmdp != NULL
6715 && (cmdp->cmdtype == CMDNORMAL
6716 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6717 delete_cmd_entry();
6718 find_command(name, &entry, DO_ERR, pathval());
6719 if (entry.cmdtype == CMDUNKNOWN)
6720 c = 1;
6721 argptr++;
6722 }
6723 return c;
6724}
6725
6726/*
6727 * Called when a cd is done. Marks all commands so the next time they
6728 * are executed they will be rehashed.
6729 */
6730static void
6731hashcd(void)
6732{
6733 struct tblentry **pp;
6734 struct tblentry *cmdp;
6735
6736 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6737 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6738 if (cmdp->cmdtype == CMDNORMAL || (
6739 cmdp->cmdtype == CMDBUILTIN &&
6740 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6741 builtinloc > 0
6742 ))
6743 cmdp->rehash = 1;
6744 }
6745 }
6746}
6747
6748/*
6749 * Fix command hash table when PATH changed.
6750 * Called before PATH is changed. The argument is the new value of PATH;
6751 * pathval() still returns the old value at this point.
6752 * Called with interrupts off.
6753 */
6754static void
6755changepath(const char *newval)
6756{
6757 const char *old, *new;
6758 int idx;
6759 int firstchange;
6760 int idx_bltin;
6761
6762 old = pathval();
6763 new = newval;
6764 firstchange = 9999; /* assume no change */
6765 idx = 0;
6766 idx_bltin = -1;
6767 for (;;) {
6768 if (*old != *new) {
6769 firstchange = idx;
6770 if ((*old == '\0' && *new == ':')
6771 || (*old == ':' && *new == '\0'))
6772 firstchange++;
6773 old = new; /* ignore subsequent differences */
6774 }
6775 if (*new == '\0')
6776 break;
6777 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6778 idx_bltin = idx;
6779 if (*new == ':') {
6780 idx++;
6781 }
6782 new++, old++;
6783 }
6784 if (builtinloc < 0 && idx_bltin >= 0)
6785 builtinloc = idx_bltin; /* zap builtins */
6786 if (builtinloc >= 0 && idx_bltin < 0)
6787 firstchange = 0;
6788 clearcmdentry(firstchange);
6789 builtinloc = idx_bltin;
6790}
6791
6792#define TEOF 0
6793#define TNL 1
6794#define TREDIR 2
6795#define TWORD 3
6796#define TSEMI 4
6797#define TBACKGND 5
6798#define TAND 6
6799#define TOR 7
6800#define TPIPE 8
6801#define TLP 9
6802#define TRP 10
6803#define TENDCASE 11
6804#define TENDBQUOTE 12
6805#define TNOT 13
6806#define TCASE 14
6807#define TDO 15
6808#define TDONE 16
6809#define TELIF 17
6810#define TELSE 18
6811#define TESAC 19
6812#define TFI 20
6813#define TFOR 21
6814#define TIF 22
6815#define TIN 23
6816#define TTHEN 24
6817#define TUNTIL 25
6818#define TWHILE 26
6819#define TBEGIN 27
6820#define TEND 28
6821
6822/* first char is indicating which tokens mark the end of a list */
6823static const char *const tokname_array[] = {
6824 "\1end of file",
6825 "\0newline",
6826 "\0redirection",
6827 "\0word",
6828 "\0;",
6829 "\0&",
6830 "\0&&",
6831 "\0||",
6832 "\0|",
6833 "\0(",
6834 "\1)",
6835 "\1;;",
6836 "\1`",
6837#define KWDOFFSET 13
6838 /* the following are keywords */
6839 "\0!",
6840 "\0case",
6841 "\1do",
6842 "\1done",
6843 "\1elif",
6844 "\1else",
6845 "\1esac",
6846 "\1fi",
6847 "\0for",
6848 "\0if",
6849 "\0in",
6850 "\1then",
6851 "\0until",
6852 "\0while",
6853 "\0{",
6854 "\1}",
6855};
6856
6857static const char *
6858tokname(int tok)
6859{
6860 static char buf[16];
6861
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006862//try this:
6863//if (tok < TSEMI) return tokname_array[tok] + 1;
6864//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6865//return buf;
6866
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006867 if (tok >= TSEMI)
6868 buf[0] = '"';
6869 sprintf(buf + (tok >= TSEMI), "%s%c",
6870 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6871 return buf;
6872}
6873
6874/* Wrapper around strcmp for qsort/bsearch/... */
6875static int
6876pstrcmp(const void *a, const void *b)
6877{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006878 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006879}
6880
6881static const char *const *
6882findkwd(const char *s)
6883{
6884 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006885 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6886 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006887}
6888
6889/*
6890 * Locate and print what a word is...
6891 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006892static int
6893describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006894{
6895 struct cmdentry entry;
6896 struct tblentry *cmdp;
6897#if ENABLE_ASH_ALIAS
6898 const struct alias *ap;
6899#endif
6900 const char *path = pathval();
6901
6902 if (describe_command_verbose) {
6903 out1str(command);
6904 }
6905
6906 /* First look at the keywords */
6907 if (findkwd(command)) {
6908 out1str(describe_command_verbose ? " is a shell keyword" : command);
6909 goto out;
6910 }
6911
6912#if ENABLE_ASH_ALIAS
6913 /* Then look at the aliases */
6914 ap = lookupalias(command, 0);
6915 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006916 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006917 out1str("alias ");
6918 printalias(ap);
6919 return 0;
6920 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006921 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006922 goto out;
6923 }
6924#endif
6925 /* Then check if it is a tracked alias */
6926 cmdp = cmdlookup(command, 0);
6927 if (cmdp != NULL) {
6928 entry.cmdtype = cmdp->cmdtype;
6929 entry.u = cmdp->param;
6930 } else {
6931 /* Finally use brute force */
6932 find_command(command, &entry, DO_ABS, path);
6933 }
6934
6935 switch (entry.cmdtype) {
6936 case CMDNORMAL: {
6937 int j = entry.u.index;
6938 char *p;
6939 if (j == -1) {
6940 p = command;
6941 } else {
6942 do {
6943 p = padvance(&path, command);
6944 stunalloc(p);
6945 } while (--j >= 0);
6946 }
6947 if (describe_command_verbose) {
6948 out1fmt(" is%s %s",
6949 (cmdp ? " a tracked alias for" : nullstr), p
6950 );
6951 } else {
6952 out1str(p);
6953 }
6954 break;
6955 }
6956
6957 case CMDFUNCTION:
6958 if (describe_command_verbose) {
6959 out1str(" is a shell function");
6960 } else {
6961 out1str(command);
6962 }
6963 break;
6964
6965 case CMDBUILTIN:
6966 if (describe_command_verbose) {
6967 out1fmt(" is a %sshell builtin",
6968 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6969 "special " : nullstr
6970 );
6971 } else {
6972 out1str(command);
6973 }
6974 break;
6975
6976 default:
6977 if (describe_command_verbose) {
6978 out1str(": not found\n");
6979 }
6980 return 127;
6981 }
6982 out:
6983 outstr("\n", stdout);
6984 return 0;
6985}
6986
6987static int
6988typecmd(int argc, char **argv)
6989{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006990 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006991 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00006992 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006993
Denis Vlasenko46846e22007-05-20 13:08:31 +00006994 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00006995 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006996 i++;
6997 verbose = 0;
6998 }
6999 while (i < argc) {
7000 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007001 }
7002 return err;
7003}
7004
7005#if ENABLE_ASH_CMDCMD
7006static int
7007commandcmd(int argc, char **argv)
7008{
7009 int c;
7010 enum {
7011 VERIFY_BRIEF = 1,
7012 VERIFY_VERBOSE = 2,
7013 } verify = 0;
7014
7015 while ((c = nextopt("pvV")) != '\0')
7016 if (c == 'V')
7017 verify |= VERIFY_VERBOSE;
7018 else if (c == 'v')
7019 verify |= VERIFY_BRIEF;
7020#if DEBUG
7021 else if (c != 'p')
7022 abort();
7023#endif
7024 if (verify)
7025 return describe_command(*argptr, verify - VERIFY_BRIEF);
7026
7027 return 0;
7028}
7029#endif
7030
7031
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007032/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007033
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007034static int funcblocksize; /* size of structures in function */
7035static int funcstringsize; /* size of strings in node */
7036static void *funcblock; /* block to allocate function from */
7037static char *funcstring; /* block to allocate strings from */
7038
Eric Andersencb57d552001-06-28 07:25:16 +00007039/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007040#define EV_EXIT 01 /* exit after evaluating tree */
7041#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7042#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007043
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007044static const short nodesize[26] = {
7045 SHELL_ALIGN(sizeof(struct ncmd)),
7046 SHELL_ALIGN(sizeof(struct npipe)),
7047 SHELL_ALIGN(sizeof(struct nredir)),
7048 SHELL_ALIGN(sizeof(struct nredir)),
7049 SHELL_ALIGN(sizeof(struct nredir)),
7050 SHELL_ALIGN(sizeof(struct nbinary)),
7051 SHELL_ALIGN(sizeof(struct nbinary)),
7052 SHELL_ALIGN(sizeof(struct nbinary)),
7053 SHELL_ALIGN(sizeof(struct nif)),
7054 SHELL_ALIGN(sizeof(struct nbinary)),
7055 SHELL_ALIGN(sizeof(struct nbinary)),
7056 SHELL_ALIGN(sizeof(struct nfor)),
7057 SHELL_ALIGN(sizeof(struct ncase)),
7058 SHELL_ALIGN(sizeof(struct nclist)),
7059 SHELL_ALIGN(sizeof(struct narg)),
7060 SHELL_ALIGN(sizeof(struct narg)),
7061 SHELL_ALIGN(sizeof(struct nfile)),
7062 SHELL_ALIGN(sizeof(struct nfile)),
7063 SHELL_ALIGN(sizeof(struct nfile)),
7064 SHELL_ALIGN(sizeof(struct nfile)),
7065 SHELL_ALIGN(sizeof(struct nfile)),
7066 SHELL_ALIGN(sizeof(struct ndup)),
7067 SHELL_ALIGN(sizeof(struct ndup)),
7068 SHELL_ALIGN(sizeof(struct nhere)),
7069 SHELL_ALIGN(sizeof(struct nhere)),
7070 SHELL_ALIGN(sizeof(struct nnot)),
7071};
7072
7073static void calcsize(union node *n);
7074
7075static void
7076sizenodelist(struct nodelist *lp)
7077{
7078 while (lp) {
7079 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7080 calcsize(lp->n);
7081 lp = lp->next;
7082 }
7083}
7084
7085static void
7086calcsize(union node *n)
7087{
7088 if (n == NULL)
7089 return;
7090 funcblocksize += nodesize[n->type];
7091 switch (n->type) {
7092 case NCMD:
7093 calcsize(n->ncmd.redirect);
7094 calcsize(n->ncmd.args);
7095 calcsize(n->ncmd.assign);
7096 break;
7097 case NPIPE:
7098 sizenodelist(n->npipe.cmdlist);
7099 break;
7100 case NREDIR:
7101 case NBACKGND:
7102 case NSUBSHELL:
7103 calcsize(n->nredir.redirect);
7104 calcsize(n->nredir.n);
7105 break;
7106 case NAND:
7107 case NOR:
7108 case NSEMI:
7109 case NWHILE:
7110 case NUNTIL:
7111 calcsize(n->nbinary.ch2);
7112 calcsize(n->nbinary.ch1);
7113 break;
7114 case NIF:
7115 calcsize(n->nif.elsepart);
7116 calcsize(n->nif.ifpart);
7117 calcsize(n->nif.test);
7118 break;
7119 case NFOR:
7120 funcstringsize += strlen(n->nfor.var) + 1;
7121 calcsize(n->nfor.body);
7122 calcsize(n->nfor.args);
7123 break;
7124 case NCASE:
7125 calcsize(n->ncase.cases);
7126 calcsize(n->ncase.expr);
7127 break;
7128 case NCLIST:
7129 calcsize(n->nclist.body);
7130 calcsize(n->nclist.pattern);
7131 calcsize(n->nclist.next);
7132 break;
7133 case NDEFUN:
7134 case NARG:
7135 sizenodelist(n->narg.backquote);
7136 funcstringsize += strlen(n->narg.text) + 1;
7137 calcsize(n->narg.next);
7138 break;
7139 case NTO:
7140 case NCLOBBER:
7141 case NFROM:
7142 case NFROMTO:
7143 case NAPPEND:
7144 calcsize(n->nfile.fname);
7145 calcsize(n->nfile.next);
7146 break;
7147 case NTOFD:
7148 case NFROMFD:
7149 calcsize(n->ndup.vname);
7150 calcsize(n->ndup.next);
7151 break;
7152 case NHERE:
7153 case NXHERE:
7154 calcsize(n->nhere.doc);
7155 calcsize(n->nhere.next);
7156 break;
7157 case NNOT:
7158 calcsize(n->nnot.com);
7159 break;
7160 };
7161}
7162
7163static char *
7164nodeckstrdup(char *s)
7165{
7166 char *rtn = funcstring;
7167
7168 strcpy(funcstring, s);
7169 funcstring += strlen(s) + 1;
7170 return rtn;
7171}
7172
7173static union node *copynode(union node *);
7174
7175static struct nodelist *
7176copynodelist(struct nodelist *lp)
7177{
7178 struct nodelist *start;
7179 struct nodelist **lpp;
7180
7181 lpp = &start;
7182 while (lp) {
7183 *lpp = funcblock;
7184 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7185 (*lpp)->n = copynode(lp->n);
7186 lp = lp->next;
7187 lpp = &(*lpp)->next;
7188 }
7189 *lpp = NULL;
7190 return start;
7191}
7192
7193static union node *
7194copynode(union node *n)
7195{
7196 union node *new;
7197
7198 if (n == NULL)
7199 return NULL;
7200 new = funcblock;
7201 funcblock = (char *) funcblock + nodesize[n->type];
7202
7203 switch (n->type) {
7204 case NCMD:
7205 new->ncmd.redirect = copynode(n->ncmd.redirect);
7206 new->ncmd.args = copynode(n->ncmd.args);
7207 new->ncmd.assign = copynode(n->ncmd.assign);
7208 break;
7209 case NPIPE:
7210 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7211 new->npipe.backgnd = n->npipe.backgnd;
7212 break;
7213 case NREDIR:
7214 case NBACKGND:
7215 case NSUBSHELL:
7216 new->nredir.redirect = copynode(n->nredir.redirect);
7217 new->nredir.n = copynode(n->nredir.n);
7218 break;
7219 case NAND:
7220 case NOR:
7221 case NSEMI:
7222 case NWHILE:
7223 case NUNTIL:
7224 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7225 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7226 break;
7227 case NIF:
7228 new->nif.elsepart = copynode(n->nif.elsepart);
7229 new->nif.ifpart = copynode(n->nif.ifpart);
7230 new->nif.test = copynode(n->nif.test);
7231 break;
7232 case NFOR:
7233 new->nfor.var = nodeckstrdup(n->nfor.var);
7234 new->nfor.body = copynode(n->nfor.body);
7235 new->nfor.args = copynode(n->nfor.args);
7236 break;
7237 case NCASE:
7238 new->ncase.cases = copynode(n->ncase.cases);
7239 new->ncase.expr = copynode(n->ncase.expr);
7240 break;
7241 case NCLIST:
7242 new->nclist.body = copynode(n->nclist.body);
7243 new->nclist.pattern = copynode(n->nclist.pattern);
7244 new->nclist.next = copynode(n->nclist.next);
7245 break;
7246 case NDEFUN:
7247 case NARG:
7248 new->narg.backquote = copynodelist(n->narg.backquote);
7249 new->narg.text = nodeckstrdup(n->narg.text);
7250 new->narg.next = copynode(n->narg.next);
7251 break;
7252 case NTO:
7253 case NCLOBBER:
7254 case NFROM:
7255 case NFROMTO:
7256 case NAPPEND:
7257 new->nfile.fname = copynode(n->nfile.fname);
7258 new->nfile.fd = n->nfile.fd;
7259 new->nfile.next = copynode(n->nfile.next);
7260 break;
7261 case NTOFD:
7262 case NFROMFD:
7263 new->ndup.vname = copynode(n->ndup.vname);
7264 new->ndup.dupfd = n->ndup.dupfd;
7265 new->ndup.fd = n->ndup.fd;
7266 new->ndup.next = copynode(n->ndup.next);
7267 break;
7268 case NHERE:
7269 case NXHERE:
7270 new->nhere.doc = copynode(n->nhere.doc);
7271 new->nhere.fd = n->nhere.fd;
7272 new->nhere.next = copynode(n->nhere.next);
7273 break;
7274 case NNOT:
7275 new->nnot.com = copynode(n->nnot.com);
7276 break;
7277 };
7278 new->type = n->type;
7279 return new;
7280}
7281
7282/*
7283 * Make a copy of a parse tree.
7284 */
7285static struct funcnode *
7286copyfunc(union node *n)
7287{
7288 struct funcnode *f;
7289 size_t blocksize;
7290
7291 funcblocksize = offsetof(struct funcnode, n);
7292 funcstringsize = 0;
7293 calcsize(n);
7294 blocksize = funcblocksize;
7295 f = ckmalloc(blocksize + funcstringsize);
7296 funcblock = (char *) f + offsetof(struct funcnode, n);
7297 funcstring = (char *) f + blocksize;
7298 copynode(n);
7299 f->count = 0;
7300 return f;
7301}
7302
7303/*
7304 * Define a shell function.
7305 */
7306static void
7307defun(char *name, union node *func)
7308{
7309 struct cmdentry entry;
7310
7311 INT_OFF;
7312 entry.cmdtype = CMDFUNCTION;
7313 entry.u.func = copyfunc(func);
7314 addcmdentry(name, &entry);
7315 INT_ON;
7316}
7317
7318static int evalskip; /* set if we are skipping commands */
7319/* reasons for skipping commands (see comment on breakcmd routine) */
7320#define SKIPBREAK (1 << 0)
7321#define SKIPCONT (1 << 1)
7322#define SKIPFUNC (1 << 2)
7323#define SKIPFILE (1 << 3)
7324#define SKIPEVAL (1 << 4)
7325static int skipcount; /* number of levels to skip */
7326static int funcnest; /* depth of function calls */
7327
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007328/* forward decl way out to parsing code - dotrap needs it */
7329static int evalstring(char *s, int mask);
7330
7331/*
7332 * Called to execute a trap. Perhaps we should avoid entering new trap
7333 * handlers while we are executing a trap handler.
7334 */
7335static int
7336dotrap(void)
7337{
7338 char *p;
7339 char *q;
7340 int i;
7341 int savestatus;
7342 int skip = 0;
7343
7344 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007345 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007346 xbarrier();
7347
7348 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7349 if (!*q)
7350 continue;
7351 *q = '\0';
7352
7353 p = trap[i + 1];
7354 if (!p)
7355 continue;
7356 skip = evalstring(p, SKIPEVAL);
7357 exitstatus = savestatus;
7358 if (skip)
7359 break;
7360 }
7361
7362 return skip;
7363}
7364
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007365/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007366static void evalloop(union node *, int);
7367static void evalfor(union node *, int);
7368static void evalcase(union node *, int);
7369static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007370static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007371static void evalpipe(union node *, int);
7372static void evalcommand(union node *, int);
7373static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007374static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007375
Eric Andersen62483552001-07-10 06:09:16 +00007376/*
Eric Andersenc470f442003-07-28 09:56:35 +00007377 * Evaluate a parse tree. The value is left in the global variable
7378 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007379 */
Eric Andersenc470f442003-07-28 09:56:35 +00007380static void
7381evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007382{
Eric Andersenc470f442003-07-28 09:56:35 +00007383 int checkexit = 0;
7384 void (*evalfn)(union node *, int);
7385 unsigned isor;
7386 int status;
7387 if (n == NULL) {
7388 TRACE(("evaltree(NULL) called\n"));
7389 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007390 }
Eric Andersenc470f442003-07-28 09:56:35 +00007391 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007392 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007393 switch (n->type) {
7394 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007395#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007396 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007397 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007398 break;
7399#endif
7400 case NNOT:
7401 evaltree(n->nnot.com, EV_TESTED);
7402 status = !exitstatus;
7403 goto setstatus;
7404 case NREDIR:
7405 expredir(n->nredir.redirect);
7406 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7407 if (!status) {
7408 evaltree(n->nredir.n, flags & EV_TESTED);
7409 status = exitstatus;
7410 }
7411 popredir(0);
7412 goto setstatus;
7413 case NCMD:
7414 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007415 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007416 if (eflag && !(flags & EV_TESTED))
7417 checkexit = ~0;
7418 goto calleval;
7419 case NFOR:
7420 evalfn = evalfor;
7421 goto calleval;
7422 case NWHILE:
7423 case NUNTIL:
7424 evalfn = evalloop;
7425 goto calleval;
7426 case NSUBSHELL:
7427 case NBACKGND:
7428 evalfn = evalsubshell;
7429 goto calleval;
7430 case NPIPE:
7431 evalfn = evalpipe;
7432 goto checkexit;
7433 case NCASE:
7434 evalfn = evalcase;
7435 goto calleval;
7436 case NAND:
7437 case NOR:
7438 case NSEMI:
7439#if NAND + 1 != NOR
7440#error NAND + 1 != NOR
7441#endif
7442#if NOR + 1 != NSEMI
7443#error NOR + 1 != NSEMI
7444#endif
7445 isor = n->type - NAND;
7446 evaltree(
7447 n->nbinary.ch1,
7448 (flags | ((isor >> 1) - 1)) & EV_TESTED
7449 );
7450 if (!exitstatus == isor)
7451 break;
7452 if (!evalskip) {
7453 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007454 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007455 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007456 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007457 evalfn(n, flags);
7458 break;
7459 }
7460 break;
7461 case NIF:
7462 evaltree(n->nif.test, EV_TESTED);
7463 if (evalskip)
7464 break;
7465 if (exitstatus == 0) {
7466 n = n->nif.ifpart;
7467 goto evaln;
7468 } else if (n->nif.elsepart) {
7469 n = n->nif.elsepart;
7470 goto evaln;
7471 }
7472 goto success;
7473 case NDEFUN:
7474 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007475 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007476 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007477 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007478 exitstatus = status;
7479 break;
7480 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007481 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007482 if ((checkexit & exitstatus))
7483 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007484 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007485 goto exexit;
7486
7487 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007488 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007489 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007490 }
Eric Andersen62483552001-07-10 06:09:16 +00007491}
7492
Eric Andersenc470f442003-07-28 09:56:35 +00007493#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7494static
7495#endif
7496void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7497
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007498static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007499
7500static void
7501evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007502{
7503 int status;
7504
7505 loopnest++;
7506 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007507 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007508 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007509 int i;
7510
Eric Andersencb57d552001-06-28 07:25:16 +00007511 evaltree(n->nbinary.ch1, EV_TESTED);
7512 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007513 skipping:
7514 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007515 evalskip = 0;
7516 continue;
7517 }
7518 if (evalskip == SKIPBREAK && --skipcount <= 0)
7519 evalskip = 0;
7520 break;
7521 }
Eric Andersenc470f442003-07-28 09:56:35 +00007522 i = exitstatus;
7523 if (n->type != NWHILE)
7524 i = !i;
7525 if (i != 0)
7526 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007527 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007528 status = exitstatus;
7529 if (evalskip)
7530 goto skipping;
7531 }
7532 loopnest--;
7533 exitstatus = status;
7534}
7535
Eric Andersenc470f442003-07-28 09:56:35 +00007536static void
7537evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007538{
7539 struct arglist arglist;
7540 union node *argp;
7541 struct strlist *sp;
7542 struct stackmark smark;
7543
7544 setstackmark(&smark);
7545 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007546 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007547 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007548 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007549 if (evalskip)
7550 goto out;
7551 }
7552 *arglist.lastp = NULL;
7553
7554 exitstatus = 0;
7555 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007556 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007557 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007558 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007559 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007560 if (evalskip) {
7561 if (evalskip == SKIPCONT && --skipcount <= 0) {
7562 evalskip = 0;
7563 continue;
7564 }
7565 if (evalskip == SKIPBREAK && --skipcount <= 0)
7566 evalskip = 0;
7567 break;
7568 }
7569 }
7570 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007571 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007572 popstackmark(&smark);
7573}
7574
Eric Andersenc470f442003-07-28 09:56:35 +00007575static void
7576evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007577{
7578 union node *cp;
7579 union node *patp;
7580 struct arglist arglist;
7581 struct stackmark smark;
7582
7583 setstackmark(&smark);
7584 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007585 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007586 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007587 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7588 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007589 if (casematch(patp, arglist.list->text)) {
7590 if (evalskip == 0) {
7591 evaltree(cp->nclist.body, flags);
7592 }
7593 goto out;
7594 }
7595 }
7596 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007597 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007598 popstackmark(&smark);
7599}
7600
Eric Andersenc470f442003-07-28 09:56:35 +00007601/*
7602 * Kick off a subshell to evaluate a tree.
7603 */
Eric Andersenc470f442003-07-28 09:56:35 +00007604static void
7605evalsubshell(union node *n, int flags)
7606{
7607 struct job *jp;
7608 int backgnd = (n->type == NBACKGND);
7609 int status;
7610
7611 expredir(n->nredir.redirect);
7612 if (!backgnd && flags & EV_EXIT && !trap[0])
7613 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007614 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007615 jp = makejob(n, 1);
7616 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007617 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007618 flags |= EV_EXIT;
7619 if (backgnd)
7620 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007621 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007622 redirect(n->nredir.redirect, 0);
7623 evaltreenr(n->nredir.n, flags);
7624 /* never returns */
7625 }
7626 status = 0;
7627 if (! backgnd)
7628 status = waitforjob(jp);
7629 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007630 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007631}
7632
Eric Andersenc470f442003-07-28 09:56:35 +00007633/*
7634 * Compute the names of the files in a redirection list.
7635 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007636static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007637static void
7638expredir(union node *n)
7639{
7640 union node *redir;
7641
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007642 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007643 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007644
7645 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007646 fn.lastp = &fn.list;
7647 switch (redir->type) {
7648 case NFROMTO:
7649 case NFROM:
7650 case NTO:
7651 case NCLOBBER:
7652 case NAPPEND:
7653 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7654 redir->nfile.expfname = fn.list->text;
7655 break;
7656 case NFROMFD:
7657 case NTOFD:
7658 if (redir->ndup.vname) {
7659 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007660 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007661 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007662 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007663 }
7664 break;
7665 }
7666 }
7667}
7668
Eric Andersencb57d552001-06-28 07:25:16 +00007669/*
Eric Andersencb57d552001-06-28 07:25:16 +00007670 * Evaluate a pipeline. All the processes in the pipeline are children
7671 * of the process creating the pipeline. (This differs from some versions
7672 * of the shell, which make the last process in a pipeline the parent
7673 * of all the rest.)
7674 */
Eric Andersenc470f442003-07-28 09:56:35 +00007675static void
7676evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007677{
7678 struct job *jp;
7679 struct nodelist *lp;
7680 int pipelen;
7681 int prevfd;
7682 int pip[2];
7683
Eric Andersenc470f442003-07-28 09:56:35 +00007684 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007685 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007686 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007687 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007688 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007689 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007690 jp = makejob(n, pipelen);
7691 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007692 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007693 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007694 pip[1] = -1;
7695 if (lp->next) {
7696 if (pipe(pip) < 0) {
7697 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007698 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007699 }
7700 }
7701 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007702 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007703 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007704 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007705 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007706 if (prevfd > 0) {
7707 dup2(prevfd, 0);
7708 close(prevfd);
7709 }
7710 if (pip[1] > 1) {
7711 dup2(pip[1], 1);
7712 close(pip[1]);
7713 }
Eric Andersenc470f442003-07-28 09:56:35 +00007714 evaltreenr(lp->n, flags);
7715 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007716 }
7717 if (prevfd >= 0)
7718 close(prevfd);
7719 prevfd = pip[0];
7720 close(pip[1]);
7721 }
Eric Andersencb57d552001-06-28 07:25:16 +00007722 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007723 exitstatus = waitforjob(jp);
7724 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007725 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007726 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007727}
7728
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007729/*
7730 * Controls whether the shell is interactive or not.
7731 */
7732static void
7733setinteractive(int on)
7734{
7735 static int is_interactive;
7736
7737 if (++on == is_interactive)
7738 return;
7739 is_interactive = on;
7740 setsignal(SIGINT);
7741 setsignal(SIGQUIT);
7742 setsignal(SIGTERM);
7743#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7744 if (is_interactive > 1) {
7745 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007746 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007747
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007748 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007749 out1fmt(
7750 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007751 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007752 "Enter 'help' for a list of built-in commands."
7753 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007754 bb_banner);
7755 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007756 }
7757 }
7758#endif
7759}
7760
7761#if ENABLE_FEATURE_EDITING_VI
7762#define setvimode(on) do { \
7763 if (on) line_input_state->flags |= VI_MODE; \
7764 else line_input_state->flags &= ~VI_MODE; \
7765} while (0)
7766#else
7767#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7768#endif
7769
7770static void
7771optschanged(void)
7772{
7773#if DEBUG
7774 opentrace();
7775#endif
7776 setinteractive(iflag);
7777 setjobctl(mflag);
7778 setvimode(viflag);
7779}
7780
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007781static struct localvar *localvars;
7782
7783/*
7784 * Called after a function returns.
7785 * Interrupts must be off.
7786 */
7787static void
7788poplocalvars(void)
7789{
7790 struct localvar *lvp;
7791 struct var *vp;
7792
7793 while ((lvp = localvars) != NULL) {
7794 localvars = lvp->next;
7795 vp = lvp->vp;
7796 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7797 if (vp == NULL) { /* $- saved */
7798 memcpy(optlist, lvp->text, sizeof(optlist));
7799 free((char*)lvp->text);
7800 optschanged();
7801 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7802 unsetvar(vp->text);
7803 } else {
7804 if (vp->func)
7805 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7806 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7807 free((char*)vp->text);
7808 vp->flags = lvp->flags;
7809 vp->text = lvp->text;
7810 }
7811 free(lvp);
7812 }
7813}
7814
7815static int
7816evalfun(struct funcnode *func, int argc, char **argv, int flags)
7817{
7818 volatile struct shparam saveparam;
7819 struct localvar *volatile savelocalvars;
7820 struct jmploc *volatile savehandler;
7821 struct jmploc jmploc;
7822 int e;
7823
7824 saveparam = shellparam;
7825 savelocalvars = localvars;
7826 e = setjmp(jmploc.loc);
7827 if (e) {
7828 goto funcdone;
7829 }
7830 INT_OFF;
7831 savehandler = exception_handler;
7832 exception_handler = &jmploc;
7833 localvars = NULL;
7834 shellparam.malloc = 0;
7835 func->count++;
7836 funcnest++;
7837 INT_ON;
7838 shellparam.nparam = argc - 1;
7839 shellparam.p = argv + 1;
7840#if ENABLE_ASH_GETOPTS
7841 shellparam.optind = 1;
7842 shellparam.optoff = -1;
7843#endif
7844 evaltree(&func->n, flags & EV_TESTED);
7845funcdone:
7846 INT_OFF;
7847 funcnest--;
7848 freefunc(func);
7849 poplocalvars();
7850 localvars = savelocalvars;
7851 freeparam(&shellparam);
7852 shellparam = saveparam;
7853 exception_handler = savehandler;
7854 INT_ON;
7855 evalskip &= ~SKIPFUNC;
7856 return e;
7857}
7858
Denis Vlasenko131ae172007-02-18 13:00:19 +00007859#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007860static char **
7861parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007862{
7863 char *cp, c;
7864
7865 for (;;) {
7866 cp = *++argv;
7867 if (!cp)
7868 return 0;
7869 if (*cp++ != '-')
7870 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007871 c = *cp++;
7872 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007873 break;
7874 if (c == '-' && !*cp) {
7875 argv++;
7876 break;
7877 }
7878 do {
7879 switch (c) {
7880 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007881 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007882 break;
7883 default:
7884 /* run 'typecmd' for other options */
7885 return 0;
7886 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007887 c = *cp++;
7888 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007889 }
7890 return argv;
7891}
7892#endif
7893
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007894/*
7895 * Make a variable a local variable. When a variable is made local, it's
7896 * value and flags are saved in a localvar structure. The saved values
7897 * will be restored when the shell function returns. We handle the name
7898 * "-" as a special case.
7899 */
7900static void
7901mklocal(char *name)
7902{
7903 struct localvar *lvp;
7904 struct var **vpp;
7905 struct var *vp;
7906
7907 INT_OFF;
7908 lvp = ckmalloc(sizeof(struct localvar));
7909 if (LONE_DASH(name)) {
7910 char *p;
7911 p = ckmalloc(sizeof(optlist));
7912 lvp->text = memcpy(p, optlist, sizeof(optlist));
7913 vp = NULL;
7914 } else {
7915 char *eq;
7916
7917 vpp = hashvar(name);
7918 vp = *findvar(vpp, name);
7919 eq = strchr(name, '=');
7920 if (vp == NULL) {
7921 if (eq)
7922 setvareq(name, VSTRFIXED);
7923 else
7924 setvar(name, NULL, VSTRFIXED);
7925 vp = *vpp; /* the new variable */
7926 lvp->flags = VUNSET;
7927 } else {
7928 lvp->text = vp->text;
7929 lvp->flags = vp->flags;
7930 vp->flags |= VSTRFIXED|VTEXTFIXED;
7931 if (eq)
7932 setvareq(name, 0);
7933 }
7934 }
7935 lvp->vp = vp;
7936 lvp->next = localvars;
7937 localvars = lvp;
7938 INT_ON;
7939}
7940
7941/*
7942 * The "local" command.
7943 */
7944static int
7945localcmd(int argc, char **argv)
7946{
7947 char *name;
7948
7949 argv = argptr;
7950 while ((name = *argv++) != NULL) {
7951 mklocal(name);
7952 }
7953 return 0;
7954}
7955
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007956static int
7957falsecmd(int argc, char **argv)
7958{
7959 return 1;
7960}
7961
7962static int
7963truecmd(int argc, char **argv)
7964{
7965 return 0;
7966}
7967
7968static int
7969execcmd(int argc, char **argv)
7970{
7971 if (argc > 1) {
7972 iflag = 0; /* exit on error */
7973 mflag = 0;
7974 optschanged();
7975 shellexec(argv + 1, pathval(), 0);
7976 }
7977 return 0;
7978}
7979
7980/*
7981 * The return command.
7982 */
7983static int
7984returncmd(int argc, char **argv)
7985{
7986 /*
7987 * If called outside a function, do what ksh does;
7988 * skip the rest of the file.
7989 */
7990 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
7991 return argv[1] ? number(argv[1]) : exitstatus;
7992}
7993
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007994/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007995static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007996static int dotcmd(int, char **);
7997static int evalcmd(int, char **);
7998#if ENABLE_ASH_BUILTIN_ECHO
7999static int echocmd(int, char **);
8000#endif
8001#if ENABLE_ASH_BUILTIN_TEST
8002static int testcmd(int, char **);
8003#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008004static int exitcmd(int, char **);
8005static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008006#if ENABLE_ASH_GETOPTS
8007static int getoptscmd(int, char **);
8008#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008009#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8010static int helpcmd(int argc, char **argv);
8011#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008012#if ENABLE_ASH_MATH_SUPPORT
8013static int letcmd(int, char **);
8014#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008015static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008016static int setcmd(int, char **);
8017static int shiftcmd(int, char **);
8018static int timescmd(int, char **);
8019static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008020static int umaskcmd(int, char **);
8021static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008022static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008023
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008024#define BUILTIN_NOSPEC "0"
8025#define BUILTIN_SPECIAL "1"
8026#define BUILTIN_REGULAR "2"
8027#define BUILTIN_SPEC_REG "3"
8028#define BUILTIN_ASSIGN "4"
8029#define BUILTIN_SPEC_ASSG "5"
8030#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008031#define BUILTIN_SPEC_REG_ASSG "7"
8032
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008033/* make sure to keep these in proper order since it is searched via bsearch() */
8034static const struct builtincmd builtintab[] = {
8035 { BUILTIN_SPEC_REG ".", dotcmd },
8036 { BUILTIN_SPEC_REG ":", truecmd },
8037#if ENABLE_ASH_BUILTIN_TEST
8038 { BUILTIN_REGULAR "[", testcmd },
8039 { BUILTIN_REGULAR "[[", testcmd },
8040#endif
8041#if ENABLE_ASH_ALIAS
8042 { BUILTIN_REG_ASSG "alias", aliascmd },
8043#endif
8044#if JOBS
8045 { BUILTIN_REGULAR "bg", fg_bgcmd },
8046#endif
8047 { BUILTIN_SPEC_REG "break", breakcmd },
8048 { BUILTIN_REGULAR "cd", cdcmd },
8049 { BUILTIN_NOSPEC "chdir", cdcmd },
8050#if ENABLE_ASH_CMDCMD
8051 { BUILTIN_REGULAR "command", commandcmd },
8052#endif
8053 { BUILTIN_SPEC_REG "continue", breakcmd },
8054#if ENABLE_ASH_BUILTIN_ECHO
8055 { BUILTIN_REGULAR "echo", echocmd },
8056#endif
8057 { BUILTIN_SPEC_REG "eval", evalcmd },
8058 { BUILTIN_SPEC_REG "exec", execcmd },
8059 { BUILTIN_SPEC_REG "exit", exitcmd },
8060 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8061 { BUILTIN_REGULAR "false", falsecmd },
8062#if JOBS
8063 { BUILTIN_REGULAR "fg", fg_bgcmd },
8064#endif
8065#if ENABLE_ASH_GETOPTS
8066 { BUILTIN_REGULAR "getopts", getoptscmd },
8067#endif
8068 { BUILTIN_NOSPEC "hash", hashcmd },
8069#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8070 { BUILTIN_NOSPEC "help", helpcmd },
8071#endif
8072#if JOBS
8073 { BUILTIN_REGULAR "jobs", jobscmd },
8074 { BUILTIN_REGULAR "kill", killcmd },
8075#endif
8076#if ENABLE_ASH_MATH_SUPPORT
8077 { BUILTIN_NOSPEC "let", letcmd },
8078#endif
8079 { BUILTIN_ASSIGN "local", localcmd },
8080 { BUILTIN_NOSPEC "pwd", pwdcmd },
8081 { BUILTIN_REGULAR "read", readcmd },
8082 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8083 { BUILTIN_SPEC_REG "return", returncmd },
8084 { BUILTIN_SPEC_REG "set", setcmd },
8085 { BUILTIN_SPEC_REG "shift", shiftcmd },
8086 { BUILTIN_SPEC_REG "source", dotcmd },
8087#if ENABLE_ASH_BUILTIN_TEST
8088 { BUILTIN_REGULAR "test", testcmd },
8089#endif
8090 { BUILTIN_SPEC_REG "times", timescmd },
8091 { BUILTIN_SPEC_REG "trap", trapcmd },
8092 { BUILTIN_REGULAR "true", truecmd },
8093 { BUILTIN_NOSPEC "type", typecmd },
8094 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8095 { BUILTIN_REGULAR "umask", umaskcmd },
8096#if ENABLE_ASH_ALIAS
8097 { BUILTIN_REGULAR "unalias", unaliascmd },
8098#endif
8099 { BUILTIN_SPEC_REG "unset", unsetcmd },
8100 { BUILTIN_REGULAR "wait", waitcmd },
8101};
8102
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008103
8104#define COMMANDCMD (builtintab + 5 + \
8105 2 * ENABLE_ASH_BUILTIN_TEST + \
8106 ENABLE_ASH_ALIAS + \
8107 ENABLE_ASH_JOB_CONTROL)
8108#define EXECCMD (builtintab + 7 + \
8109 2 * ENABLE_ASH_BUILTIN_TEST + \
8110 ENABLE_ASH_ALIAS + \
8111 ENABLE_ASH_JOB_CONTROL + \
8112 ENABLE_ASH_CMDCMD + \
8113 ENABLE_ASH_BUILTIN_ECHO)
8114
8115/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008116 * Search the table of builtin commands.
8117 */
8118static struct builtincmd *
8119find_builtin(const char *name)
8120{
8121 struct builtincmd *bp;
8122
8123 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008124 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008125 pstrcmp
8126 );
8127 return bp;
8128}
8129
8130/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008131 * Execute a simple command.
8132 */
8133static int back_exitstatus; /* exit status of backquoted command */
8134static int
8135isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008136{
8137 const char *q = endofname(p);
8138 if (p == q)
8139 return 0;
8140 return *q == '=';
8141}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008142static int
8143bltincmd(int argc, char **argv)
8144{
8145 /* Preserve exitstatus of a previous possible redirection
8146 * as POSIX mandates */
8147 return back_exitstatus;
8148}
Eric Andersenc470f442003-07-28 09:56:35 +00008149static void
8150evalcommand(union node *cmd, int flags)
8151{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008152 static const struct builtincmd bltin = {
8153 "\0\0", bltincmd
8154 };
Eric Andersenc470f442003-07-28 09:56:35 +00008155 struct stackmark smark;
8156 union node *argp;
8157 struct arglist arglist;
8158 struct arglist varlist;
8159 char **argv;
8160 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008161 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008162 struct cmdentry cmdentry;
8163 struct job *jp;
8164 char *lastarg;
8165 const char *path;
8166 int spclbltin;
8167 int cmd_is_exec;
8168 int status;
8169 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008170 struct builtincmd *bcmd;
8171 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008172
8173 /* First expand the arguments. */
8174 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8175 setstackmark(&smark);
8176 back_exitstatus = 0;
8177
8178 cmdentry.cmdtype = CMDBUILTIN;
8179 cmdentry.u.cmd = &bltin;
8180 varlist.lastp = &varlist.list;
8181 *varlist.lastp = NULL;
8182 arglist.lastp = &arglist.list;
8183 *arglist.lastp = NULL;
8184
8185 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008186 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008187 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8188 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8189 }
8190
Eric Andersenc470f442003-07-28 09:56:35 +00008191 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8192 struct strlist **spp;
8193
8194 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008195 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008196 expandarg(argp, &arglist, EXP_VARTILDE);
8197 else
8198 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8199
Eric Andersenc470f442003-07-28 09:56:35 +00008200 for (sp = *spp; sp; sp = sp->next)
8201 argc++;
8202 }
8203
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008204 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008205 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008206 TRACE(("evalcommand arg: %s\n", sp->text));
8207 *nargv++ = sp->text;
8208 }
8209 *nargv = NULL;
8210
8211 lastarg = NULL;
8212 if (iflag && funcnest == 0 && argc > 0)
8213 lastarg = nargv[-1];
8214
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008215 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008216 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008217 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008218
8219 path = vpath.text;
8220 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8221 struct strlist **spp;
8222 char *p;
8223
8224 spp = varlist.lastp;
8225 expandarg(argp, &varlist, EXP_VARTILDE);
8226
8227 /*
8228 * Modify the command lookup path, if a PATH= assignment
8229 * is present
8230 */
8231 p = (*spp)->text;
8232 if (varequal(p, path))
8233 path = p;
8234 }
8235
8236 /* Print the command if xflag is set. */
8237 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008238 int n;
8239 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008240
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008241 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008242 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008243
8244 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008245 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008246 while (sp) {
8247 dprintf(preverrout_fd, p, sp->text);
8248 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008249 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008250 p--;
8251 }
8252 }
8253 sp = arglist.list;
8254 }
Rob Landley53437472006-07-16 08:14:35 +00008255 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008256 }
8257
8258 cmd_is_exec = 0;
8259 spclbltin = -1;
8260
8261 /* Now locate the command. */
8262 if (argc) {
8263 const char *oldpath;
8264 int cmd_flag = DO_ERR;
8265
8266 path += 5;
8267 oldpath = path;
8268 for (;;) {
8269 find_command(argv[0], &cmdentry, cmd_flag, path);
8270 if (cmdentry.cmdtype == CMDUNKNOWN) {
8271 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008272 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008273 goto bail;
8274 }
8275
8276 /* implement bltin and command here */
8277 if (cmdentry.cmdtype != CMDBUILTIN)
8278 break;
8279 if (spclbltin < 0)
8280 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8281 if (cmdentry.u.cmd == EXECCMD)
8282 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008283#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008284 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008285 path = oldpath;
8286 nargv = parse_command_args(argv, &path);
8287 if (!nargv)
8288 break;
8289 argc -= nargv - argv;
8290 argv = nargv;
8291 cmd_flag |= DO_NOFUNC;
8292 } else
8293#endif
8294 break;
8295 }
8296 }
8297
8298 if (status) {
8299 /* We have a redirection error. */
8300 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008301 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008302 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008303 exitstatus = status;
8304 goto out;
8305 }
8306
8307 /* Execute the command. */
8308 switch (cmdentry.cmdtype) {
8309 default:
8310 /* Fork off a child process if necessary. */
8311 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008312 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008313 jp = makejob(cmd, 1);
8314 if (forkshell(jp, cmd, FORK_FG) != 0) {
8315 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008316 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008317 break;
8318 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008319 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008320 }
8321 listsetvar(varlist.list, VEXPORT|VSTACK);
8322 shellexec(argv, path, cmdentry.u.index);
8323 /* NOTREACHED */
8324
8325 case CMDBUILTIN:
8326 cmdenviron = varlist.list;
8327 if (cmdenviron) {
8328 struct strlist *list = cmdenviron;
8329 int i = VNOSET;
8330 if (spclbltin > 0 || argc == 0) {
8331 i = 0;
8332 if (cmd_is_exec && argc > 1)
8333 i = VEXPORT;
8334 }
8335 listsetvar(list, i);
8336 }
8337 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8338 int exit_status;
8339 int i, j;
8340
8341 i = exception;
8342 if (i == EXEXIT)
8343 goto raise;
8344
8345 exit_status = 2;
8346 j = 0;
8347 if (i == EXINT)
8348 j = SIGINT;
8349 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008350 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008351 if (j)
8352 exit_status = j + 128;
8353 exitstatus = exit_status;
8354
8355 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008356 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008357 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008358 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008359 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008360 }
8361 break;
8362
8363 case CMDFUNCTION:
8364 listsetvar(varlist.list, 0);
8365 if (evalfun(cmdentry.u.func, argc, argv, flags))
8366 goto raise;
8367 break;
8368 }
8369
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008370 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008371 popredir(cmd_is_exec);
8372 if (lastarg)
8373 /* dsl: I think this is intended to be used to support
8374 * '_' in 'vi' command mode during line editing...
8375 * However I implemented that within libedit itself.
8376 */
8377 setvar("_", lastarg, 0);
8378 popstackmark(&smark);
8379}
8380
8381static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008382evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8383{
Eric Andersenc470f442003-07-28 09:56:35 +00008384 char *volatile savecmdname;
8385 struct jmploc *volatile savehandler;
8386 struct jmploc jmploc;
8387 int i;
8388
8389 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008390 i = setjmp(jmploc.loc);
8391 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008392 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008393 savehandler = exception_handler;
8394 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008395 commandname = argv[0];
8396 argptr = argv + 1;
8397 optptr = NULL; /* initialize nextopt */
8398 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008399 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008400 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008401 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008402 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008403 commandname = savecmdname;
8404 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008405 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008406
8407 return i;
8408}
8409
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008410static int
8411goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008412{
8413 return !*endofname(p);
8414}
8415
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008416
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008417/*
8418 * Search for a command. This is called before we fork so that the
8419 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008420 * the child. The check for "goodname" is an overly conservative
8421 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008422 */
Eric Andersenc470f442003-07-28 09:56:35 +00008423static void
8424prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008425{
8426 struct cmdentry entry;
8427
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008428 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8429 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008430}
8431
Eric Andersencb57d552001-06-28 07:25:16 +00008432
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008433/* ============ Builtin commands
8434 *
8435 * Builtin commands whose functions are closely tied to evaluation
8436 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008437 */
8438
8439/*
Eric Andersencb57d552001-06-28 07:25:16 +00008440 * Handle break and continue commands. Break, continue, and return are
8441 * all handled by setting the evalskip flag. The evaluation routines
8442 * above all check this flag, and if it is set they start skipping
8443 * commands rather than executing them. The variable skipcount is
8444 * the number of loops to break/continue, or the number of function
8445 * levels to return. (The latter is always 1.) It should probably
8446 * be an error to break out of more loops than exist, but it isn't
8447 * in the standard shell so we don't make it one here.
8448 */
Eric Andersenc470f442003-07-28 09:56:35 +00008449static int
8450breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008451{
8452 int n = argc > 1 ? number(argv[1]) : 1;
8453
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008454 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008455 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008456 if (n > loopnest)
8457 n = loopnest;
8458 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008459 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008460 skipcount = n;
8461 }
8462 return 0;
8463}
8464
Eric Andersenc470f442003-07-28 09:56:35 +00008465
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008466/* ============ input.c
8467 *
Eric Andersen90898442003-08-06 11:20:52 +00008468 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008469 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008470
Eric Andersenc470f442003-07-28 09:56:35 +00008471#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008472
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008473enum {
8474 INPUT_PUSH_FILE = 1,
8475 INPUT_NOFILE_OK = 2,
8476};
Eric Andersencb57d552001-06-28 07:25:16 +00008477
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008478static int plinno = 1; /* input line number */
8479/* number of characters left in input buffer */
8480static int parsenleft; /* copy of parsefile->nleft */
8481static int parselleft; /* copy of parsefile->lleft */
8482/* next character in input buffer */
8483static char *parsenextc; /* copy of parsefile->nextc */
8484
8485static int checkkwd;
8486/* values of checkkwd variable */
8487#define CHKALIAS 0x1
8488#define CHKKWD 0x2
8489#define CHKNL 0x4
8490
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008491static void
8492popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008493{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008494 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008495
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008496 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008497#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008498 if (sp->ap) {
8499 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8500 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008501 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008502 if (sp->string != sp->ap->val) {
8503 free(sp->string);
8504 }
8505 sp->ap->flag &= ~ALIASINUSE;
8506 if (sp->ap->flag & ALIASDEAD) {
8507 unalias(sp->ap->name);
8508 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008509 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008510#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008511 parsenextc = sp->prevstring;
8512 parsenleft = sp->prevnleft;
8513/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8514 parsefile->strpush = sp->prev;
8515 if (sp != &(parsefile->basestrpush))
8516 free(sp);
8517 INT_ON;
8518}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008519
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008520static int
8521preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008522{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008523 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008524 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008525 parsenextc = buf;
8526
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008527 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008528#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008529 if (!iflag || parsefile->fd)
8530 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8531 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008532#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008533 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008534#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008535 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8536 if (nr == 0) {
8537 /* Ctrl+C pressed */
8538 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008539 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008540 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008541 raise(SIGINT);
8542 return 1;
8543 }
Eric Andersenc470f442003-07-28 09:56:35 +00008544 goto retry;
8545 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008546 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008547 /* Ctrl+D presend */
8548 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008549 }
Eric Andersencb57d552001-06-28 07:25:16 +00008550 }
8551#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008552 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008553#endif
8554
8555 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008556 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008557 int flags = fcntl(0, F_GETFL);
Eric Andersencb57d552001-06-28 07:25:16 +00008558 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008559 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008560 if (fcntl(0, F_SETFL, flags) >= 0) {
8561 out2str("sh: turning off NDELAY mode\n");
8562 goto retry;
8563 }
8564 }
8565 }
8566 }
8567 return nr;
8568}
8569
8570/*
8571 * Refill the input buffer and return the next input character:
8572 *
8573 * 1) If a string was pushed back on the input, pop it;
8574 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8575 * from a string so we can't refill the buffer, return EOF.
8576 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8577 * 4) Process input up to the next newline, deleting nul characters.
8578 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008579static int
Eric Andersenc470f442003-07-28 09:56:35 +00008580preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008581{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008582 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008583 int more;
8584 char savec;
8585
8586 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008587#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008588 if (parsenleft == -1 && parsefile->strpush->ap &&
8589 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008590 return PEOA;
8591 }
Eric Andersen2870d962001-07-02 17:27:21 +00008592#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008593 popstring();
8594 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008595 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008596 }
8597 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8598 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008599 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008600
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008601 more = parselleft;
8602 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008603 again:
8604 more = preadfd();
8605 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008606 parselleft = parsenleft = EOF_NLEFT;
8607 return PEOF;
8608 }
8609 }
8610
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008611 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008612
8613 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008614 for (;;) {
8615 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008616
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008617 more--;
8618 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008619
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008620 if (!c)
8621 memmove(q, q + 1, more);
8622 else {
8623 q++;
8624 if (c == '\n') {
8625 parsenleft = q - parsenextc - 1;
8626 break;
8627 }
Eric Andersencb57d552001-06-28 07:25:16 +00008628 }
8629
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008630 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008631 parsenleft = q - parsenextc - 1;
8632 if (parsenleft < 0)
8633 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008634 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008635 }
8636 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008637 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008638
8639 savec = *q;
8640 *q = '\0';
8641
8642 if (vflag) {
8643 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008644 }
8645
8646 *q = savec;
8647
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008648 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008649}
8650
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008651#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008652static int
8653pgetc(void)
8654{
8655 return pgetc_as_macro();
8656}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008657
8658#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8659#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008660#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008661#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008662#endif
8663
8664/*
8665 * Same as pgetc(), but ignores PEOA.
8666 */
8667#if ENABLE_ASH_ALIAS
8668static int
8669pgetc2(void)
8670{
8671 int c;
8672
8673 do {
8674 c = pgetc_macro();
8675 } while (c == PEOA);
8676 return c;
8677}
8678#else
8679static int
8680pgetc2(void)
8681{
8682 return pgetc_macro();
8683}
8684#endif
8685
8686/*
8687 * Read a line from the script.
8688 */
8689static char *
8690pfgets(char *line, int len)
8691{
8692 char *p = line;
8693 int nleft = len;
8694 int c;
8695
8696 while (--nleft > 0) {
8697 c = pgetc2();
8698 if (c == PEOF) {
8699 if (p == line)
8700 return NULL;
8701 break;
8702 }
8703 *p++ = c;
8704 if (c == '\n')
8705 break;
8706 }
8707 *p = '\0';
8708 return line;
8709}
8710
Eric Andersenc470f442003-07-28 09:56:35 +00008711/*
8712 * Undo the last call to pgetc. Only one character may be pushed back.
8713 * PEOF may be pushed back.
8714 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008715static void
Eric Andersenc470f442003-07-28 09:56:35 +00008716pungetc(void)
8717{
8718 parsenleft++;
8719 parsenextc--;
8720}
Eric Andersencb57d552001-06-28 07:25:16 +00008721
8722/*
8723 * Push a string back onto the input at this current parsefile level.
8724 * We handle aliases this way.
8725 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008726static void
Eric Andersenc470f442003-07-28 09:56:35 +00008727pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008728{
Eric Andersencb57d552001-06-28 07:25:16 +00008729 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008730 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008731
Eric Andersenc470f442003-07-28 09:56:35 +00008732 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008733 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008734/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8735 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008736 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008737 sp->prev = parsefile->strpush;
8738 parsefile->strpush = sp;
8739 } else
8740 sp = parsefile->strpush = &(parsefile->basestrpush);
8741 sp->prevstring = parsenextc;
8742 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008743#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008744 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008745 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008746 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008747 sp->string = s;
8748 }
Eric Andersen2870d962001-07-02 17:27:21 +00008749#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008750 parsenextc = s;
8751 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008752 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008753}
8754
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008755/*
8756 * To handle the "." command, a stack of input files is used. Pushfile
8757 * adds a new entry to the stack and popfile restores the previous level.
8758 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008759static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008760pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008761{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008762 struct parsefile *pf;
8763
8764 parsefile->nleft = parsenleft;
8765 parsefile->lleft = parselleft;
8766 parsefile->nextc = parsenextc;
8767 parsefile->linno = plinno;
8768 pf = ckmalloc(sizeof(*pf));
8769 pf->prev = parsefile;
8770 pf->fd = -1;
8771 pf->strpush = NULL;
8772 pf->basestrpush.prev = NULL;
8773 parsefile = pf;
8774}
8775
8776static void
8777popfile(void)
8778{
8779 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008780
Denis Vlasenkob012b102007-02-19 22:43:01 +00008781 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008782 if (pf->fd >= 0)
8783 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008784 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008785 while (pf->strpush)
8786 popstring();
8787 parsefile = pf->prev;
8788 free(pf);
8789 parsenleft = parsefile->nleft;
8790 parselleft = parsefile->lleft;
8791 parsenextc = parsefile->nextc;
8792 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008793 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008794}
8795
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008796/*
8797 * Return to top level.
8798 */
8799static void
8800popallfiles(void)
8801{
8802 while (parsefile != &basepf)
8803 popfile();
8804}
8805
8806/*
8807 * Close the file(s) that the shell is reading commands from. Called
8808 * after a fork is done.
8809 */
8810static void
8811closescript(void)
8812{
8813 popallfiles();
8814 if (parsefile->fd > 0) {
8815 close(parsefile->fd);
8816 parsefile->fd = 0;
8817 }
8818}
8819
8820/*
8821 * Like setinputfile, but takes an open file descriptor. Call this with
8822 * interrupts off.
8823 */
8824static void
8825setinputfd(int fd, int push)
8826{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008827 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008828 if (push) {
8829 pushfile();
8830 parsefile->buf = 0;
8831 }
8832 parsefile->fd = fd;
8833 if (parsefile->buf == NULL)
8834 parsefile->buf = ckmalloc(IBUFSIZ);
8835 parselleft = parsenleft = 0;
8836 plinno = 1;
8837}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008838
Eric Andersenc470f442003-07-28 09:56:35 +00008839/*
8840 * Set the input to take input from a file. If push is set, push the
8841 * old input onto the stack first.
8842 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008843static int
8844setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008845{
8846 int fd;
8847 int fd2;
8848
Denis Vlasenkob012b102007-02-19 22:43:01 +00008849 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008850 fd = open(fname, O_RDONLY);
8851 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008852 if (flags & INPUT_NOFILE_OK)
8853 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008854 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008855 }
Eric Andersenc470f442003-07-28 09:56:35 +00008856 if (fd < 10) {
8857 fd2 = copyfd(fd, 10);
8858 close(fd);
8859 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008860 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008861 fd = fd2;
8862 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008863 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008864 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008865 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008866 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008867}
8868
Eric Andersencb57d552001-06-28 07:25:16 +00008869/*
8870 * Like setinputfile, but takes input from a string.
8871 */
Eric Andersenc470f442003-07-28 09:56:35 +00008872static void
8873setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008874{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008875 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008876 pushfile();
8877 parsenextc = string;
8878 parsenleft = strlen(string);
8879 parsefile->buf = NULL;
8880 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008881 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008882}
8883
8884
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008885/* ============ mail.c
8886 *
8887 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008888 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008889
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008890#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008891
Eric Andersencb57d552001-06-28 07:25:16 +00008892#define MAXMBOXES 10
8893
Eric Andersenc470f442003-07-28 09:56:35 +00008894/* times of mailboxes */
8895static time_t mailtime[MAXMBOXES];
8896/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008897static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008898
Eric Andersencb57d552001-06-28 07:25:16 +00008899/*
Eric Andersenc470f442003-07-28 09:56:35 +00008900 * Print appropriate message(s) if mail has arrived.
8901 * If mail_var_path_changed is set,
8902 * then the value of MAIL has mail_var_path_changed,
8903 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008904 */
Eric Andersenc470f442003-07-28 09:56:35 +00008905static void
8906chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008907{
Eric Andersencb57d552001-06-28 07:25:16 +00008908 const char *mpath;
8909 char *p;
8910 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008911 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008912 struct stackmark smark;
8913 struct stat statb;
8914
Eric Andersencb57d552001-06-28 07:25:16 +00008915 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008916 mpath = mpathset() ? mpathval() : mailval();
8917 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008918 p = padvance(&mpath, nullstr);
8919 if (p == NULL)
8920 break;
8921 if (*p == '\0')
8922 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008923 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008924#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008925 if (q[-1] != '/')
8926 abort();
8927#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008928 q[-1] = '\0'; /* delete trailing '/' */
8929 if (stat(p, &statb) < 0) {
8930 *mtp = 0;
8931 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008932 }
Eric Andersenc470f442003-07-28 09:56:35 +00008933 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8934 fprintf(
8935 stderr, snlfmt,
8936 pathopt ? pathopt : "you have mail"
8937 );
8938 }
8939 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008940 }
Eric Andersenc470f442003-07-28 09:56:35 +00008941 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008942 popstackmark(&smark);
8943}
Eric Andersencb57d552001-06-28 07:25:16 +00008944
Eric Andersenc470f442003-07-28 09:56:35 +00008945static void
8946changemail(const char *val)
8947{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008948 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00008949}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008950
Denis Vlasenko131ae172007-02-18 13:00:19 +00008951#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008952
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008953
8954/* ============ ??? */
8955
Eric Andersencb57d552001-06-28 07:25:16 +00008956/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008957 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008958 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008959static void
8960setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008961{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008962 char **newparam;
8963 char **ap;
8964 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008965
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008966 for (nparam = 0; argv[nparam]; nparam++);
8967 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8968 while (*argv) {
8969 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008970 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008971 *ap = NULL;
8972 freeparam(&shellparam);
8973 shellparam.malloc = 1;
8974 shellparam.nparam = nparam;
8975 shellparam.p = newparam;
8976#if ENABLE_ASH_GETOPTS
8977 shellparam.optind = 1;
8978 shellparam.optoff = -1;
8979#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008980}
8981
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008982/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008983 * Process shell options. The global variable argptr contains a pointer
8984 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008985 */
8986static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008987minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008988{
8989 int i;
8990
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008991 if (name) {
8992 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008993 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008994 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00008995 return;
8996 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008997 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008998 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00008999 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009000 out1str("Current option settings\n");
9001 for (i = 0; i < NOPTS; i++)
9002 out1fmt("%-16s%s\n", optnames(i),
9003 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009004}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009005static void
9006setoption(int flag, int val)
9007{
9008 int i;
9009
9010 for (i = 0; i < NOPTS; i++) {
9011 if (optletters(i) == flag) {
9012 optlist[i] = val;
9013 return;
9014 }
9015 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009016 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009017 /* NOTREACHED */
9018}
Eric Andersenc470f442003-07-28 09:56:35 +00009019static void
9020options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009021{
9022 char *p;
9023 int val;
9024 int c;
9025
9026 if (cmdline)
9027 minusc = NULL;
9028 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009029 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009030 if (c != '-' && c != '+')
9031 break;
9032 argptr++;
9033 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009034 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009035 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009036 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009037 if (!cmdline) {
9038 /* "-" means turn off -x and -v */
9039 if (p[0] == '\0')
9040 xflag = vflag = 0;
9041 /* "--" means reset params */
9042 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009043 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009044 }
Eric Andersenc470f442003-07-28 09:56:35 +00009045 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009046 }
Eric Andersencb57d552001-06-28 07:25:16 +00009047 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009048 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009049 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009050 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009051 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009052 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009053 } else if (c == 'o') {
9054 minus_o(*argptr, val);
9055 if (*argptr)
9056 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009057 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9058 isloginsh = 1;
9059 /* bash does not accept +-login, we also won't */
9060 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009061 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009062 isloginsh = 1;
9063 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009064 } else {
9065 setoption(c, val);
9066 }
9067 }
9068 }
9069}
9070
Eric Andersencb57d552001-06-28 07:25:16 +00009071/*
Eric Andersencb57d552001-06-28 07:25:16 +00009072 * The shift builtin command.
9073 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009074static int
Eric Andersenc470f442003-07-28 09:56:35 +00009075shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009076{
9077 int n;
9078 char **ap1, **ap2;
9079
9080 n = 1;
9081 if (argc > 1)
9082 n = number(argv[1]);
9083 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009084 ash_msg_and_raise_error("can't shift that many");
9085 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009086 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009087 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009088 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009089 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009090 }
9091 ap2 = shellparam.p;
9092 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009093#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009094 shellparam.optind = 1;
9095 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009096#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009097 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009098 return 0;
9099}
9100
Eric Andersencb57d552001-06-28 07:25:16 +00009101/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009102 * POSIX requires that 'set' (but not export or readonly) output the
9103 * variables in lexicographic order - by the locale's collating order (sigh).
9104 * Maybe we could keep them in an ordered balanced binary tree
9105 * instead of hashed lists.
9106 * For now just roll 'em through qsort for printing...
9107 */
9108static int
9109showvars(const char *sep_prefix, int on, int off)
9110{
9111 const char *sep;
9112 char **ep, **epend;
9113
9114 ep = listvars(on, off, &epend);
9115 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9116
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009117 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009118
9119 for (; ep < epend; ep++) {
9120 const char *p;
9121 const char *q;
9122
9123 p = strchrnul(*ep, '=');
9124 q = nullstr;
9125 if (*p)
9126 q = single_quote(++p);
9127 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9128 }
9129 return 0;
9130}
9131
9132/*
Eric Andersencb57d552001-06-28 07:25:16 +00009133 * The set command builtin.
9134 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009135static int
Eric Andersenc470f442003-07-28 09:56:35 +00009136setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009137{
9138 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009139 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009140 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009141 options(0);
9142 optschanged();
9143 if (*argptr != NULL) {
9144 setparam(argptr);
9145 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009146 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009147 return 0;
9148}
9149
Denis Vlasenko131ae172007-02-18 13:00:19 +00009150#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009151/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009152static void
9153change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009154{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009155 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009156 /* "get", generate */
9157 char buf[16];
9158
9159 rseed = rseed * 1103515245 + 12345;
9160 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9161 /* set without recursion */
9162 setvar(vrandom.text, buf, VNOFUNC);
9163 vrandom.flags &= ~VNOFUNC;
9164 } else {
9165 /* set/reset */
9166 rseed = strtoul(value, (char **)NULL, 10);
9167 }
Eric Andersenef02f822004-03-11 13:34:24 +00009168}
Eric Andersen16767e22004-03-16 05:14:10 +00009169#endif
9170
Denis Vlasenko131ae172007-02-18 13:00:19 +00009171#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009172static int
Eric Andersenc470f442003-07-28 09:56:35 +00009173getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009174{
9175 char *p, *q;
9176 char c = '?';
9177 int done = 0;
9178 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009179 char s[12];
9180 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009181
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009182 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009183 return 1;
9184 optnext = optfirst + *param_optind - 1;
9185
9186 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009187 p = NULL;
9188 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009189 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009190 if (p == NULL || *p == '\0') {
9191 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009192 p = *optnext;
9193 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009194 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009195 p = NULL;
9196 done = 1;
9197 goto out;
9198 }
9199 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009200 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009201 goto atend;
9202 }
9203
9204 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009205 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009206 if (*q == '\0') {
9207 if (optstr[0] == ':') {
9208 s[0] = c;
9209 s[1] = '\0';
9210 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009211 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009212 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009213 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009214 }
9215 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009216 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009217 }
9218 if (*++q == ':')
9219 q++;
9220 }
9221
9222 if (*++q == ':') {
9223 if (*p == '\0' && (p = *optnext) == NULL) {
9224 if (optstr[0] == ':') {
9225 s[0] = c;
9226 s[1] = '\0';
9227 err |= setvarsafe("OPTARG", s, 0);
9228 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009229 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009230 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009231 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009232 c = '?';
9233 }
Eric Andersenc470f442003-07-28 09:56:35 +00009234 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009235 }
9236
9237 if (p == *optnext)
9238 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009239 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009240 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009241 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009242 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009243 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009244 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009245 *param_optind = optnext - optfirst + 1;
9246 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009247 err |= setvarsafe("OPTIND", s, VNOFUNC);
9248 s[0] = c;
9249 s[1] = '\0';
9250 err |= setvarsafe(optvar, s, 0);
9251 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009252 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009253 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009254 flush_stdout_stderr();
9255 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009256 }
9257 return done;
9258}
Eric Andersenc470f442003-07-28 09:56:35 +00009259
9260/*
9261 * The getopts builtin. Shellparam.optnext points to the next argument
9262 * to be processed. Shellparam.optptr points to the next character to
9263 * be processed in the current argument. If shellparam.optnext is NULL,
9264 * then it's the first time getopts has been called.
9265 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009266static int
Eric Andersenc470f442003-07-28 09:56:35 +00009267getoptscmd(int argc, char **argv)
9268{
9269 char **optbase;
9270
9271 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009272 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009273 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009274 optbase = shellparam.p;
9275 if (shellparam.optind > shellparam.nparam + 1) {
9276 shellparam.optind = 1;
9277 shellparam.optoff = -1;
9278 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009279 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009280 optbase = &argv[3];
9281 if (shellparam.optind > argc - 2) {
9282 shellparam.optind = 1;
9283 shellparam.optoff = -1;
9284 }
9285 }
9286
9287 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009288 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009289}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009290#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009291
Eric Andersencb57d552001-06-28 07:25:16 +00009292
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009293/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009294
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009295/*
9296 * NEOF is returned by parsecmd when it encounters an end of file. It
9297 * must be distinct from NULL, so we use the address of a variable that
9298 * happens to be handy.
9299 */
9300static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009301#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009302static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009303static int lasttoken; /* last token read */
9304static char *wordtext; /* text of last word returned by readtoken */
9305static struct nodelist *backquotelist;
9306static union node *redirnode;
9307static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009308static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009309
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009310static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9311static void
9312raise_error_syntax(const char *msg)
9313{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009314 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009315 /* NOTREACHED */
9316}
9317
9318/*
9319 * Called when an unexpected token is read during the parse. The argument
9320 * is the token that is expected, or -1 if more than one type of token can
9321 * occur at this point.
9322 */
9323static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9324static void
9325raise_error_unexpected_syntax(int token)
9326{
9327 char msg[64];
9328 int l;
9329
9330 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9331 if (token >= 0)
9332 sprintf(msg + l, " (expecting %s)", tokname(token));
9333 raise_error_syntax(msg);
9334 /* NOTREACHED */
9335}
Eric Andersencb57d552001-06-28 07:25:16 +00009336
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009337#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009338
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009339struct heredoc {
9340 struct heredoc *next; /* next here document in list */
9341 union node *here; /* redirection node */
9342 char *eofmark; /* string indicating end of input */
9343 int striptabs; /* if set, strip leading tabs */
9344};
Eric Andersencb57d552001-06-28 07:25:16 +00009345
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009346static struct heredoc *heredoclist; /* list of here documents to read */
9347
9348/* parsing is heavily cross-recursive, need these forward decls */
9349static union node *andor(void);
9350static union node *pipeline(void);
9351static union node *parse_command(void);
9352static void parseheredoc(void);
9353static char peektoken(void);
9354static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009355
Eric Andersenc470f442003-07-28 09:56:35 +00009356static union node *
9357list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009358{
9359 union node *n1, *n2, *n3;
9360 int tok;
9361
Eric Andersenc470f442003-07-28 09:56:35 +00009362 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9363 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009364 return NULL;
9365 n1 = NULL;
9366 for (;;) {
9367 n2 = andor();
9368 tok = readtoken();
9369 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009370 if (n2->type == NPIPE) {
9371 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009372 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009373 if (n2->type != NREDIR) {
9374 n3 = stalloc(sizeof(struct nredir));
9375 n3->nredir.n = n2;
9376 n3->nredir.redirect = NULL;
9377 n2 = n3;
9378 }
9379 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009380 }
9381 }
9382 if (n1 == NULL) {
9383 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009384 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009385 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009386 n3->type = NSEMI;
9387 n3->nbinary.ch1 = n1;
9388 n3->nbinary.ch2 = n2;
9389 n1 = n3;
9390 }
9391 switch (tok) {
9392 case TBACKGND:
9393 case TSEMI:
9394 tok = readtoken();
9395 /* fall through */
9396 case TNL:
9397 if (tok == TNL) {
9398 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009399 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009400 return n1;
9401 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009402 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009403 }
Eric Andersenc470f442003-07-28 09:56:35 +00009404 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009405 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009406 return n1;
9407 break;
9408 case TEOF:
9409 if (heredoclist)
9410 parseheredoc();
9411 else
Eric Andersenc470f442003-07-28 09:56:35 +00009412 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009413 return n1;
9414 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009415 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009416 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009417 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009418 return n1;
9419 }
9420 }
9421}
9422
Eric Andersenc470f442003-07-28 09:56:35 +00009423static union node *
9424andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009425{
Eric Andersencb57d552001-06-28 07:25:16 +00009426 union node *n1, *n2, *n3;
9427 int t;
9428
Eric Andersencb57d552001-06-28 07:25:16 +00009429 n1 = pipeline();
9430 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009431 t = readtoken();
9432 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009433 t = NAND;
9434 } else if (t == TOR) {
9435 t = NOR;
9436 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009437 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009438 return n1;
9439 }
Eric Andersenc470f442003-07-28 09:56:35 +00009440 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009441 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009442 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009443 n3->type = t;
9444 n3->nbinary.ch1 = n1;
9445 n3->nbinary.ch2 = n2;
9446 n1 = n3;
9447 }
9448}
9449
Eric Andersenc470f442003-07-28 09:56:35 +00009450static union node *
9451pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009452{
Eric Andersencb57d552001-06-28 07:25:16 +00009453 union node *n1, *n2, *pipenode;
9454 struct nodelist *lp, *prev;
9455 int negate;
9456
9457 negate = 0;
9458 TRACE(("pipeline: entered\n"));
9459 if (readtoken() == TNOT) {
9460 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009461 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009462 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009463 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009464 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009465 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009466 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009467 pipenode->type = NPIPE;
9468 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009469 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009470 pipenode->npipe.cmdlist = lp;
9471 lp->n = n1;
9472 do {
9473 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009474 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009475 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009476 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009477 prev->next = lp;
9478 } while (readtoken() == TPIPE);
9479 lp->next = NULL;
9480 n1 = pipenode;
9481 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009482 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009483 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009484 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009485 n2->type = NNOT;
9486 n2->nnot.com = n1;
9487 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009488 }
9489 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009490}
9491
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009492static union node *
9493makename(void)
9494{
9495 union node *n;
9496
9497 n = stalloc(sizeof(struct narg));
9498 n->type = NARG;
9499 n->narg.next = NULL;
9500 n->narg.text = wordtext;
9501 n->narg.backquote = backquotelist;
9502 return n;
9503}
9504
9505static void
9506fixredir(union node *n, const char *text, int err)
9507{
9508 TRACE(("Fix redir %s %d\n", text, err));
9509 if (!err)
9510 n->ndup.vname = NULL;
9511
9512 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009513 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009514 else if (LONE_DASH(text))
9515 n->ndup.dupfd = -1;
9516 else {
9517 if (err)
9518 raise_error_syntax("Bad fd number");
9519 n->ndup.vname = makename();
9520 }
9521}
9522
9523/*
9524 * Returns true if the text contains nothing to expand (no dollar signs
9525 * or backquotes).
9526 */
9527static int
9528noexpand(char *text)
9529{
9530 char *p;
9531 char c;
9532
9533 p = text;
9534 while ((c = *p++) != '\0') {
9535 if (c == CTLQUOTEMARK)
9536 continue;
9537 if (c == CTLESC)
9538 p++;
9539 else if (SIT(c, BASESYNTAX) == CCTL)
9540 return 0;
9541 }
9542 return 1;
9543}
9544
9545static void
9546parsefname(void)
9547{
9548 union node *n = redirnode;
9549
9550 if (readtoken() != TWORD)
9551 raise_error_unexpected_syntax(-1);
9552 if (n->type == NHERE) {
9553 struct heredoc *here = heredoc;
9554 struct heredoc *p;
9555 int i;
9556
9557 if (quoteflag == 0)
9558 n->type = NXHERE;
9559 TRACE(("Here document %d\n", n->type));
9560 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9561 raise_error_syntax("Illegal eof marker for << redirection");
9562 rmescapes(wordtext);
9563 here->eofmark = wordtext;
9564 here->next = NULL;
9565 if (heredoclist == NULL)
9566 heredoclist = here;
9567 else {
9568 for (p = heredoclist; p->next; p = p->next);
9569 p->next = here;
9570 }
9571 } else if (n->type == NTOFD || n->type == NFROMFD) {
9572 fixredir(n, wordtext, 0);
9573 } else {
9574 n->nfile.fname = makename();
9575 }
9576}
Eric Andersencb57d552001-06-28 07:25:16 +00009577
Eric Andersenc470f442003-07-28 09:56:35 +00009578static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009579simplecmd(void)
9580{
9581 union node *args, **app;
9582 union node *n = NULL;
9583 union node *vars, **vpp;
9584 union node **rpp, *redir;
9585 int savecheckkwd;
9586
9587 args = NULL;
9588 app = &args;
9589 vars = NULL;
9590 vpp = &vars;
9591 redir = NULL;
9592 rpp = &redir;
9593
9594 savecheckkwd = CHKALIAS;
9595 for (;;) {
9596 checkkwd = savecheckkwd;
9597 switch (readtoken()) {
9598 case TWORD:
9599 n = stalloc(sizeof(struct narg));
9600 n->type = NARG;
9601 n->narg.text = wordtext;
9602 n->narg.backquote = backquotelist;
9603 if (savecheckkwd && isassignment(wordtext)) {
9604 *vpp = n;
9605 vpp = &n->narg.next;
9606 } else {
9607 *app = n;
9608 app = &n->narg.next;
9609 savecheckkwd = 0;
9610 }
9611 break;
9612 case TREDIR:
9613 *rpp = n = redirnode;
9614 rpp = &n->nfile.next;
9615 parsefname(); /* read name of redirection file */
9616 break;
9617 case TLP:
9618 if (args && app == &args->narg.next
9619 && !vars && !redir
9620 ) {
9621 struct builtincmd *bcmd;
9622 const char *name;
9623
9624 /* We have a function */
9625 if (readtoken() != TRP)
9626 raise_error_unexpected_syntax(TRP);
9627 name = n->narg.text;
9628 if (!goodname(name)
9629 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9630 ) {
9631 raise_error_syntax("Bad function name");
9632 }
9633 n->type = NDEFUN;
9634 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9635 n->narg.next = parse_command();
9636 return n;
9637 }
9638 /* fall through */
9639 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009640 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009641 goto out;
9642 }
9643 }
9644 out:
9645 *app = NULL;
9646 *vpp = NULL;
9647 *rpp = NULL;
9648 n = stalloc(sizeof(struct ncmd));
9649 n->type = NCMD;
9650 n->ncmd.args = args;
9651 n->ncmd.assign = vars;
9652 n->ncmd.redirect = redir;
9653 return n;
9654}
9655
9656static union node *
9657parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009658{
Eric Andersencb57d552001-06-28 07:25:16 +00009659 union node *n1, *n2;
9660 union node *ap, **app;
9661 union node *cp, **cpp;
9662 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009663 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009664 int t;
9665
9666 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009667 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009668
Eric Andersencb57d552001-06-28 07:25:16 +00009669 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009670 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009671 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009672 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009673 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009674 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009675 n1->type = NIF;
9676 n1->nif.test = list(0);
9677 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009678 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009679 n1->nif.ifpart = list(0);
9680 n2 = n1;
9681 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009682 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009683 n2 = n2->nif.elsepart;
9684 n2->type = NIF;
9685 n2->nif.test = list(0);
9686 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009687 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009688 n2->nif.ifpart = list(0);
9689 }
9690 if (lasttoken == TELSE)
9691 n2->nif.elsepart = list(0);
9692 else {
9693 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009694 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009695 }
Eric Andersenc470f442003-07-28 09:56:35 +00009696 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009697 break;
9698 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009699 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009700 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009701 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009702 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009703 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009704 got = readtoken();
9705 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009706 TRACE(("expecting DO got %s %s\n", tokname(got),
9707 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009708 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009709 }
9710 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009711 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009712 break;
9713 }
9714 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009715 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009716 raise_error_syntax("Bad for loop variable");
9717 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009718 n1->type = NFOR;
9719 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009720 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009721 if (readtoken() == TIN) {
9722 app = &ap;
9723 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009724 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009725 n2->type = NARG;
9726 n2->narg.text = wordtext;
9727 n2->narg.backquote = backquotelist;
9728 *app = n2;
9729 app = &n2->narg.next;
9730 }
9731 *app = NULL;
9732 n1->nfor.args = ap;
9733 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009734 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009735 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009736 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009737 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009738 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009739 n2->narg.backquote = NULL;
9740 n2->narg.next = NULL;
9741 n1->nfor.args = n2;
9742 /*
9743 * Newline or semicolon here is optional (but note
9744 * that the original Bourne shell only allowed NL).
9745 */
9746 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009747 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009748 }
Eric Andersenc470f442003-07-28 09:56:35 +00009749 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009750 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009751 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009752 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009753 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009754 break;
9755 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009756 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009757 n1->type = NCASE;
9758 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009759 raise_error_unexpected_syntax(TWORD);
9760 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009761 n2->type = NARG;
9762 n2->narg.text = wordtext;
9763 n2->narg.backquote = backquotelist;
9764 n2->narg.next = NULL;
9765 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009766 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009767 } while (readtoken() == TNL);
9768 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009769 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009770 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009771 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009772 checkkwd = CHKNL | CHKKWD;
9773 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009774 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009775 if (lasttoken == TLP)
9776 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009777 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009778 cp->type = NCLIST;
9779 app = &cp->nclist.pattern;
9780 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009781 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009782 ap->type = NARG;
9783 ap->narg.text = wordtext;
9784 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009785 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009786 break;
9787 app = &ap->narg.next;
9788 readtoken();
9789 }
9790 ap->narg.next = NULL;
9791 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009792 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009793 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009794
Eric Andersenc470f442003-07-28 09:56:35 +00009795 cpp = &cp->nclist.next;
9796
9797 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009798 t = readtoken();
9799 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009800 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 raise_error_unexpected_syntax(TENDCASE);
9802 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009803 }
Eric Andersenc470f442003-07-28 09:56:35 +00009804 }
Eric Andersencb57d552001-06-28 07:25:16 +00009805 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009806 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009807 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009808 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009809 n1->type = NSUBSHELL;
9810 n1->nredir.n = list(0);
9811 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009812 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 break;
9814 case TBEGIN:
9815 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009816 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009817 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009818 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009819 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009820 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009821 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009822 }
9823
Eric Andersenc470f442003-07-28 09:56:35 +00009824 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009825 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009826
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009827 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009828 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009829 checkkwd = CHKKWD | CHKALIAS;
9830 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009831 while (readtoken() == TREDIR) {
9832 *rpp = n2 = redirnode;
9833 rpp = &n2->nfile.next;
9834 parsefname();
9835 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009836 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009837 *rpp = NULL;
9838 if (redir) {
9839 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009840 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009841 n2->type = NREDIR;
9842 n2->nredir.n = n1;
9843 n1 = n2;
9844 }
9845 n1->nredir.redirect = redir;
9846 }
Eric Andersencb57d552001-06-28 07:25:16 +00009847 return n1;
9848}
9849
Eric Andersencb57d552001-06-28 07:25:16 +00009850/*
9851 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9852 * is not NULL, read a here document. In the latter case, eofmark is the
9853 * word which marks the end of the document and striptabs is true if
9854 * leading tabs should be stripped from the document. The argument firstc
9855 * is the first character of the input token or document.
9856 *
9857 * Because C does not have internal subroutines, I have simulated them
9858 * using goto's to implement the subroutine linkage. The following macros
9859 * will run code that appears at the end of readtoken1.
9860 */
9861
Eric Andersen2870d962001-07-02 17:27:21 +00009862#define CHECKEND() {goto checkend; checkend_return:;}
9863#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9864#define PARSESUB() {goto parsesub; parsesub_return:;}
9865#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9866#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9867#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009868
9869static int
Eric Andersenc470f442003-07-28 09:56:35 +00009870readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009871{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009872 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009873 int c = firstc;
9874 char *out;
9875 int len;
9876 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009877 struct nodelist *bqlist;
9878 smallint quotef;
9879 smallint dblquote;
9880 smallint oldstyle;
9881 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009882#if ENABLE_ASH_EXPAND_PRMT
9883 smallint pssyntax; /* we are expanding a prompt string */
9884#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009885 int varnest; /* levels of variables expansion */
9886 int arinest; /* levels of arithmetic expansion */
9887 int parenlevel; /* levels of parens in arithmetic */
9888 int dqvarnest; /* levels of variables expansion within double quotes */
9889
Eric Andersencb57d552001-06-28 07:25:16 +00009890#if __GNUC__
9891 /* Avoid longjmp clobbering */
9892 (void) &out;
9893 (void) &quotef;
9894 (void) &dblquote;
9895 (void) &varnest;
9896 (void) &arinest;
9897 (void) &parenlevel;
9898 (void) &dqvarnest;
9899 (void) &oldstyle;
9900 (void) &prevsyntax;
9901 (void) &syntax;
9902#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009903 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009905 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009906 oldstyle = 0;
9907 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +00009908#if ENABLE_ASH_EXPAND_PRMT
9909 pssyntax = (syntax == PSSYNTAX);
9910 if (pssyntax)
9911 syntax = DQSYNTAX;
9912#endif
9913 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +00009914 varnest = 0;
9915 arinest = 0;
9916 parenlevel = 0;
9917 dqvarnest = 0;
9918
9919 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009920 loop: { /* for each line, until end of word */
9921 CHECKEND(); /* set c to PEOF if at end of here document */
9922 for (;;) { /* until end of line or end of word */
9923 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009924 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009925 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009926 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009927 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009928 USTPUTC(c, out);
9929 plinno++;
9930 if (doprompt)
9931 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009932 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009933 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009934 case CWORD:
9935 USTPUTC(c, out);
9936 break;
9937 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009938 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009939 USTPUTC(CTLESC, out);
9940 USTPUTC(c, out);
9941 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009942 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009943 c = pgetc2();
9944 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009945 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009946 USTPUTC('\\', out);
9947 pungetc();
9948 } else if (c == '\n') {
9949 if (doprompt)
9950 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009951 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +00009952#if ENABLE_ASH_EXPAND_PRMT
9953 if (c == '$' && pssyntax) {
9954 USTPUTC(CTLESC, out);
9955 USTPUTC('\\', out);
9956 }
9957#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009958 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009959 c != '\\' && c != '`' &&
9960 c != '$' && (
9961 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009962 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009963 ) {
9964 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009965 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009966 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009967 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009968 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009969 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009970 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009971 }
9972 break;
9973 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009974 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009975 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009976 if (eofmark == NULL) {
9977 USTPUTC(CTLQUOTEMARK, out);
9978 }
Eric Andersencb57d552001-06-28 07:25:16 +00009979 break;
9980 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009981 syntax = DQSYNTAX;
9982 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009983 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009984 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009985 if (eofmark != NULL && arinest == 0
9986 && varnest == 0
9987 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009988 USTPUTC(c, out);
9989 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009990 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009991 syntax = BASESYNTAX;
9992 dblquote = 0;
9993 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009994 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009995 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009996 }
9997 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009998 case CVAR: /* '$' */
9999 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010000 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010001 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010002 if (varnest > 0) {
10003 varnest--;
10004 if (dqvarnest > 0) {
10005 dqvarnest--;
10006 }
10007 USTPUTC(CTLENDVAR, out);
10008 } else {
10009 USTPUTC(c, out);
10010 }
10011 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010012#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010013 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010014 parenlevel++;
10015 USTPUTC(c, out);
10016 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010017 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010018 if (parenlevel > 0) {
10019 USTPUTC(c, out);
10020 --parenlevel;
10021 } else {
10022 if (pgetc() == ')') {
10023 if (--arinest == 0) {
10024 USTPUTC(CTLENDARI, out);
10025 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010026 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010027 } else
10028 USTPUTC(')', out);
10029 } else {
10030 /*
10031 * unbalanced parens
10032 * (don't 2nd guess - no error)
10033 */
10034 pungetc();
10035 USTPUTC(')', out);
10036 }
10037 }
10038 break;
10039#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010040 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010041 PARSEBACKQOLD();
10042 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010043 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010044 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010045 case CIGN:
10046 break;
10047 default:
10048 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010049 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010050#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010051 if (c != PEOA)
10052#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010053 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010054
Eric Andersencb57d552001-06-28 07:25:16 +000010055 }
10056 c = pgetc_macro();
10057 }
10058 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010059 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010060#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010061 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010062 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010063#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010064 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010065 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010066 if (varnest != 0) {
10067 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010068 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010069 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010070 }
10071 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010072 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010073 out = stackblock();
10074 if (eofmark == NULL) {
10075 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010076 && quotef == 0
10077 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010078 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010079 PARSEREDIR();
10080 return lasttoken = TREDIR;
10081 } else {
10082 pungetc();
10083 }
10084 }
10085 quoteflag = quotef;
10086 backquotelist = bqlist;
10087 grabstackblock(len);
10088 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010089 lasttoken = TWORD;
10090 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010091/* end of readtoken routine */
10092
Eric Andersencb57d552001-06-28 07:25:16 +000010093/*
10094 * Check to see whether we are at the end of the here document. When this
10095 * is called, c is set to the first character of the next input line. If
10096 * we are at the end of the here document, this routine sets the c to PEOF.
10097 */
Eric Andersenc470f442003-07-28 09:56:35 +000010098checkend: {
10099 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010100#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010101 if (c == PEOA) {
10102 c = pgetc2();
10103 }
10104#endif
10105 if (striptabs) {
10106 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010107 c = pgetc2();
10108 }
Eric Andersenc470f442003-07-28 09:56:35 +000010109 }
10110 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010111 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010112 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010113
Eric Andersenc470f442003-07-28 09:56:35 +000010114 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010115 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010116 if (*p == '\n' && *q == '\0') {
10117 c = PEOF;
10118 plinno++;
10119 needprompt = doprompt;
10120 } else {
10121 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010122 }
10123 }
10124 }
10125 }
Eric Andersenc470f442003-07-28 09:56:35 +000010126 goto checkend_return;
10127}
Eric Andersencb57d552001-06-28 07:25:16 +000010128
Eric Andersencb57d552001-06-28 07:25:16 +000010129/*
10130 * Parse a redirection operator. The variable "out" points to a string
10131 * specifying the fd to be redirected. The variable "c" contains the
10132 * first character of the redirection operator.
10133 */
Eric Andersenc470f442003-07-28 09:56:35 +000010134parseredir: {
10135 char fd = *out;
10136 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010137
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010138 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010139 if (c == '>') {
10140 np->nfile.fd = 1;
10141 c = pgetc();
10142 if (c == '>')
10143 np->type = NAPPEND;
10144 else if (c == '|')
10145 np->type = NCLOBBER;
10146 else if (c == '&')
10147 np->type = NTOFD;
10148 else {
10149 np->type = NTO;
10150 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010151 }
Eric Andersenc470f442003-07-28 09:56:35 +000010152 } else { /* c == '<' */
10153 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010154 c = pgetc();
10155 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010156 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010157 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010158 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010159 np->nfile.fd = 0;
10160 }
10161 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010162 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010163 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010164 c = pgetc();
10165 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010166 heredoc->striptabs = 1;
10167 } else {
10168 heredoc->striptabs = 0;
10169 pungetc();
10170 }
10171 break;
10172
10173 case '&':
10174 np->type = NFROMFD;
10175 break;
10176
10177 case '>':
10178 np->type = NFROMTO;
10179 break;
10180
10181 default:
10182 np->type = NFROM;
10183 pungetc();
10184 break;
10185 }
Eric Andersencb57d552001-06-28 07:25:16 +000010186 }
Eric Andersenc470f442003-07-28 09:56:35 +000010187 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010188 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010189 redirnode = np;
10190 goto parseredir_return;
10191}
Eric Andersencb57d552001-06-28 07:25:16 +000010192
Eric Andersencb57d552001-06-28 07:25:16 +000010193/*
10194 * Parse a substitution. At this point, we have read the dollar sign
10195 * and nothing else.
10196 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010197
10198/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10199 * (assuming ascii char codes, as the original implementation did) */
10200#define is_special(c) \
10201 ((((unsigned int)c) - 33 < 32) \
10202 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010203parsesub: {
10204 int subtype;
10205 int typeloc;
10206 int flags;
10207 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010208 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010209
Eric Andersenc470f442003-07-28 09:56:35 +000010210 c = pgetc();
10211 if (
10212 c <= PEOA_OR_PEOF ||
10213 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10214 ) {
10215 USTPUTC('$', out);
10216 pungetc();
10217 } else if (c == '(') { /* $(command) or $((arith)) */
10218 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010219#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010220 PARSEARITH();
10221#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010222 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010223#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010224 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010225 pungetc();
10226 PARSEBACKQNEW();
10227 }
10228 } else {
10229 USTPUTC(CTLVAR, out);
10230 typeloc = out - (char *)stackblock();
10231 USTPUTC(VSNORMAL, out);
10232 subtype = VSNORMAL;
10233 if (c == '{') {
10234 c = pgetc();
10235 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010236 c = pgetc();
10237 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010238 c = '#';
10239 else
10240 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010241 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010242 subtype = 0;
10243 }
10244 if (c > PEOA_OR_PEOF && is_name(c)) {
10245 do {
10246 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010247 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010248 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010249 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010250 do {
10251 STPUTC(c, out);
10252 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010253 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010254 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010255 USTPUTC(c, out);
10256 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010257 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010258 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010259
Eric Andersenc470f442003-07-28 09:56:35 +000010260 STPUTC('=', out);
10261 flags = 0;
10262 if (subtype == 0) {
10263 switch (c) {
10264 case ':':
10265 flags = VSNUL;
10266 c = pgetc();
10267 /*FALLTHROUGH*/
10268 default:
10269 p = strchr(types, c);
10270 if (p == NULL)
10271 goto badsub;
10272 subtype = p - types + VSNORMAL;
10273 break;
10274 case '%':
10275 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010276 {
10277 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010278 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010279 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010280 c = pgetc();
10281 if (c == cc)
10282 subtype++;
10283 else
10284 pungetc();
10285 break;
10286 }
10287 }
Eric Andersenc470f442003-07-28 09:56:35 +000010288 } else {
10289 pungetc();
10290 }
10291 if (dblquote || arinest)
10292 flags |= VSQUOTE;
10293 *((char *)stackblock() + typeloc) = subtype | flags;
10294 if (subtype != VSNORMAL) {
10295 varnest++;
10296 if (dblquote || arinest) {
10297 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010298 }
10299 }
10300 }
Eric Andersenc470f442003-07-28 09:56:35 +000010301 goto parsesub_return;
10302}
Eric Andersencb57d552001-06-28 07:25:16 +000010303
Eric Andersencb57d552001-06-28 07:25:16 +000010304/*
10305 * Called to parse command substitutions. Newstyle is set if the command
10306 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10307 * list of commands (passed by reference), and savelen is the number of
10308 * characters on the top of the stack which must be preserved.
10309 */
Eric Andersenc470f442003-07-28 09:56:35 +000010310parsebackq: {
10311 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010312 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010313 union node *n;
10314 char *volatile str;
10315 struct jmploc jmploc;
10316 struct jmploc *volatile savehandler;
10317 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010318 smallint saveprompt = 0;
10319
Eric Andersencb57d552001-06-28 07:25:16 +000010320#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010321 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010322#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010323 savepbq = parsebackquote;
10324 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010325 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010326 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010327 exception_handler = savehandler;
10328 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010329 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010330 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010331 str = NULL;
10332 savelen = out - (char *)stackblock();
10333 if (savelen > 0) {
10334 str = ckmalloc(savelen);
10335 memcpy(str, stackblock(), savelen);
10336 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010337 savehandler = exception_handler;
10338 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010339 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010340 if (oldstyle) {
10341 /* We must read until the closing backquote, giving special
10342 treatment to some slashes, and then push the string and
10343 reread it as input, interpreting it normally. */
10344 char *pout;
10345 int pc;
10346 size_t psavelen;
10347 char *pstr;
10348
10349
10350 STARTSTACKSTR(pout);
10351 for (;;) {
10352 if (needprompt) {
10353 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010354 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010355 pc = pgetc();
10356 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010357 case '`':
10358 goto done;
10359
10360 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010361 pc = pgetc();
10362 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010363 plinno++;
10364 if (doprompt)
10365 setprompt(2);
10366 /*
10367 * If eating a newline, avoid putting
10368 * the newline into the new character
10369 * stream (via the STPUTC after the
10370 * switch).
10371 */
10372 continue;
10373 }
10374 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010375 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010376 STPUTC('\\', pout);
10377 if (pc > PEOA_OR_PEOF) {
10378 break;
10379 }
10380 /* fall through */
10381
10382 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010383#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010384 case PEOA:
10385#endif
10386 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010387 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010388
10389 case '\n':
10390 plinno++;
10391 needprompt = doprompt;
10392 break;
10393
10394 default:
10395 break;
10396 }
10397 STPUTC(pc, pout);
10398 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010399 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010400 STPUTC('\0', pout);
10401 psavelen = pout - (char *)stackblock();
10402 if (psavelen > 0) {
10403 pstr = grabstackstr(pout);
10404 setinputstring(pstr);
10405 }
10406 }
10407 nlpp = &bqlist;
10408 while (*nlpp)
10409 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010410 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010411 (*nlpp)->next = NULL;
10412 parsebackquote = oldstyle;
10413
10414 if (oldstyle) {
10415 saveprompt = doprompt;
10416 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010417 }
10418
Eric Andersenc470f442003-07-28 09:56:35 +000010419 n = list(2);
10420
10421 if (oldstyle)
10422 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010423 else if (readtoken() != TRP)
10424 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010425
10426 (*nlpp)->n = n;
10427 if (oldstyle) {
10428 /*
10429 * Start reading from old file again, ignoring any pushed back
10430 * tokens left from the backquote parsing
10431 */
10432 popfile();
10433 tokpushback = 0;
10434 }
10435 while (stackblocksize() <= savelen)
10436 growstackblock();
10437 STARTSTACKSTR(out);
10438 if (str) {
10439 memcpy(out, str, savelen);
10440 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010441 INT_OFF;
10442 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010443 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010444 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010445 }
10446 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010447 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010448 if (arinest || dblquote)
10449 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10450 else
10451 USTPUTC(CTLBACKQ, out);
10452 if (oldstyle)
10453 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010454 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010455}
10456
Denis Vlasenko131ae172007-02-18 13:00:19 +000010457#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010458/*
10459 * Parse an arithmetic expansion (indicate start of one and set state)
10460 */
Eric Andersenc470f442003-07-28 09:56:35 +000010461parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010462 if (++arinest == 1) {
10463 prevsyntax = syntax;
10464 syntax = ARISYNTAX;
10465 USTPUTC(CTLARI, out);
10466 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010467 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010468 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010469 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010470 } else {
10471 /*
10472 * we collapse embedded arithmetic expansion to
10473 * parenthesis, which should be equivalent
10474 */
10475 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010476 }
Eric Andersenc470f442003-07-28 09:56:35 +000010477 goto parsearith_return;
10478}
10479#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010480
Eric Andersenc470f442003-07-28 09:56:35 +000010481} /* end of readtoken */
10482
Eric Andersencb57d552001-06-28 07:25:16 +000010483/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010484 * Read the next input token.
10485 * If the token is a word, we set backquotelist to the list of cmds in
10486 * backquotes. We set quoteflag to true if any part of the word was
10487 * quoted.
10488 * If the token is TREDIR, then we set redirnode to a structure containing
10489 * the redirection.
10490 * In all cases, the variable startlinno is set to the number of the line
10491 * on which the token starts.
10492 *
10493 * [Change comment: here documents and internal procedures]
10494 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10495 * word parsing code into a separate routine. In this case, readtoken
10496 * doesn't need to have any internal procedures, but parseword does.
10497 * We could also make parseoperator in essence the main routine, and
10498 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010499 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010500#define NEW_xxreadtoken
10501#ifdef NEW_xxreadtoken
10502/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010503static const char xxreadtoken_chars[7] ALIGN1 = {
10504 '\n', '(', ')', '&', '|', ';', 0
10505};
Eric Andersencb57d552001-06-28 07:25:16 +000010506
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010507static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010508 TNL, TLP, TRP, /* only single occurrence allowed */
10509 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10510 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010511 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010512};
10513
10514#define xxreadtoken_doubles \
10515 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10516#define xxreadtoken_singles \
10517 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10518
10519static int
10520xxreadtoken(void)
10521{
10522 int c;
10523
10524 if (tokpushback) {
10525 tokpushback = 0;
10526 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010527 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010528 if (needprompt) {
10529 setprompt(2);
10530 }
10531 startlinno = plinno;
10532 for (;;) { /* until token or start of word found */
10533 c = pgetc_macro();
10534
10535 if ((c != ' ') && (c != '\t')
10536#if ENABLE_ASH_ALIAS
10537 && (c != PEOA)
10538#endif
10539 ) {
10540 if (c == '#') {
10541 while ((c = pgetc()) != '\n' && c != PEOF);
10542 pungetc();
10543 } else if (c == '\\') {
10544 if (pgetc() != '\n') {
10545 pungetc();
10546 goto READTOKEN1;
10547 }
10548 startlinno = ++plinno;
10549 if (doprompt)
10550 setprompt(2);
10551 } else {
10552 const char *p
10553 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10554
10555 if (c != PEOF) {
10556 if (c == '\n') {
10557 plinno++;
10558 needprompt = doprompt;
10559 }
10560
10561 p = strchr(xxreadtoken_chars, c);
10562 if (p == NULL) {
10563 READTOKEN1:
10564 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10565 }
10566
10567 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10568 if (pgetc() == *p) { /* double occurrence? */
10569 p += xxreadtoken_doubles + 1;
10570 } else {
10571 pungetc();
10572 }
10573 }
10574 }
10575 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10576 }
10577 }
10578 } /* for */
10579}
10580#else
10581#define RETURN(token) return lasttoken = token
10582static int
10583xxreadtoken(void)
10584{
10585 int c;
10586
10587 if (tokpushback) {
10588 tokpushback = 0;
10589 return lasttoken;
10590 }
10591 if (needprompt) {
10592 setprompt(2);
10593 }
10594 startlinno = plinno;
10595 for (;;) { /* until token or start of word found */
10596 c = pgetc_macro();
10597 switch (c) {
10598 case ' ': case '\t':
10599#if ENABLE_ASH_ALIAS
10600 case PEOA:
10601#endif
10602 continue;
10603 case '#':
10604 while ((c = pgetc()) != '\n' && c != PEOF);
10605 pungetc();
10606 continue;
10607 case '\\':
10608 if (pgetc() == '\n') {
10609 startlinno = ++plinno;
10610 if (doprompt)
10611 setprompt(2);
10612 continue;
10613 }
10614 pungetc();
10615 goto breakloop;
10616 case '\n':
10617 plinno++;
10618 needprompt = doprompt;
10619 RETURN(TNL);
10620 case PEOF:
10621 RETURN(TEOF);
10622 case '&':
10623 if (pgetc() == '&')
10624 RETURN(TAND);
10625 pungetc();
10626 RETURN(TBACKGND);
10627 case '|':
10628 if (pgetc() == '|')
10629 RETURN(TOR);
10630 pungetc();
10631 RETURN(TPIPE);
10632 case ';':
10633 if (pgetc() == ';')
10634 RETURN(TENDCASE);
10635 pungetc();
10636 RETURN(TSEMI);
10637 case '(':
10638 RETURN(TLP);
10639 case ')':
10640 RETURN(TRP);
10641 default:
10642 goto breakloop;
10643 }
10644 }
10645 breakloop:
10646 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10647#undef RETURN
10648}
10649#endif /* NEW_xxreadtoken */
10650
10651static int
10652readtoken(void)
10653{
10654 int t;
10655#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010656 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010657#endif
10658
10659#if ENABLE_ASH_ALIAS
10660 top:
10661#endif
10662
10663 t = xxreadtoken();
10664
10665 /*
10666 * eat newlines
10667 */
10668 if (checkkwd & CHKNL) {
10669 while (t == TNL) {
10670 parseheredoc();
10671 t = xxreadtoken();
10672 }
10673 }
10674
10675 if (t != TWORD || quoteflag) {
10676 goto out;
10677 }
10678
10679 /*
10680 * check for keywords
10681 */
10682 if (checkkwd & CHKKWD) {
10683 const char *const *pp;
10684
10685 pp = findkwd(wordtext);
10686 if (pp) {
10687 lasttoken = t = pp - tokname_array;
10688 TRACE(("keyword %s recognized\n", tokname(t)));
10689 goto out;
10690 }
10691 }
10692
10693 if (checkkwd & CHKALIAS) {
10694#if ENABLE_ASH_ALIAS
10695 struct alias *ap;
10696 ap = lookupalias(wordtext, 1);
10697 if (ap != NULL) {
10698 if (*ap->val) {
10699 pushstring(ap->val, ap);
10700 }
10701 goto top;
10702 }
10703#endif
10704 }
10705 out:
10706 checkkwd = 0;
10707#if DEBUG
10708 if (!alreadyseen)
10709 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10710 else
10711 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10712#endif
10713 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010714}
10715
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010716static char
10717peektoken(void)
10718{
10719 int t;
10720
10721 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010722 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723 return tokname_array[t][0];
10724}
Eric Andersencb57d552001-06-28 07:25:16 +000010725
10726/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010727 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10728 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010729 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010730static union node *
10731parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010732{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010734
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010735 tokpushback = 0;
10736 doprompt = interact;
10737 if (doprompt)
10738 setprompt(doprompt);
10739 needprompt = 0;
10740 t = readtoken();
10741 if (t == TEOF)
10742 return NEOF;
10743 if (t == TNL)
10744 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010745 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010746 return list(1);
10747}
10748
10749/*
10750 * Input any here documents.
10751 */
10752static void
10753parseheredoc(void)
10754{
10755 struct heredoc *here;
10756 union node *n;
10757
10758 here = heredoclist;
10759 heredoclist = 0;
10760
10761 while (here) {
10762 if (needprompt) {
10763 setprompt(2);
10764 }
10765 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10766 here->eofmark, here->striptabs);
10767 n = stalloc(sizeof(struct narg));
10768 n->narg.type = NARG;
10769 n->narg.next = NULL;
10770 n->narg.text = wordtext;
10771 n->narg.backquote = backquotelist;
10772 here->here->nhere.doc = n;
10773 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010774 }
Eric Andersencb57d552001-06-28 07:25:16 +000010775}
10776
10777
10778/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010779 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010780 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010781#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010782static const char *
10783expandstr(const char *ps)
10784{
10785 union node n;
10786
10787 /* XXX Fix (char *) cast. */
10788 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010789 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010790 popfile();
10791
10792 n.narg.type = NARG;
10793 n.narg.next = NULL;
10794 n.narg.text = wordtext;
10795 n.narg.backquote = backquotelist;
10796
10797 expandarg(&n, NULL, 0);
10798 return stackblock();
10799}
10800#endif
10801
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010802/*
10803 * Execute a command or commands contained in a string.
10804 */
10805static int
10806evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010807{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010808 union node *n;
10809 struct stackmark smark;
10810 int skip;
10811
10812 setinputstring(s);
10813 setstackmark(&smark);
10814
10815 skip = 0;
10816 while ((n = parsecmd(0)) != NEOF) {
10817 evaltree(n, 0);
10818 popstackmark(&smark);
10819 skip = evalskip;
10820 if (skip)
10821 break;
10822 }
10823 popfile();
10824
10825 skip &= mask;
10826 evalskip = skip;
10827 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010828}
10829
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010830/*
10831 * The eval command.
10832 */
10833static int
10834evalcmd(int argc, char **argv)
10835{
10836 char *p;
10837 char *concat;
10838 char **ap;
10839
10840 if (argc > 1) {
10841 p = argv[1];
10842 if (argc > 2) {
10843 STARTSTACKSTR(concat);
10844 ap = argv + 2;
10845 for (;;) {
10846 concat = stack_putstr(p, concat);
10847 p = *ap++;
10848 if (p == NULL)
10849 break;
10850 STPUTC(' ', concat);
10851 }
10852 STPUTC('\0', concat);
10853 p = grabstackstr(concat);
10854 }
10855 evalstring(p, ~SKIPEVAL);
10856
10857 }
10858 return exitstatus;
10859}
10860
10861/*
10862 * Read and execute commands. "Top" is nonzero for the top level command
10863 * loop; it turns on prompting if the shell is interactive.
10864 */
10865static int
10866cmdloop(int top)
10867{
10868 union node *n;
10869 struct stackmark smark;
10870 int inter;
10871 int numeof = 0;
10872
10873 TRACE(("cmdloop(%d) called\n", top));
10874 for (;;) {
10875 int skip;
10876
10877 setstackmark(&smark);
10878#if JOBS
10879 if (jobctl)
10880 showjobs(stderr, SHOW_CHANGED);
10881#endif
10882 inter = 0;
10883 if (iflag && top) {
10884 inter++;
10885#if ENABLE_ASH_MAIL
10886 chkmail();
10887#endif
10888 }
10889 n = parsecmd(inter);
10890 /* showtree(n); DEBUG */
10891 if (n == NEOF) {
10892 if (!top || numeof >= 50)
10893 break;
10894 if (!stoppedjobs()) {
10895 if (!Iflag)
10896 break;
10897 out2str("\nUse \"exit\" to leave shell.\n");
10898 }
10899 numeof++;
10900 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010901 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10902 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010903 numeof = 0;
10904 evaltree(n, 0);
10905 }
10906 popstackmark(&smark);
10907 skip = evalskip;
10908
10909 if (skip) {
10910 evalskip = 0;
10911 return skip & SKIPEVAL;
10912 }
10913 }
10914 return 0;
10915}
10916
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010917/*
10918 * Take commands from a file. To be compatible we should do a path
10919 * search for the file, which is necessary to find sub-commands.
10920 */
10921static char *
10922find_dot_file(char *name)
10923{
10924 char *fullname;
10925 const char *path = pathval();
10926 struct stat statb;
10927
10928 /* don't try this for absolute or relative paths */
10929 if (strchr(name, '/'))
10930 return name;
10931
10932 while ((fullname = padvance(&path, name)) != NULL) {
10933 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10934 /*
10935 * Don't bother freeing here, since it will
10936 * be freed by the caller.
10937 */
10938 return fullname;
10939 }
10940 stunalloc(fullname);
10941 }
10942
10943 /* not found in the PATH */
10944 ash_msg_and_raise_error("%s: not found", name);
10945 /* NOTREACHED */
10946}
10947
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010948static int
10949dotcmd(int argc, char **argv)
10950{
10951 struct strlist *sp;
10952 volatile struct shparam saveparam;
10953 int status = 0;
10954
10955 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010956 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010957
10958 if (argc >= 2) { /* That's what SVR2 does */
10959 char *fullname;
10960
10961 fullname = find_dot_file(argv[1]);
10962
10963 if (argc > 2) {
10964 saveparam = shellparam;
10965 shellparam.malloc = 0;
10966 shellparam.nparam = argc - 2;
10967 shellparam.p = argv + 2;
10968 };
10969
10970 setinputfile(fullname, INPUT_PUSH_FILE);
10971 commandname = fullname;
10972 cmdloop(0);
10973 popfile();
10974
10975 if (argc > 2) {
10976 freeparam(&shellparam);
10977 shellparam = saveparam;
10978 };
10979 status = exitstatus;
10980 }
10981 return status;
10982}
10983
10984static int
10985exitcmd(int argc, char **argv)
10986{
10987 if (stoppedjobs())
10988 return 0;
10989 if (argc > 1)
10990 exitstatus = number(argv[1]);
10991 raise_exception(EXEXIT);
10992 /* NOTREACHED */
10993}
10994
10995#if ENABLE_ASH_BUILTIN_ECHO
10996static int
10997echocmd(int argc, char **argv)
10998{
10999 return bb_echo(argv);
11000}
11001#endif
11002
11003#if ENABLE_ASH_BUILTIN_TEST
11004static int
11005testcmd(int argc, char **argv)
11006{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011007 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011008}
11009#endif
11010
11011/*
11012 * Read a file containing shell functions.
11013 */
11014static void
11015readcmdfile(char *name)
11016{
11017 setinputfile(name, INPUT_PUSH_FILE);
11018 cmdloop(0);
11019 popfile();
11020}
11021
11022
Denis Vlasenkocc571512007-02-23 21:10:35 +000011023/* ============ find_command inplementation */
11024
11025/*
11026 * Resolve a command name. If you change this routine, you may have to
11027 * change the shellexec routine as well.
11028 */
11029static void
11030find_command(char *name, struct cmdentry *entry, int act, const char *path)
11031{
11032 struct tblentry *cmdp;
11033 int idx;
11034 int prev;
11035 char *fullname;
11036 struct stat statb;
11037 int e;
11038 int updatetbl;
11039 struct builtincmd *bcmd;
11040
11041 /* If name contains a slash, don't use PATH or hash table */
11042 if (strchr(name, '/') != NULL) {
11043 entry->u.index = -1;
11044 if (act & DO_ABS) {
11045 while (stat(name, &statb) < 0) {
11046#ifdef SYSV
11047 if (errno == EINTR)
11048 continue;
11049#endif
11050 entry->cmdtype = CMDUNKNOWN;
11051 return;
11052 }
11053 }
11054 entry->cmdtype = CMDNORMAL;
11055 return;
11056 }
11057
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011058/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011059
11060 updatetbl = (path == pathval());
11061 if (!updatetbl) {
11062 act |= DO_ALTPATH;
11063 if (strstr(path, "%builtin") != NULL)
11064 act |= DO_ALTBLTIN;
11065 }
11066
11067 /* If name is in the table, check answer will be ok */
11068 cmdp = cmdlookup(name, 0);
11069 if (cmdp != NULL) {
11070 int bit;
11071
11072 switch (cmdp->cmdtype) {
11073 default:
11074#if DEBUG
11075 abort();
11076#endif
11077 case CMDNORMAL:
11078 bit = DO_ALTPATH;
11079 break;
11080 case CMDFUNCTION:
11081 bit = DO_NOFUNC;
11082 break;
11083 case CMDBUILTIN:
11084 bit = DO_ALTBLTIN;
11085 break;
11086 }
11087 if (act & bit) {
11088 updatetbl = 0;
11089 cmdp = NULL;
11090 } else if (cmdp->rehash == 0)
11091 /* if not invalidated by cd, we're done */
11092 goto success;
11093 }
11094
11095 /* If %builtin not in path, check for builtin next */
11096 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011097 if (bcmd) {
11098 if (IS_BUILTIN_REGULAR(bcmd))
11099 goto builtin_success;
11100 if (act & DO_ALTPATH) {
11101 if (!(act & DO_ALTBLTIN))
11102 goto builtin_success;
11103 } else if (builtinloc <= 0) {
11104 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011105 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011106 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011107
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011108#if ENABLE_FEATURE_SH_STANDALONE
11109 if (find_applet_by_name(name)) {
11110 entry->cmdtype = CMDNORMAL;
11111 entry->u.index = -1;
11112 return;
11113 }
11114#endif
11115
Denis Vlasenkocc571512007-02-23 21:10:35 +000011116 /* We have to search path. */
11117 prev = -1; /* where to start */
11118 if (cmdp && cmdp->rehash) { /* doing a rehash */
11119 if (cmdp->cmdtype == CMDBUILTIN)
11120 prev = builtinloc;
11121 else
11122 prev = cmdp->param.index;
11123 }
11124
11125 e = ENOENT;
11126 idx = -1;
11127 loop:
11128 while ((fullname = padvance(&path, name)) != NULL) {
11129 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011130 /* NB: code below will still use fullname
11131 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011132 idx++;
11133 if (pathopt) {
11134 if (prefix(pathopt, "builtin")) {
11135 if (bcmd)
11136 goto builtin_success;
11137 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011138 } else if (!(act & DO_NOFUNC)
11139 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011140 /* handled below */
11141 } else {
11142 /* ignore unimplemented options */
11143 continue;
11144 }
11145 }
11146 /* if rehash, don't redo absolute path names */
11147 if (fullname[0] == '/' && idx <= prev) {
11148 if (idx < prev)
11149 continue;
11150 TRACE(("searchexec \"%s\": no change\n", name));
11151 goto success;
11152 }
11153 while (stat(fullname, &statb) < 0) {
11154#ifdef SYSV
11155 if (errno == EINTR)
11156 continue;
11157#endif
11158 if (errno != ENOENT && errno != ENOTDIR)
11159 e = errno;
11160 goto loop;
11161 }
11162 e = EACCES; /* if we fail, this will be the error */
11163 if (!S_ISREG(statb.st_mode))
11164 continue;
11165 if (pathopt) { /* this is a %func directory */
11166 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011167 /* NB: stalloc will return space pointed by fullname
11168 * (because we don't have any intervening allocations
11169 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011170 readcmdfile(fullname);
11171 cmdp = cmdlookup(name, 0);
11172 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11173 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11174 stunalloc(fullname);
11175 goto success;
11176 }
11177 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11178 if (!updatetbl) {
11179 entry->cmdtype = CMDNORMAL;
11180 entry->u.index = idx;
11181 return;
11182 }
11183 INT_OFF;
11184 cmdp = cmdlookup(name, 1);
11185 cmdp->cmdtype = CMDNORMAL;
11186 cmdp->param.index = idx;
11187 INT_ON;
11188 goto success;
11189 }
11190
11191 /* We failed. If there was an entry for this command, delete it */
11192 if (cmdp && updatetbl)
11193 delete_cmd_entry();
11194 if (act & DO_ERR)
11195 ash_msg("%s: %s", name, errmsg(e, "not found"));
11196 entry->cmdtype = CMDUNKNOWN;
11197 return;
11198
11199 builtin_success:
11200 if (!updatetbl) {
11201 entry->cmdtype = CMDBUILTIN;
11202 entry->u.cmd = bcmd;
11203 return;
11204 }
11205 INT_OFF;
11206 cmdp = cmdlookup(name, 1);
11207 cmdp->cmdtype = CMDBUILTIN;
11208 cmdp->param.cmd = bcmd;
11209 INT_ON;
11210 success:
11211 cmdp->rehash = 0;
11212 entry->cmdtype = cmdp->cmdtype;
11213 entry->u = cmdp->param;
11214}
11215
11216
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011217/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011218
Eric Andersencb57d552001-06-28 07:25:16 +000011219/*
Eric Andersencb57d552001-06-28 07:25:16 +000011220 * The trap builtin.
11221 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011222static int
Eric Andersenc470f442003-07-28 09:56:35 +000011223trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011224{
11225 char *action;
11226 char **ap;
11227 int signo;
11228
Eric Andersenc470f442003-07-28 09:56:35 +000011229 nextopt(nullstr);
11230 ap = argptr;
11231 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011232 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011233 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011234 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011235
Rob Landleyc9c1a412006-07-12 19:17:55 +000011236 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011237 out1fmt("trap -- %s %s\n",
11238 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011239 }
11240 }
11241 return 0;
11242 }
Eric Andersenc470f442003-07-28 09:56:35 +000011243 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011244 action = NULL;
11245 else
11246 action = *ap++;
11247 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011248 signo = get_signum(*ap);
11249 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011250 ash_msg_and_raise_error("%s: bad trap", *ap);
11251 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011252 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011253 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011254 action = NULL;
11255 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011256 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011257 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011258 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011259 trap[signo] = action;
11260 if (signo != 0)
11261 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011262 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011263 ap++;
11264 }
11265 return 0;
11266}
11267
Eric Andersenc470f442003-07-28 09:56:35 +000011268
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011269/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011270
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011271#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011272/*
11273 * Lists available builtins
11274 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011275static int
11276helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011277{
11278 int col, i;
11279
11280 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011281 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011282 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011283 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011284 if (col > 60) {
11285 out1fmt("\n");
11286 col = 0;
11287 }
11288 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011289#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011290 for (i = 0; i < NUM_APPLETS; i++) {
11291 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11292 if (col > 60) {
11293 out1fmt("\n");
11294 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011295 }
11296 }
11297#endif
11298 out1fmt("\n\n");
11299 return EXIT_SUCCESS;
11300}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011301#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011302
Eric Andersencb57d552001-06-28 07:25:16 +000011303/*
Eric Andersencb57d552001-06-28 07:25:16 +000011304 * The export and readonly commands.
11305 */
Eric Andersenc470f442003-07-28 09:56:35 +000011306static int
11307exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011308{
11309 struct var *vp;
11310 char *name;
11311 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011312 char **aptr;
11313 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011314
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011315 if (nextopt("p") != 'p') {
11316 aptr = argptr;
11317 name = *aptr;
11318 if (name) {
11319 do {
11320 p = strchr(name, '=');
11321 if (p != NULL) {
11322 p++;
11323 } else {
11324 vp = *findvar(hashvar(name), name);
11325 if (vp) {
11326 vp->flags |= flag;
11327 continue;
11328 }
Eric Andersencb57d552001-06-28 07:25:16 +000011329 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011330 setvar(name, p, flag);
11331 } while ((name = *++aptr) != NULL);
11332 return 0;
11333 }
Eric Andersencb57d552001-06-28 07:25:16 +000011334 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011335 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011336 return 0;
11337}
11338
Eric Andersencb57d552001-06-28 07:25:16 +000011339/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011340 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011341 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011342static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011343unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011344{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011345 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011346
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011347 cmdp = cmdlookup(name, 0);
11348 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11349 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011350}
11351
Eric Andersencb57d552001-06-28 07:25:16 +000011352/*
Eric Andersencb57d552001-06-28 07:25:16 +000011353 * The unset builtin command. We unset the function before we unset the
11354 * variable to allow a function to be unset when there is a readonly variable
11355 * with the same name.
11356 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011357static int
Eric Andersenc470f442003-07-28 09:56:35 +000011358unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011359{
11360 char **ap;
11361 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011362 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011363 int ret = 0;
11364
11365 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011366 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011367 }
Eric Andersencb57d552001-06-28 07:25:16 +000011368
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011369 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011370 if (flag != 'f') {
11371 i = unsetvar(*ap);
11372 ret |= i;
11373 if (!(i & 2))
11374 continue;
11375 }
11376 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011377 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011378 }
Eric Andersenc470f442003-07-28 09:56:35 +000011379 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011380}
11381
11382
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011383/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011384
Eric Andersenc470f442003-07-28 09:56:35 +000011385#include <sys/times.h>
11386
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011387static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011388 ' ', offsetof(struct tms, tms_utime),
11389 '\n', offsetof(struct tms, tms_stime),
11390 ' ', offsetof(struct tms, tms_cutime),
11391 '\n', offsetof(struct tms, tms_cstime),
11392 0
11393};
Eric Andersencb57d552001-06-28 07:25:16 +000011394
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011395static int
11396timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011397{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011398 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011399 const unsigned char *p;
11400 struct tms buf;
11401
11402 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011403 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011404
11405 p = timescmd_str;
11406 do {
11407 t = *(clock_t *)(((char *) &buf) + p[1]);
11408 s = t / clk_tck;
11409 out1fmt("%ldm%ld.%.3lds%c",
11410 s/60, s%60,
11411 ((t - s * clk_tck) * 1000) / clk_tck,
11412 p[0]);
11413 } while (*(p += 2));
11414
Eric Andersencb57d552001-06-28 07:25:16 +000011415 return 0;
11416}
11417
Denis Vlasenko131ae172007-02-18 13:00:19 +000011418#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011419static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011420dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011421{
Eric Andersened9ecf72004-06-22 08:29:45 +000011422 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011423 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011424
Denis Vlasenkob012b102007-02-19 22:43:01 +000011425 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011426 result = arith(s, &errcode);
11427 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011428 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011429 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011430 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011431 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011432 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011433 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011434 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011435 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011436 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011437
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011438 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011439}
Eric Andersenc470f442003-07-28 09:56:35 +000011440
Eric Andersenc470f442003-07-28 09:56:35 +000011441/*
Eric Andersen90898442003-08-06 11:20:52 +000011442 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11443 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11444 *
11445 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011446 */
11447static int
Eric Andersen90898442003-08-06 11:20:52 +000011448letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011449{
Eric Andersenc470f442003-07-28 09:56:35 +000011450 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011451 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011452
Eric Andersen90898442003-08-06 11:20:52 +000011453 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011454 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011455 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011456 for (ap = argv + 1; *ap; ap++) {
11457 i = dash_arith(*ap);
11458 }
Eric Andersenc470f442003-07-28 09:56:35 +000011459
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011460 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011461}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011462#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011463
Eric Andersenc470f442003-07-28 09:56:35 +000011464
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011465/* ============ miscbltin.c
11466 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011467 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011468 */
11469
11470#undef rflag
11471
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011472#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011473typedef enum __rlimit_resource rlim_t;
11474#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011475
Eric Andersenc470f442003-07-28 09:56:35 +000011476/*
11477 * The read builtin. The -e option causes backslashes to escape the
11478 * following character.
11479 *
11480 * This uses unbuffered input, which may be avoidable in some cases.
11481 */
Eric Andersenc470f442003-07-28 09:56:35 +000011482static int
11483readcmd(int argc, char **argv)
11484{
11485 char **ap;
11486 int backslash;
11487 char c;
11488 int rflag;
11489 char *prompt;
11490 const char *ifs;
11491 char *p;
11492 int startword;
11493 int status;
11494 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011495#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011496 int nch_flag = 0;
11497 int nchars = 0;
11498 int silent = 0;
11499 struct termios tty, old_tty;
11500#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011501#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011502 fd_set set;
11503 struct timeval ts;
11504
11505 ts.tv_sec = ts.tv_usec = 0;
11506#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011507
11508 rflag = 0;
11509 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011510#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011511 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011512#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011513 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011514#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011515 while ((i = nextopt("p:rt:")) != '\0')
11516#else
11517 while ((i = nextopt("p:r")) != '\0')
11518#endif
11519 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011520 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011521 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011522 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011523 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011524#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011525 case 'n':
11526 nchars = strtol(optionarg, &p, 10);
11527 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011529 nch_flag = (nchars > 0);
11530 break;
11531 case 's':
11532 silent = 1;
11533 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011534#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011535#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011536 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011537 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011538 ts.tv_usec = 0;
11539 if (*p == '.') {
11540 char *p2;
11541 if (*++p) {
11542 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011543 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011544 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011545 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011546 scale = p2 - p;
11547 /* normalize to usec */
11548 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011549 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011550 while (scale++ < 6)
11551 ts.tv_usec *= 10;
11552 }
11553 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011554 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011555 }
11556 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011557 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011558 break;
11559#endif
11560 case 'r':
11561 rflag = 1;
11562 break;
11563 default:
11564 break;
11565 }
Eric Andersenc470f442003-07-28 09:56:35 +000011566 }
11567 if (prompt && isatty(0)) {
11568 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011569 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011570 ap = argptr;
11571 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011572 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011573 ifs = bltinlookup("IFS");
11574 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011575 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011576#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011577 if (nch_flag || silent) {
11578 tcgetattr(0, &tty);
11579 old_tty = tty;
11580 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011581 tty.c_lflag &= ~ICANON;
11582 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011583 }
11584 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011585 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011586
11587 }
11588 tcsetattr(0, TCSANOW, &tty);
11589 }
11590#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011591#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011592 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenko87f3b262007-09-07 13:43:28 +000011593// TODO: replace with poll, it is smaller
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011594 FD_ZERO(&set);
11595 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011596
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011597 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011598 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011599#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011600 if (nch_flag)
11601 tcsetattr(0, TCSANOW, &old_tty);
11602#endif
11603 return 1;
11604 }
11605 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011606#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011607 status = 0;
11608 startword = 1;
11609 backslash = 0;
11610 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011611#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011612 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011613#else
11614 for (;;)
11615#endif
11616 {
Eric Andersenc470f442003-07-28 09:56:35 +000011617 if (read(0, &c, 1) != 1) {
11618 status = 1;
11619 break;
11620 }
11621 if (c == '\0')
11622 continue;
11623 if (backslash) {
11624 backslash = 0;
11625 if (c != '\n')
11626 goto put;
11627 continue;
11628 }
11629 if (!rflag && c == '\\') {
11630 backslash++;
11631 continue;
11632 }
11633 if (c == '\n')
11634 break;
11635 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11636 continue;
11637 }
11638 startword = 0;
11639 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11640 STACKSTRNUL(p);
11641 setvar(*ap, stackblock(), 0);
11642 ap++;
11643 startword = 1;
11644 STARTSTACKSTR(p);
11645 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011646 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011647 STPUTC(c, p);
11648 }
11649 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011650#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011651 if (nch_flag || silent)
11652 tcsetattr(0, TCSANOW, &old_tty);
11653#endif
11654
Eric Andersenc470f442003-07-28 09:56:35 +000011655 STACKSTRNUL(p);
11656 /* Remove trailing blanks */
11657 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11658 *p = '\0';
11659 setvar(*ap, stackblock(), 0);
11660 while (*++ap != NULL)
11661 setvar(*ap, nullstr, 0);
11662 return status;
11663}
11664
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011665static int
11666umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011667{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011668 static const char permuser[3] ALIGN1 = "ugo";
11669 static const char permmode[3] ALIGN1 = "rwx";
11670 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011671 S_IRUSR, S_IWUSR, S_IXUSR,
11672 S_IRGRP, S_IWGRP, S_IXGRP,
11673 S_IROTH, S_IWOTH, S_IXOTH
11674 };
11675
11676 char *ap;
11677 mode_t mask;
11678 int i;
11679 int symbolic_mode = 0;
11680
11681 while (nextopt("S") != '\0') {
11682 symbolic_mode = 1;
11683 }
11684
Denis Vlasenkob012b102007-02-19 22:43:01 +000011685 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011686 mask = umask(0);
11687 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011688 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011689
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011690 ap = *argptr;
11691 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011692 if (symbolic_mode) {
11693 char buf[18];
11694 char *p = buf;
11695
11696 for (i = 0; i < 3; i++) {
11697 int j;
11698
11699 *p++ = permuser[i];
11700 *p++ = '=';
11701 for (j = 0; j < 3; j++) {
11702 if ((mask & permmask[3 * i + j]) == 0) {
11703 *p++ = permmode[j];
11704 }
11705 }
11706 *p++ = ',';
11707 }
11708 *--p = 0;
11709 puts(buf);
11710 } else {
11711 out1fmt("%.4o\n", mask);
11712 }
11713 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011714 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011715 mask = 0;
11716 do {
11717 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011718 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011719 mask = (mask << 3) + (*ap - '0');
11720 } while (*++ap != '\0');
11721 umask(mask);
11722 } else {
11723 mask = ~mask & 0777;
11724 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011725 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011726 }
11727 umask(~mask & 0777);
11728 }
11729 }
11730 return 0;
11731}
11732
11733/*
11734 * ulimit builtin
11735 *
11736 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11737 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11738 * ash by J.T. Conklin.
11739 *
11740 * Public domain.
11741 */
11742
11743struct limits {
11744 const char *name;
11745 int cmd;
11746 int factor; /* multiply by to get rlim_{cur,max} values */
11747 char option;
11748};
11749
11750static const struct limits limits[] = {
11751#ifdef RLIMIT_CPU
11752 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11753#endif
11754#ifdef RLIMIT_FSIZE
11755 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11756#endif
11757#ifdef RLIMIT_DATA
11758 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11759#endif
11760#ifdef RLIMIT_STACK
11761 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11762#endif
11763#ifdef RLIMIT_CORE
11764 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11765#endif
11766#ifdef RLIMIT_RSS
11767 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11768#endif
11769#ifdef RLIMIT_MEMLOCK
11770 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11771#endif
11772#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011773 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011774#endif
11775#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011776 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011777#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011778#ifdef RLIMIT_AS
11779 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011780#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011781#ifdef RLIMIT_LOCKS
11782 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011783#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011784 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011785};
11786
Glenn L McGrath76620622004-01-13 10:19:37 +000011787enum limtype { SOFT = 0x1, HARD = 0x2 };
11788
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011789static void
11790printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011791 const struct limits *l)
11792{
11793 rlim_t val;
11794
11795 val = limit->rlim_max;
11796 if (how & SOFT)
11797 val = limit->rlim_cur;
11798
11799 if (val == RLIM_INFINITY)
11800 out1fmt("unlimited\n");
11801 else {
11802 val /= l->factor;
11803 out1fmt("%lld\n", (long long) val);
11804 }
11805}
11806
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011807static int
Eric Andersenc470f442003-07-28 09:56:35 +000011808ulimitcmd(int argc, char **argv)
11809{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011810 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011811 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011812 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011813 const struct limits *l;
11814 int set, all = 0;
11815 int optc, what;
11816 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011817
11818 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011819 while ((optc = nextopt("HSa"
11820#ifdef RLIMIT_CPU
11821 "t"
11822#endif
11823#ifdef RLIMIT_FSIZE
11824 "f"
11825#endif
11826#ifdef RLIMIT_DATA
11827 "d"
11828#endif
11829#ifdef RLIMIT_STACK
11830 "s"
11831#endif
11832#ifdef RLIMIT_CORE
11833 "c"
11834#endif
11835#ifdef RLIMIT_RSS
11836 "m"
11837#endif
11838#ifdef RLIMIT_MEMLOCK
11839 "l"
11840#endif
11841#ifdef RLIMIT_NPROC
11842 "p"
11843#endif
11844#ifdef RLIMIT_NOFILE
11845 "n"
11846#endif
11847#ifdef RLIMIT_AS
11848 "v"
11849#endif
11850#ifdef RLIMIT_LOCKS
11851 "w"
11852#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011853 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011854 switch (optc) {
11855 case 'H':
11856 how = HARD;
11857 break;
11858 case 'S':
11859 how = SOFT;
11860 break;
11861 case 'a':
11862 all = 1;
11863 break;
11864 default:
11865 what = optc;
11866 }
11867
Glenn L McGrath76620622004-01-13 10:19:37 +000011868 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011869 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011870
11871 set = *argptr ? 1 : 0;
11872 if (set) {
11873 char *p = *argptr;
11874
11875 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011876 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011877 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011878 val = RLIM_INFINITY;
11879 else {
11880 val = (rlim_t) 0;
11881
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011882 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011883 val = (val * 10) + (long)(c - '0');
11884 if (val < (rlim_t) 0)
11885 break;
11886 }
11887 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011888 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011889 val *= l->factor;
11890 }
11891 }
11892 if (all) {
11893 for (l = limits; l->name; l++) {
11894 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011895 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011896 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011897 }
11898 return 0;
11899 }
11900
11901 getrlimit(l->cmd, &limit);
11902 if (set) {
11903 if (how & HARD)
11904 limit.rlim_max = val;
11905 if (how & SOFT)
11906 limit.rlim_cur = val;
11907 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011908 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011909 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011910 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011911 }
11912 return 0;
11913}
11914
Eric Andersen90898442003-08-06 11:20:52 +000011915
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011916/* ============ Math support */
11917
Denis Vlasenko131ae172007-02-18 13:00:19 +000011918#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011919
11920/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11921
11922 Permission is hereby granted, free of charge, to any person obtaining
11923 a copy of this software and associated documentation files (the
11924 "Software"), to deal in the Software without restriction, including
11925 without limitation the rights to use, copy, modify, merge, publish,
11926 distribute, sublicense, and/or sell copies of the Software, and to
11927 permit persons to whom the Software is furnished to do so, subject to
11928 the following conditions:
11929
11930 The above copyright notice and this permission notice shall be
11931 included in all copies or substantial portions of the Software.
11932
11933 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11934 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11935 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11936 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11937 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11938 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11939 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11940*/
11941
11942/* This is my infix parser/evaluator. It is optimized for size, intended
11943 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011944 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011945 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011946 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011947 * be that which POSIX specifies for shells. */
11948
11949/* The code uses a simple two-stack algorithm. See
11950 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011951 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011952 * this is based (this code differs in that it applies operators immediately
11953 * to the stack instead of adding them to a queue to end up with an
11954 * expression). */
11955
11956/* To use the routine, call it with an expression string and error return
11957 * pointer */
11958
11959/*
11960 * Aug 24, 2001 Manuel Novoa III
11961 *
11962 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11963 *
11964 * 1) In arith_apply():
11965 * a) Cached values of *numptr and &(numptr[-1]).
11966 * b) Removed redundant test for zero denominator.
11967 *
11968 * 2) In arith():
11969 * a) Eliminated redundant code for processing operator tokens by moving
11970 * to a table-based implementation. Also folded handling of parens
11971 * into the table.
11972 * b) Combined all 3 loops which called arith_apply to reduce generated
11973 * code size at the cost of speed.
11974 *
11975 * 3) The following expressions were treated as valid by the original code:
11976 * 1() , 0! , 1 ( *3 ) .
11977 * These bugs have been fixed by internally enclosing the expression in
11978 * parens and then checking that all binary ops and right parens are
11979 * preceded by a valid expression (NUM_TOKEN).
11980 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011981 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011982 * ctype's isspace() if it is used by another busybox applet or if additional
11983 * whitespace chars should be considered. Look below the "#include"s for a
11984 * precompiler test.
11985 */
11986
11987/*
11988 * Aug 26, 2001 Manuel Novoa III
11989 *
11990 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11991 *
11992 * Merge in Aaron's comments previously posted to the busybox list,
11993 * modified slightly to take account of my changes to the code.
11994 *
11995 */
11996
11997/*
11998 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
11999 *
12000 * - allow access to variable,
12001 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12002 * - realize assign syntax (VAR=expr, +=, *= etc)
12003 * - realize exponentiation (** operator)
12004 * - realize comma separated - expr, expr
12005 * - realise ++expr --expr expr++ expr--
12006 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012007 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012008 * - was restored loses XOR operator
12009 * - remove one goto label, added three ;-)
12010 * - protect $((num num)) as true zero expr (Manuel`s error)
12011 * - always use special isspace(), see comment from bash ;-)
12012 */
12013
Eric Andersen90898442003-08-06 11:20:52 +000012014#define arith_isspace(arithval) \
12015 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12016
Eric Andersen90898442003-08-06 11:20:52 +000012017typedef unsigned char operator;
12018
12019/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012020 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012021 * precedence. The ID portion is so that multiple operators can have the
12022 * same precedence, ensuring that the leftmost one is evaluated first.
12023 * Consider * and /. */
12024
12025#define tok_decl(prec,id) (((id)<<5)|(prec))
12026#define PREC(op) ((op) & 0x1F)
12027
12028#define TOK_LPAREN tok_decl(0,0)
12029
12030#define TOK_COMMA tok_decl(1,0)
12031
12032#define TOK_ASSIGN tok_decl(2,0)
12033#define TOK_AND_ASSIGN tok_decl(2,1)
12034#define TOK_OR_ASSIGN tok_decl(2,2)
12035#define TOK_XOR_ASSIGN tok_decl(2,3)
12036#define TOK_PLUS_ASSIGN tok_decl(2,4)
12037#define TOK_MINUS_ASSIGN tok_decl(2,5)
12038#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12039#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12040
12041#define TOK_MUL_ASSIGN tok_decl(3,0)
12042#define TOK_DIV_ASSIGN tok_decl(3,1)
12043#define TOK_REM_ASSIGN tok_decl(3,2)
12044
12045/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012046#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012047
12048/* conditional is right associativity too */
12049#define TOK_CONDITIONAL tok_decl(4,0)
12050#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12051
12052#define TOK_OR tok_decl(5,0)
12053
12054#define TOK_AND tok_decl(6,0)
12055
12056#define TOK_BOR tok_decl(7,0)
12057
12058#define TOK_BXOR tok_decl(8,0)
12059
12060#define TOK_BAND tok_decl(9,0)
12061
12062#define TOK_EQ tok_decl(10,0)
12063#define TOK_NE tok_decl(10,1)
12064
12065#define TOK_LT tok_decl(11,0)
12066#define TOK_GT tok_decl(11,1)
12067#define TOK_GE tok_decl(11,2)
12068#define TOK_LE tok_decl(11,3)
12069
12070#define TOK_LSHIFT tok_decl(12,0)
12071#define TOK_RSHIFT tok_decl(12,1)
12072
12073#define TOK_ADD tok_decl(13,0)
12074#define TOK_SUB tok_decl(13,1)
12075
12076#define TOK_MUL tok_decl(14,0)
12077#define TOK_DIV tok_decl(14,1)
12078#define TOK_REM tok_decl(14,2)
12079
12080/* exponent is right associativity */
12081#define TOK_EXPONENT tok_decl(15,1)
12082
12083/* For now unary operators. */
12084#define UNARYPREC 16
12085#define TOK_BNOT tok_decl(UNARYPREC,0)
12086#define TOK_NOT tok_decl(UNARYPREC,1)
12087
12088#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12089#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12090
12091#define PREC_PRE (UNARYPREC+2)
12092
12093#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12094#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12095
12096#define PREC_POST (UNARYPREC+3)
12097
12098#define TOK_POST_INC tok_decl(PREC_POST, 0)
12099#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12100
12101#define SPEC_PREC (UNARYPREC+4)
12102
12103#define TOK_NUM tok_decl(SPEC_PREC, 0)
12104#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12105
12106#define NUMPTR (*numstackptr)
12107
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012108static int
12109tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012110{
12111 operator prec = PREC(op);
12112
12113 convert_prec_is_assing(prec);
12114 return (prec == PREC(TOK_ASSIGN) ||
12115 prec == PREC_PRE || prec == PREC_POST);
12116}
12117
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012118static int
12119is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012120{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012121 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12122 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012123}
12124
Eric Andersen90898442003-08-06 11:20:52 +000012125typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012126 arith_t val;
12127 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012128 char contidional_second_val_initialized;
12129 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012130 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012131} v_n_t;
12132
Eric Andersen90898442003-08-06 11:20:52 +000012133typedef struct CHK_VAR_RECURSIVE_LOOPED {
12134 const char *var;
12135 struct CHK_VAR_RECURSIVE_LOOPED *next;
12136} chk_var_recursive_looped_t;
12137
12138static chk_var_recursive_looped_t *prev_chk_var_recursive;
12139
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012140static int
12141arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012142{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012143 if (t->var) {
12144 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012145
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012146 if (p) {
12147 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012148
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012149 /* recursive try as expression */
12150 chk_var_recursive_looped_t *cur;
12151 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012152
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012153 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12154 if (strcmp(cur->var, t->var) == 0) {
12155 /* expression recursion loop detected */
12156 return -5;
12157 }
12158 }
12159 /* save current lookuped var name */
12160 cur = prev_chk_var_recursive;
12161 cur_save.var = t->var;
12162 cur_save.next = cur;
12163 prev_chk_var_recursive = &cur_save;
12164
12165 t->val = arith (p, &errcode);
12166 /* restore previous ptr after recursiving */
12167 prev_chk_var_recursive = cur;
12168 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012169 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012170 /* allow undefined var as 0 */
12171 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012172 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012173 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012174}
12175
12176/* "applying" a token means performing it on the top elements on the integer
12177 * stack. For a unary operator it will only change the top element, but a
12178 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012179static int
12180arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012181{
Eric Andersen90898442003-08-06 11:20:52 +000012182 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012183 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012184 int ret_arith_lookup_val;
12185
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012186 /* There is no operator that can work without arguments */
12187 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012188 numptr_m1 = NUMPTR - 1;
12189
12190 /* check operand is var with noninteger value */
12191 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012192 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012193 return ret_arith_lookup_val;
12194
12195 rez = numptr_m1->val;
12196 if (op == TOK_UMINUS)
12197 rez *= -1;
12198 else if (op == TOK_NOT)
12199 rez = !rez;
12200 else if (op == TOK_BNOT)
12201 rez = ~rez;
12202 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12203 rez++;
12204 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12205 rez--;
12206 else if (op != TOK_UPLUS) {
12207 /* Binary operators */
12208
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012209 /* check and binary operators need two arguments */
12210 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012211
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012212 /* ... and they pop one */
12213 --NUMPTR;
12214 numptr_val = rez;
12215 if (op == TOK_CONDITIONAL) {
12216 if (! numptr_m1->contidional_second_val_initialized) {
12217 /* protect $((expr1 ? expr2)) without ": expr" */
12218 goto err;
12219 }
12220 rez = numptr_m1->contidional_second_val;
12221 } else if (numptr_m1->contidional_second_val_initialized) {
12222 /* protect $((expr1 : expr2)) without "expr ? " */
12223 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012224 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012225 numptr_m1 = NUMPTR - 1;
12226 if (op != TOK_ASSIGN) {
12227 /* check operand is var with noninteger value for not '=' */
12228 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12229 if (ret_arith_lookup_val)
12230 return ret_arith_lookup_val;
12231 }
12232 if (op == TOK_CONDITIONAL) {
12233 numptr_m1->contidional_second_val = rez;
12234 }
12235 rez = numptr_m1->val;
12236 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012237 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012238 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012239 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012240 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012241 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012242 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012243 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012244 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012245 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012246 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012247 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012248 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012249 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012250 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012251 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012252 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012253 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012254 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012255 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012256 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012257 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012258 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012259 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012260 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012261 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012262 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012263 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012264 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012265 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012266 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012267 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012268 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012269 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012270 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012271 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012272 /* protect $((expr : expr)) without "expr ? " */
12273 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012274 }
12275 numptr_m1->contidional_second_val_initialized = op;
12276 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012277 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012278 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012279 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012280 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012281 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012282 return -3; /* exponent less than 0 */
12283 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012284 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012286 if (numptr_val)
12287 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012288 c *= rez;
12289 rez = c;
12290 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012291 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012292 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012294 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012295 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012296 rez %= numptr_val;
12297 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012298 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012299 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012300
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012301 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012302 /* Hmm, 1=2 ? */
12303 goto err;
12304 }
12305 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012306#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012307 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012308#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012309 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012310#endif
Eric Andersen90898442003-08-06 11:20:52 +000012311 setvar(numptr_m1->var, buf, 0);
12312 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012313 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012314 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012315 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012316 rez++;
12317 }
12318 numptr_m1->val = rez;
12319 /* protect geting var value, is number now */
12320 numptr_m1->var = NULL;
12321 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012322 err:
12323 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012324}
12325
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012326/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012327static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012328 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12329 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12330 '<','<', 0, TOK_LSHIFT,
12331 '>','>', 0, TOK_RSHIFT,
12332 '|','|', 0, TOK_OR,
12333 '&','&', 0, TOK_AND,
12334 '!','=', 0, TOK_NE,
12335 '<','=', 0, TOK_LE,
12336 '>','=', 0, TOK_GE,
12337 '=','=', 0, TOK_EQ,
12338 '|','=', 0, TOK_OR_ASSIGN,
12339 '&','=', 0, TOK_AND_ASSIGN,
12340 '*','=', 0, TOK_MUL_ASSIGN,
12341 '/','=', 0, TOK_DIV_ASSIGN,
12342 '%','=', 0, TOK_REM_ASSIGN,
12343 '+','=', 0, TOK_PLUS_ASSIGN,
12344 '-','=', 0, TOK_MINUS_ASSIGN,
12345 '-','-', 0, TOK_POST_DEC,
12346 '^','=', 0, TOK_XOR_ASSIGN,
12347 '+','+', 0, TOK_POST_INC,
12348 '*','*', 0, TOK_EXPONENT,
12349 '!', 0, TOK_NOT,
12350 '<', 0, TOK_LT,
12351 '>', 0, TOK_GT,
12352 '=', 0, TOK_ASSIGN,
12353 '|', 0, TOK_BOR,
12354 '&', 0, TOK_BAND,
12355 '*', 0, TOK_MUL,
12356 '/', 0, TOK_DIV,
12357 '%', 0, TOK_REM,
12358 '+', 0, TOK_ADD,
12359 '-', 0, TOK_SUB,
12360 '^', 0, TOK_BXOR,
12361 /* uniq */
12362 '~', 0, TOK_BNOT,
12363 ',', 0, TOK_COMMA,
12364 '?', 0, TOK_CONDITIONAL,
12365 ':', 0, TOK_CONDITIONAL_SEP,
12366 ')', 0, TOK_RPAREN,
12367 '(', 0, TOK_LPAREN,
12368 0
12369};
12370/* ptr to ")" */
12371#define endexpression &op_tokens[sizeof(op_tokens)-7]
12372
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012373static arith_t
12374arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012375{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 char arithval; /* Current character under analysis */
12377 operator lasttok, op;
12378 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012379
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 const char *p = endexpression;
12381 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012382
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012383 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012384
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012385 /* Stack of integers */
12386 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12387 * in any given correct or incorrect expression is left as an exercise to
12388 * the reader. */
12389 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12390 *numstackptr = numstack;
12391 /* Stack of operator tokens */
12392 operator *stack = alloca((datasizes) * sizeof(operator)),
12393 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012394
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012395 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12396 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012397
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012399 arithval = *expr;
12400 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012401 if (p == endexpression) {
12402 /* Null expression. */
12403 return 0;
12404 }
12405
12406 /* This is only reached after all tokens have been extracted from the
12407 * input stream. If there are still tokens on the operator stack, they
12408 * are to be applied in order. At the end, there should be a final
12409 * result on the integer stack */
12410
12411 if (expr != endexpression + 1) {
12412 /* If we haven't done so already, */
12413 /* append a closing right paren */
12414 expr = endexpression;
12415 /* and let the loop process it. */
12416 continue;
12417 }
12418 /* At this point, we're done with the expression. */
12419 if (numstackptr != numstack+1) {
12420 /* ... but if there isn't, it's bad */
12421 err:
12422 return (*perrcode = -1);
12423 }
12424 if (numstack->var) {
12425 /* expression is $((var)) only, lookup now */
12426 errcode = arith_lookup_val(numstack);
12427 }
12428 ret:
12429 *perrcode = errcode;
12430 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012431 }
12432
Eric Andersen90898442003-08-06 11:20:52 +000012433 /* Continue processing the expression. */
12434 if (arith_isspace(arithval)) {
12435 /* Skip whitespace */
12436 goto prologue;
12437 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012438 p = endofname(expr);
12439 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012440 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012441
12442 numstackptr->var = alloca(var_name_size);
12443 safe_strncpy(numstackptr->var, expr, var_name_size);
12444 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 num:
Eric Andersen90898442003-08-06 11:20:52 +000012446 numstackptr->contidional_second_val_initialized = 0;
12447 numstackptr++;
12448 lasttok = TOK_NUM;
12449 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012450 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012451 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012452 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012453#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012454 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012455#else
12456 numstackptr->val = strtol(expr, (char **) &expr, 0);
12457#endif
Eric Andersen90898442003-08-06 11:20:52 +000012458 goto num;
12459 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012460 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012461 const char *o;
12462
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012463 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012464 /* strange operator not found */
12465 goto err;
12466 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012467 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012468 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012469 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012470 /* found */
12471 expr = o - 1;
12472 break;
12473 }
12474 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012475 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012476 p++;
12477 /* skip zero delim */
12478 p++;
12479 }
12480 op = p[1];
12481
12482 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012483 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12484 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012485
12486 /* Plus and minus are binary (not unary) _only_ if the last
12487 * token was as number, or a right paren (which pretends to be
12488 * a number, since it evaluates to one). Think about it.
12489 * It makes sense. */
12490 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012491 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012492 case TOK_ADD:
12493 op = TOK_UPLUS;
12494 break;
12495 case TOK_SUB:
12496 op = TOK_UMINUS;
12497 break;
12498 case TOK_POST_INC:
12499 op = TOK_PRE_INC;
12500 break;
12501 case TOK_POST_DEC:
12502 op = TOK_PRE_DEC;
12503 break;
Eric Andersen90898442003-08-06 11:20:52 +000012504 }
12505 }
12506 /* We don't want a unary operator to cause recursive descent on the
12507 * stack, because there can be many in a row and it could cause an
12508 * operator to be evaluated before its argument is pushed onto the
12509 * integer stack. */
12510 /* But for binary operators, "apply" everything on the operator
12511 * stack until we find an operator with a lesser priority than the
12512 * one we have just extracted. */
12513 /* Left paren is given the lowest priority so it will never be
12514 * "applied" in this way.
12515 * if associativity is right and priority eq, applied also skip
12516 */
12517 prec = PREC(op);
12518 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12519 /* not left paren or unary */
12520 if (lasttok != TOK_NUM) {
12521 /* binary op must be preceded by a num */
12522 goto err;
12523 }
12524 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 if (op == TOK_RPAREN) {
12526 /* The algorithm employed here is simple: while we don't
12527 * hit an open paren nor the bottom of the stack, pop
12528 * tokens and apply them */
12529 if (stackptr[-1] == TOK_LPAREN) {
12530 --stackptr;
12531 /* Any operator directly after a */
12532 lasttok = TOK_NUM;
12533 /* close paren should consider itself binary */
12534 goto prologue;
12535 }
12536 } else {
12537 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012538
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012539 convert_prec_is_assing(prec);
12540 convert_prec_is_assing(prev_prec);
12541 if (prev_prec < prec)
12542 break;
12543 /* check right assoc */
12544 if (prev_prec == prec && is_right_associativity(prec))
12545 break;
12546 }
12547 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12548 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012549 }
12550 if (op == TOK_RPAREN) {
12551 goto err;
12552 }
12553 }
12554
12555 /* Push this operator to the stack and remember it. */
12556 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012557 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012558 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012559 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012560}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012561#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012562
12563
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012564/* ============ main() and helpers */
12565
12566/*
12567 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012568 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012569static void exitshell(void) ATTRIBUTE_NORETURN;
12570static void
12571exitshell(void)
12572{
12573 struct jmploc loc;
12574 char *p;
12575 int status;
12576
12577 status = exitstatus;
12578 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12579 if (setjmp(loc.loc)) {
12580 if (exception == EXEXIT)
12581/* dash bug: it just does _exit(exitstatus) here
12582 * but we have to do setjobctl(0) first!
12583 * (bug is still not fixed in dash-0.5.3 - if you run dash
12584 * under Midnight Commander, on exit from dash MC is backgrounded) */
12585 status = exitstatus;
12586 goto out;
12587 }
12588 exception_handler = &loc;
12589 p = trap[0];
12590 if (p) {
12591 trap[0] = NULL;
12592 evalstring(p, 0);
12593 }
12594 flush_stdout_stderr();
12595 out:
12596 setjobctl(0);
12597 _exit(status);
12598 /* NOTREACHED */
12599}
12600
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012601static void
12602init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012603{
12604 /* from input.c: */
12605 basepf.nextc = basepf.buf = basebuf;
12606
12607 /* from trap.c: */
12608 signal(SIGCHLD, SIG_DFL);
12609
12610 /* from var.c: */
12611 {
12612 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012613 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012614 const char *p;
12615 struct stat st1, st2;
12616
12617 initvar();
12618 for (envp = environ; envp && *envp; envp++) {
12619 if (strchr(*envp, '=')) {
12620 setvareq(*envp, VEXPORT|VTEXTFIXED);
12621 }
12622 }
12623
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012624 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012625 setvar("PPID", ppid, 0);
12626
12627 p = lookupvar("PWD");
12628 if (p)
12629 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12630 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12631 p = '\0';
12632 setpwd(p, 0);
12633 }
12634}
12635
12636/*
12637 * Process the shell command line arguments.
12638 */
12639static void
12640procargs(int argc, char **argv)
12641{
12642 int i;
12643 const char *xminusc;
12644 char **xargv;
12645
12646 xargv = argv;
12647 arg0 = xargv[0];
12648 if (argc > 0)
12649 xargv++;
12650 for (i = 0; i < NOPTS; i++)
12651 optlist[i] = 2;
12652 argptr = xargv;
12653 options(1);
12654 xargv = argptr;
12655 xminusc = minusc;
12656 if (*xargv == NULL) {
12657 if (xminusc)
12658 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12659 sflag = 1;
12660 }
12661 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12662 iflag = 1;
12663 if (mflag == 2)
12664 mflag = iflag;
12665 for (i = 0; i < NOPTS; i++)
12666 if (optlist[i] == 2)
12667 optlist[i] = 0;
12668#if DEBUG == 2
12669 debug = 1;
12670#endif
12671 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12672 if (xminusc) {
12673 minusc = *xargv++;
12674 if (*xargv)
12675 goto setarg0;
12676 } else if (!sflag) {
12677 setinputfile(*xargv, 0);
12678 setarg0:
12679 arg0 = *xargv++;
12680 commandname = arg0;
12681 }
12682
12683 shellparam.p = xargv;
12684#if ENABLE_ASH_GETOPTS
12685 shellparam.optind = 1;
12686 shellparam.optoff = -1;
12687#endif
12688 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12689 while (*xargv) {
12690 shellparam.nparam++;
12691 xargv++;
12692 }
12693 optschanged();
12694}
12695
12696/*
12697 * Read /etc/profile or .profile.
12698 */
12699static void
12700read_profile(const char *name)
12701{
12702 int skip;
12703
12704 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12705 return;
12706 skip = cmdloop(0);
12707 popfile();
12708 if (skip)
12709 exitshell();
12710}
12711
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012712/*
12713 * This routine is called when an error or an interrupt occurs in an
12714 * interactive shell and control is returned to the main command loop.
12715 */
12716static void
12717reset(void)
12718{
12719 /* from eval.c: */
12720 evalskip = 0;
12721 loopnest = 0;
12722 /* from input.c: */
12723 parselleft = parsenleft = 0; /* clear input buffer */
12724 popallfiles();
12725 /* from parser.c: */
12726 tokpushback = 0;
12727 checkkwd = 0;
12728 /* from redir.c: */
12729 clearredir(0);
12730}
12731
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012732#if PROFILE
12733static short profile_buf[16384];
12734extern int etext();
12735#endif
12736
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012737/*
12738 * Main routine. We initialize things, parse the arguments, execute
12739 * profiles if we're a login shell, and then call cmdloop to execute
12740 * commands. The setjmp call sets up the location to jump to when an
12741 * exception occurs. When an exception occurs the variable "state"
12742 * is used to figure out how far we had gotten.
12743 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012744int ash_main(int argc, char **argv);
12745int ash_main(int argc, char **argv)
12746{
12747 char *shinit;
12748 volatile int state;
12749 struct jmploc jmploc;
12750 struct stackmark smark;
12751
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012752#if PROFILE
12753 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12754#endif
12755
12756#if ENABLE_FEATURE_EDITING
12757 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12758#endif
12759 state = 0;
12760 if (setjmp(jmploc.loc)) {
12761 int e;
12762 int s;
12763
12764 reset();
12765
12766 e = exception;
12767 if (e == EXERROR)
12768 exitstatus = 2;
12769 s = state;
12770 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12771 exitshell();
12772
12773 if (e == EXINT) {
12774 outcslow('\n', stderr);
12775 }
12776 popstackmark(&smark);
12777 FORCE_INT_ON; /* enable interrupts */
12778 if (s == 1)
12779 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012780 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012781 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012782 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012783 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012784 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012785 }
12786 exception_handler = &jmploc;
12787#if DEBUG
12788 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012789 trace_puts("Shell args: ");
12790 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012791#endif
12792 rootpid = getpid();
12793
12794#if ENABLE_ASH_RANDOM_SUPPORT
12795 rseed = rootpid + time(NULL);
12796#endif
12797 init();
12798 setstackmark(&smark);
12799 procargs(argc, argv);
12800#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12801 if (iflag) {
12802 const char *hp = lookupvar("HISTFILE");
12803
12804 if (hp == NULL) {
12805 hp = lookupvar("HOME");
12806 if (hp != NULL) {
12807 char *defhp = concat_path_file(hp, ".ash_history");
12808 setvar("HISTFILE", defhp, 0);
12809 free(defhp);
12810 }
12811 }
12812 }
12813#endif
12814 if (argv[0] && argv[0][0] == '-')
12815 isloginsh = 1;
12816 if (isloginsh) {
12817 state = 1;
12818 read_profile("/etc/profile");
12819 state1:
12820 state = 2;
12821 read_profile(".profile");
12822 }
12823 state2:
12824 state = 3;
12825 if (
12826#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012827 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012828#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012829 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012830 ) {
12831 shinit = lookupvar("ENV");
12832 if (shinit != NULL && *shinit != '\0') {
12833 read_profile(shinit);
12834 }
12835 }
12836 state3:
12837 state = 4;
12838 if (minusc)
12839 evalstring(minusc, 0);
12840
12841 if (sflag || minusc == NULL) {
12842#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12843 if ( iflag ) {
12844 const char *hp = lookupvar("HISTFILE");
12845
12846 if (hp != NULL)
12847 line_input_state->hist_file = hp;
12848 }
12849#endif
12850 state4: /* XXX ??? - why isn't this before the "if" statement */
12851 cmdloop(1);
12852 }
12853#if PROFILE
12854 monitor(0);
12855#endif
12856#ifdef GPROF
12857 {
12858 extern void _mcleanup(void);
12859 _mcleanup();
12860 }
12861#endif
12862 exitshell();
12863 /* NOTREACHED */
12864}
12865
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012866#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012867const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012868int main(int argc, char **argv)
12869{
12870 return ash_main(argc, argv);
12871}
12872#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012873
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012874
Eric Andersendf82f612001-06-28 07:46:40 +000012875/*-
12876 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012877 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012878 *
12879 * This code is derived from software contributed to Berkeley by
12880 * Kenneth Almquist.
12881 *
12882 * Redistribution and use in source and binary forms, with or without
12883 * modification, are permitted provided that the following conditions
12884 * are met:
12885 * 1. Redistributions of source code must retain the above copyright
12886 * notice, this list of conditions and the following disclaimer.
12887 * 2. Redistributions in binary form must reproduce the above copyright
12888 * notice, this list of conditions and the following disclaimer in the
12889 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012890 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012891 * may be used to endorse or promote products derived from this software
12892 * without specific prior written permission.
12893 *
12894 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12895 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12896 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12897 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12898 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12899 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12900 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12901 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12902 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12903 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12904 * SUCH DAMAGE.
12905 */