blob: 9b9fe5b6db1604ad6437f2833c01bec469deef95 [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 Vlasenko1aa7e472007-11-28 06:49:03 +000056#include "busybox.h" /* for applet_names */
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! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003464 fd = 2;
3465 while (!isatty(fd))
3466 if (--fd < 0)
3467 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003468 }
3469 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003470 if (ofd >= 0)
3471 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003472 if (fd < 0)
3473 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003474 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003475 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003476 do { /* while we are in the background */
3477 pgrp = tcgetpgrp(fd);
3478 if (pgrp < 0) {
3479 out:
3480 ash_msg("can't access tty; job control turned off");
3481 mflag = on = 0;
3482 goto close;
3483 }
3484 if (pgrp == getpgrp())
3485 break;
3486 killpg(0, SIGTTIN);
3487 } while (1);
3488 initialpgrp = pgrp;
3489
3490 setsignal(SIGTSTP);
3491 setsignal(SIGTTOU);
3492 setsignal(SIGTTIN);
3493 pgrp = rootpid;
3494 setpgid(0, pgrp);
3495 xtcsetpgrp(fd, pgrp);
3496 } else {
3497 /* turning job control off */
3498 fd = ttyfd;
3499 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003500 /* was xtcsetpgrp, but this can make exiting ash
3501 * with pty already deleted loop forever */
3502 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003503 setpgid(0, pgrp);
3504 setsignal(SIGTSTP);
3505 setsignal(SIGTTOU);
3506 setsignal(SIGTTIN);
3507 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003508 if (fd >= 0)
3509 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003510 fd = -1;
3511 }
3512 ttyfd = fd;
3513 jobctl = on;
3514}
3515
3516static int
3517killcmd(int argc, char **argv)
3518{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003519 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3520 int i = 1;
3521 do {
3522 if (argv[i][0] == '%') {
3523 struct job *jp = getjob(argv[i], 0);
3524 unsigned pid = jp->ps[0].pid;
3525 /* Enough space for ' -NNN<nul>' */
3526 argv[i] = alloca(sizeof(int)*3 + 3);
3527 /* kill_main has matching code to expect
3528 * leading space. Needed to not confuse
3529 * negative pids with "kill -SIGNAL_NO" syntax */
3530 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003532 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003533 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003534 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003535}
3536
3537static void
3538showpipe(struct job *jp, FILE *out)
3539{
3540 struct procstat *sp;
3541 struct procstat *spend;
3542
3543 spend = jp->ps + jp->nprocs;
3544 for (sp = jp->ps + 1; sp < spend; sp++)
3545 fprintf(out, " | %s", sp->cmd);
3546 outcslow('\n', out);
3547 flush_stdout_stderr();
3548}
3549
3550
3551static int
3552restartjob(struct job *jp, int mode)
3553{
3554 struct procstat *ps;
3555 int i;
3556 int status;
3557 pid_t pgid;
3558
3559 INT_OFF;
3560 if (jp->state == JOBDONE)
3561 goto out;
3562 jp->state = JOBRUNNING;
3563 pgid = jp->ps->pid;
3564 if (mode == FORK_FG)
3565 xtcsetpgrp(ttyfd, pgid);
3566 killpg(pgid, SIGCONT);
3567 ps = jp->ps;
3568 i = jp->nprocs;
3569 do {
3570 if (WIFSTOPPED(ps->status)) {
3571 ps->status = -1;
3572 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003573 ps++;
3574 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575 out:
3576 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3577 INT_ON;
3578 return status;
3579}
3580
3581static int
3582fg_bgcmd(int argc, char **argv)
3583{
3584 struct job *jp;
3585 FILE *out;
3586 int mode;
3587 int retval;
3588
3589 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3590 nextopt(nullstr);
3591 argv = argptr;
3592 out = stdout;
3593 do {
3594 jp = getjob(*argv, 1);
3595 if (mode == FORK_BG) {
3596 set_curjob(jp, CUR_RUNNING);
3597 fprintf(out, "[%d] ", jobno(jp));
3598 }
3599 outstr(jp->ps->cmd, out);
3600 showpipe(jp, out);
3601 retval = restartjob(jp, mode);
3602 } while (*argv && *++argv);
3603 return retval;
3604}
3605#endif
3606
3607static int
3608sprint_status(char *s, int status, int sigonly)
3609{
3610 int col;
3611 int st;
3612
3613 col = 0;
3614 if (!WIFEXITED(status)) {
3615#if JOBS
3616 if (WIFSTOPPED(status))
3617 st = WSTOPSIG(status);
3618 else
3619#endif
3620 st = WTERMSIG(status);
3621 if (sigonly) {
3622 if (st == SIGINT || st == SIGPIPE)
3623 goto out;
3624#if JOBS
3625 if (WIFSTOPPED(status))
3626 goto out;
3627#endif
3628 }
3629 st &= 0x7f;
3630 col = fmtstr(s, 32, strsignal(st));
3631 if (WCOREDUMP(status)) {
3632 col += fmtstr(s + col, 16, " (core dumped)");
3633 }
3634 } else if (!sigonly) {
3635 st = WEXITSTATUS(status);
3636 if (st)
3637 col = fmtstr(s, 16, "Done(%d)", st);
3638 else
3639 col = fmtstr(s, 16, "Done");
3640 }
3641 out:
3642 return col;
3643}
3644
3645/*
3646 * Do a wait system call. If job control is compiled in, we accept
3647 * stopped processes. If block is zero, we return a value of zero
3648 * rather than blocking.
3649 *
3650 * System V doesn't have a non-blocking wait system call. It does
3651 * have a SIGCLD signal that is sent to a process when one of it's
3652 * children dies. The obvious way to use SIGCLD would be to install
3653 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3654 * was received, and have waitproc bump another counter when it got
3655 * the status of a process. Waitproc would then know that a wait
3656 * system call would not block if the two counters were different.
3657 * This approach doesn't work because if a process has children that
3658 * have not been waited for, System V will send it a SIGCLD when it
3659 * installs a signal handler for SIGCLD. What this means is that when
3660 * a child exits, the shell will be sent SIGCLD signals continuously
3661 * until is runs out of stack space, unless it does a wait call before
3662 * restoring the signal handler. The code below takes advantage of
3663 * this (mis)feature by installing a signal handler for SIGCLD and
3664 * then checking to see whether it was called. If there are any
3665 * children to be waited for, it will be.
3666 *
3667 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3668 * waits at all. In this case, the user will not be informed when
3669 * a background process until the next time she runs a real program
3670 * (as opposed to running a builtin command or just typing return),
3671 * and the jobs command may give out of date information.
3672 */
3673static int
3674waitproc(int block, int *status)
3675{
3676 int flags = 0;
3677
3678#if JOBS
3679 if (jobctl)
3680 flags |= WUNTRACED;
3681#endif
3682 if (block == 0)
3683 flags |= WNOHANG;
3684 return wait3(status, flags, (struct rusage *)NULL);
3685}
3686
3687/*
3688 * Wait for a process to terminate.
3689 */
3690static int
3691dowait(int block, struct job *job)
3692{
3693 int pid;
3694 int status;
3695 struct job *jp;
3696 struct job *thisjob;
3697 int state;
3698
3699 TRACE(("dowait(%d) called\n", block));
3700 pid = waitproc(block, &status);
3701 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3702 if (pid <= 0)
3703 return pid;
3704 INT_OFF;
3705 thisjob = NULL;
3706 for (jp = curjob; jp; jp = jp->prev_job) {
3707 struct procstat *sp;
3708 struct procstat *spend;
3709 if (jp->state == JOBDONE)
3710 continue;
3711 state = JOBDONE;
3712 spend = jp->ps + jp->nprocs;
3713 sp = jp->ps;
3714 do {
3715 if (sp->pid == pid) {
3716 TRACE(("Job %d: changing status of proc %d "
3717 "from 0x%x to 0x%x\n",
3718 jobno(jp), pid, sp->status, status));
3719 sp->status = status;
3720 thisjob = jp;
3721 }
3722 if (sp->status == -1)
3723 state = JOBRUNNING;
3724#if JOBS
3725 if (state == JOBRUNNING)
3726 continue;
3727 if (WIFSTOPPED(sp->status)) {
3728 jp->stopstatus = sp->status;
3729 state = JOBSTOPPED;
3730 }
3731#endif
3732 } while (++sp < spend);
3733 if (thisjob)
3734 goto gotjob;
3735 }
3736#if JOBS
3737 if (!WIFSTOPPED(status))
3738#endif
3739
3740 jobless--;
3741 goto out;
3742
3743 gotjob:
3744 if (state != JOBRUNNING) {
3745 thisjob->changed = 1;
3746
3747 if (thisjob->state != state) {
3748 TRACE(("Job %d: changing state from %d to %d\n",
3749 jobno(thisjob), thisjob->state, state));
3750 thisjob->state = state;
3751#if JOBS
3752 if (state == JOBSTOPPED) {
3753 set_curjob(thisjob, CUR_STOPPED);
3754 }
3755#endif
3756 }
3757 }
3758
3759 out:
3760 INT_ON;
3761
3762 if (thisjob && thisjob == job) {
3763 char s[48 + 1];
3764 int len;
3765
3766 len = sprint_status(s, status, 1);
3767 if (len) {
3768 s[len] = '\n';
3769 s[len + 1] = 0;
3770 out2str(s);
3771 }
3772 }
3773 return pid;
3774}
3775
3776#if JOBS
3777static void
3778showjob(FILE *out, struct job *jp, int mode)
3779{
3780 struct procstat *ps;
3781 struct procstat *psend;
3782 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003783 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784 char s[80];
3785
3786 ps = jp->ps;
3787
3788 if (mode & SHOW_PGID) {
3789 /* just output process (group) id of pipeline */
3790 fprintf(out, "%d\n", ps->pid);
3791 return;
3792 }
3793
3794 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003795 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796
3797 if (jp == curjob)
3798 s[col - 2] = '+';
3799 else if (curjob && jp == curjob->prev_job)
3800 s[col - 2] = '-';
3801
3802 if (mode & SHOW_PID)
3803 col += fmtstr(s + col, 16, "%d ", ps->pid);
3804
3805 psend = ps + jp->nprocs;
3806
3807 if (jp->state == JOBRUNNING) {
3808 strcpy(s + col, "Running");
3809 col += sizeof("Running") - 1;
3810 } else {
3811 int status = psend[-1].status;
3812 if (jp->state == JOBSTOPPED)
3813 status = jp->stopstatus;
3814 col += sprint_status(s + col, status, 0);
3815 }
3816
3817 goto start;
3818
3819 do {
3820 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003821 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003822 start:
3823 fprintf(out, "%s%*c%s",
3824 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3825 );
3826 if (!(mode & SHOW_PID)) {
3827 showpipe(jp, out);
3828 break;
3829 }
3830 if (++ps == psend) {
3831 outcslow('\n', out);
3832 break;
3833 }
3834 } while (1);
3835
3836 jp->changed = 0;
3837
3838 if (jp->state == JOBDONE) {
3839 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3840 freejob(jp);
3841 }
3842}
3843
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844/*
3845 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3846 * statuses have changed since the last call to showjobs.
3847 */
3848static void
3849showjobs(FILE *out, int mode)
3850{
3851 struct job *jp;
3852
3853 TRACE(("showjobs(%x) called\n", mode));
3854
3855 /* If not even one one job changed, there is nothing to do */
3856 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3857 continue;
3858
3859 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003860 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003861 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003862 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003863 }
3864}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003865
3866static int
3867jobscmd(int argc, char **argv)
3868{
3869 int mode, m;
3870
3871 mode = 0;
3872 while ((m = nextopt("lp"))) {
3873 if (m == 'l')
3874 mode = SHOW_PID;
3875 else
3876 mode = SHOW_PGID;
3877 }
3878
3879 argv = argptr;
3880 if (*argv) {
3881 do
3882 showjob(stdout, getjob(*argv,0), mode);
3883 while (*++argv);
3884 } else
3885 showjobs(stdout, mode);
3886
3887 return 0;
3888}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889#endif /* JOBS */
3890
3891static int
3892getstatus(struct job *job)
3893{
3894 int status;
3895 int retval;
3896
3897 status = job->ps[job->nprocs - 1].status;
3898 retval = WEXITSTATUS(status);
3899 if (!WIFEXITED(status)) {
3900#if JOBS
3901 retval = WSTOPSIG(status);
3902 if (!WIFSTOPPED(status))
3903#endif
3904 {
3905 /* XXX: limits number of signals */
3906 retval = WTERMSIG(status);
3907#if JOBS
3908 if (retval == SIGINT)
3909 job->sigint = 1;
3910#endif
3911 }
3912 retval += 128;
3913 }
3914 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3915 jobno(job), job->nprocs, status, retval));
3916 return retval;
3917}
3918
3919static int
3920waitcmd(int argc, char **argv)
3921{
3922 struct job *job;
3923 int retval;
3924 struct job *jp;
3925
3926 EXSIGON;
3927
3928 nextopt(nullstr);
3929 retval = 0;
3930
3931 argv = argptr;
3932 if (!*argv) {
3933 /* wait for all jobs */
3934 for (;;) {
3935 jp = curjob;
3936 while (1) {
3937 if (!jp) {
3938 /* no running procs */
3939 goto out;
3940 }
3941 if (jp->state == JOBRUNNING)
3942 break;
3943 jp->waited = 1;
3944 jp = jp->prev_job;
3945 }
3946 dowait(DOWAIT_BLOCK, 0);
3947 }
3948 }
3949
3950 retval = 127;
3951 do {
3952 if (**argv != '%') {
3953 pid_t pid = number(*argv);
3954 job = curjob;
3955 goto start;
3956 do {
3957 if (job->ps[job->nprocs - 1].pid == pid)
3958 break;
3959 job = job->prev_job;
3960 start:
3961 if (!job)
3962 goto repeat;
3963 } while (1);
3964 } else
3965 job = getjob(*argv, 0);
3966 /* loop until process terminated or stopped */
3967 while (job->state == JOBRUNNING)
3968 dowait(DOWAIT_BLOCK, 0);
3969 job->waited = 1;
3970 retval = getstatus(job);
3971 repeat:
3972 ;
3973 } while (*++argv);
3974
3975 out:
3976 return retval;
3977}
3978
3979static struct job *
3980growjobtab(void)
3981{
3982 size_t len;
3983 ptrdiff_t offset;
3984 struct job *jp, *jq;
3985
3986 len = njobs * sizeof(*jp);
3987 jq = jobtab;
3988 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3989
3990 offset = (char *)jp - (char *)jq;
3991 if (offset) {
3992 /* Relocate pointers */
3993 size_t l = len;
3994
3995 jq = (struct job *)((char *)jq + l);
3996 while (l) {
3997 l -= sizeof(*jp);
3998 jq--;
3999#define joff(p) ((struct job *)((char *)(p) + l))
4000#define jmove(p) (p) = (void *)((char *)(p) + offset)
4001 if (joff(jp)->ps == &jq->ps0)
4002 jmove(joff(jp)->ps);
4003 if (joff(jp)->prev_job)
4004 jmove(joff(jp)->prev_job);
4005 }
4006 if (curjob)
4007 jmove(curjob);
4008#undef joff
4009#undef jmove
4010 }
4011
4012 njobs += 4;
4013 jobtab = jp;
4014 jp = (struct job *)((char *)jp + len);
4015 jq = jp + 3;
4016 do {
4017 jq->used = 0;
4018 } while (--jq >= jp);
4019 return jp;
4020}
4021
4022/*
4023 * Return a new job structure.
4024 * Called with interrupts off.
4025 */
4026static struct job *
4027makejob(union node *node, int nprocs)
4028{
4029 int i;
4030 struct job *jp;
4031
4032 for (i = njobs, jp = jobtab; ; jp++) {
4033 if (--i < 0) {
4034 jp = growjobtab();
4035 break;
4036 }
4037 if (jp->used == 0)
4038 break;
4039 if (jp->state != JOBDONE || !jp->waited)
4040 continue;
4041#if JOBS
4042 if (jobctl)
4043 continue;
4044#endif
4045 freejob(jp);
4046 break;
4047 }
4048 memset(jp, 0, sizeof(*jp));
4049#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004050 /* jp->jobctl is a bitfield.
4051 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 if (jobctl)
4053 jp->jobctl = 1;
4054#endif
4055 jp->prev_job = curjob;
4056 curjob = jp;
4057 jp->used = 1;
4058 jp->ps = &jp->ps0;
4059 if (nprocs > 1) {
4060 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4061 }
4062 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4063 jobno(jp)));
4064 return jp;
4065}
4066
4067#if JOBS
4068/*
4069 * Return a string identifying a command (to be printed by the
4070 * jobs command).
4071 */
4072static char *cmdnextc;
4073
4074static void
4075cmdputs(const char *s)
4076{
4077 const char *p, *str;
4078 char c, cc[2] = " ";
4079 char *nextc;
4080 int subtype = 0;
4081 int quoted = 0;
4082 static const char vstype[VSTYPE + 1][4] = {
4083 "", "}", "-", "+", "?", "=",
4084 "%", "%%", "#", "##"
4085 };
4086
4087 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4088 p = s;
4089 while ((c = *p++) != 0) {
4090 str = 0;
4091 switch (c) {
4092 case CTLESC:
4093 c = *p++;
4094 break;
4095 case CTLVAR:
4096 subtype = *p++;
4097 if ((subtype & VSTYPE) == VSLENGTH)
4098 str = "${#";
4099 else
4100 str = "${";
4101 if (!(subtype & VSQUOTE) == !(quoted & 1))
4102 goto dostr;
4103 quoted ^= 1;
4104 c = '"';
4105 break;
4106 case CTLENDVAR:
4107 str = "\"}" + !(quoted & 1);
4108 quoted >>= 1;
4109 subtype = 0;
4110 goto dostr;
4111 case CTLBACKQ:
4112 str = "$(...)";
4113 goto dostr;
4114 case CTLBACKQ+CTLQUOTE:
4115 str = "\"$(...)\"";
4116 goto dostr;
4117#if ENABLE_ASH_MATH_SUPPORT
4118 case CTLARI:
4119 str = "$((";
4120 goto dostr;
4121 case CTLENDARI:
4122 str = "))";
4123 goto dostr;
4124#endif
4125 case CTLQUOTEMARK:
4126 quoted ^= 1;
4127 c = '"';
4128 break;
4129 case '=':
4130 if (subtype == 0)
4131 break;
4132 if ((subtype & VSTYPE) != VSNORMAL)
4133 quoted <<= 1;
4134 str = vstype[subtype & VSTYPE];
4135 if (subtype & VSNUL)
4136 c = ':';
4137 else
4138 goto checkstr;
4139 break;
4140 case '\'':
4141 case '\\':
4142 case '"':
4143 case '$':
4144 /* These can only happen inside quotes */
4145 cc[0] = c;
4146 str = cc;
4147 c = '\\';
4148 break;
4149 default:
4150 break;
4151 }
4152 USTPUTC(c, nextc);
4153 checkstr:
4154 if (!str)
4155 continue;
4156 dostr:
4157 while ((c = *str++)) {
4158 USTPUTC(c, nextc);
4159 }
4160 }
4161 if (quoted & 1) {
4162 USTPUTC('"', nextc);
4163 }
4164 *nextc = 0;
4165 cmdnextc = nextc;
4166}
4167
4168/* cmdtxt() and cmdlist() call each other */
4169static void cmdtxt(union node *n);
4170
4171static void
4172cmdlist(union node *np, int sep)
4173{
4174 for (; np; np = np->narg.next) {
4175 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004176 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004177 cmdtxt(np);
4178 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004179 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004180 }
4181}
4182
4183static void
4184cmdtxt(union node *n)
4185{
4186 union node *np;
4187 struct nodelist *lp;
4188 const char *p;
4189 char s[2];
4190
4191 if (!n)
4192 return;
4193 switch (n->type) {
4194 default:
4195#if DEBUG
4196 abort();
4197#endif
4198 case NPIPE:
4199 lp = n->npipe.cmdlist;
4200 for (;;) {
4201 cmdtxt(lp->n);
4202 lp = lp->next;
4203 if (!lp)
4204 break;
4205 cmdputs(" | ");
4206 }
4207 break;
4208 case NSEMI:
4209 p = "; ";
4210 goto binop;
4211 case NAND:
4212 p = " && ";
4213 goto binop;
4214 case NOR:
4215 p = " || ";
4216 binop:
4217 cmdtxt(n->nbinary.ch1);
4218 cmdputs(p);
4219 n = n->nbinary.ch2;
4220 goto donode;
4221 case NREDIR:
4222 case NBACKGND:
4223 n = n->nredir.n;
4224 goto donode;
4225 case NNOT:
4226 cmdputs("!");
4227 n = n->nnot.com;
4228 donode:
4229 cmdtxt(n);
4230 break;
4231 case NIF:
4232 cmdputs("if ");
4233 cmdtxt(n->nif.test);
4234 cmdputs("; then ");
4235 n = n->nif.ifpart;
4236 if (n->nif.elsepart) {
4237 cmdtxt(n);
4238 cmdputs("; else ");
4239 n = n->nif.elsepart;
4240 }
4241 p = "; fi";
4242 goto dotail;
4243 case NSUBSHELL:
4244 cmdputs("(");
4245 n = n->nredir.n;
4246 p = ")";
4247 goto dotail;
4248 case NWHILE:
4249 p = "while ";
4250 goto until;
4251 case NUNTIL:
4252 p = "until ";
4253 until:
4254 cmdputs(p);
4255 cmdtxt(n->nbinary.ch1);
4256 n = n->nbinary.ch2;
4257 p = "; done";
4258 dodo:
4259 cmdputs("; do ");
4260 dotail:
4261 cmdtxt(n);
4262 goto dotail2;
4263 case NFOR:
4264 cmdputs("for ");
4265 cmdputs(n->nfor.var);
4266 cmdputs(" in ");
4267 cmdlist(n->nfor.args, 1);
4268 n = n->nfor.body;
4269 p = "; done";
4270 goto dodo;
4271 case NDEFUN:
4272 cmdputs(n->narg.text);
4273 p = "() { ... }";
4274 goto dotail2;
4275 case NCMD:
4276 cmdlist(n->ncmd.args, 1);
4277 cmdlist(n->ncmd.redirect, 0);
4278 break;
4279 case NARG:
4280 p = n->narg.text;
4281 dotail2:
4282 cmdputs(p);
4283 break;
4284 case NHERE:
4285 case NXHERE:
4286 p = "<<...";
4287 goto dotail2;
4288 case NCASE:
4289 cmdputs("case ");
4290 cmdputs(n->ncase.expr->narg.text);
4291 cmdputs(" in ");
4292 for (np = n->ncase.cases; np; np = np->nclist.next) {
4293 cmdtxt(np->nclist.pattern);
4294 cmdputs(") ");
4295 cmdtxt(np->nclist.body);
4296 cmdputs(";; ");
4297 }
4298 p = "esac";
4299 goto dotail2;
4300 case NTO:
4301 p = ">";
4302 goto redir;
4303 case NCLOBBER:
4304 p = ">|";
4305 goto redir;
4306 case NAPPEND:
4307 p = ">>";
4308 goto redir;
4309 case NTOFD:
4310 p = ">&";
4311 goto redir;
4312 case NFROM:
4313 p = "<";
4314 goto redir;
4315 case NFROMFD:
4316 p = "<&";
4317 goto redir;
4318 case NFROMTO:
4319 p = "<>";
4320 redir:
4321 s[0] = n->nfile.fd + '0';
4322 s[1] = '\0';
4323 cmdputs(s);
4324 cmdputs(p);
4325 if (n->type == NTOFD || n->type == NFROMFD) {
4326 s[0] = n->ndup.dupfd + '0';
4327 p = s;
4328 goto dotail2;
4329 }
4330 n = n->nfile.fname;
4331 goto donode;
4332 }
4333}
4334
4335static char *
4336commandtext(union node *n)
4337{
4338 char *name;
4339
4340 STARTSTACKSTR(cmdnextc);
4341 cmdtxt(n);
4342 name = stackblock();
4343 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4344 name, cmdnextc, cmdnextc));
4345 return ckstrdup(name);
4346}
4347#endif /* JOBS */
4348
4349/*
4350 * Fork off a subshell. If we are doing job control, give the subshell its
4351 * own process group. Jp is a job structure that the job is to be added to.
4352 * N is the command that will be evaluated by the child. Both jp and n may
4353 * be NULL. The mode parameter can be one of the following:
4354 * FORK_FG - Fork off a foreground process.
4355 * FORK_BG - Fork off a background process.
4356 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4357 * process group even if job control is on.
4358 *
4359 * When job control is turned off, background processes have their standard
4360 * input redirected to /dev/null (except for the second and later processes
4361 * in a pipeline).
4362 *
4363 * Called with interrupts off.
4364 */
4365/*
4366 * Clear traps on a fork.
4367 */
4368static void
4369clear_traps(void)
4370{
4371 char **tp;
4372
4373 for (tp = trap; tp < &trap[NSIG]; tp++) {
4374 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4375 INT_OFF;
4376 free(*tp);
4377 *tp = NULL;
4378 if (tp != &trap[0])
4379 setsignal(tp - trap);
4380 INT_ON;
4381 }
4382 }
4383}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004384
4385/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004386static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004387
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004388/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004389static void
4390forkchild(struct job *jp, union node *n, int mode)
4391{
4392 int oldlvl;
4393
4394 TRACE(("Child shell %d\n", getpid()));
4395 oldlvl = shlvl;
4396 shlvl++;
4397
4398 closescript();
4399 clear_traps();
4400#if JOBS
4401 /* do job control only in root shell */
4402 jobctl = 0;
4403 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4404 pid_t pgrp;
4405
4406 if (jp->nprocs == 0)
4407 pgrp = getpid();
4408 else
4409 pgrp = jp->ps[0].pid;
4410 /* This can fail because we are doing it in the parent also */
4411 (void)setpgid(0, pgrp);
4412 if (mode == FORK_FG)
4413 xtcsetpgrp(ttyfd, pgrp);
4414 setsignal(SIGTSTP);
4415 setsignal(SIGTTOU);
4416 } else
4417#endif
4418 if (mode == FORK_BG) {
4419 ignoresig(SIGINT);
4420 ignoresig(SIGQUIT);
4421 if (jp->nprocs == 0) {
4422 close(0);
4423 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004424 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425 }
4426 }
4427 if (!oldlvl && iflag) {
4428 setsignal(SIGINT);
4429 setsignal(SIGQUIT);
4430 setsignal(SIGTERM);
4431 }
4432 for (jp = curjob; jp; jp = jp->prev_job)
4433 freejob(jp);
4434 jobless = 0;
4435}
4436
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004437/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004438static void
4439forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4440{
4441 TRACE(("In parent shell: child = %d\n", pid));
4442 if (!jp) {
4443 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4444 jobless++;
4445 return;
4446 }
4447#if JOBS
4448 if (mode != FORK_NOJOB && jp->jobctl) {
4449 int pgrp;
4450
4451 if (jp->nprocs == 0)
4452 pgrp = pid;
4453 else
4454 pgrp = jp->ps[0].pid;
4455 /* This can fail because we are doing it in the child also */
4456 setpgid(pid, pgrp);
4457 }
4458#endif
4459 if (mode == FORK_BG) {
4460 backgndpid = pid; /* set $! */
4461 set_curjob(jp, CUR_RUNNING);
4462 }
4463 if (jp) {
4464 struct procstat *ps = &jp->ps[jp->nprocs++];
4465 ps->pid = pid;
4466 ps->status = -1;
4467 ps->cmd = nullstr;
4468#if JOBS
4469 if (jobctl && n)
4470 ps->cmd = commandtext(n);
4471#endif
4472 }
4473}
4474
4475static int
4476forkshell(struct job *jp, union node *n, int mode)
4477{
4478 int pid;
4479
4480 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4481 pid = fork();
4482 if (pid < 0) {
4483 TRACE(("Fork failed, errno=%d", errno));
4484 if (jp)
4485 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004486 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487 }
4488 if (pid == 0)
4489 forkchild(jp, n, mode);
4490 else
4491 forkparent(jp, n, mode, pid);
4492 return pid;
4493}
4494
4495/*
4496 * Wait for job to finish.
4497 *
4498 * Under job control we have the problem that while a child process is
4499 * running interrupts generated by the user are sent to the child but not
4500 * to the shell. This means that an infinite loop started by an inter-
4501 * active user may be hard to kill. With job control turned off, an
4502 * interactive user may place an interactive program inside a loop. If
4503 * the interactive program catches interrupts, the user doesn't want
4504 * these interrupts to also abort the loop. The approach we take here
4505 * is to have the shell ignore interrupt signals while waiting for a
4506 * foreground process to terminate, and then send itself an interrupt
4507 * signal if the child process was terminated by an interrupt signal.
4508 * Unfortunately, some programs want to do a bit of cleanup and then
4509 * exit on interrupt; unless these processes terminate themselves by
4510 * sending a signal to themselves (instead of calling exit) they will
4511 * confuse this approach.
4512 *
4513 * Called with interrupts off.
4514 */
4515static int
4516waitforjob(struct job *jp)
4517{
4518 int st;
4519
4520 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4521 while (jp->state == JOBRUNNING) {
4522 dowait(DOWAIT_BLOCK, jp);
4523 }
4524 st = getstatus(jp);
4525#if JOBS
4526 if (jp->jobctl) {
4527 xtcsetpgrp(ttyfd, rootpid);
4528 /*
4529 * This is truly gross.
4530 * If we're doing job control, then we did a TIOCSPGRP which
4531 * caused us (the shell) to no longer be in the controlling
4532 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4533 * intuit from the subprocess exit status whether a SIGINT
4534 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4535 */
4536 if (jp->sigint)
4537 raise(SIGINT);
4538 }
4539 if (jp->state == JOBDONE)
4540#endif
4541 freejob(jp);
4542 return st;
4543}
4544
4545/*
4546 * return 1 if there are stopped jobs, otherwise 0
4547 */
4548static int
4549stoppedjobs(void)
4550{
4551 struct job *jp;
4552 int retval;
4553
4554 retval = 0;
4555 if (job_warning)
4556 goto out;
4557 jp = curjob;
4558 if (jp && jp->state == JOBSTOPPED) {
4559 out2str("You have stopped jobs.\n");
4560 job_warning = 2;
4561 retval++;
4562 }
4563 out:
4564 return retval;
4565}
4566
4567
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004568/* ============ redir.c
4569 *
4570 * Code for dealing with input/output redirection.
4571 */
4572
4573#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004574#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004575#ifndef PIPE_BUF
4576# define PIPESIZE 4096 /* amount of buffering in a pipe */
4577#else
4578# define PIPESIZE PIPE_BUF
4579#endif
4580
4581/*
4582 * Open a file in noclobber mode.
4583 * The code was copied from bash.
4584 */
4585static int
4586noclobberopen(const char *fname)
4587{
4588 int r, fd;
4589 struct stat finfo, finfo2;
4590
4591 /*
4592 * If the file exists and is a regular file, return an error
4593 * immediately.
4594 */
4595 r = stat(fname, &finfo);
4596 if (r == 0 && S_ISREG(finfo.st_mode)) {
4597 errno = EEXIST;
4598 return -1;
4599 }
4600
4601 /*
4602 * If the file was not present (r != 0), make sure we open it
4603 * exclusively so that if it is created before we open it, our open
4604 * will fail. Make sure that we do not truncate an existing file.
4605 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4606 * file was not a regular file, we leave O_EXCL off.
4607 */
4608 if (r != 0)
4609 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4610 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4611
4612 /* If the open failed, return the file descriptor right away. */
4613 if (fd < 0)
4614 return fd;
4615
4616 /*
4617 * OK, the open succeeded, but the file may have been changed from a
4618 * non-regular file to a regular file between the stat and the open.
4619 * We are assuming that the O_EXCL open handles the case where FILENAME
4620 * did not exist and is symlinked to an existing file between the stat
4621 * and open.
4622 */
4623
4624 /*
4625 * If we can open it and fstat the file descriptor, and neither check
4626 * revealed that it was a regular file, and the file has not been
4627 * replaced, return the file descriptor.
4628 */
4629 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4630 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4631 return fd;
4632
4633 /* The file has been replaced. badness. */
4634 close(fd);
4635 errno = EEXIST;
4636 return -1;
4637}
4638
4639/*
4640 * Handle here documents. Normally we fork off a process to write the
4641 * data to a pipe. If the document is short, we can stuff the data in
4642 * the pipe without forking.
4643 */
4644/* openhere needs this forward reference */
4645static void expandhere(union node *arg, int fd);
4646static int
4647openhere(union node *redir)
4648{
4649 int pip[2];
4650 size_t len = 0;
4651
4652 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004653 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004654 if (redir->type == NHERE) {
4655 len = strlen(redir->nhere.doc->narg.text);
4656 if (len <= PIPESIZE) {
4657 full_write(pip[1], redir->nhere.doc->narg.text, len);
4658 goto out;
4659 }
4660 }
4661 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4662 close(pip[0]);
4663 signal(SIGINT, SIG_IGN);
4664 signal(SIGQUIT, SIG_IGN);
4665 signal(SIGHUP, SIG_IGN);
4666#ifdef SIGTSTP
4667 signal(SIGTSTP, SIG_IGN);
4668#endif
4669 signal(SIGPIPE, SIG_DFL);
4670 if (redir->type == NHERE)
4671 full_write(pip[1], redir->nhere.doc->narg.text, len);
4672 else
4673 expandhere(redir->nhere.doc, pip[1]);
4674 _exit(0);
4675 }
4676 out:
4677 close(pip[1]);
4678 return pip[0];
4679}
4680
4681static int
4682openredirect(union node *redir)
4683{
4684 char *fname;
4685 int f;
4686
4687 switch (redir->nfile.type) {
4688 case NFROM:
4689 fname = redir->nfile.expfname;
4690 f = open(fname, O_RDONLY);
4691 if (f < 0)
4692 goto eopen;
4693 break;
4694 case NFROMTO:
4695 fname = redir->nfile.expfname;
4696 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4697 if (f < 0)
4698 goto ecreate;
4699 break;
4700 case NTO:
4701 /* Take care of noclobber mode. */
4702 if (Cflag) {
4703 fname = redir->nfile.expfname;
4704 f = noclobberopen(fname);
4705 if (f < 0)
4706 goto ecreate;
4707 break;
4708 }
4709 /* FALLTHROUGH */
4710 case NCLOBBER:
4711 fname = redir->nfile.expfname;
4712 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4713 if (f < 0)
4714 goto ecreate;
4715 break;
4716 case NAPPEND:
4717 fname = redir->nfile.expfname;
4718 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4719 if (f < 0)
4720 goto ecreate;
4721 break;
4722 default:
4723#if DEBUG
4724 abort();
4725#endif
4726 /* Fall through to eliminate warning. */
4727 case NTOFD:
4728 case NFROMFD:
4729 f = -1;
4730 break;
4731 case NHERE:
4732 case NXHERE:
4733 f = openhere(redir);
4734 break;
4735 }
4736
4737 return f;
4738 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004739 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004740 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004741 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004742}
4743
4744/*
4745 * Copy a file descriptor to be >= to. Returns -1
4746 * if the source file descriptor is closed, EMPTY if there are no unused
4747 * file descriptors left.
4748 */
4749static int
4750copyfd(int from, int to)
4751{
4752 int newfd;
4753
4754 newfd = fcntl(from, F_DUPFD, to);
4755 if (newfd < 0) {
4756 if (errno == EMFILE)
4757 return EMPTY;
4758 ash_msg_and_raise_error("%d: %m", from);
4759 }
4760 return newfd;
4761}
4762
4763static void
4764dupredirect(union node *redir, int f)
4765{
4766 int fd = redir->nfile.fd;
4767
4768 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4769 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4770 copyfd(redir->ndup.dupfd, fd);
4771 }
4772 return;
4773 }
4774
4775 if (f != fd) {
4776 copyfd(f, fd);
4777 close(f);
4778 }
4779}
4780
4781/*
4782 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4783 * old file descriptors are stashed away so that the redirection can be
4784 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4785 * standard output, and the standard error if it becomes a duplicate of
4786 * stdout, is saved in memory.
4787 */
4788/* flags passed to redirect */
4789#define REDIR_PUSH 01 /* save previous values of file descriptors */
4790#define REDIR_SAVEFD2 03 /* set preverrout */
4791static void
4792redirect(union node *redir, int flags)
4793{
4794 union node *n;
4795 struct redirtab *sv;
4796 int i;
4797 int fd;
4798 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004799
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004800 nullredirs++;
4801 if (!redir) {
4802 return;
4803 }
4804 sv = NULL;
4805 INT_OFF;
4806 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004807 sv = ckmalloc(sizeof(*sv));
4808 sv->next = redirlist;
4809 redirlist = sv;
4810 sv->nullredirs = nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004811 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004812 sv->renamed[i] = EMPTY;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004813 nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004814 }
4815 n = redir;
4816 do {
4817 fd = n->nfile.fd;
4818 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4819 && n->ndup.dupfd == fd)
4820 continue; /* redirect from/to same file descriptor */
4821
4822 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004823 if (fd == newfd) {
4824 /* Descriptor wasn't open before redirect.
4825 * Mark it for close in the future */
4826 if (sv && sv->renamed[fd] == EMPTY)
4827 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004828 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004829 }
4830 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004831 i = fcntl(fd, F_DUPFD, 10);
4832
4833 if (i == -1) {
4834 i = errno;
4835 if (i != EBADF) {
4836 close(newfd);
4837 errno = i;
4838 ash_msg_and_raise_error("%d: %m", fd);
4839 /* NOTREACHED */
4840 }
4841 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004842 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004843 close(fd);
4844 }
4845 } else {
4846 close(fd);
4847 }
4848 dupredirect(n, newfd);
4849 } while ((n = n->nfile.next));
4850 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004851 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004852 preverrout_fd = sv->renamed[2];
4853}
4854
4855/*
4856 * Undo the effects of the last redirection.
4857 */
4858static void
4859popredir(int drop)
4860{
4861 struct redirtab *rp;
4862 int i;
4863
4864 if (--nullredirs >= 0)
4865 return;
4866 INT_OFF;
4867 rp = redirlist;
4868 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004869 if (rp->renamed[i] == CLOSED) {
4870 if (!drop)
4871 close(i);
4872 continue;
4873 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004874 if (rp->renamed[i] != EMPTY) {
4875 if (!drop) {
4876 close(i);
4877 copyfd(rp->renamed[i], i);
4878 }
4879 close(rp->renamed[i]);
4880 }
4881 }
4882 redirlist = rp->next;
4883 nullredirs = rp->nullredirs;
4884 free(rp);
4885 INT_ON;
4886}
4887
4888/*
4889 * Undo all redirections. Called on error or interrupt.
4890 */
4891
4892/*
4893 * Discard all saved file descriptors.
4894 */
4895static void
4896clearredir(int drop)
4897{
4898 for (;;) {
4899 nullredirs = 0;
4900 if (!redirlist)
4901 break;
4902 popredir(drop);
4903 }
4904}
4905
4906static int
4907redirectsafe(union node *redir, int flags)
4908{
4909 int err;
4910 volatile int saveint;
4911 struct jmploc *volatile savehandler = exception_handler;
4912 struct jmploc jmploc;
4913
4914 SAVE_INT(saveint);
4915 err = setjmp(jmploc.loc) * 2;
4916 if (!err) {
4917 exception_handler = &jmploc;
4918 redirect(redir, flags);
4919 }
4920 exception_handler = savehandler;
4921 if (err && exception != EXERROR)
4922 longjmp(exception_handler->loc, 1);
4923 RESTORE_INT(saveint);
4924 return err;
4925}
4926
4927
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004928/* ============ Routines to expand arguments to commands
4929 *
4930 * We have to deal with backquotes, shell variables, and file metacharacters.
4931 */
4932
4933/*
4934 * expandarg flags
4935 */
4936#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4937#define EXP_TILDE 0x2 /* do normal tilde expansion */
4938#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4939#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4940#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4941#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4942#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4943#define EXP_WORD 0x80 /* expand word in parameter expansion */
4944#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4945/*
4946 * _rmescape() flags
4947 */
4948#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4949#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4950#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4951#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4952#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4953
4954/*
4955 * Structure specifying which parts of the string should be searched
4956 * for IFS characters.
4957 */
4958struct ifsregion {
4959 struct ifsregion *next; /* next region in list */
4960 int begoff; /* offset of start of region */
4961 int endoff; /* offset of end of region */
4962 int nulonly; /* search for nul bytes only */
4963};
4964
4965struct arglist {
4966 struct strlist *list;
4967 struct strlist **lastp;
4968};
4969
4970/* output of current string */
4971static char *expdest;
4972/* list of back quote expressions */
4973static struct nodelist *argbackq;
4974/* first struct in list of ifs regions */
4975static struct ifsregion ifsfirst;
4976/* last struct in list */
4977static struct ifsregion *ifslastp;
4978/* holds expanded arg list */
4979static struct arglist exparg;
4980
4981/*
4982 * Our own itoa().
4983 */
4984static int
4985cvtnum(arith_t num)
4986{
4987 int len;
4988
4989 expdest = makestrspace(32, expdest);
4990#if ENABLE_ASH_MATH_SUPPORT_64
4991 len = fmtstr(expdest, 32, "%lld", (long long) num);
4992#else
4993 len = fmtstr(expdest, 32, "%ld", num);
4994#endif
4995 STADJUST(len, expdest);
4996 return len;
4997}
4998
4999static size_t
5000esclen(const char *start, const char *p)
5001{
5002 size_t esc = 0;
5003
5004 while (p > start && *--p == CTLESC) {
5005 esc++;
5006 }
5007 return esc;
5008}
5009
5010/*
5011 * Remove any CTLESC characters from a string.
5012 */
5013static char *
5014_rmescapes(char *str, int flag)
5015{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005016 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005017
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005018 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005019 unsigned inquotes;
5020 int notescaped;
5021 int globbing;
5022
5023 p = strpbrk(str, qchars);
5024 if (!p) {
5025 return str;
5026 }
5027 q = p;
5028 r = str;
5029 if (flag & RMESCAPE_ALLOC) {
5030 size_t len = p - str;
5031 size_t fulllen = len + strlen(p) + 1;
5032
5033 if (flag & RMESCAPE_GROW) {
5034 r = makestrspace(fulllen, expdest);
5035 } else if (flag & RMESCAPE_HEAP) {
5036 r = ckmalloc(fulllen);
5037 } else {
5038 r = stalloc(fulllen);
5039 }
5040 q = r;
5041 if (len > 0) {
5042 q = memcpy(q, str, len) + len;
5043 }
5044 }
5045 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5046 globbing = flag & RMESCAPE_GLOB;
5047 notescaped = globbing;
5048 while (*p) {
5049 if (*p == CTLQUOTEMARK) {
5050 inquotes = ~inquotes;
5051 p++;
5052 notescaped = globbing;
5053 continue;
5054 }
5055 if (*p == '\\') {
5056 /* naked back slash */
5057 notescaped = 0;
5058 goto copy;
5059 }
5060 if (*p == CTLESC) {
5061 p++;
5062 if (notescaped && inquotes && *p != '/') {
5063 *q++ = '\\';
5064 }
5065 }
5066 notescaped = globbing;
5067 copy:
5068 *q++ = *p++;
5069 }
5070 *q = '\0';
5071 if (flag & RMESCAPE_GROW) {
5072 expdest = r;
5073 STADJUST(q - r + 1, expdest);
5074 }
5075 return r;
5076}
5077#define rmescapes(p) _rmescapes((p), 0)
5078
5079#define pmatch(a, b) !fnmatch((a), (b), 0)
5080
5081/*
5082 * Prepare a pattern for a expmeta (internal glob(3)) call.
5083 *
5084 * Returns an stalloced string.
5085 */
5086static char *
5087preglob(const char *pattern, int quoted, int flag)
5088{
5089 flag |= RMESCAPE_GLOB;
5090 if (quoted) {
5091 flag |= RMESCAPE_QUOTED;
5092 }
5093 return _rmescapes((char *)pattern, flag);
5094}
5095
5096/*
5097 * Put a string on the stack.
5098 */
5099static void
5100memtodest(const char *p, size_t len, int syntax, int quotes)
5101{
5102 char *q = expdest;
5103
5104 q = makestrspace(len * 2, q);
5105
5106 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005107 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005108 if (!c)
5109 continue;
5110 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5111 USTPUTC(CTLESC, q);
5112 USTPUTC(c, q);
5113 }
5114
5115 expdest = q;
5116}
5117
5118static void
5119strtodest(const char *p, int syntax, int quotes)
5120{
5121 memtodest(p, strlen(p), syntax, quotes);
5122}
5123
5124/*
5125 * Record the fact that we have to scan this region of the
5126 * string for IFS characters.
5127 */
5128static void
5129recordregion(int start, int end, int nulonly)
5130{
5131 struct ifsregion *ifsp;
5132
5133 if (ifslastp == NULL) {
5134 ifsp = &ifsfirst;
5135 } else {
5136 INT_OFF;
5137 ifsp = ckmalloc(sizeof(*ifsp));
5138 ifsp->next = NULL;
5139 ifslastp->next = ifsp;
5140 INT_ON;
5141 }
5142 ifslastp = ifsp;
5143 ifslastp->begoff = start;
5144 ifslastp->endoff = end;
5145 ifslastp->nulonly = nulonly;
5146}
5147
5148static void
5149removerecordregions(int endoff)
5150{
5151 if (ifslastp == NULL)
5152 return;
5153
5154 if (ifsfirst.endoff > endoff) {
5155 while (ifsfirst.next != NULL) {
5156 struct ifsregion *ifsp;
5157 INT_OFF;
5158 ifsp = ifsfirst.next->next;
5159 free(ifsfirst.next);
5160 ifsfirst.next = ifsp;
5161 INT_ON;
5162 }
5163 if (ifsfirst.begoff > endoff)
5164 ifslastp = NULL;
5165 else {
5166 ifslastp = &ifsfirst;
5167 ifsfirst.endoff = endoff;
5168 }
5169 return;
5170 }
5171
5172 ifslastp = &ifsfirst;
5173 while (ifslastp->next && ifslastp->next->begoff < endoff)
5174 ifslastp=ifslastp->next;
5175 while (ifslastp->next != NULL) {
5176 struct ifsregion *ifsp;
5177 INT_OFF;
5178 ifsp = ifslastp->next->next;
5179 free(ifslastp->next);
5180 ifslastp->next = ifsp;
5181 INT_ON;
5182 }
5183 if (ifslastp->endoff > endoff)
5184 ifslastp->endoff = endoff;
5185}
5186
5187static char *
5188exptilde(char *startp, char *p, int flag)
5189{
5190 char c;
5191 char *name;
5192 struct passwd *pw;
5193 const char *home;
5194 int quotes = flag & (EXP_FULL | EXP_CASE);
5195 int startloc;
5196
5197 name = p + 1;
5198
5199 while ((c = *++p) != '\0') {
5200 switch (c) {
5201 case CTLESC:
5202 return startp;
5203 case CTLQUOTEMARK:
5204 return startp;
5205 case ':':
5206 if (flag & EXP_VARTILDE)
5207 goto done;
5208 break;
5209 case '/':
5210 case CTLENDVAR:
5211 goto done;
5212 }
5213 }
5214 done:
5215 *p = '\0';
5216 if (*name == '\0') {
5217 home = lookupvar(homestr);
5218 } else {
5219 pw = getpwnam(name);
5220 if (pw == NULL)
5221 goto lose;
5222 home = pw->pw_dir;
5223 }
5224 if (!home || !*home)
5225 goto lose;
5226 *p = c;
5227 startloc = expdest - (char *)stackblock();
5228 strtodest(home, SQSYNTAX, quotes);
5229 recordregion(startloc, expdest - (char *)stackblock(), 0);
5230 return p;
5231 lose:
5232 *p = c;
5233 return startp;
5234}
5235
5236/*
5237 * Execute a command inside back quotes. If it's a builtin command, we
5238 * want to save its output in a block obtained from malloc. Otherwise
5239 * we fork off a subprocess and get the output of the command via a pipe.
5240 * Should be called with interrupts off.
5241 */
5242struct backcmd { /* result of evalbackcmd */
5243 int fd; /* file descriptor to read from */
5244 char *buf; /* buffer */
5245 int nleft; /* number of chars in buffer */
5246 struct job *jp; /* job structure for command */
5247};
5248
5249/* These forward decls are needed to use "eval" code for backticks handling: */
5250static int back_exitstatus; /* exit status of backquoted command */
5251#define EV_EXIT 01 /* exit after evaluating tree */
5252static void evaltree(union node *, int);
5253
5254static void
5255evalbackcmd(union node *n, struct backcmd *result)
5256{
5257 int saveherefd;
5258
5259 result->fd = -1;
5260 result->buf = NULL;
5261 result->nleft = 0;
5262 result->jp = NULL;
5263 if (n == NULL) {
5264 goto out;
5265 }
5266
5267 saveherefd = herefd;
5268 herefd = -1;
5269
5270 {
5271 int pip[2];
5272 struct job *jp;
5273
5274 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005275 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005276 jp = makejob(n, 1);
5277 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5278 FORCE_INT_ON;
5279 close(pip[0]);
5280 if (pip[1] != 1) {
5281 close(1);
5282 copyfd(pip[1], 1);
5283 close(pip[1]);
5284 }
5285 eflag = 0;
5286 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5287 /* NOTREACHED */
5288 }
5289 close(pip[1]);
5290 result->fd = pip[0];
5291 result->jp = jp;
5292 }
5293 herefd = saveherefd;
5294 out:
5295 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5296 result->fd, result->buf, result->nleft, result->jp));
5297}
5298
5299/*
5300 * Expand stuff in backwards quotes.
5301 */
5302static void
5303expbackq(union node *cmd, int quoted, int quotes)
5304{
5305 struct backcmd in;
5306 int i;
5307 char buf[128];
5308 char *p;
5309 char *dest;
5310 int startloc;
5311 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5312 struct stackmark smark;
5313
5314 INT_OFF;
5315 setstackmark(&smark);
5316 dest = expdest;
5317 startloc = dest - (char *)stackblock();
5318 grabstackstr(dest);
5319 evalbackcmd(cmd, &in);
5320 popstackmark(&smark);
5321
5322 p = in.buf;
5323 i = in.nleft;
5324 if (i == 0)
5325 goto read;
5326 for (;;) {
5327 memtodest(p, i, syntax, quotes);
5328 read:
5329 if (in.fd < 0)
5330 break;
5331 i = safe_read(in.fd, buf, sizeof(buf));
5332 TRACE(("expbackq: read returns %d\n", i));
5333 if (i <= 0)
5334 break;
5335 p = buf;
5336 }
5337
Denis Vlasenko60818682007-09-28 22:07:23 +00005338 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005339 if (in.fd >= 0) {
5340 close(in.fd);
5341 back_exitstatus = waitforjob(in.jp);
5342 }
5343 INT_ON;
5344
5345 /* Eat all trailing newlines */
5346 dest = expdest;
5347 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5348 STUNPUTC(dest);
5349 expdest = dest;
5350
5351 if (quoted == 0)
5352 recordregion(startloc, dest - (char *)stackblock(), 0);
5353 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5354 (dest - (char *)stackblock()) - startloc,
5355 (dest - (char *)stackblock()) - startloc,
5356 stackblock() + startloc));
5357}
5358
5359#if ENABLE_ASH_MATH_SUPPORT
5360/*
5361 * Expand arithmetic expression. Backup to start of expression,
5362 * evaluate, place result in (backed up) result, adjust string position.
5363 */
5364static void
5365expari(int quotes)
5366{
5367 char *p, *start;
5368 int begoff;
5369 int flag;
5370 int len;
5371
5372 /* ifsfree(); */
5373
5374 /*
5375 * This routine is slightly over-complicated for
5376 * efficiency. Next we scan backwards looking for the
5377 * start of arithmetic.
5378 */
5379 start = stackblock();
5380 p = expdest - 1;
5381 *p = '\0';
5382 p--;
5383 do {
5384 int esc;
5385
5386 while (*p != CTLARI) {
5387 p--;
5388#if DEBUG
5389 if (p < start) {
5390 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5391 }
5392#endif
5393 }
5394
5395 esc = esclen(start, p);
5396 if (!(esc % 2)) {
5397 break;
5398 }
5399
5400 p -= esc + 1;
5401 } while (1);
5402
5403 begoff = p - start;
5404
5405 removerecordregions(begoff);
5406
5407 flag = p[1];
5408
5409 expdest = p;
5410
5411 if (quotes)
5412 rmescapes(p + 2);
5413
5414 len = cvtnum(dash_arith(p + 2));
5415
5416 if (flag != '"')
5417 recordregion(begoff, begoff + len, 0);
5418}
5419#endif
5420
5421/* argstr needs it */
5422static char *evalvar(char *p, int flag);
5423
5424/*
5425 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5426 * characters to allow for further processing. Otherwise treat
5427 * $@ like $* since no splitting will be performed.
5428 */
5429static void
5430argstr(char *p, int flag)
5431{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005432 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005433 '=',
5434 ':',
5435 CTLQUOTEMARK,
5436 CTLENDVAR,
5437 CTLESC,
5438 CTLVAR,
5439 CTLBACKQ,
5440 CTLBACKQ | CTLQUOTE,
5441#if ENABLE_ASH_MATH_SUPPORT
5442 CTLENDARI,
5443#endif
5444 0
5445 };
5446 const char *reject = spclchars;
5447 int c;
5448 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5449 int breakall = flag & EXP_WORD;
5450 int inquotes;
5451 size_t length;
5452 int startloc;
5453
5454 if (!(flag & EXP_VARTILDE)) {
5455 reject += 2;
5456 } else if (flag & EXP_VARTILDE2) {
5457 reject++;
5458 }
5459 inquotes = 0;
5460 length = 0;
5461 if (flag & EXP_TILDE) {
5462 char *q;
5463
5464 flag &= ~EXP_TILDE;
5465 tilde:
5466 q = p;
5467 if (*q == CTLESC && (flag & EXP_QWORD))
5468 q++;
5469 if (*q == '~')
5470 p = exptilde(p, q, flag);
5471 }
5472 start:
5473 startloc = expdest - (char *)stackblock();
5474 for (;;) {
5475 length += strcspn(p + length, reject);
5476 c = p[length];
5477 if (c && (!(c & 0x80)
5478#if ENABLE_ASH_MATH_SUPPORT
5479 || c == CTLENDARI
5480#endif
5481 )) {
5482 /* c == '=' || c == ':' || c == CTLENDARI */
5483 length++;
5484 }
5485 if (length > 0) {
5486 int newloc;
5487 expdest = stack_nputstr(p, length, expdest);
5488 newloc = expdest - (char *)stackblock();
5489 if (breakall && !inquotes && newloc > startloc) {
5490 recordregion(startloc, newloc, 0);
5491 }
5492 startloc = newloc;
5493 }
5494 p += length + 1;
5495 length = 0;
5496
5497 switch (c) {
5498 case '\0':
5499 goto breakloop;
5500 case '=':
5501 if (flag & EXP_VARTILDE2) {
5502 p--;
5503 continue;
5504 }
5505 flag |= EXP_VARTILDE2;
5506 reject++;
5507 /* fall through */
5508 case ':':
5509 /*
5510 * sort of a hack - expand tildes in variable
5511 * assignments (after the first '=' and after ':'s).
5512 */
5513 if (*--p == '~') {
5514 goto tilde;
5515 }
5516 continue;
5517 }
5518
5519 switch (c) {
5520 case CTLENDVAR: /* ??? */
5521 goto breakloop;
5522 case CTLQUOTEMARK:
5523 /* "$@" syntax adherence hack */
5524 if (
5525 !inquotes &&
5526 !memcmp(p, dolatstr, 4) &&
5527 (p[4] == CTLQUOTEMARK || (
5528 p[4] == CTLENDVAR &&
5529 p[5] == CTLQUOTEMARK
5530 ))
5531 ) {
5532 p = evalvar(p + 1, flag) + 1;
5533 goto start;
5534 }
5535 inquotes = !inquotes;
5536 addquote:
5537 if (quotes) {
5538 p--;
5539 length++;
5540 startloc++;
5541 }
5542 break;
5543 case CTLESC:
5544 startloc++;
5545 length++;
5546 goto addquote;
5547 case CTLVAR:
5548 p = evalvar(p, flag);
5549 goto start;
5550 case CTLBACKQ:
5551 c = 0;
5552 case CTLBACKQ|CTLQUOTE:
5553 expbackq(argbackq->n, c, quotes);
5554 argbackq = argbackq->next;
5555 goto start;
5556#if ENABLE_ASH_MATH_SUPPORT
5557 case CTLENDARI:
5558 p--;
5559 expari(quotes);
5560 goto start;
5561#endif
5562 }
5563 }
5564 breakloop:
5565 ;
5566}
5567
5568static char *
5569scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5570 int zero)
5571{
5572 char *loc;
5573 char *loc2;
5574 char c;
5575
5576 loc = startp;
5577 loc2 = rmesc;
5578 do {
5579 int match;
5580 const char *s = loc2;
5581 c = *loc2;
5582 if (zero) {
5583 *loc2 = '\0';
5584 s = rmesc;
5585 }
5586 match = pmatch(str, s);
5587 *loc2 = c;
5588 if (match)
5589 return loc;
5590 if (quotes && *loc == CTLESC)
5591 loc++;
5592 loc++;
5593 loc2++;
5594 } while (c);
5595 return 0;
5596}
5597
5598static char *
5599scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5600 int zero)
5601{
5602 int esc = 0;
5603 char *loc;
5604 char *loc2;
5605
5606 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5607 int match;
5608 char c = *loc2;
5609 const char *s = loc2;
5610 if (zero) {
5611 *loc2 = '\0';
5612 s = rmesc;
5613 }
5614 match = pmatch(str, s);
5615 *loc2 = c;
5616 if (match)
5617 return loc;
5618 loc--;
5619 if (quotes) {
5620 if (--esc < 0) {
5621 esc = esclen(startp, loc);
5622 }
5623 if (esc % 2) {
5624 esc--;
5625 loc--;
5626 }
5627 }
5628 }
5629 return 0;
5630}
5631
5632static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5633static void
5634varunset(const char *end, const char *var, const char *umsg, int varflags)
5635{
5636 const char *msg;
5637 const char *tail;
5638
5639 tail = nullstr;
5640 msg = "parameter not set";
5641 if (umsg) {
5642 if (*end == CTLENDVAR) {
5643 if (varflags & VSNUL)
5644 tail = " or null";
5645 } else
5646 msg = umsg;
5647 }
5648 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5649}
5650
5651static const char *
5652subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5653{
5654 char *startp;
5655 char *loc;
5656 int saveherefd = herefd;
5657 struct nodelist *saveargbackq = argbackq;
5658 int amount;
5659 char *rmesc, *rmescend;
5660 int zero;
5661 char *(*scan)(char *, char *, char *, char *, int , int);
5662
5663 herefd = -1;
5664 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5665 STPUTC('\0', expdest);
5666 herefd = saveherefd;
5667 argbackq = saveargbackq;
5668 startp = stackblock() + startloc;
5669
5670 switch (subtype) {
5671 case VSASSIGN:
5672 setvar(str, startp, 0);
5673 amount = startp - expdest;
5674 STADJUST(amount, expdest);
5675 return startp;
5676
5677 case VSQUESTION:
5678 varunset(p, str, startp, varflags);
5679 /* NOTREACHED */
5680 }
5681
5682 subtype -= VSTRIMRIGHT;
5683#if DEBUG
5684 if (subtype < 0 || subtype > 3)
5685 abort();
5686#endif
5687
5688 rmesc = startp;
5689 rmescend = stackblock() + strloc;
5690 if (quotes) {
5691 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5692 if (rmesc != startp) {
5693 rmescend = expdest;
5694 startp = stackblock() + startloc;
5695 }
5696 }
5697 rmescend--;
5698 str = stackblock() + strloc;
5699 preglob(str, varflags & VSQUOTE, 0);
5700
5701 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5702 zero = subtype >> 1;
5703 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5704 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5705
5706 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5707 if (loc) {
5708 if (zero) {
5709 memmove(startp, loc, str - loc);
5710 loc = startp + (str - loc) - 1;
5711 }
5712 *loc = '\0';
5713 amount = loc - expdest;
5714 STADJUST(amount, expdest);
5715 }
5716 return loc;
5717}
5718
5719/*
5720 * Add the value of a specialized variable to the stack string.
5721 */
5722static ssize_t
5723varvalue(char *name, int varflags, int flags)
5724{
5725 int num;
5726 char *p;
5727 int i;
5728 int sep = 0;
5729 int sepq = 0;
5730 ssize_t len = 0;
5731 char **ap;
5732 int syntax;
5733 int quoted = varflags & VSQUOTE;
5734 int subtype = varflags & VSTYPE;
5735 int quotes = flags & (EXP_FULL | EXP_CASE);
5736
5737 if (quoted && (flags & EXP_FULL))
5738 sep = 1 << CHAR_BIT;
5739
5740 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5741 switch (*name) {
5742 case '$':
5743 num = rootpid;
5744 goto numvar;
5745 case '?':
5746 num = exitstatus;
5747 goto numvar;
5748 case '#':
5749 num = shellparam.nparam;
5750 goto numvar;
5751 case '!':
5752 num = backgndpid;
5753 if (num == 0)
5754 return -1;
5755 numvar:
5756 len = cvtnum(num);
5757 break;
5758 case '-':
5759 p = makestrspace(NOPTS, expdest);
5760 for (i = NOPTS - 1; i >= 0; i--) {
5761 if (optlist[i]) {
5762 USTPUTC(optletters(i), p);
5763 len++;
5764 }
5765 }
5766 expdest = p;
5767 break;
5768 case '@':
5769 if (sep)
5770 goto param;
5771 /* fall through */
5772 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005773 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005774 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5775 sepq = 1;
5776 param:
5777 ap = shellparam.p;
5778 if (!ap)
5779 return -1;
5780 while ((p = *ap++)) {
5781 size_t partlen;
5782
5783 partlen = strlen(p);
5784 len += partlen;
5785
5786 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5787 memtodest(p, partlen, syntax, quotes);
5788
5789 if (*ap && sep) {
5790 char *q;
5791
5792 len++;
5793 if (subtype == VSPLUS || subtype == VSLENGTH) {
5794 continue;
5795 }
5796 q = expdest;
5797 if (sepq)
5798 STPUTC(CTLESC, q);
5799 STPUTC(sep, q);
5800 expdest = q;
5801 }
5802 }
5803 return len;
5804 case '0':
5805 case '1':
5806 case '2':
5807 case '3':
5808 case '4':
5809 case '5':
5810 case '6':
5811 case '7':
5812 case '8':
5813 case '9':
5814 num = atoi(name);
5815 if (num < 0 || num > shellparam.nparam)
5816 return -1;
5817 p = num ? shellparam.p[num - 1] : arg0;
5818 goto value;
5819 default:
5820 p = lookupvar(name);
5821 value:
5822 if (!p)
5823 return -1;
5824
5825 len = strlen(p);
5826 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5827 memtodest(p, len, syntax, quotes);
5828 return len;
5829 }
5830
5831 if (subtype == VSPLUS || subtype == VSLENGTH)
5832 STADJUST(-len, expdest);
5833 return len;
5834}
5835
5836/*
5837 * Expand a variable, and return a pointer to the next character in the
5838 * input string.
5839 */
5840static char *
5841evalvar(char *p, int flag)
5842{
5843 int subtype;
5844 int varflags;
5845 char *var;
5846 int patloc;
5847 int c;
5848 int startloc;
5849 ssize_t varlen;
5850 int easy;
5851 int quotes;
5852 int quoted;
5853
5854 quotes = flag & (EXP_FULL | EXP_CASE);
5855 varflags = *p++;
5856 subtype = varflags & VSTYPE;
5857 quoted = varflags & VSQUOTE;
5858 var = p;
5859 easy = (!quoted || (*var == '@' && shellparam.nparam));
5860 startloc = expdest - (char *)stackblock();
5861 p = strchr(p, '=') + 1;
5862
5863 again:
5864 varlen = varvalue(var, varflags, flag);
5865 if (varflags & VSNUL)
5866 varlen--;
5867
5868 if (subtype == VSPLUS) {
5869 varlen = -1 - varlen;
5870 goto vsplus;
5871 }
5872
5873 if (subtype == VSMINUS) {
5874 vsplus:
5875 if (varlen < 0) {
5876 argstr(
5877 p, flag | EXP_TILDE |
5878 (quoted ? EXP_QWORD : EXP_WORD)
5879 );
5880 goto end;
5881 }
5882 if (easy)
5883 goto record;
5884 goto end;
5885 }
5886
5887 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5888 if (varlen < 0) {
5889 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5890 varflags &= ~VSNUL;
5891 /*
5892 * Remove any recorded regions beyond
5893 * start of variable
5894 */
5895 removerecordregions(startloc);
5896 goto again;
5897 }
5898 goto end;
5899 }
5900 if (easy)
5901 goto record;
5902 goto end;
5903 }
5904
5905 if (varlen < 0 && uflag)
5906 varunset(p, var, 0, 0);
5907
5908 if (subtype == VSLENGTH) {
5909 cvtnum(varlen > 0 ? varlen : 0);
5910 goto record;
5911 }
5912
5913 if (subtype == VSNORMAL) {
5914 if (!easy)
5915 goto end;
5916 record:
5917 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5918 goto end;
5919 }
5920
5921#if DEBUG
5922 switch (subtype) {
5923 case VSTRIMLEFT:
5924 case VSTRIMLEFTMAX:
5925 case VSTRIMRIGHT:
5926 case VSTRIMRIGHTMAX:
5927 break;
5928 default:
5929 abort();
5930 }
5931#endif
5932
5933 if (varlen >= 0) {
5934 /*
5935 * Terminate the string and start recording the pattern
5936 * right after it
5937 */
5938 STPUTC('\0', expdest);
5939 patloc = expdest - (char *)stackblock();
5940 if (subevalvar(p, NULL, patloc, subtype,
5941 startloc, varflags, quotes) == 0) {
5942 int amount = expdest - (
5943 (char *)stackblock() + patloc - 1
5944 );
5945 STADJUST(-amount, expdest);
5946 }
5947 /* Remove any recorded regions beyond start of variable */
5948 removerecordregions(startloc);
5949 goto record;
5950 }
5951
5952 end:
5953 if (subtype != VSNORMAL) { /* skip to end of alternative */
5954 int nesting = 1;
5955 for (;;) {
5956 c = *p++;
5957 if (c == CTLESC)
5958 p++;
5959 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5960 if (varlen >= 0)
5961 argbackq = argbackq->next;
5962 } else if (c == CTLVAR) {
5963 if ((*p++ & VSTYPE) != VSNORMAL)
5964 nesting++;
5965 } else if (c == CTLENDVAR) {
5966 if (--nesting == 0)
5967 break;
5968 }
5969 }
5970 }
5971 return p;
5972}
5973
5974/*
5975 * Break the argument string into pieces based upon IFS and add the
5976 * strings to the argument list. The regions of the string to be
5977 * searched for IFS characters have been stored by recordregion.
5978 */
5979static void
5980ifsbreakup(char *string, struct arglist *arglist)
5981{
5982 struct ifsregion *ifsp;
5983 struct strlist *sp;
5984 char *start;
5985 char *p;
5986 char *q;
5987 const char *ifs, *realifs;
5988 int ifsspc;
5989 int nulonly;
5990
5991 start = string;
5992 if (ifslastp != NULL) {
5993 ifsspc = 0;
5994 nulonly = 0;
5995 realifs = ifsset() ? ifsval() : defifs;
5996 ifsp = &ifsfirst;
5997 do {
5998 p = string + ifsp->begoff;
5999 nulonly = ifsp->nulonly;
6000 ifs = nulonly ? nullstr : realifs;
6001 ifsspc = 0;
6002 while (p < string + ifsp->endoff) {
6003 q = p;
6004 if (*p == CTLESC)
6005 p++;
6006 if (!strchr(ifs, *p)) {
6007 p++;
6008 continue;
6009 }
6010 if (!nulonly)
6011 ifsspc = (strchr(defifs, *p) != NULL);
6012 /* Ignore IFS whitespace at start */
6013 if (q == start && ifsspc) {
6014 p++;
6015 start = p;
6016 continue;
6017 }
6018 *q = '\0';
6019 sp = stalloc(sizeof(*sp));
6020 sp->text = start;
6021 *arglist->lastp = sp;
6022 arglist->lastp = &sp->next;
6023 p++;
6024 if (!nulonly) {
6025 for (;;) {
6026 if (p >= string + ifsp->endoff) {
6027 break;
6028 }
6029 q = p;
6030 if (*p == CTLESC)
6031 p++;
6032 if (strchr(ifs, *p) == NULL ) {
6033 p = q;
6034 break;
6035 } else if (strchr(defifs, *p) == NULL) {
6036 if (ifsspc) {
6037 p++;
6038 ifsspc = 0;
6039 } else {
6040 p = q;
6041 break;
6042 }
6043 } else
6044 p++;
6045 }
6046 }
6047 start = p;
6048 } /* while */
6049 ifsp = ifsp->next;
6050 } while (ifsp != NULL);
6051 if (nulonly)
6052 goto add;
6053 }
6054
6055 if (!*start)
6056 return;
6057
6058 add:
6059 sp = stalloc(sizeof(*sp));
6060 sp->text = start;
6061 *arglist->lastp = sp;
6062 arglist->lastp = &sp->next;
6063}
6064
6065static void
6066ifsfree(void)
6067{
6068 struct ifsregion *p;
6069
6070 INT_OFF;
6071 p = ifsfirst.next;
6072 do {
6073 struct ifsregion *ifsp;
6074 ifsp = p->next;
6075 free(p);
6076 p = ifsp;
6077 } while (p);
6078 ifslastp = NULL;
6079 ifsfirst.next = NULL;
6080 INT_ON;
6081}
6082
6083/*
6084 * Add a file name to the list.
6085 */
6086static void
6087addfname(const char *name)
6088{
6089 struct strlist *sp;
6090
6091 sp = stalloc(sizeof(*sp));
6092 sp->text = ststrdup(name);
6093 *exparg.lastp = sp;
6094 exparg.lastp = &sp->next;
6095}
6096
6097static char *expdir;
6098
6099/*
6100 * Do metacharacter (i.e. *, ?, [...]) expansion.
6101 */
6102static void
6103expmeta(char *enddir, char *name)
6104{
6105 char *p;
6106 const char *cp;
6107 char *start;
6108 char *endname;
6109 int metaflag;
6110 struct stat statb;
6111 DIR *dirp;
6112 struct dirent *dp;
6113 int atend;
6114 int matchdot;
6115
6116 metaflag = 0;
6117 start = name;
6118 for (p = name; *p; p++) {
6119 if (*p == '*' || *p == '?')
6120 metaflag = 1;
6121 else if (*p == '[') {
6122 char *q = p + 1;
6123 if (*q == '!')
6124 q++;
6125 for (;;) {
6126 if (*q == '\\')
6127 q++;
6128 if (*q == '/' || *q == '\0')
6129 break;
6130 if (*++q == ']') {
6131 metaflag = 1;
6132 break;
6133 }
6134 }
6135 } else if (*p == '\\')
6136 p++;
6137 else if (*p == '/') {
6138 if (metaflag)
6139 goto out;
6140 start = p + 1;
6141 }
6142 }
6143 out:
6144 if (metaflag == 0) { /* we've reached the end of the file name */
6145 if (enddir != expdir)
6146 metaflag++;
6147 p = name;
6148 do {
6149 if (*p == '\\')
6150 p++;
6151 *enddir++ = *p;
6152 } while (*p++);
6153 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6154 addfname(expdir);
6155 return;
6156 }
6157 endname = p;
6158 if (name < start) {
6159 p = name;
6160 do {
6161 if (*p == '\\')
6162 p++;
6163 *enddir++ = *p++;
6164 } while (p < start);
6165 }
6166 if (enddir == expdir) {
6167 cp = ".";
6168 } else if (enddir == expdir + 1 && *expdir == '/') {
6169 cp = "/";
6170 } else {
6171 cp = expdir;
6172 enddir[-1] = '\0';
6173 }
6174 dirp = opendir(cp);
6175 if (dirp == NULL)
6176 return;
6177 if (enddir != expdir)
6178 enddir[-1] = '/';
6179 if (*endname == 0) {
6180 atend = 1;
6181 } else {
6182 atend = 0;
6183 *endname++ = '\0';
6184 }
6185 matchdot = 0;
6186 p = start;
6187 if (*p == '\\')
6188 p++;
6189 if (*p == '.')
6190 matchdot++;
6191 while (! intpending && (dp = readdir(dirp)) != NULL) {
6192 if (dp->d_name[0] == '.' && ! matchdot)
6193 continue;
6194 if (pmatch(start, dp->d_name)) {
6195 if (atend) {
6196 strcpy(enddir, dp->d_name);
6197 addfname(expdir);
6198 } else {
6199 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6200 continue;
6201 p[-1] = '/';
6202 expmeta(p, endname);
6203 }
6204 }
6205 }
6206 closedir(dirp);
6207 if (! atend)
6208 endname[-1] = '/';
6209}
6210
6211static struct strlist *
6212msort(struct strlist *list, int len)
6213{
6214 struct strlist *p, *q = NULL;
6215 struct strlist **lpp;
6216 int half;
6217 int n;
6218
6219 if (len <= 1)
6220 return list;
6221 half = len >> 1;
6222 p = list;
6223 for (n = half; --n >= 0; ) {
6224 q = p;
6225 p = p->next;
6226 }
6227 q->next = NULL; /* terminate first half of list */
6228 q = msort(list, half); /* sort first half of list */
6229 p = msort(p, len - half); /* sort second half */
6230 lpp = &list;
6231 for (;;) {
6232#if ENABLE_LOCALE_SUPPORT
6233 if (strcoll(p->text, q->text) < 0)
6234#else
6235 if (strcmp(p->text, q->text) < 0)
6236#endif
6237 {
6238 *lpp = p;
6239 lpp = &p->next;
6240 p = *lpp;
6241 if (p == NULL) {
6242 *lpp = q;
6243 break;
6244 }
6245 } else {
6246 *lpp = q;
6247 lpp = &q->next;
6248 q = *lpp;
6249 if (q == NULL) {
6250 *lpp = p;
6251 break;
6252 }
6253 }
6254 }
6255 return list;
6256}
6257
6258/*
6259 * Sort the results of file name expansion. It calculates the number of
6260 * strings to sort and then calls msort (short for merge sort) to do the
6261 * work.
6262 */
6263static struct strlist *
6264expsort(struct strlist *str)
6265{
6266 int len;
6267 struct strlist *sp;
6268
6269 len = 0;
6270 for (sp = str; sp; sp = sp->next)
6271 len++;
6272 return msort(str, len);
6273}
6274
6275static void
6276expandmeta(struct strlist *str, int flag)
6277{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006278 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006279 '*', '?', '[', 0
6280 };
6281 /* TODO - EXP_REDIR */
6282
6283 while (str) {
6284 struct strlist **savelastp;
6285 struct strlist *sp;
6286 char *p;
6287
6288 if (fflag)
6289 goto nometa;
6290 if (!strpbrk(str->text, metachars))
6291 goto nometa;
6292 savelastp = exparg.lastp;
6293
6294 INT_OFF;
6295 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6296 {
6297 int i = strlen(str->text);
6298 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6299 }
6300
6301 expmeta(expdir, p);
6302 free(expdir);
6303 if (p != str->text)
6304 free(p);
6305 INT_ON;
6306 if (exparg.lastp == savelastp) {
6307 /*
6308 * no matches
6309 */
6310 nometa:
6311 *exparg.lastp = str;
6312 rmescapes(str->text);
6313 exparg.lastp = &str->next;
6314 } else {
6315 *exparg.lastp = NULL;
6316 *savelastp = sp = expsort(*savelastp);
6317 while (sp->next != NULL)
6318 sp = sp->next;
6319 exparg.lastp = &sp->next;
6320 }
6321 str = str->next;
6322 }
6323}
6324
6325/*
6326 * Perform variable substitution and command substitution on an argument,
6327 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6328 * perform splitting and file name expansion. When arglist is NULL, perform
6329 * here document expansion.
6330 */
6331static void
6332expandarg(union node *arg, struct arglist *arglist, int flag)
6333{
6334 struct strlist *sp;
6335 char *p;
6336
6337 argbackq = arg->narg.backquote;
6338 STARTSTACKSTR(expdest);
6339 ifsfirst.next = NULL;
6340 ifslastp = NULL;
6341 argstr(arg->narg.text, flag);
6342 p = _STPUTC('\0', expdest);
6343 expdest = p - 1;
6344 if (arglist == NULL) {
6345 return; /* here document expanded */
6346 }
6347 p = grabstackstr(p);
6348 exparg.lastp = &exparg.list;
6349 /*
6350 * TODO - EXP_REDIR
6351 */
6352 if (flag & EXP_FULL) {
6353 ifsbreakup(p, &exparg);
6354 *exparg.lastp = NULL;
6355 exparg.lastp = &exparg.list;
6356 expandmeta(exparg.list, flag);
6357 } else {
6358 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6359 rmescapes(p);
6360 sp = stalloc(sizeof(*sp));
6361 sp->text = p;
6362 *exparg.lastp = sp;
6363 exparg.lastp = &sp->next;
6364 }
6365 if (ifsfirst.next)
6366 ifsfree();
6367 *exparg.lastp = NULL;
6368 if (exparg.list) {
6369 *arglist->lastp = exparg.list;
6370 arglist->lastp = exparg.lastp;
6371 }
6372}
6373
6374/*
6375 * Expand shell variables and backquotes inside a here document.
6376 */
6377static void
6378expandhere(union node *arg, int fd)
6379{
6380 herefd = fd;
6381 expandarg(arg, (struct arglist *)NULL, 0);
6382 full_write(fd, stackblock(), expdest - (char *)stackblock());
6383}
6384
6385/*
6386 * Returns true if the pattern matches the string.
6387 */
6388static int
6389patmatch(char *pattern, const char *string)
6390{
6391 return pmatch(preglob(pattern, 0, 0), string);
6392}
6393
6394/*
6395 * See if a pattern matches in a case statement.
6396 */
6397static int
6398casematch(union node *pattern, char *val)
6399{
6400 struct stackmark smark;
6401 int result;
6402
6403 setstackmark(&smark);
6404 argbackq = pattern->narg.backquote;
6405 STARTSTACKSTR(expdest);
6406 ifslastp = NULL;
6407 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6408 STACKSTRNUL(expdest);
6409 result = patmatch(stackblock(), val);
6410 popstackmark(&smark);
6411 return result;
6412}
6413
6414
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006415/* ============ find_command */
6416
6417struct builtincmd {
6418 const char *name;
6419 int (*builtin)(int, char **);
6420 /* unsigned flags; */
6421};
6422#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6423#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6424#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6425
6426struct cmdentry {
6427 int cmdtype;
6428 union param {
6429 int index;
6430 const struct builtincmd *cmd;
6431 struct funcnode *func;
6432 } u;
6433};
6434/* values of cmdtype */
6435#define CMDUNKNOWN -1 /* no entry in table for command */
6436#define CMDNORMAL 0 /* command is an executable program */
6437#define CMDFUNCTION 1 /* command is a shell function */
6438#define CMDBUILTIN 2 /* command is a shell builtin */
6439
6440/* action to find_command() */
6441#define DO_ERR 0x01 /* prints errors */
6442#define DO_ABS 0x02 /* checks absolute paths */
6443#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6444#define DO_ALTPATH 0x08 /* using alternate path */
6445#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6446
6447static void find_command(char *, struct cmdentry *, int, const char *);
6448
6449
6450/* ============ Hashing commands */
6451
6452/*
6453 * When commands are first encountered, they are entered in a hash table.
6454 * This ensures that a full path search will not have to be done for them
6455 * on each invocation.
6456 *
6457 * We should investigate converting to a linear search, even though that
6458 * would make the command name "hash" a misnomer.
6459 */
6460
6461#define CMDTABLESIZE 31 /* should be prime */
6462#define ARB 1 /* actual size determined at run time */
6463
6464struct tblentry {
6465 struct tblentry *next; /* next entry in hash chain */
6466 union param param; /* definition of builtin function */
6467 short cmdtype; /* index identifying command */
6468 char rehash; /* if set, cd done since entry created */
6469 char cmdname[ARB]; /* name of command */
6470};
6471
6472static struct tblentry *cmdtable[CMDTABLESIZE];
6473static int builtinloc = -1; /* index in path of %builtin, or -1 */
6474
6475static void
6476tryexec(char *cmd, char **argv, char **envp)
6477{
6478 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006479
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006480#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006481 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006482 int a = find_applet_by_name(cmd);
6483 if (a >= 0) {
6484 if (APPLET_IS_NOEXEC(a))
6485 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006486 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006487 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006488 /* If they called chroot or otherwise made the binary no longer
6489 * executable, fall through */
6490 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006491 }
6492#endif
6493
6494 repeat:
6495#ifdef SYSV
6496 do {
6497 execve(cmd, argv, envp);
6498 } while (errno == EINTR);
6499#else
6500 execve(cmd, argv, envp);
6501#endif
6502 if (repeated++) {
6503 free(argv);
6504 } else if (errno == ENOEXEC) {
6505 char **ap;
6506 char **new;
6507
6508 for (ap = argv; *ap; ap++)
6509 ;
6510 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6511 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006512 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006513 ap += 2;
6514 argv++;
6515 while ((*ap++ = *argv++))
6516 ;
6517 argv = new;
6518 goto repeat;
6519 }
6520}
6521
6522/*
6523 * Exec a program. Never returns. If you change this routine, you may
6524 * have to change the find_command routine as well.
6525 */
6526#define environment() listvars(VEXPORT, VUNSET, 0)
6527static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6528static void
6529shellexec(char **argv, const char *path, int idx)
6530{
6531 char *cmdname;
6532 int e;
6533 char **envp;
6534 int exerrno;
6535
6536 clearredir(1);
6537 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006538 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006539#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006540 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006541#endif
6542 ) {
6543 tryexec(argv[0], argv, envp);
6544 e = errno;
6545 } else {
6546 e = ENOENT;
6547 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6548 if (--idx < 0 && pathopt == NULL) {
6549 tryexec(cmdname, argv, envp);
6550 if (errno != ENOENT && errno != ENOTDIR)
6551 e = errno;
6552 }
6553 stunalloc(cmdname);
6554 }
6555 }
6556
6557 /* Map to POSIX errors */
6558 switch (e) {
6559 case EACCES:
6560 exerrno = 126;
6561 break;
6562 case ENOENT:
6563 exerrno = 127;
6564 break;
6565 default:
6566 exerrno = 2;
6567 break;
6568 }
6569 exitstatus = exerrno;
6570 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6571 argv[0], e, suppressint ));
6572 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6573 /* NOTREACHED */
6574}
6575
6576static void
6577printentry(struct tblentry *cmdp)
6578{
6579 int idx;
6580 const char *path;
6581 char *name;
6582
6583 idx = cmdp->param.index;
6584 path = pathval();
6585 do {
6586 name = padvance(&path, cmdp->cmdname);
6587 stunalloc(name);
6588 } while (--idx >= 0);
6589 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6590}
6591
6592/*
6593 * Clear out command entries. The argument specifies the first entry in
6594 * PATH which has changed.
6595 */
6596static void
6597clearcmdentry(int firstchange)
6598{
6599 struct tblentry **tblp;
6600 struct tblentry **pp;
6601 struct tblentry *cmdp;
6602
6603 INT_OFF;
6604 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6605 pp = tblp;
6606 while ((cmdp = *pp) != NULL) {
6607 if ((cmdp->cmdtype == CMDNORMAL &&
6608 cmdp->param.index >= firstchange)
6609 || (cmdp->cmdtype == CMDBUILTIN &&
6610 builtinloc >= firstchange)
6611 ) {
6612 *pp = cmdp->next;
6613 free(cmdp);
6614 } else {
6615 pp = &cmdp->next;
6616 }
6617 }
6618 }
6619 INT_ON;
6620}
6621
6622/*
6623 * Locate a command in the command hash table. If "add" is nonzero,
6624 * add the command to the table if it is not already present. The
6625 * variable "lastcmdentry" is set to point to the address of the link
6626 * pointing to the entry, so that delete_cmd_entry can delete the
6627 * entry.
6628 *
6629 * Interrupts must be off if called with add != 0.
6630 */
6631static struct tblentry **lastcmdentry;
6632
6633static struct tblentry *
6634cmdlookup(const char *name, int add)
6635{
6636 unsigned int hashval;
6637 const char *p;
6638 struct tblentry *cmdp;
6639 struct tblentry **pp;
6640
6641 p = name;
6642 hashval = (unsigned char)*p << 4;
6643 while (*p)
6644 hashval += (unsigned char)*p++;
6645 hashval &= 0x7FFF;
6646 pp = &cmdtable[hashval % CMDTABLESIZE];
6647 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6648 if (strcmp(cmdp->cmdname, name) == 0)
6649 break;
6650 pp = &cmdp->next;
6651 }
6652 if (add && cmdp == NULL) {
6653 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6654 + strlen(name) + 1);
6655 cmdp->next = NULL;
6656 cmdp->cmdtype = CMDUNKNOWN;
6657 strcpy(cmdp->cmdname, name);
6658 }
6659 lastcmdentry = pp;
6660 return cmdp;
6661}
6662
6663/*
6664 * Delete the command entry returned on the last lookup.
6665 */
6666static void
6667delete_cmd_entry(void)
6668{
6669 struct tblentry *cmdp;
6670
6671 INT_OFF;
6672 cmdp = *lastcmdentry;
6673 *lastcmdentry = cmdp->next;
6674 if (cmdp->cmdtype == CMDFUNCTION)
6675 freefunc(cmdp->param.func);
6676 free(cmdp);
6677 INT_ON;
6678}
6679
6680/*
6681 * Add a new command entry, replacing any existing command entry for
6682 * the same name - except special builtins.
6683 */
6684static void
6685addcmdentry(char *name, struct cmdentry *entry)
6686{
6687 struct tblentry *cmdp;
6688
6689 cmdp = cmdlookup(name, 1);
6690 if (cmdp->cmdtype == CMDFUNCTION) {
6691 freefunc(cmdp->param.func);
6692 }
6693 cmdp->cmdtype = entry->cmdtype;
6694 cmdp->param = entry->u;
6695 cmdp->rehash = 0;
6696}
6697
6698static int
6699hashcmd(int argc, char **argv)
6700{
6701 struct tblentry **pp;
6702 struct tblentry *cmdp;
6703 int c;
6704 struct cmdentry entry;
6705 char *name;
6706
6707 while ((c = nextopt("r")) != '\0') {
6708 clearcmdentry(0);
6709 return 0;
6710 }
6711 if (*argptr == NULL) {
6712 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6713 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6714 if (cmdp->cmdtype == CMDNORMAL)
6715 printentry(cmdp);
6716 }
6717 }
6718 return 0;
6719 }
6720 c = 0;
6721 while ((name = *argptr) != NULL) {
6722 cmdp = cmdlookup(name, 0);
6723 if (cmdp != NULL
6724 && (cmdp->cmdtype == CMDNORMAL
6725 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6726 delete_cmd_entry();
6727 find_command(name, &entry, DO_ERR, pathval());
6728 if (entry.cmdtype == CMDUNKNOWN)
6729 c = 1;
6730 argptr++;
6731 }
6732 return c;
6733}
6734
6735/*
6736 * Called when a cd is done. Marks all commands so the next time they
6737 * are executed they will be rehashed.
6738 */
6739static void
6740hashcd(void)
6741{
6742 struct tblentry **pp;
6743 struct tblentry *cmdp;
6744
6745 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6746 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6747 if (cmdp->cmdtype == CMDNORMAL || (
6748 cmdp->cmdtype == CMDBUILTIN &&
6749 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6750 builtinloc > 0
6751 ))
6752 cmdp->rehash = 1;
6753 }
6754 }
6755}
6756
6757/*
6758 * Fix command hash table when PATH changed.
6759 * Called before PATH is changed. The argument is the new value of PATH;
6760 * pathval() still returns the old value at this point.
6761 * Called with interrupts off.
6762 */
6763static void
6764changepath(const char *newval)
6765{
6766 const char *old, *new;
6767 int idx;
6768 int firstchange;
6769 int idx_bltin;
6770
6771 old = pathval();
6772 new = newval;
6773 firstchange = 9999; /* assume no change */
6774 idx = 0;
6775 idx_bltin = -1;
6776 for (;;) {
6777 if (*old != *new) {
6778 firstchange = idx;
6779 if ((*old == '\0' && *new == ':')
6780 || (*old == ':' && *new == '\0'))
6781 firstchange++;
6782 old = new; /* ignore subsequent differences */
6783 }
6784 if (*new == '\0')
6785 break;
6786 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6787 idx_bltin = idx;
6788 if (*new == ':') {
6789 idx++;
6790 }
6791 new++, old++;
6792 }
6793 if (builtinloc < 0 && idx_bltin >= 0)
6794 builtinloc = idx_bltin; /* zap builtins */
6795 if (builtinloc >= 0 && idx_bltin < 0)
6796 firstchange = 0;
6797 clearcmdentry(firstchange);
6798 builtinloc = idx_bltin;
6799}
6800
6801#define TEOF 0
6802#define TNL 1
6803#define TREDIR 2
6804#define TWORD 3
6805#define TSEMI 4
6806#define TBACKGND 5
6807#define TAND 6
6808#define TOR 7
6809#define TPIPE 8
6810#define TLP 9
6811#define TRP 10
6812#define TENDCASE 11
6813#define TENDBQUOTE 12
6814#define TNOT 13
6815#define TCASE 14
6816#define TDO 15
6817#define TDONE 16
6818#define TELIF 17
6819#define TELSE 18
6820#define TESAC 19
6821#define TFI 20
6822#define TFOR 21
6823#define TIF 22
6824#define TIN 23
6825#define TTHEN 24
6826#define TUNTIL 25
6827#define TWHILE 26
6828#define TBEGIN 27
6829#define TEND 28
6830
6831/* first char is indicating which tokens mark the end of a list */
6832static const char *const tokname_array[] = {
6833 "\1end of file",
6834 "\0newline",
6835 "\0redirection",
6836 "\0word",
6837 "\0;",
6838 "\0&",
6839 "\0&&",
6840 "\0||",
6841 "\0|",
6842 "\0(",
6843 "\1)",
6844 "\1;;",
6845 "\1`",
6846#define KWDOFFSET 13
6847 /* the following are keywords */
6848 "\0!",
6849 "\0case",
6850 "\1do",
6851 "\1done",
6852 "\1elif",
6853 "\1else",
6854 "\1esac",
6855 "\1fi",
6856 "\0for",
6857 "\0if",
6858 "\0in",
6859 "\1then",
6860 "\0until",
6861 "\0while",
6862 "\0{",
6863 "\1}",
6864};
6865
6866static const char *
6867tokname(int tok)
6868{
6869 static char buf[16];
6870
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006871//try this:
6872//if (tok < TSEMI) return tokname_array[tok] + 1;
6873//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6874//return buf;
6875
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006876 if (tok >= TSEMI)
6877 buf[0] = '"';
6878 sprintf(buf + (tok >= TSEMI), "%s%c",
6879 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6880 return buf;
6881}
6882
6883/* Wrapper around strcmp for qsort/bsearch/... */
6884static int
6885pstrcmp(const void *a, const void *b)
6886{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006887 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006888}
6889
6890static const char *const *
6891findkwd(const char *s)
6892{
6893 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006894 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6895 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006896}
6897
6898/*
6899 * Locate and print what a word is...
6900 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901static int
6902describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006903{
6904 struct cmdentry entry;
6905 struct tblentry *cmdp;
6906#if ENABLE_ASH_ALIAS
6907 const struct alias *ap;
6908#endif
6909 const char *path = pathval();
6910
6911 if (describe_command_verbose) {
6912 out1str(command);
6913 }
6914
6915 /* First look at the keywords */
6916 if (findkwd(command)) {
6917 out1str(describe_command_verbose ? " is a shell keyword" : command);
6918 goto out;
6919 }
6920
6921#if ENABLE_ASH_ALIAS
6922 /* Then look at the aliases */
6923 ap = lookupalias(command, 0);
6924 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006925 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006926 out1str("alias ");
6927 printalias(ap);
6928 return 0;
6929 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006930 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006931 goto out;
6932 }
6933#endif
6934 /* Then check if it is a tracked alias */
6935 cmdp = cmdlookup(command, 0);
6936 if (cmdp != NULL) {
6937 entry.cmdtype = cmdp->cmdtype;
6938 entry.u = cmdp->param;
6939 } else {
6940 /* Finally use brute force */
6941 find_command(command, &entry, DO_ABS, path);
6942 }
6943
6944 switch (entry.cmdtype) {
6945 case CMDNORMAL: {
6946 int j = entry.u.index;
6947 char *p;
6948 if (j == -1) {
6949 p = command;
6950 } else {
6951 do {
6952 p = padvance(&path, command);
6953 stunalloc(p);
6954 } while (--j >= 0);
6955 }
6956 if (describe_command_verbose) {
6957 out1fmt(" is%s %s",
6958 (cmdp ? " a tracked alias for" : nullstr), p
6959 );
6960 } else {
6961 out1str(p);
6962 }
6963 break;
6964 }
6965
6966 case CMDFUNCTION:
6967 if (describe_command_verbose) {
6968 out1str(" is a shell function");
6969 } else {
6970 out1str(command);
6971 }
6972 break;
6973
6974 case CMDBUILTIN:
6975 if (describe_command_verbose) {
6976 out1fmt(" is a %sshell builtin",
6977 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6978 "special " : nullstr
6979 );
6980 } else {
6981 out1str(command);
6982 }
6983 break;
6984
6985 default:
6986 if (describe_command_verbose) {
6987 out1str(": not found\n");
6988 }
6989 return 127;
6990 }
6991 out:
6992 outstr("\n", stdout);
6993 return 0;
6994}
6995
6996static int
6997typecmd(int argc, char **argv)
6998{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006999 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007000 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007001 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007002
Denis Vlasenko46846e22007-05-20 13:08:31 +00007003 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007004 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007005 i++;
7006 verbose = 0;
7007 }
7008 while (i < argc) {
7009 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007010 }
7011 return err;
7012}
7013
7014#if ENABLE_ASH_CMDCMD
7015static int
7016commandcmd(int argc, char **argv)
7017{
7018 int c;
7019 enum {
7020 VERIFY_BRIEF = 1,
7021 VERIFY_VERBOSE = 2,
7022 } verify = 0;
7023
7024 while ((c = nextopt("pvV")) != '\0')
7025 if (c == 'V')
7026 verify |= VERIFY_VERBOSE;
7027 else if (c == 'v')
7028 verify |= VERIFY_BRIEF;
7029#if DEBUG
7030 else if (c != 'p')
7031 abort();
7032#endif
7033 if (verify)
7034 return describe_command(*argptr, verify - VERIFY_BRIEF);
7035
7036 return 0;
7037}
7038#endif
7039
7040
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007041/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007042
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043static int funcblocksize; /* size of structures in function */
7044static int funcstringsize; /* size of strings in node */
7045static void *funcblock; /* block to allocate function from */
7046static char *funcstring; /* block to allocate strings from */
7047
Eric Andersencb57d552001-06-28 07:25:16 +00007048/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007049#define EV_EXIT 01 /* exit after evaluating tree */
7050#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7051#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007052
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007053static const short nodesize[26] = {
7054 SHELL_ALIGN(sizeof(struct ncmd)),
7055 SHELL_ALIGN(sizeof(struct npipe)),
7056 SHELL_ALIGN(sizeof(struct nredir)),
7057 SHELL_ALIGN(sizeof(struct nredir)),
7058 SHELL_ALIGN(sizeof(struct nredir)),
7059 SHELL_ALIGN(sizeof(struct nbinary)),
7060 SHELL_ALIGN(sizeof(struct nbinary)),
7061 SHELL_ALIGN(sizeof(struct nbinary)),
7062 SHELL_ALIGN(sizeof(struct nif)),
7063 SHELL_ALIGN(sizeof(struct nbinary)),
7064 SHELL_ALIGN(sizeof(struct nbinary)),
7065 SHELL_ALIGN(sizeof(struct nfor)),
7066 SHELL_ALIGN(sizeof(struct ncase)),
7067 SHELL_ALIGN(sizeof(struct nclist)),
7068 SHELL_ALIGN(sizeof(struct narg)),
7069 SHELL_ALIGN(sizeof(struct narg)),
7070 SHELL_ALIGN(sizeof(struct nfile)),
7071 SHELL_ALIGN(sizeof(struct nfile)),
7072 SHELL_ALIGN(sizeof(struct nfile)),
7073 SHELL_ALIGN(sizeof(struct nfile)),
7074 SHELL_ALIGN(sizeof(struct nfile)),
7075 SHELL_ALIGN(sizeof(struct ndup)),
7076 SHELL_ALIGN(sizeof(struct ndup)),
7077 SHELL_ALIGN(sizeof(struct nhere)),
7078 SHELL_ALIGN(sizeof(struct nhere)),
7079 SHELL_ALIGN(sizeof(struct nnot)),
7080};
7081
7082static void calcsize(union node *n);
7083
7084static void
7085sizenodelist(struct nodelist *lp)
7086{
7087 while (lp) {
7088 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7089 calcsize(lp->n);
7090 lp = lp->next;
7091 }
7092}
7093
7094static void
7095calcsize(union node *n)
7096{
7097 if (n == NULL)
7098 return;
7099 funcblocksize += nodesize[n->type];
7100 switch (n->type) {
7101 case NCMD:
7102 calcsize(n->ncmd.redirect);
7103 calcsize(n->ncmd.args);
7104 calcsize(n->ncmd.assign);
7105 break;
7106 case NPIPE:
7107 sizenodelist(n->npipe.cmdlist);
7108 break;
7109 case NREDIR:
7110 case NBACKGND:
7111 case NSUBSHELL:
7112 calcsize(n->nredir.redirect);
7113 calcsize(n->nredir.n);
7114 break;
7115 case NAND:
7116 case NOR:
7117 case NSEMI:
7118 case NWHILE:
7119 case NUNTIL:
7120 calcsize(n->nbinary.ch2);
7121 calcsize(n->nbinary.ch1);
7122 break;
7123 case NIF:
7124 calcsize(n->nif.elsepart);
7125 calcsize(n->nif.ifpart);
7126 calcsize(n->nif.test);
7127 break;
7128 case NFOR:
7129 funcstringsize += strlen(n->nfor.var) + 1;
7130 calcsize(n->nfor.body);
7131 calcsize(n->nfor.args);
7132 break;
7133 case NCASE:
7134 calcsize(n->ncase.cases);
7135 calcsize(n->ncase.expr);
7136 break;
7137 case NCLIST:
7138 calcsize(n->nclist.body);
7139 calcsize(n->nclist.pattern);
7140 calcsize(n->nclist.next);
7141 break;
7142 case NDEFUN:
7143 case NARG:
7144 sizenodelist(n->narg.backquote);
7145 funcstringsize += strlen(n->narg.text) + 1;
7146 calcsize(n->narg.next);
7147 break;
7148 case NTO:
7149 case NCLOBBER:
7150 case NFROM:
7151 case NFROMTO:
7152 case NAPPEND:
7153 calcsize(n->nfile.fname);
7154 calcsize(n->nfile.next);
7155 break;
7156 case NTOFD:
7157 case NFROMFD:
7158 calcsize(n->ndup.vname);
7159 calcsize(n->ndup.next);
7160 break;
7161 case NHERE:
7162 case NXHERE:
7163 calcsize(n->nhere.doc);
7164 calcsize(n->nhere.next);
7165 break;
7166 case NNOT:
7167 calcsize(n->nnot.com);
7168 break;
7169 };
7170}
7171
7172static char *
7173nodeckstrdup(char *s)
7174{
7175 char *rtn = funcstring;
7176
7177 strcpy(funcstring, s);
7178 funcstring += strlen(s) + 1;
7179 return rtn;
7180}
7181
7182static union node *copynode(union node *);
7183
7184static struct nodelist *
7185copynodelist(struct nodelist *lp)
7186{
7187 struct nodelist *start;
7188 struct nodelist **lpp;
7189
7190 lpp = &start;
7191 while (lp) {
7192 *lpp = funcblock;
7193 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7194 (*lpp)->n = copynode(lp->n);
7195 lp = lp->next;
7196 lpp = &(*lpp)->next;
7197 }
7198 *lpp = NULL;
7199 return start;
7200}
7201
7202static union node *
7203copynode(union node *n)
7204{
7205 union node *new;
7206
7207 if (n == NULL)
7208 return NULL;
7209 new = funcblock;
7210 funcblock = (char *) funcblock + nodesize[n->type];
7211
7212 switch (n->type) {
7213 case NCMD:
7214 new->ncmd.redirect = copynode(n->ncmd.redirect);
7215 new->ncmd.args = copynode(n->ncmd.args);
7216 new->ncmd.assign = copynode(n->ncmd.assign);
7217 break;
7218 case NPIPE:
7219 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7220 new->npipe.backgnd = n->npipe.backgnd;
7221 break;
7222 case NREDIR:
7223 case NBACKGND:
7224 case NSUBSHELL:
7225 new->nredir.redirect = copynode(n->nredir.redirect);
7226 new->nredir.n = copynode(n->nredir.n);
7227 break;
7228 case NAND:
7229 case NOR:
7230 case NSEMI:
7231 case NWHILE:
7232 case NUNTIL:
7233 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7234 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7235 break;
7236 case NIF:
7237 new->nif.elsepart = copynode(n->nif.elsepart);
7238 new->nif.ifpart = copynode(n->nif.ifpart);
7239 new->nif.test = copynode(n->nif.test);
7240 break;
7241 case NFOR:
7242 new->nfor.var = nodeckstrdup(n->nfor.var);
7243 new->nfor.body = copynode(n->nfor.body);
7244 new->nfor.args = copynode(n->nfor.args);
7245 break;
7246 case NCASE:
7247 new->ncase.cases = copynode(n->ncase.cases);
7248 new->ncase.expr = copynode(n->ncase.expr);
7249 break;
7250 case NCLIST:
7251 new->nclist.body = copynode(n->nclist.body);
7252 new->nclist.pattern = copynode(n->nclist.pattern);
7253 new->nclist.next = copynode(n->nclist.next);
7254 break;
7255 case NDEFUN:
7256 case NARG:
7257 new->narg.backquote = copynodelist(n->narg.backquote);
7258 new->narg.text = nodeckstrdup(n->narg.text);
7259 new->narg.next = copynode(n->narg.next);
7260 break;
7261 case NTO:
7262 case NCLOBBER:
7263 case NFROM:
7264 case NFROMTO:
7265 case NAPPEND:
7266 new->nfile.fname = copynode(n->nfile.fname);
7267 new->nfile.fd = n->nfile.fd;
7268 new->nfile.next = copynode(n->nfile.next);
7269 break;
7270 case NTOFD:
7271 case NFROMFD:
7272 new->ndup.vname = copynode(n->ndup.vname);
7273 new->ndup.dupfd = n->ndup.dupfd;
7274 new->ndup.fd = n->ndup.fd;
7275 new->ndup.next = copynode(n->ndup.next);
7276 break;
7277 case NHERE:
7278 case NXHERE:
7279 new->nhere.doc = copynode(n->nhere.doc);
7280 new->nhere.fd = n->nhere.fd;
7281 new->nhere.next = copynode(n->nhere.next);
7282 break;
7283 case NNOT:
7284 new->nnot.com = copynode(n->nnot.com);
7285 break;
7286 };
7287 new->type = n->type;
7288 return new;
7289}
7290
7291/*
7292 * Make a copy of a parse tree.
7293 */
7294static struct funcnode *
7295copyfunc(union node *n)
7296{
7297 struct funcnode *f;
7298 size_t blocksize;
7299
7300 funcblocksize = offsetof(struct funcnode, n);
7301 funcstringsize = 0;
7302 calcsize(n);
7303 blocksize = funcblocksize;
7304 f = ckmalloc(blocksize + funcstringsize);
7305 funcblock = (char *) f + offsetof(struct funcnode, n);
7306 funcstring = (char *) f + blocksize;
7307 copynode(n);
7308 f->count = 0;
7309 return f;
7310}
7311
7312/*
7313 * Define a shell function.
7314 */
7315static void
7316defun(char *name, union node *func)
7317{
7318 struct cmdentry entry;
7319
7320 INT_OFF;
7321 entry.cmdtype = CMDFUNCTION;
7322 entry.u.func = copyfunc(func);
7323 addcmdentry(name, &entry);
7324 INT_ON;
7325}
7326
7327static int evalskip; /* set if we are skipping commands */
7328/* reasons for skipping commands (see comment on breakcmd routine) */
7329#define SKIPBREAK (1 << 0)
7330#define SKIPCONT (1 << 1)
7331#define SKIPFUNC (1 << 2)
7332#define SKIPFILE (1 << 3)
7333#define SKIPEVAL (1 << 4)
7334static int skipcount; /* number of levels to skip */
7335static int funcnest; /* depth of function calls */
7336
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007337/* forward decl way out to parsing code - dotrap needs it */
7338static int evalstring(char *s, int mask);
7339
7340/*
7341 * Called to execute a trap. Perhaps we should avoid entering new trap
7342 * handlers while we are executing a trap handler.
7343 */
7344static int
7345dotrap(void)
7346{
7347 char *p;
7348 char *q;
7349 int i;
7350 int savestatus;
7351 int skip = 0;
7352
7353 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007354 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007355 xbarrier();
7356
7357 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7358 if (!*q)
7359 continue;
7360 *q = '\0';
7361
7362 p = trap[i + 1];
7363 if (!p)
7364 continue;
7365 skip = evalstring(p, SKIPEVAL);
7366 exitstatus = savestatus;
7367 if (skip)
7368 break;
7369 }
7370
7371 return skip;
7372}
7373
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007374/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007375static void evalloop(union node *, int);
7376static void evalfor(union node *, int);
7377static void evalcase(union node *, int);
7378static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007379static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007380static void evalpipe(union node *, int);
7381static void evalcommand(union node *, int);
7382static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007383static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007384
Eric Andersen62483552001-07-10 06:09:16 +00007385/*
Eric Andersenc470f442003-07-28 09:56:35 +00007386 * Evaluate a parse tree. The value is left in the global variable
7387 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007388 */
Eric Andersenc470f442003-07-28 09:56:35 +00007389static void
7390evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007391{
Eric Andersenc470f442003-07-28 09:56:35 +00007392 int checkexit = 0;
7393 void (*evalfn)(union node *, int);
7394 unsigned isor;
7395 int status;
7396 if (n == NULL) {
7397 TRACE(("evaltree(NULL) called\n"));
7398 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007399 }
Eric Andersenc470f442003-07-28 09:56:35 +00007400 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007401 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007402 switch (n->type) {
7403 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007404#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007405 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007406 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007407 break;
7408#endif
7409 case NNOT:
7410 evaltree(n->nnot.com, EV_TESTED);
7411 status = !exitstatus;
7412 goto setstatus;
7413 case NREDIR:
7414 expredir(n->nredir.redirect);
7415 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7416 if (!status) {
7417 evaltree(n->nredir.n, flags & EV_TESTED);
7418 status = exitstatus;
7419 }
7420 popredir(0);
7421 goto setstatus;
7422 case NCMD:
7423 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007424 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007425 if (eflag && !(flags & EV_TESTED))
7426 checkexit = ~0;
7427 goto calleval;
7428 case NFOR:
7429 evalfn = evalfor;
7430 goto calleval;
7431 case NWHILE:
7432 case NUNTIL:
7433 evalfn = evalloop;
7434 goto calleval;
7435 case NSUBSHELL:
7436 case NBACKGND:
7437 evalfn = evalsubshell;
7438 goto calleval;
7439 case NPIPE:
7440 evalfn = evalpipe;
7441 goto checkexit;
7442 case NCASE:
7443 evalfn = evalcase;
7444 goto calleval;
7445 case NAND:
7446 case NOR:
7447 case NSEMI:
7448#if NAND + 1 != NOR
7449#error NAND + 1 != NOR
7450#endif
7451#if NOR + 1 != NSEMI
7452#error NOR + 1 != NSEMI
7453#endif
7454 isor = n->type - NAND;
7455 evaltree(
7456 n->nbinary.ch1,
7457 (flags | ((isor >> 1) - 1)) & EV_TESTED
7458 );
7459 if (!exitstatus == isor)
7460 break;
7461 if (!evalskip) {
7462 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007463 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007464 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007465 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007466 evalfn(n, flags);
7467 break;
7468 }
7469 break;
7470 case NIF:
7471 evaltree(n->nif.test, EV_TESTED);
7472 if (evalskip)
7473 break;
7474 if (exitstatus == 0) {
7475 n = n->nif.ifpart;
7476 goto evaln;
7477 } else if (n->nif.elsepart) {
7478 n = n->nif.elsepart;
7479 goto evaln;
7480 }
7481 goto success;
7482 case NDEFUN:
7483 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007484 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007485 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007486 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007487 exitstatus = status;
7488 break;
7489 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007490 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007491 if ((checkexit & exitstatus))
7492 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007493 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007494 goto exexit;
7495
7496 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007497 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007498 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007499 }
Eric Andersen62483552001-07-10 06:09:16 +00007500}
7501
Eric Andersenc470f442003-07-28 09:56:35 +00007502#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7503static
7504#endif
7505void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7506
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007507static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007508
7509static void
7510evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007511{
7512 int status;
7513
7514 loopnest++;
7515 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007516 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007517 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007518 int i;
7519
Eric Andersencb57d552001-06-28 07:25:16 +00007520 evaltree(n->nbinary.ch1, EV_TESTED);
7521 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007522 skipping:
7523 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007524 evalskip = 0;
7525 continue;
7526 }
7527 if (evalskip == SKIPBREAK && --skipcount <= 0)
7528 evalskip = 0;
7529 break;
7530 }
Eric Andersenc470f442003-07-28 09:56:35 +00007531 i = exitstatus;
7532 if (n->type != NWHILE)
7533 i = !i;
7534 if (i != 0)
7535 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007536 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007537 status = exitstatus;
7538 if (evalskip)
7539 goto skipping;
7540 }
7541 loopnest--;
7542 exitstatus = status;
7543}
7544
Eric Andersenc470f442003-07-28 09:56:35 +00007545static void
7546evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007547{
7548 struct arglist arglist;
7549 union node *argp;
7550 struct strlist *sp;
7551 struct stackmark smark;
7552
7553 setstackmark(&smark);
7554 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007555 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007556 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007557 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007558 if (evalskip)
7559 goto out;
7560 }
7561 *arglist.lastp = NULL;
7562
7563 exitstatus = 0;
7564 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007565 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007566 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007567 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007568 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007569 if (evalskip) {
7570 if (evalskip == SKIPCONT && --skipcount <= 0) {
7571 evalskip = 0;
7572 continue;
7573 }
7574 if (evalskip == SKIPBREAK && --skipcount <= 0)
7575 evalskip = 0;
7576 break;
7577 }
7578 }
7579 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007580 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007581 popstackmark(&smark);
7582}
7583
Eric Andersenc470f442003-07-28 09:56:35 +00007584static void
7585evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007586{
7587 union node *cp;
7588 union node *patp;
7589 struct arglist arglist;
7590 struct stackmark smark;
7591
7592 setstackmark(&smark);
7593 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007594 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007595 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007596 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7597 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007598 if (casematch(patp, arglist.list->text)) {
7599 if (evalskip == 0) {
7600 evaltree(cp->nclist.body, flags);
7601 }
7602 goto out;
7603 }
7604 }
7605 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007606 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007607 popstackmark(&smark);
7608}
7609
Eric Andersenc470f442003-07-28 09:56:35 +00007610/*
7611 * Kick off a subshell to evaluate a tree.
7612 */
Eric Andersenc470f442003-07-28 09:56:35 +00007613static void
7614evalsubshell(union node *n, int flags)
7615{
7616 struct job *jp;
7617 int backgnd = (n->type == NBACKGND);
7618 int status;
7619
7620 expredir(n->nredir.redirect);
7621 if (!backgnd && flags & EV_EXIT && !trap[0])
7622 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007623 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007624 jp = makejob(n, 1);
7625 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007626 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007627 flags |= EV_EXIT;
7628 if (backgnd)
7629 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007630 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007631 redirect(n->nredir.redirect, 0);
7632 evaltreenr(n->nredir.n, flags);
7633 /* never returns */
7634 }
7635 status = 0;
7636 if (! backgnd)
7637 status = waitforjob(jp);
7638 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007639 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007640}
7641
Eric Andersenc470f442003-07-28 09:56:35 +00007642/*
7643 * Compute the names of the files in a redirection list.
7644 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007645static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007646static void
7647expredir(union node *n)
7648{
7649 union node *redir;
7650
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007651 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007652 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007653
7654 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007655 fn.lastp = &fn.list;
7656 switch (redir->type) {
7657 case NFROMTO:
7658 case NFROM:
7659 case NTO:
7660 case NCLOBBER:
7661 case NAPPEND:
7662 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7663 redir->nfile.expfname = fn.list->text;
7664 break;
7665 case NFROMFD:
7666 case NTOFD:
7667 if (redir->ndup.vname) {
7668 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007669 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007670 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007671 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007672 }
7673 break;
7674 }
7675 }
7676}
7677
Eric Andersencb57d552001-06-28 07:25:16 +00007678/*
Eric Andersencb57d552001-06-28 07:25:16 +00007679 * Evaluate a pipeline. All the processes in the pipeline are children
7680 * of the process creating the pipeline. (This differs from some versions
7681 * of the shell, which make the last process in a pipeline the parent
7682 * of all the rest.)
7683 */
Eric Andersenc470f442003-07-28 09:56:35 +00007684static void
7685evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007686{
7687 struct job *jp;
7688 struct nodelist *lp;
7689 int pipelen;
7690 int prevfd;
7691 int pip[2];
7692
Eric Andersenc470f442003-07-28 09:56:35 +00007693 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007694 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007695 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007696 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007697 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007698 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007699 jp = makejob(n, pipelen);
7700 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007701 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007702 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007703 pip[1] = -1;
7704 if (lp->next) {
7705 if (pipe(pip) < 0) {
7706 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007707 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007708 }
7709 }
7710 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007711 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007712 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007713 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007714 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007715 if (prevfd > 0) {
7716 dup2(prevfd, 0);
7717 close(prevfd);
7718 }
7719 if (pip[1] > 1) {
7720 dup2(pip[1], 1);
7721 close(pip[1]);
7722 }
Eric Andersenc470f442003-07-28 09:56:35 +00007723 evaltreenr(lp->n, flags);
7724 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007725 }
7726 if (prevfd >= 0)
7727 close(prevfd);
7728 prevfd = pip[0];
7729 close(pip[1]);
7730 }
Eric Andersencb57d552001-06-28 07:25:16 +00007731 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007732 exitstatus = waitforjob(jp);
7733 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007734 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007735 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007736}
7737
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007738/*
7739 * Controls whether the shell is interactive or not.
7740 */
7741static void
7742setinteractive(int on)
7743{
7744 static int is_interactive;
7745
7746 if (++on == is_interactive)
7747 return;
7748 is_interactive = on;
7749 setsignal(SIGINT);
7750 setsignal(SIGQUIT);
7751 setsignal(SIGTERM);
7752#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7753 if (is_interactive > 1) {
7754 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007755 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007756
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007757 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007758 out1fmt(
7759 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007760 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007761 "Enter 'help' for a list of built-in commands."
7762 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007763 bb_banner);
7764 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007765 }
7766 }
7767#endif
7768}
7769
7770#if ENABLE_FEATURE_EDITING_VI
7771#define setvimode(on) do { \
7772 if (on) line_input_state->flags |= VI_MODE; \
7773 else line_input_state->flags &= ~VI_MODE; \
7774} while (0)
7775#else
7776#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7777#endif
7778
7779static void
7780optschanged(void)
7781{
7782#if DEBUG
7783 opentrace();
7784#endif
7785 setinteractive(iflag);
7786 setjobctl(mflag);
7787 setvimode(viflag);
7788}
7789
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007790static struct localvar *localvars;
7791
7792/*
7793 * Called after a function returns.
7794 * Interrupts must be off.
7795 */
7796static void
7797poplocalvars(void)
7798{
7799 struct localvar *lvp;
7800 struct var *vp;
7801
7802 while ((lvp = localvars) != NULL) {
7803 localvars = lvp->next;
7804 vp = lvp->vp;
7805 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7806 if (vp == NULL) { /* $- saved */
7807 memcpy(optlist, lvp->text, sizeof(optlist));
7808 free((char*)lvp->text);
7809 optschanged();
7810 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7811 unsetvar(vp->text);
7812 } else {
7813 if (vp->func)
7814 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7815 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7816 free((char*)vp->text);
7817 vp->flags = lvp->flags;
7818 vp->text = lvp->text;
7819 }
7820 free(lvp);
7821 }
7822}
7823
7824static int
7825evalfun(struct funcnode *func, int argc, char **argv, int flags)
7826{
7827 volatile struct shparam saveparam;
7828 struct localvar *volatile savelocalvars;
7829 struct jmploc *volatile savehandler;
7830 struct jmploc jmploc;
7831 int e;
7832
7833 saveparam = shellparam;
7834 savelocalvars = localvars;
7835 e = setjmp(jmploc.loc);
7836 if (e) {
7837 goto funcdone;
7838 }
7839 INT_OFF;
7840 savehandler = exception_handler;
7841 exception_handler = &jmploc;
7842 localvars = NULL;
7843 shellparam.malloc = 0;
7844 func->count++;
7845 funcnest++;
7846 INT_ON;
7847 shellparam.nparam = argc - 1;
7848 shellparam.p = argv + 1;
7849#if ENABLE_ASH_GETOPTS
7850 shellparam.optind = 1;
7851 shellparam.optoff = -1;
7852#endif
7853 evaltree(&func->n, flags & EV_TESTED);
7854funcdone:
7855 INT_OFF;
7856 funcnest--;
7857 freefunc(func);
7858 poplocalvars();
7859 localvars = savelocalvars;
7860 freeparam(&shellparam);
7861 shellparam = saveparam;
7862 exception_handler = savehandler;
7863 INT_ON;
7864 evalskip &= ~SKIPFUNC;
7865 return e;
7866}
7867
Denis Vlasenko131ae172007-02-18 13:00:19 +00007868#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007869static char **
7870parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007871{
7872 char *cp, c;
7873
7874 for (;;) {
7875 cp = *++argv;
7876 if (!cp)
7877 return 0;
7878 if (*cp++ != '-')
7879 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007880 c = *cp++;
7881 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007882 break;
7883 if (c == '-' && !*cp) {
7884 argv++;
7885 break;
7886 }
7887 do {
7888 switch (c) {
7889 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007890 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007891 break;
7892 default:
7893 /* run 'typecmd' for other options */
7894 return 0;
7895 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007896 c = *cp++;
7897 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007898 }
7899 return argv;
7900}
7901#endif
7902
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007903/*
7904 * Make a variable a local variable. When a variable is made local, it's
7905 * value and flags are saved in a localvar structure. The saved values
7906 * will be restored when the shell function returns. We handle the name
7907 * "-" as a special case.
7908 */
7909static void
7910mklocal(char *name)
7911{
7912 struct localvar *lvp;
7913 struct var **vpp;
7914 struct var *vp;
7915
7916 INT_OFF;
7917 lvp = ckmalloc(sizeof(struct localvar));
7918 if (LONE_DASH(name)) {
7919 char *p;
7920 p = ckmalloc(sizeof(optlist));
7921 lvp->text = memcpy(p, optlist, sizeof(optlist));
7922 vp = NULL;
7923 } else {
7924 char *eq;
7925
7926 vpp = hashvar(name);
7927 vp = *findvar(vpp, name);
7928 eq = strchr(name, '=');
7929 if (vp == NULL) {
7930 if (eq)
7931 setvareq(name, VSTRFIXED);
7932 else
7933 setvar(name, NULL, VSTRFIXED);
7934 vp = *vpp; /* the new variable */
7935 lvp->flags = VUNSET;
7936 } else {
7937 lvp->text = vp->text;
7938 lvp->flags = vp->flags;
7939 vp->flags |= VSTRFIXED|VTEXTFIXED;
7940 if (eq)
7941 setvareq(name, 0);
7942 }
7943 }
7944 lvp->vp = vp;
7945 lvp->next = localvars;
7946 localvars = lvp;
7947 INT_ON;
7948}
7949
7950/*
7951 * The "local" command.
7952 */
7953static int
7954localcmd(int argc, char **argv)
7955{
7956 char *name;
7957
7958 argv = argptr;
7959 while ((name = *argv++) != NULL) {
7960 mklocal(name);
7961 }
7962 return 0;
7963}
7964
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007965static int
7966falsecmd(int argc, char **argv)
7967{
7968 return 1;
7969}
7970
7971static int
7972truecmd(int argc, char **argv)
7973{
7974 return 0;
7975}
7976
7977static int
7978execcmd(int argc, char **argv)
7979{
7980 if (argc > 1) {
7981 iflag = 0; /* exit on error */
7982 mflag = 0;
7983 optschanged();
7984 shellexec(argv + 1, pathval(), 0);
7985 }
7986 return 0;
7987}
7988
7989/*
7990 * The return command.
7991 */
7992static int
7993returncmd(int argc, char **argv)
7994{
7995 /*
7996 * If called outside a function, do what ksh does;
7997 * skip the rest of the file.
7998 */
7999 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8000 return argv[1] ? number(argv[1]) : exitstatus;
8001}
8002
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008003/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008004static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008005static int dotcmd(int, char **);
8006static int evalcmd(int, char **);
8007#if ENABLE_ASH_BUILTIN_ECHO
8008static int echocmd(int, char **);
8009#endif
8010#if ENABLE_ASH_BUILTIN_TEST
8011static int testcmd(int, char **);
8012#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008013static int exitcmd(int, char **);
8014static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008015#if ENABLE_ASH_GETOPTS
8016static int getoptscmd(int, char **);
8017#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008018#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8019static int helpcmd(int argc, char **argv);
8020#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008021#if ENABLE_ASH_MATH_SUPPORT
8022static int letcmd(int, char **);
8023#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008024static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008025static int setcmd(int, char **);
8026static int shiftcmd(int, char **);
8027static int timescmd(int, char **);
8028static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008029static int umaskcmd(int, char **);
8030static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008031static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008032
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008033#define BUILTIN_NOSPEC "0"
8034#define BUILTIN_SPECIAL "1"
8035#define BUILTIN_REGULAR "2"
8036#define BUILTIN_SPEC_REG "3"
8037#define BUILTIN_ASSIGN "4"
8038#define BUILTIN_SPEC_ASSG "5"
8039#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008040#define BUILTIN_SPEC_REG_ASSG "7"
8041
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008042/* make sure to keep these in proper order since it is searched via bsearch() */
8043static const struct builtincmd builtintab[] = {
8044 { BUILTIN_SPEC_REG ".", dotcmd },
8045 { BUILTIN_SPEC_REG ":", truecmd },
8046#if ENABLE_ASH_BUILTIN_TEST
8047 { BUILTIN_REGULAR "[", testcmd },
8048 { BUILTIN_REGULAR "[[", testcmd },
8049#endif
8050#if ENABLE_ASH_ALIAS
8051 { BUILTIN_REG_ASSG "alias", aliascmd },
8052#endif
8053#if JOBS
8054 { BUILTIN_REGULAR "bg", fg_bgcmd },
8055#endif
8056 { BUILTIN_SPEC_REG "break", breakcmd },
8057 { BUILTIN_REGULAR "cd", cdcmd },
8058 { BUILTIN_NOSPEC "chdir", cdcmd },
8059#if ENABLE_ASH_CMDCMD
8060 { BUILTIN_REGULAR "command", commandcmd },
8061#endif
8062 { BUILTIN_SPEC_REG "continue", breakcmd },
8063#if ENABLE_ASH_BUILTIN_ECHO
8064 { BUILTIN_REGULAR "echo", echocmd },
8065#endif
8066 { BUILTIN_SPEC_REG "eval", evalcmd },
8067 { BUILTIN_SPEC_REG "exec", execcmd },
8068 { BUILTIN_SPEC_REG "exit", exitcmd },
8069 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8070 { BUILTIN_REGULAR "false", falsecmd },
8071#if JOBS
8072 { BUILTIN_REGULAR "fg", fg_bgcmd },
8073#endif
8074#if ENABLE_ASH_GETOPTS
8075 { BUILTIN_REGULAR "getopts", getoptscmd },
8076#endif
8077 { BUILTIN_NOSPEC "hash", hashcmd },
8078#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8079 { BUILTIN_NOSPEC "help", helpcmd },
8080#endif
8081#if JOBS
8082 { BUILTIN_REGULAR "jobs", jobscmd },
8083 { BUILTIN_REGULAR "kill", killcmd },
8084#endif
8085#if ENABLE_ASH_MATH_SUPPORT
8086 { BUILTIN_NOSPEC "let", letcmd },
8087#endif
8088 { BUILTIN_ASSIGN "local", localcmd },
8089 { BUILTIN_NOSPEC "pwd", pwdcmd },
8090 { BUILTIN_REGULAR "read", readcmd },
8091 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8092 { BUILTIN_SPEC_REG "return", returncmd },
8093 { BUILTIN_SPEC_REG "set", setcmd },
8094 { BUILTIN_SPEC_REG "shift", shiftcmd },
8095 { BUILTIN_SPEC_REG "source", dotcmd },
8096#if ENABLE_ASH_BUILTIN_TEST
8097 { BUILTIN_REGULAR "test", testcmd },
8098#endif
8099 { BUILTIN_SPEC_REG "times", timescmd },
8100 { BUILTIN_SPEC_REG "trap", trapcmd },
8101 { BUILTIN_REGULAR "true", truecmd },
8102 { BUILTIN_NOSPEC "type", typecmd },
8103 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8104 { BUILTIN_REGULAR "umask", umaskcmd },
8105#if ENABLE_ASH_ALIAS
8106 { BUILTIN_REGULAR "unalias", unaliascmd },
8107#endif
8108 { BUILTIN_SPEC_REG "unset", unsetcmd },
8109 { BUILTIN_REGULAR "wait", waitcmd },
8110};
8111
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008112
8113#define COMMANDCMD (builtintab + 5 + \
8114 2 * ENABLE_ASH_BUILTIN_TEST + \
8115 ENABLE_ASH_ALIAS + \
8116 ENABLE_ASH_JOB_CONTROL)
8117#define EXECCMD (builtintab + 7 + \
8118 2 * ENABLE_ASH_BUILTIN_TEST + \
8119 ENABLE_ASH_ALIAS + \
8120 ENABLE_ASH_JOB_CONTROL + \
8121 ENABLE_ASH_CMDCMD + \
8122 ENABLE_ASH_BUILTIN_ECHO)
8123
8124/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008125 * Search the table of builtin commands.
8126 */
8127static struct builtincmd *
8128find_builtin(const char *name)
8129{
8130 struct builtincmd *bp;
8131
8132 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008133 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008134 pstrcmp
8135 );
8136 return bp;
8137}
8138
8139/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008140 * Execute a simple command.
8141 */
8142static int back_exitstatus; /* exit status of backquoted command */
8143static int
8144isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008145{
8146 const char *q = endofname(p);
8147 if (p == q)
8148 return 0;
8149 return *q == '=';
8150}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008151static int
8152bltincmd(int argc, char **argv)
8153{
8154 /* Preserve exitstatus of a previous possible redirection
8155 * as POSIX mandates */
8156 return back_exitstatus;
8157}
Eric Andersenc470f442003-07-28 09:56:35 +00008158static void
8159evalcommand(union node *cmd, int flags)
8160{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008161 static const struct builtincmd bltin = {
8162 "\0\0", bltincmd
8163 };
Eric Andersenc470f442003-07-28 09:56:35 +00008164 struct stackmark smark;
8165 union node *argp;
8166 struct arglist arglist;
8167 struct arglist varlist;
8168 char **argv;
8169 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008170 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008171 struct cmdentry cmdentry;
8172 struct job *jp;
8173 char *lastarg;
8174 const char *path;
8175 int spclbltin;
8176 int cmd_is_exec;
8177 int status;
8178 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008179 struct builtincmd *bcmd;
8180 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008181
8182 /* First expand the arguments. */
8183 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8184 setstackmark(&smark);
8185 back_exitstatus = 0;
8186
8187 cmdentry.cmdtype = CMDBUILTIN;
8188 cmdentry.u.cmd = &bltin;
8189 varlist.lastp = &varlist.list;
8190 *varlist.lastp = NULL;
8191 arglist.lastp = &arglist.list;
8192 *arglist.lastp = NULL;
8193
8194 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008195 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008196 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8197 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8198 }
8199
Eric Andersenc470f442003-07-28 09:56:35 +00008200 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8201 struct strlist **spp;
8202
8203 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008204 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008205 expandarg(argp, &arglist, EXP_VARTILDE);
8206 else
8207 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8208
Eric Andersenc470f442003-07-28 09:56:35 +00008209 for (sp = *spp; sp; sp = sp->next)
8210 argc++;
8211 }
8212
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008213 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008214 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008215 TRACE(("evalcommand arg: %s\n", sp->text));
8216 *nargv++ = sp->text;
8217 }
8218 *nargv = NULL;
8219
8220 lastarg = NULL;
8221 if (iflag && funcnest == 0 && argc > 0)
8222 lastarg = nargv[-1];
8223
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008224 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008225 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008226 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008227
8228 path = vpath.text;
8229 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8230 struct strlist **spp;
8231 char *p;
8232
8233 spp = varlist.lastp;
8234 expandarg(argp, &varlist, EXP_VARTILDE);
8235
8236 /*
8237 * Modify the command lookup path, if a PATH= assignment
8238 * is present
8239 */
8240 p = (*spp)->text;
8241 if (varequal(p, path))
8242 path = p;
8243 }
8244
8245 /* Print the command if xflag is set. */
8246 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008247 int n;
8248 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008249
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008250 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008251 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008252
8253 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008254 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008255 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008256 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008257 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008258 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008259 p--;
8260 }
8261 }
8262 sp = arglist.list;
8263 }
Rob Landley53437472006-07-16 08:14:35 +00008264 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008265 }
8266
8267 cmd_is_exec = 0;
8268 spclbltin = -1;
8269
8270 /* Now locate the command. */
8271 if (argc) {
8272 const char *oldpath;
8273 int cmd_flag = DO_ERR;
8274
8275 path += 5;
8276 oldpath = path;
8277 for (;;) {
8278 find_command(argv[0], &cmdentry, cmd_flag, path);
8279 if (cmdentry.cmdtype == CMDUNKNOWN) {
8280 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008281 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008282 goto bail;
8283 }
8284
8285 /* implement bltin and command here */
8286 if (cmdentry.cmdtype != CMDBUILTIN)
8287 break;
8288 if (spclbltin < 0)
8289 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8290 if (cmdentry.u.cmd == EXECCMD)
8291 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008292#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008293 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008294 path = oldpath;
8295 nargv = parse_command_args(argv, &path);
8296 if (!nargv)
8297 break;
8298 argc -= nargv - argv;
8299 argv = nargv;
8300 cmd_flag |= DO_NOFUNC;
8301 } else
8302#endif
8303 break;
8304 }
8305 }
8306
8307 if (status) {
8308 /* We have a redirection error. */
8309 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008310 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008311 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008312 exitstatus = status;
8313 goto out;
8314 }
8315
8316 /* Execute the command. */
8317 switch (cmdentry.cmdtype) {
8318 default:
8319 /* Fork off a child process if necessary. */
8320 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008321 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008322 jp = makejob(cmd, 1);
8323 if (forkshell(jp, cmd, FORK_FG) != 0) {
8324 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008325 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008326 break;
8327 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008328 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008329 }
8330 listsetvar(varlist.list, VEXPORT|VSTACK);
8331 shellexec(argv, path, cmdentry.u.index);
8332 /* NOTREACHED */
8333
8334 case CMDBUILTIN:
8335 cmdenviron = varlist.list;
8336 if (cmdenviron) {
8337 struct strlist *list = cmdenviron;
8338 int i = VNOSET;
8339 if (spclbltin > 0 || argc == 0) {
8340 i = 0;
8341 if (cmd_is_exec && argc > 1)
8342 i = VEXPORT;
8343 }
8344 listsetvar(list, i);
8345 }
8346 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8347 int exit_status;
8348 int i, j;
8349
8350 i = exception;
8351 if (i == EXEXIT)
8352 goto raise;
8353
8354 exit_status = 2;
8355 j = 0;
8356 if (i == EXINT)
8357 j = SIGINT;
8358 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008359 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008360 if (j)
8361 exit_status = j + 128;
8362 exitstatus = exit_status;
8363
8364 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008365 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008366 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008367 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008368 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008369 }
8370 break;
8371
8372 case CMDFUNCTION:
8373 listsetvar(varlist.list, 0);
8374 if (evalfun(cmdentry.u.func, argc, argv, flags))
8375 goto raise;
8376 break;
8377 }
8378
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008379 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008380 popredir(cmd_is_exec);
8381 if (lastarg)
8382 /* dsl: I think this is intended to be used to support
8383 * '_' in 'vi' command mode during line editing...
8384 * However I implemented that within libedit itself.
8385 */
8386 setvar("_", lastarg, 0);
8387 popstackmark(&smark);
8388}
8389
8390static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008391evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8392{
Eric Andersenc470f442003-07-28 09:56:35 +00008393 char *volatile savecmdname;
8394 struct jmploc *volatile savehandler;
8395 struct jmploc jmploc;
8396 int i;
8397
8398 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008399 i = setjmp(jmploc.loc);
8400 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008401 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008402 savehandler = exception_handler;
8403 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008404 commandname = argv[0];
8405 argptr = argv + 1;
8406 optptr = NULL; /* initialize nextopt */
8407 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008408 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008409 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008410 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008411 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008412 commandname = savecmdname;
8413 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008414 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008415
8416 return i;
8417}
8418
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008419static int
8420goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008421{
8422 return !*endofname(p);
8423}
8424
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008425
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008426/*
8427 * Search for a command. This is called before we fork so that the
8428 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008429 * the child. The check for "goodname" is an overly conservative
8430 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008431 */
Eric Andersenc470f442003-07-28 09:56:35 +00008432static void
8433prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008434{
8435 struct cmdentry entry;
8436
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008437 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8438 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008439}
8440
Eric Andersencb57d552001-06-28 07:25:16 +00008441
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008442/* ============ Builtin commands
8443 *
8444 * Builtin commands whose functions are closely tied to evaluation
8445 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008446 */
8447
8448/*
Eric Andersencb57d552001-06-28 07:25:16 +00008449 * Handle break and continue commands. Break, continue, and return are
8450 * all handled by setting the evalskip flag. The evaluation routines
8451 * above all check this flag, and if it is set they start skipping
8452 * commands rather than executing them. The variable skipcount is
8453 * the number of loops to break/continue, or the number of function
8454 * levels to return. (The latter is always 1.) It should probably
8455 * be an error to break out of more loops than exist, but it isn't
8456 * in the standard shell so we don't make it one here.
8457 */
Eric Andersenc470f442003-07-28 09:56:35 +00008458static int
8459breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008460{
8461 int n = argc > 1 ? number(argv[1]) : 1;
8462
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008463 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008464 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008465 if (n > loopnest)
8466 n = loopnest;
8467 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008468 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008469 skipcount = n;
8470 }
8471 return 0;
8472}
8473
Eric Andersenc470f442003-07-28 09:56:35 +00008474
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008475/* ============ input.c
8476 *
Eric Andersen90898442003-08-06 11:20:52 +00008477 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008478 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008479
Eric Andersenc470f442003-07-28 09:56:35 +00008480#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008481
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008482enum {
8483 INPUT_PUSH_FILE = 1,
8484 INPUT_NOFILE_OK = 2,
8485};
Eric Andersencb57d552001-06-28 07:25:16 +00008486
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008487static int plinno = 1; /* input line number */
8488/* number of characters left in input buffer */
8489static int parsenleft; /* copy of parsefile->nleft */
8490static int parselleft; /* copy of parsefile->lleft */
8491/* next character in input buffer */
8492static char *parsenextc; /* copy of parsefile->nextc */
8493
8494static int checkkwd;
8495/* values of checkkwd variable */
8496#define CHKALIAS 0x1
8497#define CHKKWD 0x2
8498#define CHKNL 0x4
8499
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008500static void
8501popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008502{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008503 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008504
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008505 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008506#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008507 if (sp->ap) {
8508 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8509 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008510 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008511 if (sp->string != sp->ap->val) {
8512 free(sp->string);
8513 }
8514 sp->ap->flag &= ~ALIASINUSE;
8515 if (sp->ap->flag & ALIASDEAD) {
8516 unalias(sp->ap->name);
8517 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008518 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008519#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008520 parsenextc = sp->prevstring;
8521 parsenleft = sp->prevnleft;
8522/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8523 parsefile->strpush = sp->prev;
8524 if (sp != &(parsefile->basestrpush))
8525 free(sp);
8526 INT_ON;
8527}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008528
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008529static int
8530preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008531{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008532 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008533 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008534 parsenextc = buf;
8535
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008536 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008537#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008538 if (!iflag || parsefile->fd)
8539 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8540 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008541#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008542 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008543#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008544 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8545 if (nr == 0) {
8546 /* Ctrl+C pressed */
8547 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008548 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008549 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008550 raise(SIGINT);
8551 return 1;
8552 }
Eric Andersenc470f442003-07-28 09:56:35 +00008553 goto retry;
8554 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008555 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008556 /* Ctrl+D presend */
8557 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008558 }
Eric Andersencb57d552001-06-28 07:25:16 +00008559 }
8560#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008561 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008562#endif
8563
8564 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008565 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008566 int flags = fcntl(0, F_GETFL);
Eric Andersencb57d552001-06-28 07:25:16 +00008567 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008568 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008569 if (fcntl(0, F_SETFL, flags) >= 0) {
8570 out2str("sh: turning off NDELAY mode\n");
8571 goto retry;
8572 }
8573 }
8574 }
8575 }
8576 return nr;
8577}
8578
8579/*
8580 * Refill the input buffer and return the next input character:
8581 *
8582 * 1) If a string was pushed back on the input, pop it;
8583 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8584 * from a string so we can't refill the buffer, return EOF.
8585 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8586 * 4) Process input up to the next newline, deleting nul characters.
8587 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008588static int
Eric Andersenc470f442003-07-28 09:56:35 +00008589preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008590{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008591 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008592 int more;
8593 char savec;
8594
8595 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008596#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008597 if (parsenleft == -1 && parsefile->strpush->ap &&
8598 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008599 return PEOA;
8600 }
Eric Andersen2870d962001-07-02 17:27:21 +00008601#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008602 popstring();
8603 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008604 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008605 }
8606 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8607 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008608 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008609
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008610 more = parselleft;
8611 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008612 again:
8613 more = preadfd();
8614 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008615 parselleft = parsenleft = EOF_NLEFT;
8616 return PEOF;
8617 }
8618 }
8619
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008620 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008621
8622 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008623 for (;;) {
8624 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008625
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008626 more--;
8627 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008628
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008629 if (!c)
8630 memmove(q, q + 1, more);
8631 else {
8632 q++;
8633 if (c == '\n') {
8634 parsenleft = q - parsenextc - 1;
8635 break;
8636 }
Eric Andersencb57d552001-06-28 07:25:16 +00008637 }
8638
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008639 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008640 parsenleft = q - parsenextc - 1;
8641 if (parsenleft < 0)
8642 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008643 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008644 }
8645 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008646 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008647
8648 savec = *q;
8649 *q = '\0';
8650
8651 if (vflag) {
8652 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008653 }
8654
8655 *q = savec;
8656
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008657 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008658}
8659
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008660#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008661static int
8662pgetc(void)
8663{
8664 return pgetc_as_macro();
8665}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008666
8667#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8668#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008669#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008670#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008671#endif
8672
8673/*
8674 * Same as pgetc(), but ignores PEOA.
8675 */
8676#if ENABLE_ASH_ALIAS
8677static int
8678pgetc2(void)
8679{
8680 int c;
8681
8682 do {
8683 c = pgetc_macro();
8684 } while (c == PEOA);
8685 return c;
8686}
8687#else
8688static int
8689pgetc2(void)
8690{
8691 return pgetc_macro();
8692}
8693#endif
8694
8695/*
8696 * Read a line from the script.
8697 */
8698static char *
8699pfgets(char *line, int len)
8700{
8701 char *p = line;
8702 int nleft = len;
8703 int c;
8704
8705 while (--nleft > 0) {
8706 c = pgetc2();
8707 if (c == PEOF) {
8708 if (p == line)
8709 return NULL;
8710 break;
8711 }
8712 *p++ = c;
8713 if (c == '\n')
8714 break;
8715 }
8716 *p = '\0';
8717 return line;
8718}
8719
Eric Andersenc470f442003-07-28 09:56:35 +00008720/*
8721 * Undo the last call to pgetc. Only one character may be pushed back.
8722 * PEOF may be pushed back.
8723 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008724static void
Eric Andersenc470f442003-07-28 09:56:35 +00008725pungetc(void)
8726{
8727 parsenleft++;
8728 parsenextc--;
8729}
Eric Andersencb57d552001-06-28 07:25:16 +00008730
8731/*
8732 * Push a string back onto the input at this current parsefile level.
8733 * We handle aliases this way.
8734 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008735static void
Eric Andersenc470f442003-07-28 09:56:35 +00008736pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008737{
Eric Andersencb57d552001-06-28 07:25:16 +00008738 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008739 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008740
Eric Andersenc470f442003-07-28 09:56:35 +00008741 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008742 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008743/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8744 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008745 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008746 sp->prev = parsefile->strpush;
8747 parsefile->strpush = sp;
8748 } else
8749 sp = parsefile->strpush = &(parsefile->basestrpush);
8750 sp->prevstring = parsenextc;
8751 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008752#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008753 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008754 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008755 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008756 sp->string = s;
8757 }
Eric Andersen2870d962001-07-02 17:27:21 +00008758#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008759 parsenextc = s;
8760 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008761 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008762}
8763
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008764/*
8765 * To handle the "." command, a stack of input files is used. Pushfile
8766 * adds a new entry to the stack and popfile restores the previous level.
8767 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008768static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008769pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008770{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008771 struct parsefile *pf;
8772
8773 parsefile->nleft = parsenleft;
8774 parsefile->lleft = parselleft;
8775 parsefile->nextc = parsenextc;
8776 parsefile->linno = plinno;
8777 pf = ckmalloc(sizeof(*pf));
8778 pf->prev = parsefile;
8779 pf->fd = -1;
8780 pf->strpush = NULL;
8781 pf->basestrpush.prev = NULL;
8782 parsefile = pf;
8783}
8784
8785static void
8786popfile(void)
8787{
8788 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008789
Denis Vlasenkob012b102007-02-19 22:43:01 +00008790 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008791 if (pf->fd >= 0)
8792 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008793 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008794 while (pf->strpush)
8795 popstring();
8796 parsefile = pf->prev;
8797 free(pf);
8798 parsenleft = parsefile->nleft;
8799 parselleft = parsefile->lleft;
8800 parsenextc = parsefile->nextc;
8801 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008802 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008803}
8804
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008805/*
8806 * Return to top level.
8807 */
8808static void
8809popallfiles(void)
8810{
8811 while (parsefile != &basepf)
8812 popfile();
8813}
8814
8815/*
8816 * Close the file(s) that the shell is reading commands from. Called
8817 * after a fork is done.
8818 */
8819static void
8820closescript(void)
8821{
8822 popallfiles();
8823 if (parsefile->fd > 0) {
8824 close(parsefile->fd);
8825 parsefile->fd = 0;
8826 }
8827}
8828
8829/*
8830 * Like setinputfile, but takes an open file descriptor. Call this with
8831 * interrupts off.
8832 */
8833static void
8834setinputfd(int fd, int push)
8835{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008836 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008837 if (push) {
8838 pushfile();
8839 parsefile->buf = 0;
8840 }
8841 parsefile->fd = fd;
8842 if (parsefile->buf == NULL)
8843 parsefile->buf = ckmalloc(IBUFSIZ);
8844 parselleft = parsenleft = 0;
8845 plinno = 1;
8846}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008847
Eric Andersenc470f442003-07-28 09:56:35 +00008848/*
8849 * Set the input to take input from a file. If push is set, push the
8850 * old input onto the stack first.
8851 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008852static int
8853setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008854{
8855 int fd;
8856 int fd2;
8857
Denis Vlasenkob012b102007-02-19 22:43:01 +00008858 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008859 fd = open(fname, O_RDONLY);
8860 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008861 if (flags & INPUT_NOFILE_OK)
8862 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008863 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008864 }
Eric Andersenc470f442003-07-28 09:56:35 +00008865 if (fd < 10) {
8866 fd2 = copyfd(fd, 10);
8867 close(fd);
8868 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008869 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008870 fd = fd2;
8871 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008872 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008873 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008874 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008875 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008876}
8877
Eric Andersencb57d552001-06-28 07:25:16 +00008878/*
8879 * Like setinputfile, but takes input from a string.
8880 */
Eric Andersenc470f442003-07-28 09:56:35 +00008881static void
8882setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008883{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008884 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008885 pushfile();
8886 parsenextc = string;
8887 parsenleft = strlen(string);
8888 parsefile->buf = NULL;
8889 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008890 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008891}
8892
8893
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008894/* ============ mail.c
8895 *
8896 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008897 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008898
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008899#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008900
Eric Andersencb57d552001-06-28 07:25:16 +00008901#define MAXMBOXES 10
8902
Eric Andersenc470f442003-07-28 09:56:35 +00008903/* times of mailboxes */
8904static time_t mailtime[MAXMBOXES];
8905/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008906static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008907
Eric Andersencb57d552001-06-28 07:25:16 +00008908/*
Eric Andersenc470f442003-07-28 09:56:35 +00008909 * Print appropriate message(s) if mail has arrived.
8910 * If mail_var_path_changed is set,
8911 * then the value of MAIL has mail_var_path_changed,
8912 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008913 */
Eric Andersenc470f442003-07-28 09:56:35 +00008914static void
8915chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008916{
Eric Andersencb57d552001-06-28 07:25:16 +00008917 const char *mpath;
8918 char *p;
8919 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008920 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008921 struct stackmark smark;
8922 struct stat statb;
8923
Eric Andersencb57d552001-06-28 07:25:16 +00008924 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008925 mpath = mpathset() ? mpathval() : mailval();
8926 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008927 p = padvance(&mpath, nullstr);
8928 if (p == NULL)
8929 break;
8930 if (*p == '\0')
8931 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008932 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008933#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008934 if (q[-1] != '/')
8935 abort();
8936#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008937 q[-1] = '\0'; /* delete trailing '/' */
8938 if (stat(p, &statb) < 0) {
8939 *mtp = 0;
8940 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008941 }
Eric Andersenc470f442003-07-28 09:56:35 +00008942 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8943 fprintf(
8944 stderr, snlfmt,
8945 pathopt ? pathopt : "you have mail"
8946 );
8947 }
8948 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008949 }
Eric Andersenc470f442003-07-28 09:56:35 +00008950 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008951 popstackmark(&smark);
8952}
Eric Andersencb57d552001-06-28 07:25:16 +00008953
Eric Andersenc470f442003-07-28 09:56:35 +00008954static void
8955changemail(const char *val)
8956{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008957 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00008958}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008959
Denis Vlasenko131ae172007-02-18 13:00:19 +00008960#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008961
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008962
8963/* ============ ??? */
8964
Eric Andersencb57d552001-06-28 07:25:16 +00008965/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008966 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008967 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008968static void
8969setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008970{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008971 char **newparam;
8972 char **ap;
8973 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008974
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008975 for (nparam = 0; argv[nparam]; nparam++);
8976 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8977 while (*argv) {
8978 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008979 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008980 *ap = NULL;
8981 freeparam(&shellparam);
8982 shellparam.malloc = 1;
8983 shellparam.nparam = nparam;
8984 shellparam.p = newparam;
8985#if ENABLE_ASH_GETOPTS
8986 shellparam.optind = 1;
8987 shellparam.optoff = -1;
8988#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008989}
8990
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008991/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008992 * Process shell options. The global variable argptr contains a pointer
8993 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008994 */
8995static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008996minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008997{
8998 int i;
8999
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009000 if (name) {
9001 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009002 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009003 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009004 return;
9005 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009006 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009007 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009008 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009009 out1str("Current option settings\n");
9010 for (i = 0; i < NOPTS; i++)
9011 out1fmt("%-16s%s\n", optnames(i),
9012 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009013}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009014static void
9015setoption(int flag, int val)
9016{
9017 int i;
9018
9019 for (i = 0; i < NOPTS; i++) {
9020 if (optletters(i) == flag) {
9021 optlist[i] = val;
9022 return;
9023 }
9024 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009025 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009026 /* NOTREACHED */
9027}
Eric Andersenc470f442003-07-28 09:56:35 +00009028static void
9029options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009030{
9031 char *p;
9032 int val;
9033 int c;
9034
9035 if (cmdline)
9036 minusc = NULL;
9037 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009038 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009039 if (c != '-' && c != '+')
9040 break;
9041 argptr++;
9042 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009043 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009044 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009045 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009046 if (!cmdline) {
9047 /* "-" means turn off -x and -v */
9048 if (p[0] == '\0')
9049 xflag = vflag = 0;
9050 /* "--" means reset params */
9051 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009052 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009053 }
Eric Andersenc470f442003-07-28 09:56:35 +00009054 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009055 }
Eric Andersencb57d552001-06-28 07:25:16 +00009056 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009057 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009058 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009059 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009060 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009061 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009062 } else if (c == 'o') {
9063 minus_o(*argptr, val);
9064 if (*argptr)
9065 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009066 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9067 isloginsh = 1;
9068 /* bash does not accept +-login, we also won't */
9069 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009070 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009071 isloginsh = 1;
9072 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009073 } else {
9074 setoption(c, val);
9075 }
9076 }
9077 }
9078}
9079
Eric Andersencb57d552001-06-28 07:25:16 +00009080/*
Eric Andersencb57d552001-06-28 07:25:16 +00009081 * The shift builtin command.
9082 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009083static int
Eric Andersenc470f442003-07-28 09:56:35 +00009084shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009085{
9086 int n;
9087 char **ap1, **ap2;
9088
9089 n = 1;
9090 if (argc > 1)
9091 n = number(argv[1]);
9092 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009093 ash_msg_and_raise_error("can't shift that many");
9094 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009095 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009096 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009097 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009098 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009099 }
9100 ap2 = shellparam.p;
9101 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009102#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009103 shellparam.optind = 1;
9104 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009105#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009106 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009107 return 0;
9108}
9109
Eric Andersencb57d552001-06-28 07:25:16 +00009110/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009111 * POSIX requires that 'set' (but not export or readonly) output the
9112 * variables in lexicographic order - by the locale's collating order (sigh).
9113 * Maybe we could keep them in an ordered balanced binary tree
9114 * instead of hashed lists.
9115 * For now just roll 'em through qsort for printing...
9116 */
9117static int
9118showvars(const char *sep_prefix, int on, int off)
9119{
9120 const char *sep;
9121 char **ep, **epend;
9122
9123 ep = listvars(on, off, &epend);
9124 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9125
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009126 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009127
9128 for (; ep < epend; ep++) {
9129 const char *p;
9130 const char *q;
9131
9132 p = strchrnul(*ep, '=');
9133 q = nullstr;
9134 if (*p)
9135 q = single_quote(++p);
9136 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9137 }
9138 return 0;
9139}
9140
9141/*
Eric Andersencb57d552001-06-28 07:25:16 +00009142 * The set command builtin.
9143 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009144static int
Eric Andersenc470f442003-07-28 09:56:35 +00009145setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009146{
9147 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009148 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009149 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009150 options(0);
9151 optschanged();
9152 if (*argptr != NULL) {
9153 setparam(argptr);
9154 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009155 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009156 return 0;
9157}
9158
Denis Vlasenko131ae172007-02-18 13:00:19 +00009159#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009160/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009161static void
9162change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009163{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009164 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009165 /* "get", generate */
9166 char buf[16];
9167
9168 rseed = rseed * 1103515245 + 12345;
9169 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9170 /* set without recursion */
9171 setvar(vrandom.text, buf, VNOFUNC);
9172 vrandom.flags &= ~VNOFUNC;
9173 } else {
9174 /* set/reset */
9175 rseed = strtoul(value, (char **)NULL, 10);
9176 }
Eric Andersenef02f822004-03-11 13:34:24 +00009177}
Eric Andersen16767e22004-03-16 05:14:10 +00009178#endif
9179
Denis Vlasenko131ae172007-02-18 13:00:19 +00009180#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009181static int
Eric Andersenc470f442003-07-28 09:56:35 +00009182getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009183{
9184 char *p, *q;
9185 char c = '?';
9186 int done = 0;
9187 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009188 char s[12];
9189 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009190
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009191 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009192 return 1;
9193 optnext = optfirst + *param_optind - 1;
9194
9195 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009196 p = NULL;
9197 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009198 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009199 if (p == NULL || *p == '\0') {
9200 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009201 p = *optnext;
9202 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009203 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009204 p = NULL;
9205 done = 1;
9206 goto out;
9207 }
9208 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009209 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009210 goto atend;
9211 }
9212
9213 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009214 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009215 if (*q == '\0') {
9216 if (optstr[0] == ':') {
9217 s[0] = c;
9218 s[1] = '\0';
9219 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009220 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009221 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009222 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009223 }
9224 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009225 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009226 }
9227 if (*++q == ':')
9228 q++;
9229 }
9230
9231 if (*++q == ':') {
9232 if (*p == '\0' && (p = *optnext) == NULL) {
9233 if (optstr[0] == ':') {
9234 s[0] = c;
9235 s[1] = '\0';
9236 err |= setvarsafe("OPTARG", s, 0);
9237 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009238 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009239 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009240 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009241 c = '?';
9242 }
Eric Andersenc470f442003-07-28 09:56:35 +00009243 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009244 }
9245
9246 if (p == *optnext)
9247 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009248 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009249 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009250 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009251 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009252 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009253 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009254 *param_optind = optnext - optfirst + 1;
9255 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009256 err |= setvarsafe("OPTIND", s, VNOFUNC);
9257 s[0] = c;
9258 s[1] = '\0';
9259 err |= setvarsafe(optvar, s, 0);
9260 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009261 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009262 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009263 flush_stdout_stderr();
9264 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009265 }
9266 return done;
9267}
Eric Andersenc470f442003-07-28 09:56:35 +00009268
9269/*
9270 * The getopts builtin. Shellparam.optnext points to the next argument
9271 * to be processed. Shellparam.optptr points to the next character to
9272 * be processed in the current argument. If shellparam.optnext is NULL,
9273 * then it's the first time getopts has been called.
9274 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009275static int
Eric Andersenc470f442003-07-28 09:56:35 +00009276getoptscmd(int argc, char **argv)
9277{
9278 char **optbase;
9279
9280 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009281 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009282 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009283 optbase = shellparam.p;
9284 if (shellparam.optind > shellparam.nparam + 1) {
9285 shellparam.optind = 1;
9286 shellparam.optoff = -1;
9287 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009288 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009289 optbase = &argv[3];
9290 if (shellparam.optind > argc - 2) {
9291 shellparam.optind = 1;
9292 shellparam.optoff = -1;
9293 }
9294 }
9295
9296 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009297 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009298}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009299#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009300
Eric Andersencb57d552001-06-28 07:25:16 +00009301
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009302/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009303
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009304/*
9305 * NEOF is returned by parsecmd when it encounters an end of file. It
9306 * must be distinct from NULL, so we use the address of a variable that
9307 * happens to be handy.
9308 */
9309static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009310#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009311static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009312static int lasttoken; /* last token read */
9313static char *wordtext; /* text of last word returned by readtoken */
9314static struct nodelist *backquotelist;
9315static union node *redirnode;
9316static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009317static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009318
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009319static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9320static void
9321raise_error_syntax(const char *msg)
9322{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009323 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009324 /* NOTREACHED */
9325}
9326
9327/*
9328 * Called when an unexpected token is read during the parse. The argument
9329 * is the token that is expected, or -1 if more than one type of token can
9330 * occur at this point.
9331 */
9332static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9333static void
9334raise_error_unexpected_syntax(int token)
9335{
9336 char msg[64];
9337 int l;
9338
9339 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9340 if (token >= 0)
9341 sprintf(msg + l, " (expecting %s)", tokname(token));
9342 raise_error_syntax(msg);
9343 /* NOTREACHED */
9344}
Eric Andersencb57d552001-06-28 07:25:16 +00009345
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009346#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009347
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009348struct heredoc {
9349 struct heredoc *next; /* next here document in list */
9350 union node *here; /* redirection node */
9351 char *eofmark; /* string indicating end of input */
9352 int striptabs; /* if set, strip leading tabs */
9353};
Eric Andersencb57d552001-06-28 07:25:16 +00009354
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009355static struct heredoc *heredoclist; /* list of here documents to read */
9356
9357/* parsing is heavily cross-recursive, need these forward decls */
9358static union node *andor(void);
9359static union node *pipeline(void);
9360static union node *parse_command(void);
9361static void parseheredoc(void);
9362static char peektoken(void);
9363static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009364
Eric Andersenc470f442003-07-28 09:56:35 +00009365static union node *
9366list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009367{
9368 union node *n1, *n2, *n3;
9369 int tok;
9370
Eric Andersenc470f442003-07-28 09:56:35 +00009371 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9372 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009373 return NULL;
9374 n1 = NULL;
9375 for (;;) {
9376 n2 = andor();
9377 tok = readtoken();
9378 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009379 if (n2->type == NPIPE) {
9380 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009381 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009382 if (n2->type != NREDIR) {
9383 n3 = stalloc(sizeof(struct nredir));
9384 n3->nredir.n = n2;
9385 n3->nredir.redirect = NULL;
9386 n2 = n3;
9387 }
9388 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009389 }
9390 }
9391 if (n1 == NULL) {
9392 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009393 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009394 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009395 n3->type = NSEMI;
9396 n3->nbinary.ch1 = n1;
9397 n3->nbinary.ch2 = n2;
9398 n1 = n3;
9399 }
9400 switch (tok) {
9401 case TBACKGND:
9402 case TSEMI:
9403 tok = readtoken();
9404 /* fall through */
9405 case TNL:
9406 if (tok == TNL) {
9407 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009408 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009409 return n1;
9410 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009411 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009412 }
Eric Andersenc470f442003-07-28 09:56:35 +00009413 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009414 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009415 return n1;
9416 break;
9417 case TEOF:
9418 if (heredoclist)
9419 parseheredoc();
9420 else
Eric Andersenc470f442003-07-28 09:56:35 +00009421 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009422 return n1;
9423 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009424 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009425 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009426 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009427 return n1;
9428 }
9429 }
9430}
9431
Eric Andersenc470f442003-07-28 09:56:35 +00009432static union node *
9433andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009434{
Eric Andersencb57d552001-06-28 07:25:16 +00009435 union node *n1, *n2, *n3;
9436 int t;
9437
Eric Andersencb57d552001-06-28 07:25:16 +00009438 n1 = pipeline();
9439 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009440 t = readtoken();
9441 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009442 t = NAND;
9443 } else if (t == TOR) {
9444 t = NOR;
9445 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009446 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009447 return n1;
9448 }
Eric Andersenc470f442003-07-28 09:56:35 +00009449 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009450 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009451 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009452 n3->type = t;
9453 n3->nbinary.ch1 = n1;
9454 n3->nbinary.ch2 = n2;
9455 n1 = n3;
9456 }
9457}
9458
Eric Andersenc470f442003-07-28 09:56:35 +00009459static union node *
9460pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009461{
Eric Andersencb57d552001-06-28 07:25:16 +00009462 union node *n1, *n2, *pipenode;
9463 struct nodelist *lp, *prev;
9464 int negate;
9465
9466 negate = 0;
9467 TRACE(("pipeline: entered\n"));
9468 if (readtoken() == TNOT) {
9469 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009470 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009471 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009472 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009473 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009474 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009475 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009476 pipenode->type = NPIPE;
9477 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009478 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009479 pipenode->npipe.cmdlist = lp;
9480 lp->n = n1;
9481 do {
9482 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009483 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009484 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009485 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009486 prev->next = lp;
9487 } while (readtoken() == TPIPE);
9488 lp->next = NULL;
9489 n1 = pipenode;
9490 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009491 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009492 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009493 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009494 n2->type = NNOT;
9495 n2->nnot.com = n1;
9496 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009497 }
9498 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009499}
9500
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009501static union node *
9502makename(void)
9503{
9504 union node *n;
9505
9506 n = stalloc(sizeof(struct narg));
9507 n->type = NARG;
9508 n->narg.next = NULL;
9509 n->narg.text = wordtext;
9510 n->narg.backquote = backquotelist;
9511 return n;
9512}
9513
9514static void
9515fixredir(union node *n, const char *text, int err)
9516{
9517 TRACE(("Fix redir %s %d\n", text, err));
9518 if (!err)
9519 n->ndup.vname = NULL;
9520
9521 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009522 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009523 else if (LONE_DASH(text))
9524 n->ndup.dupfd = -1;
9525 else {
9526 if (err)
9527 raise_error_syntax("Bad fd number");
9528 n->ndup.vname = makename();
9529 }
9530}
9531
9532/*
9533 * Returns true if the text contains nothing to expand (no dollar signs
9534 * or backquotes).
9535 */
9536static int
9537noexpand(char *text)
9538{
9539 char *p;
9540 char c;
9541
9542 p = text;
9543 while ((c = *p++) != '\0') {
9544 if (c == CTLQUOTEMARK)
9545 continue;
9546 if (c == CTLESC)
9547 p++;
9548 else if (SIT(c, BASESYNTAX) == CCTL)
9549 return 0;
9550 }
9551 return 1;
9552}
9553
9554static void
9555parsefname(void)
9556{
9557 union node *n = redirnode;
9558
9559 if (readtoken() != TWORD)
9560 raise_error_unexpected_syntax(-1);
9561 if (n->type == NHERE) {
9562 struct heredoc *here = heredoc;
9563 struct heredoc *p;
9564 int i;
9565
9566 if (quoteflag == 0)
9567 n->type = NXHERE;
9568 TRACE(("Here document %d\n", n->type));
9569 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9570 raise_error_syntax("Illegal eof marker for << redirection");
9571 rmescapes(wordtext);
9572 here->eofmark = wordtext;
9573 here->next = NULL;
9574 if (heredoclist == NULL)
9575 heredoclist = here;
9576 else {
9577 for (p = heredoclist; p->next; p = p->next);
9578 p->next = here;
9579 }
9580 } else if (n->type == NTOFD || n->type == NFROMFD) {
9581 fixredir(n, wordtext, 0);
9582 } else {
9583 n->nfile.fname = makename();
9584 }
9585}
Eric Andersencb57d552001-06-28 07:25:16 +00009586
Eric Andersenc470f442003-07-28 09:56:35 +00009587static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009588simplecmd(void)
9589{
9590 union node *args, **app;
9591 union node *n = NULL;
9592 union node *vars, **vpp;
9593 union node **rpp, *redir;
9594 int savecheckkwd;
9595
9596 args = NULL;
9597 app = &args;
9598 vars = NULL;
9599 vpp = &vars;
9600 redir = NULL;
9601 rpp = &redir;
9602
9603 savecheckkwd = CHKALIAS;
9604 for (;;) {
9605 checkkwd = savecheckkwd;
9606 switch (readtoken()) {
9607 case TWORD:
9608 n = stalloc(sizeof(struct narg));
9609 n->type = NARG;
9610 n->narg.text = wordtext;
9611 n->narg.backquote = backquotelist;
9612 if (savecheckkwd && isassignment(wordtext)) {
9613 *vpp = n;
9614 vpp = &n->narg.next;
9615 } else {
9616 *app = n;
9617 app = &n->narg.next;
9618 savecheckkwd = 0;
9619 }
9620 break;
9621 case TREDIR:
9622 *rpp = n = redirnode;
9623 rpp = &n->nfile.next;
9624 parsefname(); /* read name of redirection file */
9625 break;
9626 case TLP:
9627 if (args && app == &args->narg.next
9628 && !vars && !redir
9629 ) {
9630 struct builtincmd *bcmd;
9631 const char *name;
9632
9633 /* We have a function */
9634 if (readtoken() != TRP)
9635 raise_error_unexpected_syntax(TRP);
9636 name = n->narg.text;
9637 if (!goodname(name)
9638 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9639 ) {
9640 raise_error_syntax("Bad function name");
9641 }
9642 n->type = NDEFUN;
9643 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9644 n->narg.next = parse_command();
9645 return n;
9646 }
9647 /* fall through */
9648 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009649 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009650 goto out;
9651 }
9652 }
9653 out:
9654 *app = NULL;
9655 *vpp = NULL;
9656 *rpp = NULL;
9657 n = stalloc(sizeof(struct ncmd));
9658 n->type = NCMD;
9659 n->ncmd.args = args;
9660 n->ncmd.assign = vars;
9661 n->ncmd.redirect = redir;
9662 return n;
9663}
9664
9665static union node *
9666parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009667{
Eric Andersencb57d552001-06-28 07:25:16 +00009668 union node *n1, *n2;
9669 union node *ap, **app;
9670 union node *cp, **cpp;
9671 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009672 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009673 int t;
9674
9675 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009676 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009677
Eric Andersencb57d552001-06-28 07:25:16 +00009678 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009679 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009680 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009681 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009682 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009683 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009684 n1->type = NIF;
9685 n1->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 n1->nif.ifpart = list(0);
9689 n2 = n1;
9690 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009691 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009692 n2 = n2->nif.elsepart;
9693 n2->type = NIF;
9694 n2->nif.test = list(0);
9695 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009696 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009697 n2->nif.ifpart = list(0);
9698 }
9699 if (lasttoken == TELSE)
9700 n2->nif.elsepart = list(0);
9701 else {
9702 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009703 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
Eric Andersenc470f442003-07-28 09:56:35 +00009705 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009706 break;
9707 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009708 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009709 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009710 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009711 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009712 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009713 got = readtoken();
9714 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009715 TRACE(("expecting DO got %s %s\n", tokname(got),
9716 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009717 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009718 }
9719 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009720 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009721 break;
9722 }
9723 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009724 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009725 raise_error_syntax("Bad for loop variable");
9726 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009727 n1->type = NFOR;
9728 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009729 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009730 if (readtoken() == TIN) {
9731 app = &ap;
9732 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009733 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009734 n2->type = NARG;
9735 n2->narg.text = wordtext;
9736 n2->narg.backquote = backquotelist;
9737 *app = n2;
9738 app = &n2->narg.next;
9739 }
9740 *app = NULL;
9741 n1->nfor.args = ap;
9742 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009743 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009744 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009745 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009746 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009747 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009748 n2->narg.backquote = NULL;
9749 n2->narg.next = NULL;
9750 n1->nfor.args = n2;
9751 /*
9752 * Newline or semicolon here is optional (but note
9753 * that the original Bourne shell only allowed NL).
9754 */
9755 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009756 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009757 }
Eric Andersenc470f442003-07-28 09:56:35 +00009758 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009759 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009760 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009761 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009762 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009763 break;
9764 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009765 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009766 n1->type = NCASE;
9767 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009768 raise_error_unexpected_syntax(TWORD);
9769 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009770 n2->type = NARG;
9771 n2->narg.text = wordtext;
9772 n2->narg.backquote = backquotelist;
9773 n2->narg.next = NULL;
9774 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009775 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009776 } while (readtoken() == TNL);
9777 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009778 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009779 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009780 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009781 checkkwd = CHKNL | CHKKWD;
9782 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009783 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009784 if (lasttoken == TLP)
9785 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009786 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009787 cp->type = NCLIST;
9788 app = &cp->nclist.pattern;
9789 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009790 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009791 ap->type = NARG;
9792 ap->narg.text = wordtext;
9793 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009794 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009795 break;
9796 app = &ap->narg.next;
9797 readtoken();
9798 }
9799 ap->narg.next = NULL;
9800 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009802 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009803
Eric Andersenc470f442003-07-28 09:56:35 +00009804 cpp = &cp->nclist.next;
9805
9806 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009807 t = readtoken();
9808 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009809 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009810 raise_error_unexpected_syntax(TENDCASE);
9811 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009812 }
Eric Andersenc470f442003-07-28 09:56:35 +00009813 }
Eric Andersencb57d552001-06-28 07:25:16 +00009814 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009815 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009816 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009817 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009818 n1->type = NSUBSHELL;
9819 n1->nredir.n = list(0);
9820 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009821 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009822 break;
9823 case TBEGIN:
9824 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009825 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009826 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009827 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009828 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009829 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009830 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009831 }
9832
Eric Andersenc470f442003-07-28 09:56:35 +00009833 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009834 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009835
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009836 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009837 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009838 checkkwd = CHKKWD | CHKALIAS;
9839 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009840 while (readtoken() == TREDIR) {
9841 *rpp = n2 = redirnode;
9842 rpp = &n2->nfile.next;
9843 parsefname();
9844 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009845 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009846 *rpp = NULL;
9847 if (redir) {
9848 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009849 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009850 n2->type = NREDIR;
9851 n2->nredir.n = n1;
9852 n1 = n2;
9853 }
9854 n1->nredir.redirect = redir;
9855 }
Eric Andersencb57d552001-06-28 07:25:16 +00009856 return n1;
9857}
9858
Eric Andersencb57d552001-06-28 07:25:16 +00009859/*
9860 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9861 * is not NULL, read a here document. In the latter case, eofmark is the
9862 * word which marks the end of the document and striptabs is true if
9863 * leading tabs should be stripped from the document. The argument firstc
9864 * is the first character of the input token or document.
9865 *
9866 * Because C does not have internal subroutines, I have simulated them
9867 * using goto's to implement the subroutine linkage. The following macros
9868 * will run code that appears at the end of readtoken1.
9869 */
9870
Eric Andersen2870d962001-07-02 17:27:21 +00009871#define CHECKEND() {goto checkend; checkend_return:;}
9872#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9873#define PARSESUB() {goto parsesub; parsesub_return:;}
9874#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9875#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9876#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009877
9878static int
Eric Andersenc470f442003-07-28 09:56:35 +00009879readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009880{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009881 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009882 int c = firstc;
9883 char *out;
9884 int len;
9885 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009886 struct nodelist *bqlist;
9887 smallint quotef;
9888 smallint dblquote;
9889 smallint oldstyle;
9890 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009891#if ENABLE_ASH_EXPAND_PRMT
9892 smallint pssyntax; /* we are expanding a prompt string */
9893#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009894 int varnest; /* levels of variables expansion */
9895 int arinest; /* levels of arithmetic expansion */
9896 int parenlevel; /* levels of parens in arithmetic */
9897 int dqvarnest; /* levels of variables expansion within double quotes */
9898
Eric Andersencb57d552001-06-28 07:25:16 +00009899#if __GNUC__
9900 /* Avoid longjmp clobbering */
9901 (void) &out;
9902 (void) &quotef;
9903 (void) &dblquote;
9904 (void) &varnest;
9905 (void) &arinest;
9906 (void) &parenlevel;
9907 (void) &dqvarnest;
9908 (void) &oldstyle;
9909 (void) &prevsyntax;
9910 (void) &syntax;
9911#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009912 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009913 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009914 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009915 oldstyle = 0;
9916 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +00009917#if ENABLE_ASH_EXPAND_PRMT
9918 pssyntax = (syntax == PSSYNTAX);
9919 if (pssyntax)
9920 syntax = DQSYNTAX;
9921#endif
9922 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +00009923 varnest = 0;
9924 arinest = 0;
9925 parenlevel = 0;
9926 dqvarnest = 0;
9927
9928 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009929 loop: { /* for each line, until end of word */
9930 CHECKEND(); /* set c to PEOF if at end of here document */
9931 for (;;) { /* until end of line or end of word */
9932 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009933 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009934 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009935 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009936 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009937 USTPUTC(c, out);
9938 plinno++;
9939 if (doprompt)
9940 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009941 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009942 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009943 case CWORD:
9944 USTPUTC(c, out);
9945 break;
9946 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009947 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009948 USTPUTC(CTLESC, out);
9949 USTPUTC(c, out);
9950 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009951 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009952 c = pgetc2();
9953 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009954 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009955 USTPUTC('\\', out);
9956 pungetc();
9957 } else if (c == '\n') {
9958 if (doprompt)
9959 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009960 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +00009961#if ENABLE_ASH_EXPAND_PRMT
9962 if (c == '$' && pssyntax) {
9963 USTPUTC(CTLESC, out);
9964 USTPUTC('\\', out);
9965 }
9966#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009967 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009968 c != '\\' && c != '`' &&
9969 c != '$' && (
9970 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009971 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009972 ) {
9973 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009974 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009975 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009976 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009977 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009978 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009979 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009980 }
9981 break;
9982 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009983 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009984 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009985 if (eofmark == NULL) {
9986 USTPUTC(CTLQUOTEMARK, out);
9987 }
Eric Andersencb57d552001-06-28 07:25:16 +00009988 break;
9989 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009990 syntax = DQSYNTAX;
9991 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009992 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009993 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009994 if (eofmark != NULL && arinest == 0
9995 && varnest == 0
9996 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009997 USTPUTC(c, out);
9998 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009999 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010000 syntax = BASESYNTAX;
10001 dblquote = 0;
10002 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010003 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010004 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010005 }
10006 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010007 case CVAR: /* '$' */
10008 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010009 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010010 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010011 if (varnest > 0) {
10012 varnest--;
10013 if (dqvarnest > 0) {
10014 dqvarnest--;
10015 }
10016 USTPUTC(CTLENDVAR, out);
10017 } else {
10018 USTPUTC(c, out);
10019 }
10020 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010021#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010022 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010023 parenlevel++;
10024 USTPUTC(c, out);
10025 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010026 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010027 if (parenlevel > 0) {
10028 USTPUTC(c, out);
10029 --parenlevel;
10030 } else {
10031 if (pgetc() == ')') {
10032 if (--arinest == 0) {
10033 USTPUTC(CTLENDARI, out);
10034 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010035 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010036 } else
10037 USTPUTC(')', out);
10038 } else {
10039 /*
10040 * unbalanced parens
10041 * (don't 2nd guess - no error)
10042 */
10043 pungetc();
10044 USTPUTC(')', out);
10045 }
10046 }
10047 break;
10048#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010049 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010050 PARSEBACKQOLD();
10051 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010052 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010053 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010054 case CIGN:
10055 break;
10056 default:
10057 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010058 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010059#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010060 if (c != PEOA)
10061#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010062 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010063
Eric Andersencb57d552001-06-28 07:25:16 +000010064 }
10065 c = pgetc_macro();
10066 }
10067 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010068 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010069#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010070 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010071 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010072#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010073 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010074 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010075 if (varnest != 0) {
10076 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010077 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010078 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010079 }
10080 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010081 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010082 out = stackblock();
10083 if (eofmark == NULL) {
10084 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010085 && quotef == 0
10086 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010087 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010088 PARSEREDIR();
10089 return lasttoken = TREDIR;
10090 } else {
10091 pungetc();
10092 }
10093 }
10094 quoteflag = quotef;
10095 backquotelist = bqlist;
10096 grabstackblock(len);
10097 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010098 lasttoken = TWORD;
10099 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010100/* end of readtoken routine */
10101
Eric Andersencb57d552001-06-28 07:25:16 +000010102/*
10103 * Check to see whether we are at the end of the here document. When this
10104 * is called, c is set to the first character of the next input line. If
10105 * we are at the end of the here document, this routine sets the c to PEOF.
10106 */
Eric Andersenc470f442003-07-28 09:56:35 +000010107checkend: {
10108 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010109#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010110 if (c == PEOA) {
10111 c = pgetc2();
10112 }
10113#endif
10114 if (striptabs) {
10115 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010116 c = pgetc2();
10117 }
Eric Andersenc470f442003-07-28 09:56:35 +000010118 }
10119 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010120 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010121 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010122
Eric Andersenc470f442003-07-28 09:56:35 +000010123 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010124 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010125 if (*p == '\n' && *q == '\0') {
10126 c = PEOF;
10127 plinno++;
10128 needprompt = doprompt;
10129 } else {
10130 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010131 }
10132 }
10133 }
10134 }
Eric Andersenc470f442003-07-28 09:56:35 +000010135 goto checkend_return;
10136}
Eric Andersencb57d552001-06-28 07:25:16 +000010137
Eric Andersencb57d552001-06-28 07:25:16 +000010138/*
10139 * Parse a redirection operator. The variable "out" points to a string
10140 * specifying the fd to be redirected. The variable "c" contains the
10141 * first character of the redirection operator.
10142 */
Eric Andersenc470f442003-07-28 09:56:35 +000010143parseredir: {
10144 char fd = *out;
10145 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010146
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010147 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010148 if (c == '>') {
10149 np->nfile.fd = 1;
10150 c = pgetc();
10151 if (c == '>')
10152 np->type = NAPPEND;
10153 else if (c == '|')
10154 np->type = NCLOBBER;
10155 else if (c == '&')
10156 np->type = NTOFD;
10157 else {
10158 np->type = NTO;
10159 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010160 }
Eric Andersenc470f442003-07-28 09:56:35 +000010161 } else { /* c == '<' */
10162 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010163 c = pgetc();
10164 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010165 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010166 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010167 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010168 np->nfile.fd = 0;
10169 }
10170 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010171 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010172 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010173 c = pgetc();
10174 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010175 heredoc->striptabs = 1;
10176 } else {
10177 heredoc->striptabs = 0;
10178 pungetc();
10179 }
10180 break;
10181
10182 case '&':
10183 np->type = NFROMFD;
10184 break;
10185
10186 case '>':
10187 np->type = NFROMTO;
10188 break;
10189
10190 default:
10191 np->type = NFROM;
10192 pungetc();
10193 break;
10194 }
Eric Andersencb57d552001-06-28 07:25:16 +000010195 }
Eric Andersenc470f442003-07-28 09:56:35 +000010196 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010197 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010198 redirnode = np;
10199 goto parseredir_return;
10200}
Eric Andersencb57d552001-06-28 07:25:16 +000010201
Eric Andersencb57d552001-06-28 07:25:16 +000010202/*
10203 * Parse a substitution. At this point, we have read the dollar sign
10204 * and nothing else.
10205 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010206
10207/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10208 * (assuming ascii char codes, as the original implementation did) */
10209#define is_special(c) \
10210 ((((unsigned int)c) - 33 < 32) \
10211 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010212parsesub: {
10213 int subtype;
10214 int typeloc;
10215 int flags;
10216 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010217 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010218
Eric Andersenc470f442003-07-28 09:56:35 +000010219 c = pgetc();
10220 if (
10221 c <= PEOA_OR_PEOF ||
10222 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10223 ) {
10224 USTPUTC('$', out);
10225 pungetc();
10226 } else if (c == '(') { /* $(command) or $((arith)) */
10227 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010228#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010229 PARSEARITH();
10230#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010231 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010232#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010233 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010234 pungetc();
10235 PARSEBACKQNEW();
10236 }
10237 } else {
10238 USTPUTC(CTLVAR, out);
10239 typeloc = out - (char *)stackblock();
10240 USTPUTC(VSNORMAL, out);
10241 subtype = VSNORMAL;
10242 if (c == '{') {
10243 c = pgetc();
10244 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010245 c = pgetc();
10246 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010247 c = '#';
10248 else
10249 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010250 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010251 subtype = 0;
10252 }
10253 if (c > PEOA_OR_PEOF && is_name(c)) {
10254 do {
10255 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010256 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010257 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010258 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010259 do {
10260 STPUTC(c, out);
10261 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010262 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010263 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010264 USTPUTC(c, out);
10265 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010266 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010267 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010268
Eric Andersenc470f442003-07-28 09:56:35 +000010269 STPUTC('=', out);
10270 flags = 0;
10271 if (subtype == 0) {
10272 switch (c) {
10273 case ':':
10274 flags = VSNUL;
10275 c = pgetc();
10276 /*FALLTHROUGH*/
10277 default:
10278 p = strchr(types, c);
10279 if (p == NULL)
10280 goto badsub;
10281 subtype = p - types + VSNORMAL;
10282 break;
10283 case '%':
10284 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010285 {
10286 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010287 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010288 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010289 c = pgetc();
10290 if (c == cc)
10291 subtype++;
10292 else
10293 pungetc();
10294 break;
10295 }
10296 }
Eric Andersenc470f442003-07-28 09:56:35 +000010297 } else {
10298 pungetc();
10299 }
10300 if (dblquote || arinest)
10301 flags |= VSQUOTE;
10302 *((char *)stackblock() + typeloc) = subtype | flags;
10303 if (subtype != VSNORMAL) {
10304 varnest++;
10305 if (dblquote || arinest) {
10306 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010307 }
10308 }
10309 }
Eric Andersenc470f442003-07-28 09:56:35 +000010310 goto parsesub_return;
10311}
Eric Andersencb57d552001-06-28 07:25:16 +000010312
Eric Andersencb57d552001-06-28 07:25:16 +000010313/*
10314 * Called to parse command substitutions. Newstyle is set if the command
10315 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10316 * list of commands (passed by reference), and savelen is the number of
10317 * characters on the top of the stack which must be preserved.
10318 */
Eric Andersenc470f442003-07-28 09:56:35 +000010319parsebackq: {
10320 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010321 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010322 union node *n;
10323 char *volatile str;
10324 struct jmploc jmploc;
10325 struct jmploc *volatile savehandler;
10326 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010327 smallint saveprompt = 0;
10328
Eric Andersencb57d552001-06-28 07:25:16 +000010329#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010330 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010331#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010332 savepbq = parsebackquote;
10333 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010334 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010335 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010336 exception_handler = savehandler;
10337 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010338 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010339 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010340 str = NULL;
10341 savelen = out - (char *)stackblock();
10342 if (savelen > 0) {
10343 str = ckmalloc(savelen);
10344 memcpy(str, stackblock(), savelen);
10345 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010346 savehandler = exception_handler;
10347 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010348 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010349 if (oldstyle) {
10350 /* We must read until the closing backquote, giving special
10351 treatment to some slashes, and then push the string and
10352 reread it as input, interpreting it normally. */
10353 char *pout;
10354 int pc;
10355 size_t psavelen;
10356 char *pstr;
10357
10358
10359 STARTSTACKSTR(pout);
10360 for (;;) {
10361 if (needprompt) {
10362 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010363 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010364 pc = pgetc();
10365 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010366 case '`':
10367 goto done;
10368
10369 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010370 pc = pgetc();
10371 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010372 plinno++;
10373 if (doprompt)
10374 setprompt(2);
10375 /*
10376 * If eating a newline, avoid putting
10377 * the newline into the new character
10378 * stream (via the STPUTC after the
10379 * switch).
10380 */
10381 continue;
10382 }
10383 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010384 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010385 STPUTC('\\', pout);
10386 if (pc > PEOA_OR_PEOF) {
10387 break;
10388 }
10389 /* fall through */
10390
10391 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010392#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010393 case PEOA:
10394#endif
10395 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010396 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010397
10398 case '\n':
10399 plinno++;
10400 needprompt = doprompt;
10401 break;
10402
10403 default:
10404 break;
10405 }
10406 STPUTC(pc, pout);
10407 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010408 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010409 STPUTC('\0', pout);
10410 psavelen = pout - (char *)stackblock();
10411 if (psavelen > 0) {
10412 pstr = grabstackstr(pout);
10413 setinputstring(pstr);
10414 }
10415 }
10416 nlpp = &bqlist;
10417 while (*nlpp)
10418 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010419 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010420 (*nlpp)->next = NULL;
10421 parsebackquote = oldstyle;
10422
10423 if (oldstyle) {
10424 saveprompt = doprompt;
10425 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010426 }
10427
Eric Andersenc470f442003-07-28 09:56:35 +000010428 n = list(2);
10429
10430 if (oldstyle)
10431 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010432 else if (readtoken() != TRP)
10433 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010434
10435 (*nlpp)->n = n;
10436 if (oldstyle) {
10437 /*
10438 * Start reading from old file again, ignoring any pushed back
10439 * tokens left from the backquote parsing
10440 */
10441 popfile();
10442 tokpushback = 0;
10443 }
10444 while (stackblocksize() <= savelen)
10445 growstackblock();
10446 STARTSTACKSTR(out);
10447 if (str) {
10448 memcpy(out, str, savelen);
10449 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010450 INT_OFF;
10451 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010452 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010453 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010454 }
10455 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010456 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010457 if (arinest || dblquote)
10458 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10459 else
10460 USTPUTC(CTLBACKQ, out);
10461 if (oldstyle)
10462 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010463 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010464}
10465
Denis Vlasenko131ae172007-02-18 13:00:19 +000010466#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010467/*
10468 * Parse an arithmetic expansion (indicate start of one and set state)
10469 */
Eric Andersenc470f442003-07-28 09:56:35 +000010470parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010471 if (++arinest == 1) {
10472 prevsyntax = syntax;
10473 syntax = ARISYNTAX;
10474 USTPUTC(CTLARI, out);
10475 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010476 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010477 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010478 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010479 } else {
10480 /*
10481 * we collapse embedded arithmetic expansion to
10482 * parenthesis, which should be equivalent
10483 */
10484 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010485 }
Eric Andersenc470f442003-07-28 09:56:35 +000010486 goto parsearith_return;
10487}
10488#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010489
Eric Andersenc470f442003-07-28 09:56:35 +000010490} /* end of readtoken */
10491
Eric Andersencb57d552001-06-28 07:25:16 +000010492/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010493 * Read the next input token.
10494 * If the token is a word, we set backquotelist to the list of cmds in
10495 * backquotes. We set quoteflag to true if any part of the word was
10496 * quoted.
10497 * If the token is TREDIR, then we set redirnode to a structure containing
10498 * the redirection.
10499 * In all cases, the variable startlinno is set to the number of the line
10500 * on which the token starts.
10501 *
10502 * [Change comment: here documents and internal procedures]
10503 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10504 * word parsing code into a separate routine. In this case, readtoken
10505 * doesn't need to have any internal procedures, but parseword does.
10506 * We could also make parseoperator in essence the main routine, and
10507 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010508 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010509#define NEW_xxreadtoken
10510#ifdef NEW_xxreadtoken
10511/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010512static const char xxreadtoken_chars[7] ALIGN1 = {
10513 '\n', '(', ')', '&', '|', ';', 0
10514};
Eric Andersencb57d552001-06-28 07:25:16 +000010515
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010516static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010517 TNL, TLP, TRP, /* only single occurrence allowed */
10518 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10519 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010520 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010521};
10522
10523#define xxreadtoken_doubles \
10524 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10525#define xxreadtoken_singles \
10526 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10527
10528static int
10529xxreadtoken(void)
10530{
10531 int c;
10532
10533 if (tokpushback) {
10534 tokpushback = 0;
10535 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010536 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010537 if (needprompt) {
10538 setprompt(2);
10539 }
10540 startlinno = plinno;
10541 for (;;) { /* until token or start of word found */
10542 c = pgetc_macro();
10543
10544 if ((c != ' ') && (c != '\t')
10545#if ENABLE_ASH_ALIAS
10546 && (c != PEOA)
10547#endif
10548 ) {
10549 if (c == '#') {
10550 while ((c = pgetc()) != '\n' && c != PEOF);
10551 pungetc();
10552 } else if (c == '\\') {
10553 if (pgetc() != '\n') {
10554 pungetc();
10555 goto READTOKEN1;
10556 }
10557 startlinno = ++plinno;
10558 if (doprompt)
10559 setprompt(2);
10560 } else {
10561 const char *p
10562 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10563
10564 if (c != PEOF) {
10565 if (c == '\n') {
10566 plinno++;
10567 needprompt = doprompt;
10568 }
10569
10570 p = strchr(xxreadtoken_chars, c);
10571 if (p == NULL) {
10572 READTOKEN1:
10573 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10574 }
10575
10576 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10577 if (pgetc() == *p) { /* double occurrence? */
10578 p += xxreadtoken_doubles + 1;
10579 } else {
10580 pungetc();
10581 }
10582 }
10583 }
10584 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10585 }
10586 }
10587 } /* for */
10588}
10589#else
10590#define RETURN(token) return lasttoken = token
10591static int
10592xxreadtoken(void)
10593{
10594 int c;
10595
10596 if (tokpushback) {
10597 tokpushback = 0;
10598 return lasttoken;
10599 }
10600 if (needprompt) {
10601 setprompt(2);
10602 }
10603 startlinno = plinno;
10604 for (;;) { /* until token or start of word found */
10605 c = pgetc_macro();
10606 switch (c) {
10607 case ' ': case '\t':
10608#if ENABLE_ASH_ALIAS
10609 case PEOA:
10610#endif
10611 continue;
10612 case '#':
10613 while ((c = pgetc()) != '\n' && c != PEOF);
10614 pungetc();
10615 continue;
10616 case '\\':
10617 if (pgetc() == '\n') {
10618 startlinno = ++plinno;
10619 if (doprompt)
10620 setprompt(2);
10621 continue;
10622 }
10623 pungetc();
10624 goto breakloop;
10625 case '\n':
10626 plinno++;
10627 needprompt = doprompt;
10628 RETURN(TNL);
10629 case PEOF:
10630 RETURN(TEOF);
10631 case '&':
10632 if (pgetc() == '&')
10633 RETURN(TAND);
10634 pungetc();
10635 RETURN(TBACKGND);
10636 case '|':
10637 if (pgetc() == '|')
10638 RETURN(TOR);
10639 pungetc();
10640 RETURN(TPIPE);
10641 case ';':
10642 if (pgetc() == ';')
10643 RETURN(TENDCASE);
10644 pungetc();
10645 RETURN(TSEMI);
10646 case '(':
10647 RETURN(TLP);
10648 case ')':
10649 RETURN(TRP);
10650 default:
10651 goto breakloop;
10652 }
10653 }
10654 breakloop:
10655 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10656#undef RETURN
10657}
10658#endif /* NEW_xxreadtoken */
10659
10660static int
10661readtoken(void)
10662{
10663 int t;
10664#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010665 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010666#endif
10667
10668#if ENABLE_ASH_ALIAS
10669 top:
10670#endif
10671
10672 t = xxreadtoken();
10673
10674 /*
10675 * eat newlines
10676 */
10677 if (checkkwd & CHKNL) {
10678 while (t == TNL) {
10679 parseheredoc();
10680 t = xxreadtoken();
10681 }
10682 }
10683
10684 if (t != TWORD || quoteflag) {
10685 goto out;
10686 }
10687
10688 /*
10689 * check for keywords
10690 */
10691 if (checkkwd & CHKKWD) {
10692 const char *const *pp;
10693
10694 pp = findkwd(wordtext);
10695 if (pp) {
10696 lasttoken = t = pp - tokname_array;
10697 TRACE(("keyword %s recognized\n", tokname(t)));
10698 goto out;
10699 }
10700 }
10701
10702 if (checkkwd & CHKALIAS) {
10703#if ENABLE_ASH_ALIAS
10704 struct alias *ap;
10705 ap = lookupalias(wordtext, 1);
10706 if (ap != NULL) {
10707 if (*ap->val) {
10708 pushstring(ap->val, ap);
10709 }
10710 goto top;
10711 }
10712#endif
10713 }
10714 out:
10715 checkkwd = 0;
10716#if DEBUG
10717 if (!alreadyseen)
10718 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10719 else
10720 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10721#endif
10722 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010723}
10724
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010725static char
10726peektoken(void)
10727{
10728 int t;
10729
10730 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010731 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010732 return tokname_array[t][0];
10733}
Eric Andersencb57d552001-06-28 07:25:16 +000010734
10735/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010736 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10737 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010738 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010739static union node *
10740parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010741{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010742 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010743
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010744 tokpushback = 0;
10745 doprompt = interact;
10746 if (doprompt)
10747 setprompt(doprompt);
10748 needprompt = 0;
10749 t = readtoken();
10750 if (t == TEOF)
10751 return NEOF;
10752 if (t == TNL)
10753 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010754 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010755 return list(1);
10756}
10757
10758/*
10759 * Input any here documents.
10760 */
10761static void
10762parseheredoc(void)
10763{
10764 struct heredoc *here;
10765 union node *n;
10766
10767 here = heredoclist;
10768 heredoclist = 0;
10769
10770 while (here) {
10771 if (needprompt) {
10772 setprompt(2);
10773 }
10774 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10775 here->eofmark, here->striptabs);
10776 n = stalloc(sizeof(struct narg));
10777 n->narg.type = NARG;
10778 n->narg.next = NULL;
10779 n->narg.text = wordtext;
10780 n->narg.backquote = backquotelist;
10781 here->here->nhere.doc = n;
10782 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010783 }
Eric Andersencb57d552001-06-28 07:25:16 +000010784}
10785
10786
10787/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010788 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010789 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010790#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010791static const char *
10792expandstr(const char *ps)
10793{
10794 union node n;
10795
10796 /* XXX Fix (char *) cast. */
10797 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010798 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010799 popfile();
10800
10801 n.narg.type = NARG;
10802 n.narg.next = NULL;
10803 n.narg.text = wordtext;
10804 n.narg.backquote = backquotelist;
10805
10806 expandarg(&n, NULL, 0);
10807 return stackblock();
10808}
10809#endif
10810
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010811/*
10812 * Execute a command or commands contained in a string.
10813 */
10814static int
10815evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010816{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010817 union node *n;
10818 struct stackmark smark;
10819 int skip;
10820
10821 setinputstring(s);
10822 setstackmark(&smark);
10823
10824 skip = 0;
10825 while ((n = parsecmd(0)) != NEOF) {
10826 evaltree(n, 0);
10827 popstackmark(&smark);
10828 skip = evalskip;
10829 if (skip)
10830 break;
10831 }
10832 popfile();
10833
10834 skip &= mask;
10835 evalskip = skip;
10836 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010837}
10838
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010839/*
10840 * The eval command.
10841 */
10842static int
10843evalcmd(int argc, char **argv)
10844{
10845 char *p;
10846 char *concat;
10847 char **ap;
10848
10849 if (argc > 1) {
10850 p = argv[1];
10851 if (argc > 2) {
10852 STARTSTACKSTR(concat);
10853 ap = argv + 2;
10854 for (;;) {
10855 concat = stack_putstr(p, concat);
10856 p = *ap++;
10857 if (p == NULL)
10858 break;
10859 STPUTC(' ', concat);
10860 }
10861 STPUTC('\0', concat);
10862 p = grabstackstr(concat);
10863 }
10864 evalstring(p, ~SKIPEVAL);
10865
10866 }
10867 return exitstatus;
10868}
10869
10870/*
10871 * Read and execute commands. "Top" is nonzero for the top level command
10872 * loop; it turns on prompting if the shell is interactive.
10873 */
10874static int
10875cmdloop(int top)
10876{
10877 union node *n;
10878 struct stackmark smark;
10879 int inter;
10880 int numeof = 0;
10881
10882 TRACE(("cmdloop(%d) called\n", top));
10883 for (;;) {
10884 int skip;
10885
10886 setstackmark(&smark);
10887#if JOBS
10888 if (jobctl)
10889 showjobs(stderr, SHOW_CHANGED);
10890#endif
10891 inter = 0;
10892 if (iflag && top) {
10893 inter++;
10894#if ENABLE_ASH_MAIL
10895 chkmail();
10896#endif
10897 }
10898 n = parsecmd(inter);
10899 /* showtree(n); DEBUG */
10900 if (n == NEOF) {
10901 if (!top || numeof >= 50)
10902 break;
10903 if (!stoppedjobs()) {
10904 if (!Iflag)
10905 break;
10906 out2str("\nUse \"exit\" to leave shell.\n");
10907 }
10908 numeof++;
10909 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010910 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10911 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010912 numeof = 0;
10913 evaltree(n, 0);
10914 }
10915 popstackmark(&smark);
10916 skip = evalskip;
10917
10918 if (skip) {
10919 evalskip = 0;
10920 return skip & SKIPEVAL;
10921 }
10922 }
10923 return 0;
10924}
10925
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010926/*
10927 * Take commands from a file. To be compatible we should do a path
10928 * search for the file, which is necessary to find sub-commands.
10929 */
10930static char *
10931find_dot_file(char *name)
10932{
10933 char *fullname;
10934 const char *path = pathval();
10935 struct stat statb;
10936
10937 /* don't try this for absolute or relative paths */
10938 if (strchr(name, '/'))
10939 return name;
10940
10941 while ((fullname = padvance(&path, name)) != NULL) {
10942 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10943 /*
10944 * Don't bother freeing here, since it will
10945 * be freed by the caller.
10946 */
10947 return fullname;
10948 }
10949 stunalloc(fullname);
10950 }
10951
10952 /* not found in the PATH */
10953 ash_msg_and_raise_error("%s: not found", name);
10954 /* NOTREACHED */
10955}
10956
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010957static int
10958dotcmd(int argc, char **argv)
10959{
10960 struct strlist *sp;
10961 volatile struct shparam saveparam;
10962 int status = 0;
10963
10964 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010965 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010966
10967 if (argc >= 2) { /* That's what SVR2 does */
10968 char *fullname;
10969
10970 fullname = find_dot_file(argv[1]);
10971
10972 if (argc > 2) {
10973 saveparam = shellparam;
10974 shellparam.malloc = 0;
10975 shellparam.nparam = argc - 2;
10976 shellparam.p = argv + 2;
10977 };
10978
10979 setinputfile(fullname, INPUT_PUSH_FILE);
10980 commandname = fullname;
10981 cmdloop(0);
10982 popfile();
10983
10984 if (argc > 2) {
10985 freeparam(&shellparam);
10986 shellparam = saveparam;
10987 };
10988 status = exitstatus;
10989 }
10990 return status;
10991}
10992
10993static int
10994exitcmd(int argc, char **argv)
10995{
10996 if (stoppedjobs())
10997 return 0;
10998 if (argc > 1)
10999 exitstatus = number(argv[1]);
11000 raise_exception(EXEXIT);
11001 /* NOTREACHED */
11002}
11003
11004#if ENABLE_ASH_BUILTIN_ECHO
11005static int
11006echocmd(int argc, char **argv)
11007{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011008 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011009}
11010#endif
11011
11012#if ENABLE_ASH_BUILTIN_TEST
11013static int
11014testcmd(int argc, char **argv)
11015{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011016 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011017}
11018#endif
11019
11020/*
11021 * Read a file containing shell functions.
11022 */
11023static void
11024readcmdfile(char *name)
11025{
11026 setinputfile(name, INPUT_PUSH_FILE);
11027 cmdloop(0);
11028 popfile();
11029}
11030
11031
Denis Vlasenkocc571512007-02-23 21:10:35 +000011032/* ============ find_command inplementation */
11033
11034/*
11035 * Resolve a command name. If you change this routine, you may have to
11036 * change the shellexec routine as well.
11037 */
11038static void
11039find_command(char *name, struct cmdentry *entry, int act, const char *path)
11040{
11041 struct tblentry *cmdp;
11042 int idx;
11043 int prev;
11044 char *fullname;
11045 struct stat statb;
11046 int e;
11047 int updatetbl;
11048 struct builtincmd *bcmd;
11049
11050 /* If name contains a slash, don't use PATH or hash table */
11051 if (strchr(name, '/') != NULL) {
11052 entry->u.index = -1;
11053 if (act & DO_ABS) {
11054 while (stat(name, &statb) < 0) {
11055#ifdef SYSV
11056 if (errno == EINTR)
11057 continue;
11058#endif
11059 entry->cmdtype = CMDUNKNOWN;
11060 return;
11061 }
11062 }
11063 entry->cmdtype = CMDNORMAL;
11064 return;
11065 }
11066
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011067/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011068
11069 updatetbl = (path == pathval());
11070 if (!updatetbl) {
11071 act |= DO_ALTPATH;
11072 if (strstr(path, "%builtin") != NULL)
11073 act |= DO_ALTBLTIN;
11074 }
11075
11076 /* If name is in the table, check answer will be ok */
11077 cmdp = cmdlookup(name, 0);
11078 if (cmdp != NULL) {
11079 int bit;
11080
11081 switch (cmdp->cmdtype) {
11082 default:
11083#if DEBUG
11084 abort();
11085#endif
11086 case CMDNORMAL:
11087 bit = DO_ALTPATH;
11088 break;
11089 case CMDFUNCTION:
11090 bit = DO_NOFUNC;
11091 break;
11092 case CMDBUILTIN:
11093 bit = DO_ALTBLTIN;
11094 break;
11095 }
11096 if (act & bit) {
11097 updatetbl = 0;
11098 cmdp = NULL;
11099 } else if (cmdp->rehash == 0)
11100 /* if not invalidated by cd, we're done */
11101 goto success;
11102 }
11103
11104 /* If %builtin not in path, check for builtin next */
11105 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011106 if (bcmd) {
11107 if (IS_BUILTIN_REGULAR(bcmd))
11108 goto builtin_success;
11109 if (act & DO_ALTPATH) {
11110 if (!(act & DO_ALTBLTIN))
11111 goto builtin_success;
11112 } else if (builtinloc <= 0) {
11113 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011114 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011115 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011116
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011117#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011118 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011119 entry->cmdtype = CMDNORMAL;
11120 entry->u.index = -1;
11121 return;
11122 }
11123#endif
11124
Denis Vlasenkocc571512007-02-23 21:10:35 +000011125 /* We have to search path. */
11126 prev = -1; /* where to start */
11127 if (cmdp && cmdp->rehash) { /* doing a rehash */
11128 if (cmdp->cmdtype == CMDBUILTIN)
11129 prev = builtinloc;
11130 else
11131 prev = cmdp->param.index;
11132 }
11133
11134 e = ENOENT;
11135 idx = -1;
11136 loop:
11137 while ((fullname = padvance(&path, name)) != NULL) {
11138 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011139 /* NB: code below will still use fullname
11140 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011141 idx++;
11142 if (pathopt) {
11143 if (prefix(pathopt, "builtin")) {
11144 if (bcmd)
11145 goto builtin_success;
11146 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011147 } else if (!(act & DO_NOFUNC)
11148 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011149 /* handled below */
11150 } else {
11151 /* ignore unimplemented options */
11152 continue;
11153 }
11154 }
11155 /* if rehash, don't redo absolute path names */
11156 if (fullname[0] == '/' && idx <= prev) {
11157 if (idx < prev)
11158 continue;
11159 TRACE(("searchexec \"%s\": no change\n", name));
11160 goto success;
11161 }
11162 while (stat(fullname, &statb) < 0) {
11163#ifdef SYSV
11164 if (errno == EINTR)
11165 continue;
11166#endif
11167 if (errno != ENOENT && errno != ENOTDIR)
11168 e = errno;
11169 goto loop;
11170 }
11171 e = EACCES; /* if we fail, this will be the error */
11172 if (!S_ISREG(statb.st_mode))
11173 continue;
11174 if (pathopt) { /* this is a %func directory */
11175 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011176 /* NB: stalloc will return space pointed by fullname
11177 * (because we don't have any intervening allocations
11178 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011179 readcmdfile(fullname);
11180 cmdp = cmdlookup(name, 0);
11181 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11182 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11183 stunalloc(fullname);
11184 goto success;
11185 }
11186 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11187 if (!updatetbl) {
11188 entry->cmdtype = CMDNORMAL;
11189 entry->u.index = idx;
11190 return;
11191 }
11192 INT_OFF;
11193 cmdp = cmdlookup(name, 1);
11194 cmdp->cmdtype = CMDNORMAL;
11195 cmdp->param.index = idx;
11196 INT_ON;
11197 goto success;
11198 }
11199
11200 /* We failed. If there was an entry for this command, delete it */
11201 if (cmdp && updatetbl)
11202 delete_cmd_entry();
11203 if (act & DO_ERR)
11204 ash_msg("%s: %s", name, errmsg(e, "not found"));
11205 entry->cmdtype = CMDUNKNOWN;
11206 return;
11207
11208 builtin_success:
11209 if (!updatetbl) {
11210 entry->cmdtype = CMDBUILTIN;
11211 entry->u.cmd = bcmd;
11212 return;
11213 }
11214 INT_OFF;
11215 cmdp = cmdlookup(name, 1);
11216 cmdp->cmdtype = CMDBUILTIN;
11217 cmdp->param.cmd = bcmd;
11218 INT_ON;
11219 success:
11220 cmdp->rehash = 0;
11221 entry->cmdtype = cmdp->cmdtype;
11222 entry->u = cmdp->param;
11223}
11224
11225
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011226/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011227
Eric Andersencb57d552001-06-28 07:25:16 +000011228/*
Eric Andersencb57d552001-06-28 07:25:16 +000011229 * The trap builtin.
11230 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011231static int
Eric Andersenc470f442003-07-28 09:56:35 +000011232trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011233{
11234 char *action;
11235 char **ap;
11236 int signo;
11237
Eric Andersenc470f442003-07-28 09:56:35 +000011238 nextopt(nullstr);
11239 ap = argptr;
11240 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011241 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011242 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011243 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011244
Rob Landleyc9c1a412006-07-12 19:17:55 +000011245 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011246 out1fmt("trap -- %s %s\n",
11247 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011248 }
11249 }
11250 return 0;
11251 }
Eric Andersenc470f442003-07-28 09:56:35 +000011252 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011253 action = NULL;
11254 else
11255 action = *ap++;
11256 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011257 signo = get_signum(*ap);
11258 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011259 ash_msg_and_raise_error("%s: bad trap", *ap);
11260 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011261 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011262 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011263 action = NULL;
11264 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011265 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011266 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011267 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011268 trap[signo] = action;
11269 if (signo != 0)
11270 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011271 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011272 ap++;
11273 }
11274 return 0;
11275}
11276
Eric Andersenc470f442003-07-28 09:56:35 +000011277
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011278/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011279
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011280#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011281/*
11282 * Lists available builtins
11283 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011284static int
11285helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011286{
11287 int col, i;
11288
11289 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011290 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011291 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011292 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011293 if (col > 60) {
11294 out1fmt("\n");
11295 col = 0;
11296 }
11297 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011298#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011299 {
11300 const char *a = applet_names;
11301 while (*a) {
11302 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11303 if (col > 60) {
11304 out1fmt("\n");
11305 col = 0;
11306 }
11307 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011308 }
11309 }
11310#endif
11311 out1fmt("\n\n");
11312 return EXIT_SUCCESS;
11313}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011314#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011315
Eric Andersencb57d552001-06-28 07:25:16 +000011316/*
Eric Andersencb57d552001-06-28 07:25:16 +000011317 * The export and readonly commands.
11318 */
Eric Andersenc470f442003-07-28 09:56:35 +000011319static int
11320exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011321{
11322 struct var *vp;
11323 char *name;
11324 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011325 char **aptr;
11326 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011327
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011328 if (nextopt("p") != 'p') {
11329 aptr = argptr;
11330 name = *aptr;
11331 if (name) {
11332 do {
11333 p = strchr(name, '=');
11334 if (p != NULL) {
11335 p++;
11336 } else {
11337 vp = *findvar(hashvar(name), name);
11338 if (vp) {
11339 vp->flags |= flag;
11340 continue;
11341 }
Eric Andersencb57d552001-06-28 07:25:16 +000011342 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011343 setvar(name, p, flag);
11344 } while ((name = *++aptr) != NULL);
11345 return 0;
11346 }
Eric Andersencb57d552001-06-28 07:25:16 +000011347 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011348 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011349 return 0;
11350}
11351
Eric Andersencb57d552001-06-28 07:25:16 +000011352/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011353 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011354 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011355static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011356unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011357{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011358 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011359
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011360 cmdp = cmdlookup(name, 0);
11361 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11362 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011363}
11364
Eric Andersencb57d552001-06-28 07:25:16 +000011365/*
Eric Andersencb57d552001-06-28 07:25:16 +000011366 * The unset builtin command. We unset the function before we unset the
11367 * variable to allow a function to be unset when there is a readonly variable
11368 * with the same name.
11369 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011370static int
Eric Andersenc470f442003-07-28 09:56:35 +000011371unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011372{
11373 char **ap;
11374 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011375 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011376 int ret = 0;
11377
11378 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011379 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011380 }
Eric Andersencb57d552001-06-28 07:25:16 +000011381
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011382 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011383 if (flag != 'f') {
11384 i = unsetvar(*ap);
11385 ret |= i;
11386 if (!(i & 2))
11387 continue;
11388 }
11389 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011390 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011391 }
Eric Andersenc470f442003-07-28 09:56:35 +000011392 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011393}
11394
11395
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011396/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011397
Eric Andersenc470f442003-07-28 09:56:35 +000011398#include <sys/times.h>
11399
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011400static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011401 ' ', offsetof(struct tms, tms_utime),
11402 '\n', offsetof(struct tms, tms_stime),
11403 ' ', offsetof(struct tms, tms_cutime),
11404 '\n', offsetof(struct tms, tms_cstime),
11405 0
11406};
Eric Andersencb57d552001-06-28 07:25:16 +000011407
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011408static int
11409timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011410{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011411 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011412 const unsigned char *p;
11413 struct tms buf;
11414
11415 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011416 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011417
11418 p = timescmd_str;
11419 do {
11420 t = *(clock_t *)(((char *) &buf) + p[1]);
11421 s = t / clk_tck;
11422 out1fmt("%ldm%ld.%.3lds%c",
11423 s/60, s%60,
11424 ((t - s * clk_tck) * 1000) / clk_tck,
11425 p[0]);
11426 } while (*(p += 2));
11427
Eric Andersencb57d552001-06-28 07:25:16 +000011428 return 0;
11429}
11430
Denis Vlasenko131ae172007-02-18 13:00:19 +000011431#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011432static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011433dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011434{
Eric Andersened9ecf72004-06-22 08:29:45 +000011435 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011436 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011437
Denis Vlasenkob012b102007-02-19 22:43:01 +000011438 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011439 result = arith(s, &errcode);
11440 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011441 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011442 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011443 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011444 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011445 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011446 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011447 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011448 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011449 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011450
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011451 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011452}
Eric Andersenc470f442003-07-28 09:56:35 +000011453
Eric Andersenc470f442003-07-28 09:56:35 +000011454/*
Eric Andersen90898442003-08-06 11:20:52 +000011455 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11456 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11457 *
11458 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011459 */
11460static int
Eric Andersen90898442003-08-06 11:20:52 +000011461letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011462{
Eric Andersenc470f442003-07-28 09:56:35 +000011463 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011464 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011465
Eric Andersen90898442003-08-06 11:20:52 +000011466 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011467 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011468 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011469 for (ap = argv + 1; *ap; ap++) {
11470 i = dash_arith(*ap);
11471 }
Eric Andersenc470f442003-07-28 09:56:35 +000011472
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011473 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011474}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011475#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011476
Eric Andersenc470f442003-07-28 09:56:35 +000011477
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011478/* ============ miscbltin.c
11479 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011480 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011481 */
11482
11483#undef rflag
11484
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011485#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011486typedef enum __rlimit_resource rlim_t;
11487#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011488
Eric Andersenc470f442003-07-28 09:56:35 +000011489/*
11490 * The read builtin. The -e option causes backslashes to escape the
11491 * following character.
11492 *
11493 * This uses unbuffered input, which may be avoidable in some cases.
11494 */
Eric Andersenc470f442003-07-28 09:56:35 +000011495static int
11496readcmd(int argc, char **argv)
11497{
11498 char **ap;
11499 int backslash;
11500 char c;
11501 int rflag;
11502 char *prompt;
11503 const char *ifs;
11504 char *p;
11505 int startword;
11506 int status;
11507 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011508#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011509 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011510 int nchars = 0;
11511 int silent = 0;
11512 struct termios tty, old_tty;
11513#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011514#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011515 fd_set set;
11516 struct timeval ts;
11517
11518 ts.tv_sec = ts.tv_usec = 0;
11519#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011520
11521 rflag = 0;
11522 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011523#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011524 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011525#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011526 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011527#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011528 while ((i = nextopt("p:rt:")) != '\0')
11529#else
11530 while ((i = nextopt("p:r")) != '\0')
11531#endif
11532 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011533 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011534 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011535 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011536 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011537#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011538 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011539 nchars = bb_strtou(optionarg, NULL, 10);
11540 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011541 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011542 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011543 break;
11544 case 's':
11545 silent = 1;
11546 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011547#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011548#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011549 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011550 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011551 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011552 /* EINVAL means number is ok, but not terminated by NUL */
11553 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011554 char *p2;
11555 if (*++p) {
11556 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011557 ts.tv_usec = bb_strtou(p, &p2, 10);
11558 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011559 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011560 scale = p2 - p;
11561 /* normalize to usec */
11562 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011563 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011564 while (scale++ < 6)
11565 ts.tv_usec *= 10;
11566 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011567 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011568 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011569 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011570 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011571 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011572 }
Paul Fox02eb9342005-09-07 16:56:02 +000011573 break;
11574#endif
11575 case 'r':
11576 rflag = 1;
11577 break;
11578 default:
11579 break;
11580 }
Eric Andersenc470f442003-07-28 09:56:35 +000011581 }
11582 if (prompt && isatty(0)) {
11583 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011584 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011585 ap = argptr;
11586 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011587 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011588 ifs = bltinlookup("IFS");
11589 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011590 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011591#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011592 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011593 if (tcgetattr(0, &tty) != 0) {
11594 /* Not a tty */
11595 n_flag = 0;
11596 silent = 0;
11597 } else {
11598 old_tty = tty;
11599 if (n_flag) {
11600 tty.c_lflag &= ~ICANON;
11601 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11602 }
11603 if (silent) {
11604 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11605 }
11606 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011607 }
Paul Fox02eb9342005-09-07 16:56:02 +000011608 }
11609#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011610#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011611 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011612 FD_ZERO(&set);
11613 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011614
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011615 /* poll-based wait produces bigger code, using select */
11616 i = select(1, &set, NULL, NULL, &ts);
11617 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011618#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011619 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011620 tcsetattr(0, TCSANOW, &old_tty);
11621#endif
11622 return 1;
11623 }
11624 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011625#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011626 status = 0;
11627 startword = 1;
11628 backslash = 0;
11629 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011630 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011631 if (read(0, &c, 1) != 1) {
11632 status = 1;
11633 break;
11634 }
11635 if (c == '\0')
11636 continue;
11637 if (backslash) {
11638 backslash = 0;
11639 if (c != '\n')
11640 goto put;
11641 continue;
11642 }
11643 if (!rflag && c == '\\') {
11644 backslash++;
11645 continue;
11646 }
11647 if (c == '\n')
11648 break;
11649 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11650 continue;
11651 }
11652 startword = 0;
11653 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11654 STACKSTRNUL(p);
11655 setvar(*ap, stackblock(), 0);
11656 ap++;
11657 startword = 1;
11658 STARTSTACKSTR(p);
11659 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011660 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011661 STPUTC(c, p);
11662 }
11663 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011664/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011665#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011666 while (!n_flag || --nchars);
11667#else
11668 while (1);
11669#endif
11670
11671#if ENABLE_ASH_READ_NCHARS
11672 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011673 tcsetattr(0, TCSANOW, &old_tty);
11674#endif
11675
Eric Andersenc470f442003-07-28 09:56:35 +000011676 STACKSTRNUL(p);
11677 /* Remove trailing blanks */
11678 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11679 *p = '\0';
11680 setvar(*ap, stackblock(), 0);
11681 while (*++ap != NULL)
11682 setvar(*ap, nullstr, 0);
11683 return status;
11684}
11685
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011686static int
11687umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011688{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011689 static const char permuser[3] ALIGN1 = "ugo";
11690 static const char permmode[3] ALIGN1 = "rwx";
11691 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011692 S_IRUSR, S_IWUSR, S_IXUSR,
11693 S_IRGRP, S_IWGRP, S_IXGRP,
11694 S_IROTH, S_IWOTH, S_IXOTH
11695 };
11696
11697 char *ap;
11698 mode_t mask;
11699 int i;
11700 int symbolic_mode = 0;
11701
11702 while (nextopt("S") != '\0') {
11703 symbolic_mode = 1;
11704 }
11705
Denis Vlasenkob012b102007-02-19 22:43:01 +000011706 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011707 mask = umask(0);
11708 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011709 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011710
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011711 ap = *argptr;
11712 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011713 if (symbolic_mode) {
11714 char buf[18];
11715 char *p = buf;
11716
11717 for (i = 0; i < 3; i++) {
11718 int j;
11719
11720 *p++ = permuser[i];
11721 *p++ = '=';
11722 for (j = 0; j < 3; j++) {
11723 if ((mask & permmask[3 * i + j]) == 0) {
11724 *p++ = permmode[j];
11725 }
11726 }
11727 *p++ = ',';
11728 }
11729 *--p = 0;
11730 puts(buf);
11731 } else {
11732 out1fmt("%.4o\n", mask);
11733 }
11734 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011735 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011736 mask = 0;
11737 do {
11738 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011739 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011740 mask = (mask << 3) + (*ap - '0');
11741 } while (*++ap != '\0');
11742 umask(mask);
11743 } else {
11744 mask = ~mask & 0777;
11745 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011746 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011747 }
11748 umask(~mask & 0777);
11749 }
11750 }
11751 return 0;
11752}
11753
11754/*
11755 * ulimit builtin
11756 *
11757 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11758 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11759 * ash by J.T. Conklin.
11760 *
11761 * Public domain.
11762 */
11763
11764struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011765 uint8_t cmd; /* RLIMIT_xxx fit into it */
11766 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011767 char option;
11768};
11769
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011770static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011771#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011772 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011773#endif
11774#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011775 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011776#endif
11777#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011778 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011779#endif
11780#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011781 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011782#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011783#ifdef RLIMIT_CORE
11784 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011785#endif
11786#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011787 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011788#endif
11789#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011790 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011791#endif
11792#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011793 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011794#endif
11795#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011796 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011797#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011798#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011799 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011800#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011801#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011802 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011803#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011804};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011805static const char limits_name[] =
11806#ifdef RLIMIT_CPU
11807 "time(seconds)" "\0"
11808#endif
11809#ifdef RLIMIT_FSIZE
11810 "file(blocks)" "\0"
11811#endif
11812#ifdef RLIMIT_DATA
11813 "data(kb)" "\0"
11814#endif
11815#ifdef RLIMIT_STACK
11816 "stack(kb)" "\0"
11817#endif
11818#ifdef RLIMIT_CORE
11819 "coredump(blocks)" "\0"
11820#endif
11821#ifdef RLIMIT_RSS
11822 "memory(kb)" "\0"
11823#endif
11824#ifdef RLIMIT_MEMLOCK
11825 "locked memory(kb)" "\0"
11826#endif
11827#ifdef RLIMIT_NPROC
11828 "process" "\0"
11829#endif
11830#ifdef RLIMIT_NOFILE
11831 "nofiles" "\0"
11832#endif
11833#ifdef RLIMIT_AS
11834 "vmemory(kb)" "\0"
11835#endif
11836#ifdef RLIMIT_LOCKS
11837 "locks" "\0"
11838#endif
11839;
Eric Andersenc470f442003-07-28 09:56:35 +000011840
Glenn L McGrath76620622004-01-13 10:19:37 +000011841enum limtype { SOFT = 0x1, HARD = 0x2 };
11842
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011843static void
11844printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011845 const struct limits *l)
11846{
11847 rlim_t val;
11848
11849 val = limit->rlim_max;
11850 if (how & SOFT)
11851 val = limit->rlim_cur;
11852
11853 if (val == RLIM_INFINITY)
11854 out1fmt("unlimited\n");
11855 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011856 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011857 out1fmt("%lld\n", (long long) val);
11858 }
11859}
11860
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011861static int
Eric Andersenc470f442003-07-28 09:56:35 +000011862ulimitcmd(int argc, char **argv)
11863{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011864 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011865 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011866 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011867 const struct limits *l;
11868 int set, all = 0;
11869 int optc, what;
11870 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011871
11872 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011873 while ((optc = nextopt("HSa"
11874#ifdef RLIMIT_CPU
11875 "t"
11876#endif
11877#ifdef RLIMIT_FSIZE
11878 "f"
11879#endif
11880#ifdef RLIMIT_DATA
11881 "d"
11882#endif
11883#ifdef RLIMIT_STACK
11884 "s"
11885#endif
11886#ifdef RLIMIT_CORE
11887 "c"
11888#endif
11889#ifdef RLIMIT_RSS
11890 "m"
11891#endif
11892#ifdef RLIMIT_MEMLOCK
11893 "l"
11894#endif
11895#ifdef RLIMIT_NPROC
11896 "p"
11897#endif
11898#ifdef RLIMIT_NOFILE
11899 "n"
11900#endif
11901#ifdef RLIMIT_AS
11902 "v"
11903#endif
11904#ifdef RLIMIT_LOCKS
11905 "w"
11906#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011907 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011908 switch (optc) {
11909 case 'H':
11910 how = HARD;
11911 break;
11912 case 'S':
11913 how = SOFT;
11914 break;
11915 case 'a':
11916 all = 1;
11917 break;
11918 default:
11919 what = optc;
11920 }
11921
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011922 for (l = limits_tbl; l->option != what; l++)
11923 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011924
11925 set = *argptr ? 1 : 0;
11926 if (set) {
11927 char *p = *argptr;
11928
11929 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011930 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011931 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011932 val = RLIM_INFINITY;
11933 else {
11934 val = (rlim_t) 0;
11935
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011936 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011937 val = (val * 10) + (long)(c - '0');
11938 if (val < (rlim_t) 0)
11939 break;
11940 }
11941 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011942 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011943 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000011944 }
11945 }
11946 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011947 const char *lname = limits_name;
11948 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011949 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011950 out1fmt("%-20s ", lname);
11951 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000011952 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011953 }
11954 return 0;
11955 }
11956
11957 getrlimit(l->cmd, &limit);
11958 if (set) {
11959 if (how & HARD)
11960 limit.rlim_max = val;
11961 if (how & SOFT)
11962 limit.rlim_cur = val;
11963 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011964 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011965 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011966 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011967 }
11968 return 0;
11969}
11970
Eric Andersen90898442003-08-06 11:20:52 +000011971
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011972/* ============ Math support */
11973
Denis Vlasenko131ae172007-02-18 13:00:19 +000011974#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011975
11976/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11977
11978 Permission is hereby granted, free of charge, to any person obtaining
11979 a copy of this software and associated documentation files (the
11980 "Software"), to deal in the Software without restriction, including
11981 without limitation the rights to use, copy, modify, merge, publish,
11982 distribute, sublicense, and/or sell copies of the Software, and to
11983 permit persons to whom the Software is furnished to do so, subject to
11984 the following conditions:
11985
11986 The above copyright notice and this permission notice shall be
11987 included in all copies or substantial portions of the Software.
11988
11989 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11990 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11991 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11992 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11993 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11994 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11995 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11996*/
11997
11998/* This is my infix parser/evaluator. It is optimized for size, intended
11999 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012000 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012001 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012002 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012003 * be that which POSIX specifies for shells. */
12004
12005/* The code uses a simple two-stack algorithm. See
12006 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012007 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012008 * this is based (this code differs in that it applies operators immediately
12009 * to the stack instead of adding them to a queue to end up with an
12010 * expression). */
12011
12012/* To use the routine, call it with an expression string and error return
12013 * pointer */
12014
12015/*
12016 * Aug 24, 2001 Manuel Novoa III
12017 *
12018 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12019 *
12020 * 1) In arith_apply():
12021 * a) Cached values of *numptr and &(numptr[-1]).
12022 * b) Removed redundant test for zero denominator.
12023 *
12024 * 2) In arith():
12025 * a) Eliminated redundant code for processing operator tokens by moving
12026 * to a table-based implementation. Also folded handling of parens
12027 * into the table.
12028 * b) Combined all 3 loops which called arith_apply to reduce generated
12029 * code size at the cost of speed.
12030 *
12031 * 3) The following expressions were treated as valid by the original code:
12032 * 1() , 0! , 1 ( *3 ) .
12033 * These bugs have been fixed by internally enclosing the expression in
12034 * parens and then checking that all binary ops and right parens are
12035 * preceded by a valid expression (NUM_TOKEN).
12036 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012037 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012038 * ctype's isspace() if it is used by another busybox applet or if additional
12039 * whitespace chars should be considered. Look below the "#include"s for a
12040 * precompiler test.
12041 */
12042
12043/*
12044 * Aug 26, 2001 Manuel Novoa III
12045 *
12046 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12047 *
12048 * Merge in Aaron's comments previously posted to the busybox list,
12049 * modified slightly to take account of my changes to the code.
12050 *
12051 */
12052
12053/*
12054 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12055 *
12056 * - allow access to variable,
12057 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12058 * - realize assign syntax (VAR=expr, +=, *= etc)
12059 * - realize exponentiation (** operator)
12060 * - realize comma separated - expr, expr
12061 * - realise ++expr --expr expr++ expr--
12062 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012063 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012064 * - was restored loses XOR operator
12065 * - remove one goto label, added three ;-)
12066 * - protect $((num num)) as true zero expr (Manuel`s error)
12067 * - always use special isspace(), see comment from bash ;-)
12068 */
12069
Eric Andersen90898442003-08-06 11:20:52 +000012070#define arith_isspace(arithval) \
12071 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12072
Eric Andersen90898442003-08-06 11:20:52 +000012073typedef unsigned char operator;
12074
12075/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012076 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012077 * precedence. The ID portion is so that multiple operators can have the
12078 * same precedence, ensuring that the leftmost one is evaluated first.
12079 * Consider * and /. */
12080
12081#define tok_decl(prec,id) (((id)<<5)|(prec))
12082#define PREC(op) ((op) & 0x1F)
12083
12084#define TOK_LPAREN tok_decl(0,0)
12085
12086#define TOK_COMMA tok_decl(1,0)
12087
12088#define TOK_ASSIGN tok_decl(2,0)
12089#define TOK_AND_ASSIGN tok_decl(2,1)
12090#define TOK_OR_ASSIGN tok_decl(2,2)
12091#define TOK_XOR_ASSIGN tok_decl(2,3)
12092#define TOK_PLUS_ASSIGN tok_decl(2,4)
12093#define TOK_MINUS_ASSIGN tok_decl(2,5)
12094#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12095#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12096
12097#define TOK_MUL_ASSIGN tok_decl(3,0)
12098#define TOK_DIV_ASSIGN tok_decl(3,1)
12099#define TOK_REM_ASSIGN tok_decl(3,2)
12100
12101/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012102#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012103
12104/* conditional is right associativity too */
12105#define TOK_CONDITIONAL tok_decl(4,0)
12106#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12107
12108#define TOK_OR tok_decl(5,0)
12109
12110#define TOK_AND tok_decl(6,0)
12111
12112#define TOK_BOR tok_decl(7,0)
12113
12114#define TOK_BXOR tok_decl(8,0)
12115
12116#define TOK_BAND tok_decl(9,0)
12117
12118#define TOK_EQ tok_decl(10,0)
12119#define TOK_NE tok_decl(10,1)
12120
12121#define TOK_LT tok_decl(11,0)
12122#define TOK_GT tok_decl(11,1)
12123#define TOK_GE tok_decl(11,2)
12124#define TOK_LE tok_decl(11,3)
12125
12126#define TOK_LSHIFT tok_decl(12,0)
12127#define TOK_RSHIFT tok_decl(12,1)
12128
12129#define TOK_ADD tok_decl(13,0)
12130#define TOK_SUB tok_decl(13,1)
12131
12132#define TOK_MUL tok_decl(14,0)
12133#define TOK_DIV tok_decl(14,1)
12134#define TOK_REM tok_decl(14,2)
12135
12136/* exponent is right associativity */
12137#define TOK_EXPONENT tok_decl(15,1)
12138
12139/* For now unary operators. */
12140#define UNARYPREC 16
12141#define TOK_BNOT tok_decl(UNARYPREC,0)
12142#define TOK_NOT tok_decl(UNARYPREC,1)
12143
12144#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12145#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12146
12147#define PREC_PRE (UNARYPREC+2)
12148
12149#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12150#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12151
12152#define PREC_POST (UNARYPREC+3)
12153
12154#define TOK_POST_INC tok_decl(PREC_POST, 0)
12155#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12156
12157#define SPEC_PREC (UNARYPREC+4)
12158
12159#define TOK_NUM tok_decl(SPEC_PREC, 0)
12160#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12161
12162#define NUMPTR (*numstackptr)
12163
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012164static int
12165tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012166{
12167 operator prec = PREC(op);
12168
12169 convert_prec_is_assing(prec);
12170 return (prec == PREC(TOK_ASSIGN) ||
12171 prec == PREC_PRE || prec == PREC_POST);
12172}
12173
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012174static int
12175is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012176{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012177 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12178 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012179}
12180
Eric Andersen90898442003-08-06 11:20:52 +000012181typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012182 arith_t val;
12183 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012184 char contidional_second_val_initialized;
12185 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012186 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012187} v_n_t;
12188
Eric Andersen90898442003-08-06 11:20:52 +000012189typedef struct CHK_VAR_RECURSIVE_LOOPED {
12190 const char *var;
12191 struct CHK_VAR_RECURSIVE_LOOPED *next;
12192} chk_var_recursive_looped_t;
12193
12194static chk_var_recursive_looped_t *prev_chk_var_recursive;
12195
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012196static int
12197arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012198{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012199 if (t->var) {
12200 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012201
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012202 if (p) {
12203 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012204
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012205 /* recursive try as expression */
12206 chk_var_recursive_looped_t *cur;
12207 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012208
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012209 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12210 if (strcmp(cur->var, t->var) == 0) {
12211 /* expression recursion loop detected */
12212 return -5;
12213 }
12214 }
12215 /* save current lookuped var name */
12216 cur = prev_chk_var_recursive;
12217 cur_save.var = t->var;
12218 cur_save.next = cur;
12219 prev_chk_var_recursive = &cur_save;
12220
12221 t->val = arith (p, &errcode);
12222 /* restore previous ptr after recursiving */
12223 prev_chk_var_recursive = cur;
12224 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012225 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012226 /* allow undefined var as 0 */
12227 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012228 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012229 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012230}
12231
12232/* "applying" a token means performing it on the top elements on the integer
12233 * stack. For a unary operator it will only change the top element, but a
12234 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012235static int
12236arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012237{
Eric Andersen90898442003-08-06 11:20:52 +000012238 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012239 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012240 int ret_arith_lookup_val;
12241
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012242 /* There is no operator that can work without arguments */
12243 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012244 numptr_m1 = NUMPTR - 1;
12245
12246 /* check operand is var with noninteger value */
12247 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012248 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012249 return ret_arith_lookup_val;
12250
12251 rez = numptr_m1->val;
12252 if (op == TOK_UMINUS)
12253 rez *= -1;
12254 else if (op == TOK_NOT)
12255 rez = !rez;
12256 else if (op == TOK_BNOT)
12257 rez = ~rez;
12258 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12259 rez++;
12260 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12261 rez--;
12262 else if (op != TOK_UPLUS) {
12263 /* Binary operators */
12264
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012265 /* check and binary operators need two arguments */
12266 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012267
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012268 /* ... and they pop one */
12269 --NUMPTR;
12270 numptr_val = rez;
12271 if (op == TOK_CONDITIONAL) {
12272 if (! numptr_m1->contidional_second_val_initialized) {
12273 /* protect $((expr1 ? expr2)) without ": expr" */
12274 goto err;
12275 }
12276 rez = numptr_m1->contidional_second_val;
12277 } else if (numptr_m1->contidional_second_val_initialized) {
12278 /* protect $((expr1 : expr2)) without "expr ? " */
12279 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012280 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012281 numptr_m1 = NUMPTR - 1;
12282 if (op != TOK_ASSIGN) {
12283 /* check operand is var with noninteger value for not '=' */
12284 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12285 if (ret_arith_lookup_val)
12286 return ret_arith_lookup_val;
12287 }
12288 if (op == TOK_CONDITIONAL) {
12289 numptr_m1->contidional_second_val = rez;
12290 }
12291 rez = numptr_m1->val;
12292 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012293 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012294 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012295 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012296 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012297 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012298 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012299 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012300 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012301 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012302 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012303 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012304 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012305 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012306 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012307 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012308 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012309 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012311 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012312 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012313 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012314 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012315 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012316 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012317 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012318 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012319 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012320 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012321 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012322 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012323 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012324 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012325 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012326 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012327 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012328 /* protect $((expr : expr)) without "expr ? " */
12329 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012330 }
12331 numptr_m1->contidional_second_val_initialized = op;
12332 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012333 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012334 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012335 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012336 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012337 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012338 return -3; /* exponent less than 0 */
12339 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012340 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012341
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012342 if (numptr_val)
12343 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012344 c *= rez;
12345 rez = c;
12346 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012347 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012348 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012350 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012351 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012352 rez %= numptr_val;
12353 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012354 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012355 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012356
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012357 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012358 /* Hmm, 1=2 ? */
12359 goto err;
12360 }
12361 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012362#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012363 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012364#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012365 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012366#endif
Eric Andersen90898442003-08-06 11:20:52 +000012367 setvar(numptr_m1->var, buf, 0);
12368 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012369 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012370 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012371 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012372 rez++;
12373 }
12374 numptr_m1->val = rez;
12375 /* protect geting var value, is number now */
12376 numptr_m1->var = NULL;
12377 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012378 err:
12379 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012380}
12381
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012382/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012383static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012384 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12385 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12386 '<','<', 0, TOK_LSHIFT,
12387 '>','>', 0, TOK_RSHIFT,
12388 '|','|', 0, TOK_OR,
12389 '&','&', 0, TOK_AND,
12390 '!','=', 0, TOK_NE,
12391 '<','=', 0, TOK_LE,
12392 '>','=', 0, TOK_GE,
12393 '=','=', 0, TOK_EQ,
12394 '|','=', 0, TOK_OR_ASSIGN,
12395 '&','=', 0, TOK_AND_ASSIGN,
12396 '*','=', 0, TOK_MUL_ASSIGN,
12397 '/','=', 0, TOK_DIV_ASSIGN,
12398 '%','=', 0, TOK_REM_ASSIGN,
12399 '+','=', 0, TOK_PLUS_ASSIGN,
12400 '-','=', 0, TOK_MINUS_ASSIGN,
12401 '-','-', 0, TOK_POST_DEC,
12402 '^','=', 0, TOK_XOR_ASSIGN,
12403 '+','+', 0, TOK_POST_INC,
12404 '*','*', 0, TOK_EXPONENT,
12405 '!', 0, TOK_NOT,
12406 '<', 0, TOK_LT,
12407 '>', 0, TOK_GT,
12408 '=', 0, TOK_ASSIGN,
12409 '|', 0, TOK_BOR,
12410 '&', 0, TOK_BAND,
12411 '*', 0, TOK_MUL,
12412 '/', 0, TOK_DIV,
12413 '%', 0, TOK_REM,
12414 '+', 0, TOK_ADD,
12415 '-', 0, TOK_SUB,
12416 '^', 0, TOK_BXOR,
12417 /* uniq */
12418 '~', 0, TOK_BNOT,
12419 ',', 0, TOK_COMMA,
12420 '?', 0, TOK_CONDITIONAL,
12421 ':', 0, TOK_CONDITIONAL_SEP,
12422 ')', 0, TOK_RPAREN,
12423 '(', 0, TOK_LPAREN,
12424 0
12425};
12426/* ptr to ")" */
12427#define endexpression &op_tokens[sizeof(op_tokens)-7]
12428
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012429static arith_t
12430arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012431{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012432 char arithval; /* Current character under analysis */
12433 operator lasttok, op;
12434 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012435
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012436 const char *p = endexpression;
12437 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012438
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012440
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 /* Stack of integers */
12442 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12443 * in any given correct or incorrect expression is left as an exercise to
12444 * the reader. */
12445 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12446 *numstackptr = numstack;
12447 /* Stack of operator tokens */
12448 operator *stack = alloca((datasizes) * sizeof(operator)),
12449 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012450
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12452 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012453
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012454 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012455 arithval = *expr;
12456 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012457 if (p == endexpression) {
12458 /* Null expression. */
12459 return 0;
12460 }
12461
12462 /* This is only reached after all tokens have been extracted from the
12463 * input stream. If there are still tokens on the operator stack, they
12464 * are to be applied in order. At the end, there should be a final
12465 * result on the integer stack */
12466
12467 if (expr != endexpression + 1) {
12468 /* If we haven't done so already, */
12469 /* append a closing right paren */
12470 expr = endexpression;
12471 /* and let the loop process it. */
12472 continue;
12473 }
12474 /* At this point, we're done with the expression. */
12475 if (numstackptr != numstack+1) {
12476 /* ... but if there isn't, it's bad */
12477 err:
12478 return (*perrcode = -1);
12479 }
12480 if (numstack->var) {
12481 /* expression is $((var)) only, lookup now */
12482 errcode = arith_lookup_val(numstack);
12483 }
12484 ret:
12485 *perrcode = errcode;
12486 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012487 }
12488
Eric Andersen90898442003-08-06 11:20:52 +000012489 /* Continue processing the expression. */
12490 if (arith_isspace(arithval)) {
12491 /* Skip whitespace */
12492 goto prologue;
12493 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012494 p = endofname(expr);
12495 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012496 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012497
12498 numstackptr->var = alloca(var_name_size);
12499 safe_strncpy(numstackptr->var, expr, var_name_size);
12500 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012501 num:
Eric Andersen90898442003-08-06 11:20:52 +000012502 numstackptr->contidional_second_val_initialized = 0;
12503 numstackptr++;
12504 lasttok = TOK_NUM;
12505 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012506 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012507 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012508 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012509#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012510 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012511#else
12512 numstackptr->val = strtol(expr, (char **) &expr, 0);
12513#endif
Eric Andersen90898442003-08-06 11:20:52 +000012514 goto num;
12515 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012516 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012517 const char *o;
12518
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012519 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012520 /* strange operator not found */
12521 goto err;
12522 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012524 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012526 /* found */
12527 expr = o - 1;
12528 break;
12529 }
12530 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012532 p++;
12533 /* skip zero delim */
12534 p++;
12535 }
12536 op = p[1];
12537
12538 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012539 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12540 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012541
12542 /* Plus and minus are binary (not unary) _only_ if the last
12543 * token was as number, or a right paren (which pretends to be
12544 * a number, since it evaluates to one). Think about it.
12545 * It makes sense. */
12546 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012547 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012548 case TOK_ADD:
12549 op = TOK_UPLUS;
12550 break;
12551 case TOK_SUB:
12552 op = TOK_UMINUS;
12553 break;
12554 case TOK_POST_INC:
12555 op = TOK_PRE_INC;
12556 break;
12557 case TOK_POST_DEC:
12558 op = TOK_PRE_DEC;
12559 break;
Eric Andersen90898442003-08-06 11:20:52 +000012560 }
12561 }
12562 /* We don't want a unary operator to cause recursive descent on the
12563 * stack, because there can be many in a row and it could cause an
12564 * operator to be evaluated before its argument is pushed onto the
12565 * integer stack. */
12566 /* But for binary operators, "apply" everything on the operator
12567 * stack until we find an operator with a lesser priority than the
12568 * one we have just extracted. */
12569 /* Left paren is given the lowest priority so it will never be
12570 * "applied" in this way.
12571 * if associativity is right and priority eq, applied also skip
12572 */
12573 prec = PREC(op);
12574 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12575 /* not left paren or unary */
12576 if (lasttok != TOK_NUM) {
12577 /* binary op must be preceded by a num */
12578 goto err;
12579 }
12580 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012581 if (op == TOK_RPAREN) {
12582 /* The algorithm employed here is simple: while we don't
12583 * hit an open paren nor the bottom of the stack, pop
12584 * tokens and apply them */
12585 if (stackptr[-1] == TOK_LPAREN) {
12586 --stackptr;
12587 /* Any operator directly after a */
12588 lasttok = TOK_NUM;
12589 /* close paren should consider itself binary */
12590 goto prologue;
12591 }
12592 } else {
12593 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012594
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012595 convert_prec_is_assing(prec);
12596 convert_prec_is_assing(prev_prec);
12597 if (prev_prec < prec)
12598 break;
12599 /* check right assoc */
12600 if (prev_prec == prec && is_right_associativity(prec))
12601 break;
12602 }
12603 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12604 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012605 }
12606 if (op == TOK_RPAREN) {
12607 goto err;
12608 }
12609 }
12610
12611 /* Push this operator to the stack and remember it. */
12612 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012613 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012614 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012615 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012616}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012617#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012618
12619
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012620/* ============ main() and helpers */
12621
12622/*
12623 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012624 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012625static void exitshell(void) ATTRIBUTE_NORETURN;
12626static void
12627exitshell(void)
12628{
12629 struct jmploc loc;
12630 char *p;
12631 int status;
12632
12633 status = exitstatus;
12634 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12635 if (setjmp(loc.loc)) {
12636 if (exception == EXEXIT)
12637/* dash bug: it just does _exit(exitstatus) here
12638 * but we have to do setjobctl(0) first!
12639 * (bug is still not fixed in dash-0.5.3 - if you run dash
12640 * under Midnight Commander, on exit from dash MC is backgrounded) */
12641 status = exitstatus;
12642 goto out;
12643 }
12644 exception_handler = &loc;
12645 p = trap[0];
12646 if (p) {
12647 trap[0] = NULL;
12648 evalstring(p, 0);
12649 }
12650 flush_stdout_stderr();
12651 out:
12652 setjobctl(0);
12653 _exit(status);
12654 /* NOTREACHED */
12655}
12656
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012657static void
12658init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012659{
12660 /* from input.c: */
12661 basepf.nextc = basepf.buf = basebuf;
12662
12663 /* from trap.c: */
12664 signal(SIGCHLD, SIG_DFL);
12665
12666 /* from var.c: */
12667 {
12668 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012669 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012670 const char *p;
12671 struct stat st1, st2;
12672
12673 initvar();
12674 for (envp = environ; envp && *envp; envp++) {
12675 if (strchr(*envp, '=')) {
12676 setvareq(*envp, VEXPORT|VTEXTFIXED);
12677 }
12678 }
12679
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012680 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012681 setvar("PPID", ppid, 0);
12682
12683 p = lookupvar("PWD");
12684 if (p)
12685 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12686 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12687 p = '\0';
12688 setpwd(p, 0);
12689 }
12690}
12691
12692/*
12693 * Process the shell command line arguments.
12694 */
12695static void
12696procargs(int argc, char **argv)
12697{
12698 int i;
12699 const char *xminusc;
12700 char **xargv;
12701
12702 xargv = argv;
12703 arg0 = xargv[0];
12704 if (argc > 0)
12705 xargv++;
12706 for (i = 0; i < NOPTS; i++)
12707 optlist[i] = 2;
12708 argptr = xargv;
12709 options(1);
12710 xargv = argptr;
12711 xminusc = minusc;
12712 if (*xargv == NULL) {
12713 if (xminusc)
12714 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12715 sflag = 1;
12716 }
12717 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12718 iflag = 1;
12719 if (mflag == 2)
12720 mflag = iflag;
12721 for (i = 0; i < NOPTS; i++)
12722 if (optlist[i] == 2)
12723 optlist[i] = 0;
12724#if DEBUG == 2
12725 debug = 1;
12726#endif
12727 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12728 if (xminusc) {
12729 minusc = *xargv++;
12730 if (*xargv)
12731 goto setarg0;
12732 } else if (!sflag) {
12733 setinputfile(*xargv, 0);
12734 setarg0:
12735 arg0 = *xargv++;
12736 commandname = arg0;
12737 }
12738
12739 shellparam.p = xargv;
12740#if ENABLE_ASH_GETOPTS
12741 shellparam.optind = 1;
12742 shellparam.optoff = -1;
12743#endif
12744 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12745 while (*xargv) {
12746 shellparam.nparam++;
12747 xargv++;
12748 }
12749 optschanged();
12750}
12751
12752/*
12753 * Read /etc/profile or .profile.
12754 */
12755static void
12756read_profile(const char *name)
12757{
12758 int skip;
12759
12760 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12761 return;
12762 skip = cmdloop(0);
12763 popfile();
12764 if (skip)
12765 exitshell();
12766}
12767
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012768/*
12769 * This routine is called when an error or an interrupt occurs in an
12770 * interactive shell and control is returned to the main command loop.
12771 */
12772static void
12773reset(void)
12774{
12775 /* from eval.c: */
12776 evalskip = 0;
12777 loopnest = 0;
12778 /* from input.c: */
12779 parselleft = parsenleft = 0; /* clear input buffer */
12780 popallfiles();
12781 /* from parser.c: */
12782 tokpushback = 0;
12783 checkkwd = 0;
12784 /* from redir.c: */
12785 clearredir(0);
12786}
12787
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012788#if PROFILE
12789static short profile_buf[16384];
12790extern int etext();
12791#endif
12792
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012793/*
12794 * Main routine. We initialize things, parse the arguments, execute
12795 * profiles if we're a login shell, and then call cmdloop to execute
12796 * commands. The setjmp call sets up the location to jump to when an
12797 * exception occurs. When an exception occurs the variable "state"
12798 * is used to figure out how far we had gotten.
12799 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012800int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012801int ash_main(int argc, char **argv)
12802{
12803 char *shinit;
12804 volatile int state;
12805 struct jmploc jmploc;
12806 struct stackmark smark;
12807
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012808#if PROFILE
12809 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12810#endif
12811
12812#if ENABLE_FEATURE_EDITING
12813 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12814#endif
12815 state = 0;
12816 if (setjmp(jmploc.loc)) {
12817 int e;
12818 int s;
12819
12820 reset();
12821
12822 e = exception;
12823 if (e == EXERROR)
12824 exitstatus = 2;
12825 s = state;
12826 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12827 exitshell();
12828
12829 if (e == EXINT) {
12830 outcslow('\n', stderr);
12831 }
12832 popstackmark(&smark);
12833 FORCE_INT_ON; /* enable interrupts */
12834 if (s == 1)
12835 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012836 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012837 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012838 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012839 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012840 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012841 }
12842 exception_handler = &jmploc;
12843#if DEBUG
12844 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012845 trace_puts("Shell args: ");
12846 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012847#endif
12848 rootpid = getpid();
12849
12850#if ENABLE_ASH_RANDOM_SUPPORT
12851 rseed = rootpid + time(NULL);
12852#endif
12853 init();
12854 setstackmark(&smark);
12855 procargs(argc, argv);
12856#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12857 if (iflag) {
12858 const char *hp = lookupvar("HISTFILE");
12859
12860 if (hp == NULL) {
12861 hp = lookupvar("HOME");
12862 if (hp != NULL) {
12863 char *defhp = concat_path_file(hp, ".ash_history");
12864 setvar("HISTFILE", defhp, 0);
12865 free(defhp);
12866 }
12867 }
12868 }
12869#endif
12870 if (argv[0] && argv[0][0] == '-')
12871 isloginsh = 1;
12872 if (isloginsh) {
12873 state = 1;
12874 read_profile("/etc/profile");
12875 state1:
12876 state = 2;
12877 read_profile(".profile");
12878 }
12879 state2:
12880 state = 3;
12881 if (
12882#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012883 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012884#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012885 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012886 ) {
12887 shinit = lookupvar("ENV");
12888 if (shinit != NULL && *shinit != '\0') {
12889 read_profile(shinit);
12890 }
12891 }
12892 state3:
12893 state = 4;
12894 if (minusc)
12895 evalstring(minusc, 0);
12896
12897 if (sflag || minusc == NULL) {
12898#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12899 if ( iflag ) {
12900 const char *hp = lookupvar("HISTFILE");
12901
12902 if (hp != NULL)
12903 line_input_state->hist_file = hp;
12904 }
12905#endif
12906 state4: /* XXX ??? - why isn't this before the "if" statement */
12907 cmdloop(1);
12908 }
12909#if PROFILE
12910 monitor(0);
12911#endif
12912#ifdef GPROF
12913 {
12914 extern void _mcleanup(void);
12915 _mcleanup();
12916 }
12917#endif
12918 exitshell();
12919 /* NOTREACHED */
12920}
12921
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012922#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012923const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012924int main(int argc, char **argv)
12925{
12926 return ash_main(argc, argv);
12927}
12928#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012929
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012930
Eric Andersendf82f612001-06-28 07:46:40 +000012931/*-
12932 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012933 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012934 *
12935 * This code is derived from software contributed to Berkeley by
12936 * Kenneth Almquist.
12937 *
12938 * Redistribution and use in source and binary forms, with or without
12939 * modification, are permitted provided that the following conditions
12940 * are met:
12941 * 1. Redistributions of source code must retain the above copyright
12942 * notice, this list of conditions and the following disclaimer.
12943 * 2. Redistributions in binary form must reproduce the above copyright
12944 * notice, this list of conditions and the following disclaimer in the
12945 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012946 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012947 * may be used to endorse or promote products derived from this software
12948 * without specific prior written permission.
12949 *
12950 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12951 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12952 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12953 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12954 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12955 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12956 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12957 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12958 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12959 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12960 * SUCH DAMAGE.
12961 */