blob: 4a3d2d48edc62bcebd518f00c30f4ba30b6ef1fc [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 Andersenc470f442003-07-28 09:56:35 +000045#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000046
47#define IFS_BROKEN
48
49#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000050
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000052#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#define _GNU_SOURCE
54#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000055#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000056
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000057#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000061#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000062#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
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 Vlasenko01631112007-12-16 17:20:38 +000070/* ============ Hash table sizes. Configurable. */
71
72#define VTABSIZE 39
73#define ATABSIZE 39
74#define CMDTABLESIZE 31 /* should be prime */
75
76
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000077/* ============ Misc helpers */
78
79#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
80
81/* C99 say: "char" declaration may be signed or unsigned default */
82#define signed_char2int(sc) ((int)((signed char)sc))
83
84
Denis Vlasenkob012b102007-02-19 22:43:01 +000085/* ============ Shell options */
86
87static const char *const optletters_optnames[] = {
88 "e" "errexit",
89 "f" "noglob",
90 "I" "ignoreeof",
91 "i" "interactive",
92 "m" "monitor",
93 "n" "noexec",
94 "s" "stdin",
95 "x" "xtrace",
96 "v" "verbose",
97 "C" "noclobber",
98 "a" "allexport",
99 "b" "notify",
100 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000101 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000102#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 ,"\0" "nolog"
104 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000105#endif
106};
107
108#define optletters(n) optletters_optnames[(n)][0]
109#define optnames(n) (&optletters_optnames[(n)][1])
110
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000111enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000112
Eric Andersenc470f442003-07-28 09:56:35 +0000113
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000115
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000116static const char homestr[] ALIGN1 = "HOME";
117static const char snlfmt[] ALIGN1 = "%s\n";
118static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000119
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000120/*
Eric Andersenc470f442003-07-28 09:56:35 +0000121 * We enclose jmp_buf in a structure so that we can declare pointers to
122 * jump locations. The global variable handler contains the location to
123 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000124 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000125 * exception handlers, the user should save the value of handler on entry
126 * to an inner scope, set handler to point to a jmploc structure for the
127 * inner scope, and restore handler on exit from the scope.
128 */
Eric Andersenc470f442003-07-28 09:56:35 +0000129struct jmploc {
130 jmp_buf loc;
131};
Denis Vlasenko01631112007-12-16 17:20:38 +0000132
133struct globals_misc {
134 /* pid of main shell */
135 int rootpid;
136 /* shell level: 0 for the main shell, 1 for its children, and so on */
137 int shlvl;
138#define rootshell (!shlvl)
139 char *minusc; /* argument to -c option */
140
141 char *curdir; // = nullstr; /* current working directory */
142 char *physdir; // = nullstr; /* physical working directory */
143
144 char *arg0; /* value of $0 */
145
146 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000147
148// disabled by vda: cannot understand how it was supposed to work -
149// cannot fix bugs. That's why you have to explain your non-trivial designs!
150// /* do we generate EXSIG events */
151// int exsig; /* counter */
152 volatile int suppressint; /* counter */
153 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
154 /* last pending signal */
155 volatile /*sig_atomic_t*/ smallint pendingsig;
156 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000157 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000158#define EXINT 0 /* SIGINT received */
159#define EXERROR 1 /* a generic error */
160#define EXSHELLPROC 2 /* execute a shell procedure */
161#define EXEXEC 3 /* command execution failed */
162#define EXEXIT 4 /* exit the shell */
163#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000164
Denis Vlasenko01631112007-12-16 17:20:38 +0000165 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000166 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000167
168 char optlist[NOPTS];
169#define eflag optlist[0]
170#define fflag optlist[1]
171#define Iflag optlist[2]
172#define iflag optlist[3]
173#define mflag optlist[4]
174#define nflag optlist[5]
175#define sflag optlist[6]
176#define xflag optlist[7]
177#define vflag optlist[8]
178#define Cflag optlist[9]
179#define aflag optlist[10]
180#define bflag optlist[11]
181#define uflag optlist[12]
182#define viflag optlist[13]
183#if DEBUG
184#define nolog optlist[14]
185#define debug optlist[15]
186#endif
187
188 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000189 /*
190 * Sigmode records the current value of the signal handlers for the various
191 * modes. A value of zero means that the current handler is not known.
192 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
193 */
194 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000195#define S_DFL 1 /* default signal handling (SIG_DFL) */
196#define S_CATCH 2 /* signal is caught */
197#define S_IGN 3 /* signal is ignored (SIG_IGN) */
198#define S_HARD_IGN 4 /* signal is ignored permenantly */
199#define S_RESET 5 /* temporary - to reset a hard ignored sig */
200
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /* indicates specified signal received */
202 char gotsig[NSIG - 1];
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000203 char *trap[NSIG];
Denis Vlasenko01631112007-12-16 17:20:38 +0000204};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000205extern struct globals_misc *const ash_ptr_to_globals_misc;
206#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko01631112007-12-16 17:20:38 +0000207#define rootpid (G_misc.rootpid )
208#define shlvl (G_misc.shlvl )
209#define minusc (G_misc.minusc )
210#define curdir (G_misc.curdir )
211#define physdir (G_misc.physdir )
212#define arg0 (G_misc.arg0 )
213#define exception_handler (G_misc.exception_handler)
214#define exception (G_misc.exception )
215#define suppressint (G_misc.suppressint )
216#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000217//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000218#define pendingsig (G_misc.pendingsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000219#define isloginsh (G_misc.isloginsh)
220#define nullstr (G_misc.nullstr )
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000221#define optlist (G_misc.optlist )
Denis Vlasenko01631112007-12-16 17:20:38 +0000222#define sigmode (G_misc.sigmode )
223#define gotsig (G_misc.gotsig )
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000224#define trap (G_misc.trap )
Denis Vlasenko01631112007-12-16 17:20:38 +0000225#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000226 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
227 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000228 curdir = nullstr; \
229 physdir = nullstr; \
230} while (0)
231
232
233/* ============ Interrupts / exceptions */
234
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000235/*
Eric Andersen2870d962001-07-02 17:27:21 +0000236 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000237 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000238 * much more efficient and portable. (But hacking the kernel is so much
239 * more fun than worrying about efficiency and portability. :-))
240 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000241#define INT_OFF do { \
242 suppressint++; \
243 xbarrier(); \
244} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000245
246/*
247 * Called to raise an exception. Since C doesn't include exceptions, we
248 * just do a longjmp to the exception handler. The type of exception is
249 * stored in the global variable "exception".
250 */
251static void raise_exception(int) ATTRIBUTE_NORETURN;
252static void
253raise_exception(int e)
254{
255#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000256 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000257 abort();
258#endif
259 INT_OFF;
260 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000261 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000262}
263
264/*
265 * Called from trap.c when a SIGINT is received. (If the user specifies
266 * that SIGINT is to be trapped or ignored using the trap builtin, then
267 * this routine is not called.) Suppressint is nonzero when interrupts
268 * are held using the INT_OFF macro. (The test for iflag is just
269 * defensive programming.)
270 */
271static void raise_interrupt(void) ATTRIBUTE_NORETURN;
272static void
273raise_interrupt(void)
274{
275 int i;
276
277 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000278 /* Signal is not automatically unmasked after it is raised,
279 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000280 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000281 /* pendingsig = 0; - now done in onsig() */
282
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 i = EXSIG;
284 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
285 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000286 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287 signal(SIGINT, SIG_DFL);
288 raise(SIGINT);
289 }
290 i = EXINT;
291 }
292 raise_exception(i);
293 /* NOTREACHED */
294}
295
296#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000297static void
298int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299{
300 if (--suppressint == 0 && intpending) {
301 raise_interrupt();
302 }
303}
304#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000305static void
306force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307{
308 suppressint = 0;
309 if (intpending)
310 raise_interrupt();
311}
312#define FORCE_INT_ON force_int_on()
313#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000314#define INT_ON do { \
315 xbarrier(); \
316 if (--suppressint == 0 && intpending) \
317 raise_interrupt(); \
318} while (0)
319#define FORCE_INT_ON do { \
320 xbarrier(); \
321 suppressint = 0; \
322 if (intpending) \
323 raise_interrupt(); \
324} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000325#endif /* ASH_OPTIMIZE_FOR_SIZE */
326
327#define SAVE_INT(v) ((v) = suppressint)
328
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000329#define RESTORE_INT(v) do { \
330 xbarrier(); \
331 suppressint = (v); \
332 if (suppressint == 0 && intpending) \
333 raise_interrupt(); \
334} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000335
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000336/*
337 * Ignore a signal. Only one usage site - in forkchild()
338 */
339static void
340ignoresig(int signo)
341{
342 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
343 signal(signo, SIG_IGN);
344 }
345 sigmode[signo - 1] = S_HARD_IGN;
346}
347
348/*
349 * Signal handler. Only one usage site - in setsignal()
350 */
351static void
352onsig(int signo)
353{
354 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000355 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000356
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000357 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000358 if (!suppressint) {
359 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000360 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000361 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000362 intpending = 1;
363 }
364}
365
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000366
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000367/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000368
Eric Andersenc470f442003-07-28 09:56:35 +0000369static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000370outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000371{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000372 INT_OFF;
373 fputs(p, file);
374 INT_ON;
375}
376
377static void
378flush_stdout_stderr(void)
379{
380 INT_OFF;
381 fflush(stdout);
382 fflush(stderr);
383 INT_ON;
384}
385
386static void
387flush_stderr(void)
388{
389 INT_OFF;
390 fflush(stderr);
391 INT_ON;
392}
393
394static void
395outcslow(int c, FILE *dest)
396{
397 INT_OFF;
398 putc(c, dest);
399 fflush(dest);
400 INT_ON;
401}
402
403static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
404static int
405out1fmt(const char *fmt, ...)
406{
407 va_list ap;
408 int r;
409
410 INT_OFF;
411 va_start(ap, fmt);
412 r = vprintf(fmt, ap);
413 va_end(ap);
414 INT_ON;
415 return r;
416}
417
418static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
419static int
420fmtstr(char *outbuf, size_t length, const char *fmt, ...)
421{
422 va_list ap;
423 int ret;
424
425 va_start(ap, fmt);
426 INT_OFF;
427 ret = vsnprintf(outbuf, length, fmt, ap);
428 va_end(ap);
429 INT_ON;
430 return ret;
431}
432
433static void
434out1str(const char *p)
435{
436 outstr(p, stdout);
437}
438
439static void
440out2str(const char *p)
441{
442 outstr(p, stderr);
443 flush_stderr();
444}
445
446
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000447/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000448
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000449/* control characters in argument strings */
450#define CTLESC '\201' /* escape next character */
451#define CTLVAR '\202' /* variable defn */
452#define CTLENDVAR '\203'
453#define CTLBACKQ '\204'
454#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
455/* CTLBACKQ | CTLQUOTE == '\205' */
456#define CTLARI '\206' /* arithmetic expression */
457#define CTLENDARI '\207'
458#define CTLQUOTEMARK '\210'
459
460/* variable substitution byte (follows CTLVAR) */
461#define VSTYPE 0x0f /* type of variable substitution */
462#define VSNUL 0x10 /* colon--treat the empty string as unset */
463#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
464
465/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000466#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
467#define VSMINUS 0x2 /* ${var-text} */
468#define VSPLUS 0x3 /* ${var+text} */
469#define VSQUESTION 0x4 /* ${var?message} */
470#define VSASSIGN 0x5 /* ${var=text} */
471#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
472#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
473#define VSTRIMLEFT 0x8 /* ${var#pattern} */
474#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
475#define VSLENGTH 0xa /* ${#var} */
476#if ENABLE_ASH_BASH_COMPAT
477#define VSSUBSTR 0xc /* ${var:position:length} */
478#define VSREPLACE 0xd /* ${var/pattern/replacement} */
479#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
480#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000481
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000482static const char dolatstr[] ALIGN1 = {
483 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
484};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000485
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000486#define NCMD 0
487#define NPIPE 1
488#define NREDIR 2
489#define NBACKGND 3
490#define NSUBSHELL 4
491#define NAND 5
492#define NOR 6
493#define NSEMI 7
494#define NIF 8
495#define NWHILE 9
496#define NUNTIL 10
497#define NFOR 11
498#define NCASE 12
499#define NCLIST 13
500#define NDEFUN 14
501#define NARG 15
502#define NTO 16
503#define NCLOBBER 17
504#define NFROM 18
505#define NFROMTO 19
506#define NAPPEND 20
507#define NTOFD 21
508#define NFROMFD 22
509#define NHERE 23
510#define NXHERE 24
511#define NNOT 25
512
513union node;
514
515struct ncmd {
516 int type;
517 union node *assign;
518 union node *args;
519 union node *redirect;
520};
521
522struct npipe {
523 int type;
524 int backgnd;
525 struct nodelist *cmdlist;
526};
527
528struct nredir {
529 int type;
530 union node *n;
531 union node *redirect;
532};
533
534struct nbinary {
535 int type;
536 union node *ch1;
537 union node *ch2;
538};
539
540struct nif {
541 int type;
542 union node *test;
543 union node *ifpart;
544 union node *elsepart;
545};
546
547struct nfor {
548 int type;
549 union node *args;
550 union node *body;
551 char *var;
552};
553
554struct ncase {
555 int type;
556 union node *expr;
557 union node *cases;
558};
559
560struct nclist {
561 int type;
562 union node *next;
563 union node *pattern;
564 union node *body;
565};
566
567struct narg {
568 int type;
569 union node *next;
570 char *text;
571 struct nodelist *backquote;
572};
573
574struct nfile {
575 int type;
576 union node *next;
577 int fd;
578 union node *fname;
579 char *expfname;
580};
581
582struct ndup {
583 int type;
584 union node *next;
585 int fd;
586 int dupfd;
587 union node *vname;
588};
589
590struct nhere {
591 int type;
592 union node *next;
593 int fd;
594 union node *doc;
595};
596
597struct nnot {
598 int type;
599 union node *com;
600};
601
602union node {
603 int type;
604 struct ncmd ncmd;
605 struct npipe npipe;
606 struct nredir nredir;
607 struct nbinary nbinary;
608 struct nif nif;
609 struct nfor nfor;
610 struct ncase ncase;
611 struct nclist nclist;
612 struct narg narg;
613 struct nfile nfile;
614 struct ndup ndup;
615 struct nhere nhere;
616 struct nnot nnot;
617};
618
619struct nodelist {
620 struct nodelist *next;
621 union node *n;
622};
623
624struct funcnode {
625 int count;
626 union node n;
627};
628
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000629/*
630 * Free a parse tree.
631 */
632static void
633freefunc(struct funcnode *f)
634{
635 if (f && --f->count < 0)
636 free(f);
637}
638
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000639
640/* ============ Debugging output */
641
642#if DEBUG
643
644static FILE *tracefile;
645
646static void
647trace_printf(const char *fmt, ...)
648{
649 va_list va;
650
651 if (debug != 1)
652 return;
653 va_start(va, fmt);
654 vfprintf(tracefile, fmt, va);
655 va_end(va);
656}
657
658static void
659trace_vprintf(const char *fmt, va_list va)
660{
661 if (debug != 1)
662 return;
663 vfprintf(tracefile, fmt, va);
664}
665
666static void
667trace_puts(const char *s)
668{
669 if (debug != 1)
670 return;
671 fputs(s, tracefile);
672}
673
674static void
675trace_puts_quoted(char *s)
676{
677 char *p;
678 char c;
679
680 if (debug != 1)
681 return;
682 putc('"', tracefile);
683 for (p = s; *p; p++) {
684 switch (*p) {
685 case '\n': c = 'n'; goto backslash;
686 case '\t': c = 't'; goto backslash;
687 case '\r': c = 'r'; goto backslash;
688 case '"': c = '"'; goto backslash;
689 case '\\': c = '\\'; goto backslash;
690 case CTLESC: c = 'e'; goto backslash;
691 case CTLVAR: c = 'v'; goto backslash;
692 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
693 case CTLBACKQ: c = 'q'; goto backslash;
694 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
695 backslash:
696 putc('\\', tracefile);
697 putc(c, tracefile);
698 break;
699 default:
700 if (*p >= ' ' && *p <= '~')
701 putc(*p, tracefile);
702 else {
703 putc('\\', tracefile);
704 putc(*p >> 6 & 03, tracefile);
705 putc(*p >> 3 & 07, tracefile);
706 putc(*p & 07, tracefile);
707 }
708 break;
709 }
710 }
711 putc('"', tracefile);
712}
713
714static void
715trace_puts_args(char **ap)
716{
717 if (debug != 1)
718 return;
719 if (!*ap)
720 return;
721 while (1) {
722 trace_puts_quoted(*ap);
723 if (!*++ap) {
724 putc('\n', tracefile);
725 break;
726 }
727 putc(' ', tracefile);
728 }
729}
730
731static void
732opentrace(void)
733{
734 char s[100];
735#ifdef O_APPEND
736 int flags;
737#endif
738
739 if (debug != 1) {
740 if (tracefile)
741 fflush(tracefile);
742 /* leave open because libedit might be using it */
743 return;
744 }
745 strcpy(s, "./trace");
746 if (tracefile) {
747 if (!freopen(s, "a", tracefile)) {
748 fprintf(stderr, "Can't re-open %s\n", s);
749 debug = 0;
750 return;
751 }
752 } else {
753 tracefile = fopen(s, "a");
754 if (tracefile == NULL) {
755 fprintf(stderr, "Can't open %s\n", s);
756 debug = 0;
757 return;
758 }
759 }
760#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000761 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000762 if (flags >= 0)
763 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
764#endif
765 setlinebuf(tracefile);
766 fputs("\nTracing started.\n", tracefile);
767}
768
769static void
770indent(int amount, char *pfx, FILE *fp)
771{
772 int i;
773
774 for (i = 0; i < amount; i++) {
775 if (pfx && i == amount - 1)
776 fputs(pfx, fp);
777 putc('\t', fp);
778 }
779}
780
781/* little circular references here... */
782static void shtree(union node *n, int ind, char *pfx, FILE *fp);
783
784static void
785sharg(union node *arg, FILE *fp)
786{
787 char *p;
788 struct nodelist *bqlist;
789 int subtype;
790
791 if (arg->type != NARG) {
792 out1fmt("<node type %d>\n", arg->type);
793 abort();
794 }
795 bqlist = arg->narg.backquote;
796 for (p = arg->narg.text; *p; p++) {
797 switch (*p) {
798 case CTLESC:
799 putc(*++p, fp);
800 break;
801 case CTLVAR:
802 putc('$', fp);
803 putc('{', fp);
804 subtype = *++p;
805 if (subtype == VSLENGTH)
806 putc('#', fp);
807
808 while (*p != '=')
809 putc(*p++, fp);
810
811 if (subtype & VSNUL)
812 putc(':', fp);
813
814 switch (subtype & VSTYPE) {
815 case VSNORMAL:
816 putc('}', fp);
817 break;
818 case VSMINUS:
819 putc('-', fp);
820 break;
821 case VSPLUS:
822 putc('+', fp);
823 break;
824 case VSQUESTION:
825 putc('?', fp);
826 break;
827 case VSASSIGN:
828 putc('=', fp);
829 break;
830 case VSTRIMLEFT:
831 putc('#', fp);
832 break;
833 case VSTRIMLEFTMAX:
834 putc('#', fp);
835 putc('#', fp);
836 break;
837 case VSTRIMRIGHT:
838 putc('%', fp);
839 break;
840 case VSTRIMRIGHTMAX:
841 putc('%', fp);
842 putc('%', fp);
843 break;
844 case VSLENGTH:
845 break;
846 default:
847 out1fmt("<subtype %d>", subtype);
848 }
849 break;
850 case CTLENDVAR:
851 putc('}', fp);
852 break;
853 case CTLBACKQ:
854 case CTLBACKQ|CTLQUOTE:
855 putc('$', fp);
856 putc('(', fp);
857 shtree(bqlist->n, -1, NULL, fp);
858 putc(')', fp);
859 break;
860 default:
861 putc(*p, fp);
862 break;
863 }
864 }
865}
866
867static void
868shcmd(union node *cmd, FILE *fp)
869{
870 union node *np;
871 int first;
872 const char *s;
873 int dftfd;
874
875 first = 1;
876 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000877 if (!first)
878 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000879 sharg(np, fp);
880 first = 0;
881 }
882 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000883 if (!first)
884 putc(' ', fp);
885 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000886 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000887 case NTO: s = ">>"+1; dftfd = 1; break;
888 case NCLOBBER: s = ">|"; dftfd = 1; break;
889 case NAPPEND: s = ">>"; dftfd = 1; break;
890 case NTOFD: s = ">&"; dftfd = 1; break;
891 case NFROM: s = "<"; break;
892 case NFROMFD: s = "<&"; break;
893 case NFROMTO: s = "<>"; break;
894 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000895 }
896 if (np->nfile.fd != dftfd)
897 fprintf(fp, "%d", np->nfile.fd);
898 fputs(s, fp);
899 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
900 fprintf(fp, "%d", np->ndup.dupfd);
901 } else {
902 sharg(np->nfile.fname, fp);
903 }
904 first = 0;
905 }
906}
907
908static void
909shtree(union node *n, int ind, char *pfx, FILE *fp)
910{
911 struct nodelist *lp;
912 const char *s;
913
914 if (n == NULL)
915 return;
916
917 indent(ind, pfx, fp);
918 switch (n->type) {
919 case NSEMI:
920 s = "; ";
921 goto binop;
922 case NAND:
923 s = " && ";
924 goto binop;
925 case NOR:
926 s = " || ";
927 binop:
928 shtree(n->nbinary.ch1, ind, NULL, fp);
929 /* if (ind < 0) */
930 fputs(s, fp);
931 shtree(n->nbinary.ch2, ind, NULL, fp);
932 break;
933 case NCMD:
934 shcmd(n, fp);
935 if (ind >= 0)
936 putc('\n', fp);
937 break;
938 case NPIPE:
939 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
940 shcmd(lp->n, fp);
941 if (lp->next)
942 fputs(" | ", fp);
943 }
944 if (n->npipe.backgnd)
945 fputs(" &", fp);
946 if (ind >= 0)
947 putc('\n', fp);
948 break;
949 default:
950 fprintf(fp, "<node type %d>", n->type);
951 if (ind >= 0)
952 putc('\n', fp);
953 break;
954 }
955}
956
957static void
958showtree(union node *n)
959{
960 trace_puts("showtree called\n");
961 shtree(n, 1, NULL, stdout);
962}
963
964#define TRACE(param) trace_printf param
965#define TRACEV(param) trace_vprintf param
966
967#else
968
969#define TRACE(param)
970#define TRACEV(param)
971
972#endif /* DEBUG */
973
974
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000975/* ============ Parser data */
976
977/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000978 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
979 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000980struct strlist {
981 struct strlist *next;
982 char *text;
983};
984
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000985struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000986
Denis Vlasenkob012b102007-02-19 22:43:01 +0000987struct strpush {
988 struct strpush *prev; /* preceding string on stack */
989 char *prevstring;
990 int prevnleft;
991#if ENABLE_ASH_ALIAS
992 struct alias *ap; /* if push was associated with an alias */
993#endif
994 char *string; /* remember the string since it may change */
995};
996
997struct parsefile {
998 struct parsefile *prev; /* preceding file on stack */
999 int linno; /* current line */
1000 int fd; /* file descriptor (or -1 if string) */
1001 int nleft; /* number of chars left in this line */
1002 int lleft; /* number of chars left in this buffer */
1003 char *nextc; /* next char in buffer */
1004 char *buf; /* input buffer */
1005 struct strpush *strpush; /* for pushing strings at this level */
1006 struct strpush basestrpush; /* so pushing one is fast */
1007};
1008
1009static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001010static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001011static int startlinno; /* line # where last token started */
1012static char *commandname; /* currently executing command */
1013static struct strlist *cmdenviron; /* environment for builtin command */
1014static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001015
1016
1017/* ============ Message printing */
1018
1019static void
1020ash_vmsg(const char *msg, va_list ap)
1021{
1022 fprintf(stderr, "%s: ", arg0);
1023 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001024 if (strcmp(arg0, commandname))
1025 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001026 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001027 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001028 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001029 vfprintf(stderr, msg, ap);
1030 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001031}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001032
1033/*
1034 * Exverror is called to raise the error exception. If the second argument
1035 * is not NULL then error prints an error message using printf style
1036 * formatting. It then raises the error exception.
1037 */
1038static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1039static void
1040ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001041{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001042#if DEBUG
1043 if (msg) {
1044 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1045 TRACEV((msg, ap));
1046 TRACE(("\") pid=%d\n", getpid()));
1047 } else
1048 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1049 if (msg)
1050#endif
1051 ash_vmsg(msg, ap);
1052
1053 flush_stdout_stderr();
1054 raise_exception(cond);
1055 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001056}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001057
1058static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1059static void
1060ash_msg_and_raise_error(const char *msg, ...)
1061{
1062 va_list ap;
1063
1064 va_start(ap, msg);
1065 ash_vmsg_and_raise(EXERROR, msg, ap);
1066 /* NOTREACHED */
1067 va_end(ap);
1068}
1069
1070static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1071static void
1072ash_msg_and_raise(int cond, const char *msg, ...)
1073{
1074 va_list ap;
1075
1076 va_start(ap, msg);
1077 ash_vmsg_and_raise(cond, msg, ap);
1078 /* NOTREACHED */
1079 va_end(ap);
1080}
1081
1082/*
1083 * error/warning routines for external builtins
1084 */
1085static void
1086ash_msg(const char *fmt, ...)
1087{
1088 va_list ap;
1089
1090 va_start(ap, fmt);
1091 ash_vmsg(fmt, ap);
1092 va_end(ap);
1093}
1094
1095/*
1096 * Return a string describing an error. The returned string may be a
1097 * pointer to a static buffer that will be overwritten on the next call.
1098 * Action describes the operation that got the error.
1099 */
1100static const char *
1101errmsg(int e, const char *em)
1102{
1103 if (e == ENOENT || e == ENOTDIR) {
1104 return em;
1105 }
1106 return strerror(e);
1107}
1108
1109
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001110/* ============ Memory allocation */
1111
1112/*
1113 * It appears that grabstackstr() will barf with such alignments
1114 * because stalloc() will return a string allocated in a new stackblock.
1115 */
1116#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1117enum {
1118 /* Most machines require the value returned from malloc to be aligned
1119 * in some way. The following macro will get this right
1120 * on many machines. */
1121 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1122 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001123 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001124};
1125
1126struct stack_block {
1127 struct stack_block *prev;
1128 char space[MINSIZE];
1129};
1130
1131struct stackmark {
1132 struct stack_block *stackp;
1133 char *stacknxt;
1134 size_t stacknleft;
1135 struct stackmark *marknext;
1136};
1137
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001138
Denis Vlasenko01631112007-12-16 17:20:38 +00001139struct globals_memstack {
1140 struct stack_block *g_stackp; // = &stackbase;
1141 struct stackmark *markp;
1142 char *g_stacknxt; // = stackbase.space;
1143 char *sstrend; // = stackbase.space + MINSIZE;
1144 size_t g_stacknleft; // = MINSIZE;
1145 int herefd; // = -1;
1146 struct stack_block stackbase;
1147};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001148extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1149#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001150#define g_stackp (G_memstack.g_stackp )
1151#define markp (G_memstack.markp )
1152#define g_stacknxt (G_memstack.g_stacknxt )
1153#define sstrend (G_memstack.sstrend )
1154#define g_stacknleft (G_memstack.g_stacknleft)
1155#define herefd (G_memstack.herefd )
1156#define stackbase (G_memstack.stackbase )
1157#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001158 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1159 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001160 g_stackp = &stackbase; \
1161 g_stacknxt = stackbase.space; \
1162 g_stacknleft = MINSIZE; \
1163 sstrend = stackbase.space + MINSIZE; \
1164 herefd = -1; \
1165} while (0)
1166
1167#define stackblock() ((void *)g_stacknxt)
1168#define stackblocksize() g_stacknleft
1169
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001170
1171static void *
1172ckrealloc(void * p, size_t nbytes)
1173{
1174 p = realloc(p, nbytes);
1175 if (!p)
1176 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1177 return p;
1178}
1179
1180static void *
1181ckmalloc(size_t nbytes)
1182{
1183 return ckrealloc(NULL, nbytes);
1184}
1185
Denis Vlasenko597906c2008-02-20 16:38:54 +00001186static void *
1187ckzalloc(size_t nbytes)
1188{
1189 return memset(ckmalloc(nbytes), 0, nbytes);
1190}
1191
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001192/*
1193 * Make a copy of a string in safe storage.
1194 */
1195static char *
1196ckstrdup(const char *s)
1197{
1198 char *p = strdup(s);
1199 if (!p)
1200 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1201 return p;
1202}
1203
1204/*
1205 * Parse trees for commands are allocated in lifo order, so we use a stack
1206 * to make this more efficient, and also to avoid all sorts of exception
1207 * handling code to handle interrupts in the middle of a parse.
1208 *
1209 * The size 504 was chosen because the Ultrix malloc handles that size
1210 * well.
1211 */
1212static void *
1213stalloc(size_t nbytes)
1214{
1215 char *p;
1216 size_t aligned;
1217
1218 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001219 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001220 size_t len;
1221 size_t blocksize;
1222 struct stack_block *sp;
1223
1224 blocksize = aligned;
1225 if (blocksize < MINSIZE)
1226 blocksize = MINSIZE;
1227 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1228 if (len < blocksize)
1229 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1230 INT_OFF;
1231 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001232 sp->prev = g_stackp;
1233 g_stacknxt = sp->space;
1234 g_stacknleft = blocksize;
1235 sstrend = g_stacknxt + blocksize;
1236 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001237 INT_ON;
1238 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001239 p = g_stacknxt;
1240 g_stacknxt += aligned;
1241 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001242 return p;
1243}
1244
Denis Vlasenko597906c2008-02-20 16:38:54 +00001245static void *
1246stzalloc(size_t nbytes)
1247{
1248 return memset(stalloc(nbytes), 0, nbytes);
1249}
1250
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001251static void
1252stunalloc(void *p)
1253{
1254#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001255 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001256 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001257 abort();
1258 }
1259#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001260 g_stacknleft += g_stacknxt - (char *)p;
1261 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001262}
1263
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001264/*
1265 * Like strdup but works with the ash stack.
1266 */
1267static char *
1268ststrdup(const char *p)
1269{
1270 size_t len = strlen(p) + 1;
1271 return memcpy(stalloc(len), p, len);
1272}
1273
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274static void
1275setstackmark(struct stackmark *mark)
1276{
Denis Vlasenko01631112007-12-16 17:20:38 +00001277 mark->stackp = g_stackp;
1278 mark->stacknxt = g_stacknxt;
1279 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001280 mark->marknext = markp;
1281 markp = mark;
1282}
1283
1284static void
1285popstackmark(struct stackmark *mark)
1286{
1287 struct stack_block *sp;
1288
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001289 if (!mark->stackp)
1290 return;
1291
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001292 INT_OFF;
1293 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001294 while (g_stackp != mark->stackp) {
1295 sp = g_stackp;
1296 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297 free(sp);
1298 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001299 g_stacknxt = mark->stacknxt;
1300 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001301 sstrend = mark->stacknxt + mark->stacknleft;
1302 INT_ON;
1303}
1304
1305/*
1306 * When the parser reads in a string, it wants to stick the string on the
1307 * stack and only adjust the stack pointer when it knows how big the
1308 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1309 * of space on top of the stack and stackblocklen returns the length of
1310 * this block. Growstackblock will grow this space by at least one byte,
1311 * possibly moving it (like realloc). Grabstackblock actually allocates the
1312 * part of the block that has been used.
1313 */
1314static void
1315growstackblock(void)
1316{
1317 size_t newlen;
1318
Denis Vlasenko01631112007-12-16 17:20:38 +00001319 newlen = g_stacknleft * 2;
1320 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1322 if (newlen < 128)
1323 newlen += 128;
1324
Denis Vlasenko01631112007-12-16 17:20:38 +00001325 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 struct stack_block *oldstackp;
1327 struct stackmark *xmark;
1328 struct stack_block *sp;
1329 struct stack_block *prevstackp;
1330 size_t grosslen;
1331
1332 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001333 oldstackp = g_stackp;
1334 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001335 prevstackp = sp->prev;
1336 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1337 sp = ckrealloc(sp, grosslen);
1338 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001339 g_stackp = sp;
1340 g_stacknxt = sp->space;
1341 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001342 sstrend = sp->space + newlen;
1343
1344 /*
1345 * Stack marks pointing to the start of the old block
1346 * must be relocated to point to the new block
1347 */
1348 xmark = markp;
1349 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001350 xmark->stackp = g_stackp;
1351 xmark->stacknxt = g_stacknxt;
1352 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001353 xmark = xmark->marknext;
1354 }
1355 INT_ON;
1356 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001357 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001358 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001359 char *p = stalloc(newlen);
1360
1361 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 g_stacknxt = memcpy(p, oldspace, oldlen);
1363 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001364 }
1365}
1366
1367static void
1368grabstackblock(size_t len)
1369{
1370 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001371 g_stacknxt += len;
1372 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001373}
1374
1375/*
1376 * The following routines are somewhat easier to use than the above.
1377 * The user declares a variable of type STACKSTR, which may be declared
1378 * to be a register. The macro STARTSTACKSTR initializes things. Then
1379 * the user uses the macro STPUTC to add characters to the string. In
1380 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1381 * grown as necessary. When the user is done, she can just leave the
1382 * string there and refer to it using stackblock(). Or she can allocate
1383 * the space for it using grabstackstr(). If it is necessary to allow
1384 * someone else to use the stack temporarily and then continue to grow
1385 * the string, the user should use grabstack to allocate the space, and
1386 * then call ungrabstr(p) to return to the previous mode of operation.
1387 *
1388 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1389 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1390 * is space for at least one character.
1391 */
1392static void *
1393growstackstr(void)
1394{
1395 size_t len = stackblocksize();
1396 if (herefd >= 0 && len >= 1024) {
1397 full_write(herefd, stackblock(), len);
1398 return stackblock();
1399 }
1400 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001401 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001402}
1403
1404/*
1405 * Called from CHECKSTRSPACE.
1406 */
1407static char *
1408makestrspace(size_t newlen, char *p)
1409{
Denis Vlasenko01631112007-12-16 17:20:38 +00001410 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001411 size_t size = stackblocksize();
1412
1413 for (;;) {
1414 size_t nleft;
1415
1416 size = stackblocksize();
1417 nleft = size - len;
1418 if (nleft >= newlen)
1419 break;
1420 growstackblock();
1421 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001422 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423}
1424
1425static char *
1426stack_nputstr(const char *s, size_t n, char *p)
1427{
1428 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001429 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001430 return p;
1431}
1432
1433static char *
1434stack_putstr(const char *s, char *p)
1435{
1436 return stack_nputstr(s, strlen(s), p);
1437}
1438
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001439static char *
1440_STPUTC(int c, char *p)
1441{
1442 if (p == sstrend)
1443 p = growstackstr();
1444 *p++ = c;
1445 return p;
1446}
1447
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001448#define STARTSTACKSTR(p) ((p) = stackblock())
1449#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001450#define CHECKSTRSPACE(n, p) do { \
1451 char *q = (p); \
1452 size_t l = (n); \
1453 size_t m = sstrend - q; \
1454 if (l > m) \
1455 (p) = makestrspace(l, q); \
1456} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001457#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001458#define STACKSTRNUL(p) do { \
1459 if ((p) == sstrend) \
1460 (p) = growstackstr(); \
1461 *(p) = '\0'; \
1462} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001463#define STUNPUTC(p) (--(p))
1464#define STTOPC(p) ((p)[-1])
1465#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001466
1467#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001468#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001469#define stackstrend() ((void *)sstrend)
1470
1471
1472/* ============ String helpers */
1473
1474/*
1475 * prefix -- see if pfx is a prefix of string.
1476 */
1477static char *
1478prefix(const char *string, const char *pfx)
1479{
1480 while (*pfx) {
1481 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001482 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001483 }
1484 return (char *) string;
1485}
1486
1487/*
1488 * Check for a valid number. This should be elsewhere.
1489 */
1490static int
1491is_number(const char *p)
1492{
1493 do {
1494 if (!isdigit(*p))
1495 return 0;
1496 } while (*++p != '\0');
1497 return 1;
1498}
1499
1500/*
1501 * Convert a string of digits to an integer, printing an error message on
1502 * failure.
1503 */
1504static int
1505number(const char *s)
1506{
1507 if (!is_number(s))
1508 ash_msg_and_raise_error(illnum, s);
1509 return atoi(s);
1510}
1511
1512/*
1513 * Produce a possibly single quoted string suitable as input to the shell.
1514 * The return string is allocated on the stack.
1515 */
1516static char *
1517single_quote(const char *s)
1518{
1519 char *p;
1520
1521 STARTSTACKSTR(p);
1522
1523 do {
1524 char *q;
1525 size_t len;
1526
1527 len = strchrnul(s, '\'') - s;
1528
1529 q = p = makestrspace(len + 3, p);
1530
1531 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001532 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001533 *q++ = '\'';
1534 s += len;
1535
1536 STADJUST(q - p, p);
1537
1538 len = strspn(s, "'");
1539 if (!len)
1540 break;
1541
1542 q = p = makestrspace(len + 3, p);
1543
1544 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001545 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001546 *q++ = '"';
1547 s += len;
1548
1549 STADJUST(q - p, p);
1550 } while (*s);
1551
1552 USTPUTC(0, p);
1553
1554 return stackblock();
1555}
1556
1557
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001558/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001559
1560static char **argptr; /* argument list for builtin commands */
1561static char *optionarg; /* set by nextopt (like getopt) */
1562static char *optptr; /* used by nextopt */
1563
1564/*
1565 * XXX - should get rid of. have all builtins use getopt(3). the
1566 * library getopt must have the BSD extension static variable "optreset"
1567 * otherwise it can't be used within the shell safely.
1568 *
1569 * Standard option processing (a la getopt) for builtin routines. The
1570 * only argument that is passed to nextopt is the option string; the
1571 * other arguments are unnecessary. It return the character, or '\0' on
1572 * end of input.
1573 */
1574static int
1575nextopt(const char *optstring)
1576{
1577 char *p;
1578 const char *q;
1579 char c;
1580
1581 p = optptr;
1582 if (p == NULL || *p == '\0') {
1583 p = *argptr;
1584 if (p == NULL || *p != '-' || *++p == '\0')
1585 return '\0';
1586 argptr++;
1587 if (LONE_DASH(p)) /* check for "--" */
1588 return '\0';
1589 }
1590 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001591 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001592 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001593 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001594 if (*++q == ':')
1595 q++;
1596 }
1597 if (*++q == ':') {
1598 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001599 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001600 optionarg = p;
1601 p = NULL;
1602 }
1603 optptr = p;
1604 return c;
1605}
1606
1607
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001608/* ============ Math support definitions */
1609
1610#if ENABLE_ASH_MATH_SUPPORT_64
1611typedef int64_t arith_t;
1612#define arith_t_type long long
1613#else
1614typedef long arith_t;
1615#define arith_t_type long
1616#endif
1617
1618#if ENABLE_ASH_MATH_SUPPORT
1619static arith_t dash_arith(const char *);
1620static arith_t arith(const char *expr, int *perrcode);
1621#endif
1622
1623#if ENABLE_ASH_RANDOM_SUPPORT
1624static unsigned long rseed;
1625#ifndef DYNAMIC_VAR
1626#define DYNAMIC_VAR
1627#endif
1628#endif
1629
1630
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001631/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001632
Denis Vlasenko01631112007-12-16 17:20:38 +00001633/*
1634 * The parsefile structure pointed to by the global variable parsefile
1635 * contains information about the current file being read.
1636 */
1637struct redirtab {
1638 struct redirtab *next;
1639 int renamed[10];
1640 int nullredirs;
1641};
1642
1643struct shparam {
1644 int nparam; /* # of positional parameters (without $0) */
1645#if ENABLE_ASH_GETOPTS
1646 int optind; /* next parameter to be processed by getopts */
1647 int optoff; /* used by getopts */
1648#endif
1649 unsigned char malloced; /* if parameter list dynamically allocated */
1650 char **p; /* parameter list */
1651};
1652
1653/*
1654 * Free the list of positional parameters.
1655 */
1656static void
1657freeparam(volatile struct shparam *param)
1658{
1659 char **ap;
1660
1661 if (param->malloced) {
1662 for (ap = param->p; *ap; ap++)
1663 free(*ap);
1664 free(param->p);
1665 }
1666}
1667
1668#if ENABLE_ASH_GETOPTS
1669static void getoptsreset(const char *value);
1670#endif
1671
1672struct var {
1673 struct var *next; /* next entry in hash list */
1674 int flags; /* flags are defined above */
1675 const char *text; /* name=value */
1676 void (*func)(const char *); /* function to be called when */
1677 /* the variable gets set/unset */
1678};
1679
1680struct localvar {
1681 struct localvar *next; /* next local variable in list */
1682 struct var *vp; /* the variable that was made local */
1683 int flags; /* saved flags */
1684 const char *text; /* saved text */
1685};
1686
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687/* flags */
1688#define VEXPORT 0x01 /* variable is exported */
1689#define VREADONLY 0x02 /* variable cannot be modified */
1690#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1691#define VTEXTFIXED 0x08 /* text is statically allocated */
1692#define VSTACK 0x10 /* text is allocated on the stack */
1693#define VUNSET 0x20 /* the variable is not set */
1694#define VNOFUNC 0x40 /* don't call the callback function */
1695#define VNOSET 0x80 /* do not set variable - just readonly test */
1696#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1697#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001698# define VDYNAMIC 0x200 /* dynamic variable */
1699#else
1700# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#endif
1702
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001703#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001704static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705#define defifs (defifsvar + 4)
1706#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001707static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708#endif
1709
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001710
Denis Vlasenko01631112007-12-16 17:20:38 +00001711/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001712#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001713static void
1714change_lc_all(const char *value)
1715{
1716 if (value && *value != '\0')
1717 setlocale(LC_ALL, value);
1718}
1719static void
1720change_lc_ctype(const char *value)
1721{
1722 if (value && *value != '\0')
1723 setlocale(LC_CTYPE, value);
1724}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001725#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726#if ENABLE_ASH_MAIL
1727static void chkmail(void);
1728static void changemail(const char *);
1729#endif
1730static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#if ENABLE_ASH_RANDOM_SUPPORT
1732static void change_random(const char *);
1733#endif
1734
Denis Vlasenko01631112007-12-16 17:20:38 +00001735static const struct {
1736 int flags;
1737 const char *text;
1738 void (*func)(const char *);
1739} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001741 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001746 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1747 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1750 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1751 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1752 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
1756#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001757 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#endif
1759#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1761 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
1766};
1767
Denis Vlasenko01631112007-12-16 17:20:38 +00001768
1769struct globals_var {
1770 struct shparam shellparam; /* $@ current positional parameters */
1771 struct redirtab *redirlist;
1772 int g_nullredirs;
1773 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1774 struct var *vartab[VTABSIZE];
1775 struct var varinit[ARRAY_SIZE(varinit_data)];
1776};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001777extern struct globals_var *const ash_ptr_to_globals_var;
1778#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001779#define shellparam (G_var.shellparam )
1780#define redirlist (G_var.redirlist )
1781#define g_nullredirs (G_var.g_nullredirs )
1782#define preverrout_fd (G_var.preverrout_fd)
1783#define vartab (G_var.vartab )
1784#define varinit (G_var.varinit )
1785#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001786 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001787 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1788 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001789 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1790 varinit[i].flags = varinit_data[i].flags; \
1791 varinit[i].text = varinit_data[i].text; \
1792 varinit[i].func = varinit_data[i].func; \
1793 } \
1794} while (0)
1795
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001796#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001798# define vmail (&vifs)[1]
1799# define vmpath (&vmail)[1]
1800# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001801#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001802# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001804#define vps1 (&vpath)[1]
1805#define vps2 (&vps1)[1]
1806#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001807#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001808# define voptind (&vps4)[1]
1809# if ENABLE_ASH_RANDOM_SUPPORT
1810# define vrandom (&voptind)[1]
1811# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001812#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001813# if ENABLE_ASH_RANDOM_SUPPORT
1814# define vrandom (&vps4)[1]
1815# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001816#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001817
1818/*
1819 * The following macros access the values of the above variables.
1820 * They have to skip over the name. They return the null string
1821 * for unset variables.
1822 */
1823#define ifsval() (vifs.text + 4)
1824#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001825#if ENABLE_ASH_MAIL
1826# define mailval() (vmail.text + 5)
1827# define mpathval() (vmpath.text + 9)
1828# define mpathset() ((vmpath.flags & VUNSET) == 0)
1829#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#define pathval() (vpath.text + 5)
1831#define ps1val() (vps1.text + 4)
1832#define ps2val() (vps2.text + 4)
1833#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001834#if ENABLE_ASH_GETOPTS
1835# define optindval() (voptind.text + 7)
1836#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001838
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001839#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1840#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1841
Denis Vlasenko01631112007-12-16 17:20:38 +00001842#if ENABLE_ASH_GETOPTS
1843static void
1844getoptsreset(const char *value)
1845{
1846 shellparam.optind = number(value);
1847 shellparam.optoff = -1;
1848}
1849#endif
1850
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001851/*
1852 * Return of a legal variable name (a letter or underscore followed by zero or
1853 * more letters, underscores, and digits).
1854 */
1855static char *
1856endofname(const char *name)
1857{
1858 char *p;
1859
1860 p = (char *) name;
1861 if (!is_name(*p))
1862 return p;
1863 while (*++p) {
1864 if (!is_in_name(*p))
1865 break;
1866 }
1867 return p;
1868}
1869
1870/*
1871 * Compares two strings up to the first = or '\0'. The first
1872 * string must be terminated by '='; the second may be terminated by
1873 * either '=' or '\0'.
1874 */
1875static int
1876varcmp(const char *p, const char *q)
1877{
1878 int c, d;
1879
1880 while ((c = *p) == (d = *q)) {
1881 if (!c || c == '=')
1882 goto out;
1883 p++;
1884 q++;
1885 }
1886 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001887 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001888 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001889 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890 out:
1891 return c - d;
1892}
1893
1894static int
1895varequal(const char *a, const char *b)
1896{
1897 return !varcmp(a, b);
1898}
1899
1900/*
1901 * Find the appropriate entry in the hash table from the name.
1902 */
1903static struct var **
1904hashvar(const char *p)
1905{
1906 unsigned hashval;
1907
1908 hashval = ((unsigned char) *p) << 4;
1909 while (*p && *p != '=')
1910 hashval += (unsigned char) *p++;
1911 return &vartab[hashval % VTABSIZE];
1912}
1913
1914static int
1915vpcmp(const void *a, const void *b)
1916{
1917 return varcmp(*(const char **)a, *(const char **)b);
1918}
1919
1920/*
1921 * This routine initializes the builtin variables.
1922 */
1923static void
1924initvar(void)
1925{
1926 struct var *vp;
1927 struct var *end;
1928 struct var **vpp;
1929
1930 /*
1931 * PS1 depends on uid
1932 */
1933#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1934 vps1.text = "PS1=\\w \\$ ";
1935#else
1936 if (!geteuid())
1937 vps1.text = "PS1=# ";
1938#endif
1939 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001940 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001941 do {
1942 vpp = hashvar(vp->text);
1943 vp->next = *vpp;
1944 *vpp = vp;
1945 } while (++vp < end);
1946}
1947
1948static struct var **
1949findvar(struct var **vpp, const char *name)
1950{
1951 for (; *vpp; vpp = &(*vpp)->next) {
1952 if (varequal((*vpp)->text, name)) {
1953 break;
1954 }
1955 }
1956 return vpp;
1957}
1958
1959/*
1960 * Find the value of a variable. Returns NULL if not set.
1961 */
1962static char *
1963lookupvar(const char *name)
1964{
1965 struct var *v;
1966
1967 v = *findvar(hashvar(name), name);
1968 if (v) {
1969#ifdef DYNAMIC_VAR
1970 /*
1971 * Dynamic variables are implemented roughly the same way they are
1972 * in bash. Namely, they're "special" so long as they aren't unset.
1973 * As soon as they're unset, they're no longer dynamic, and dynamic
1974 * lookup will no longer happen at that point. -- PFM.
1975 */
1976 if ((v->flags & VDYNAMIC))
1977 (*v->func)(NULL);
1978#endif
1979 if (!(v->flags & VUNSET))
1980 return strchrnul(v->text, '=') + 1;
1981 }
1982 return NULL;
1983}
1984
1985/*
1986 * Search the environment of a builtin command.
1987 */
1988static char *
1989bltinlookup(const char *name)
1990{
1991 struct strlist *sp;
1992
1993 for (sp = cmdenviron; sp; sp = sp->next) {
1994 if (varequal(sp->text, name))
1995 return strchrnul(sp->text, '=') + 1;
1996 }
1997 return lookupvar(name);
1998}
1999
2000/*
2001 * Same as setvar except that the variable and value are passed in
2002 * the first argument as name=value. Since the first argument will
2003 * be actually stored in the table, it should not be a string that
2004 * will go away.
2005 * Called with interrupts off.
2006 */
2007static void
2008setvareq(char *s, int flags)
2009{
2010 struct var *vp, **vpp;
2011
2012 vpp = hashvar(s);
2013 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2014 vp = *findvar(vpp, s);
2015 if (vp) {
2016 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2017 const char *n;
2018
2019 if (flags & VNOSAVE)
2020 free(s);
2021 n = vp->text;
2022 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2023 }
2024
2025 if (flags & VNOSET)
2026 return;
2027
2028 if (vp->func && (flags & VNOFUNC) == 0)
2029 (*vp->func)(strchrnul(s, '=') + 1);
2030
2031 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2032 free((char*)vp->text);
2033
2034 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2035 } else {
2036 if (flags & VNOSET)
2037 return;
2038 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002039 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002040 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002041 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002042 *vpp = vp;
2043 }
2044 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2045 s = ckstrdup(s);
2046 vp->text = s;
2047 vp->flags = flags;
2048}
2049
2050/*
2051 * Set the value of a variable. The flags argument is ored with the
2052 * flags of the variable. If val is NULL, the variable is unset.
2053 */
2054static void
2055setvar(const char *name, const char *val, int flags)
2056{
2057 char *p, *q;
2058 size_t namelen;
2059 char *nameeq;
2060 size_t vallen;
2061
2062 q = endofname(name);
2063 p = strchrnul(q, '=');
2064 namelen = p - name;
2065 if (!namelen || p != q)
2066 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2067 vallen = 0;
2068 if (val == NULL) {
2069 flags |= VUNSET;
2070 } else {
2071 vallen = strlen(val);
2072 }
2073 INT_OFF;
2074 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002075 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002076 if (val) {
2077 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002078 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079 }
2080 *p = '\0';
2081 setvareq(nameeq, flags | VNOSAVE);
2082 INT_ON;
2083}
2084
2085#if ENABLE_ASH_GETOPTS
2086/*
2087 * Safe version of setvar, returns 1 on success 0 on failure.
2088 */
2089static int
2090setvarsafe(const char *name, const char *val, int flags)
2091{
2092 int err;
2093 volatile int saveint;
2094 struct jmploc *volatile savehandler = exception_handler;
2095 struct jmploc jmploc;
2096
2097 SAVE_INT(saveint);
2098 if (setjmp(jmploc.loc))
2099 err = 1;
2100 else {
2101 exception_handler = &jmploc;
2102 setvar(name, val, flags);
2103 err = 0;
2104 }
2105 exception_handler = savehandler;
2106 RESTORE_INT(saveint);
2107 return err;
2108}
2109#endif
2110
2111/*
2112 * Unset the specified variable.
2113 */
2114static int
2115unsetvar(const char *s)
2116{
2117 struct var **vpp;
2118 struct var *vp;
2119 int retval;
2120
2121 vpp = findvar(hashvar(s), s);
2122 vp = *vpp;
2123 retval = 2;
2124 if (vp) {
2125 int flags = vp->flags;
2126
2127 retval = 1;
2128 if (flags & VREADONLY)
2129 goto out;
2130#ifdef DYNAMIC_VAR
2131 vp->flags &= ~VDYNAMIC;
2132#endif
2133 if (flags & VUNSET)
2134 goto ok;
2135 if ((flags & VSTRFIXED) == 0) {
2136 INT_OFF;
2137 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2138 free((char*)vp->text);
2139 *vpp = vp->next;
2140 free(vp);
2141 INT_ON;
2142 } else {
2143 setvar(s, 0, 0);
2144 vp->flags &= ~VEXPORT;
2145 }
2146 ok:
2147 retval = 0;
2148 }
2149 out:
2150 return retval;
2151}
2152
2153/*
2154 * Process a linked list of variable assignments.
2155 */
2156static void
2157listsetvar(struct strlist *list_set_var, int flags)
2158{
2159 struct strlist *lp = list_set_var;
2160
2161 if (!lp)
2162 return;
2163 INT_OFF;
2164 do {
2165 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002166 lp = lp->next;
2167 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002168 INT_ON;
2169}
2170
2171/*
2172 * Generate a list of variables satisfying the given conditions.
2173 */
2174static char **
2175listvars(int on, int off, char ***end)
2176{
2177 struct var **vpp;
2178 struct var *vp;
2179 char **ep;
2180 int mask;
2181
2182 STARTSTACKSTR(ep);
2183 vpp = vartab;
2184 mask = on | off;
2185 do {
2186 for (vp = *vpp; vp; vp = vp->next) {
2187 if ((vp->flags & mask) == on) {
2188 if (ep == stackstrend())
2189 ep = growstackstr();
2190 *ep++ = (char *) vp->text;
2191 }
2192 }
2193 } while (++vpp < vartab + VTABSIZE);
2194 if (ep == stackstrend())
2195 ep = growstackstr();
2196 if (end)
2197 *end = ep;
2198 *ep++ = NULL;
2199 return grabstackstr(ep);
2200}
2201
2202
2203/* ============ Path search helper
2204 *
2205 * The variable path (passed by reference) should be set to the start
2206 * of the path before the first call; padvance will update
2207 * this value as it proceeds. Successive calls to padvance will return
2208 * the possible path expansions in sequence. If an option (indicated by
2209 * a percent sign) appears in the path entry then the global variable
2210 * pathopt will be set to point to it; otherwise pathopt will be set to
2211 * NULL.
2212 */
2213static const char *pathopt; /* set by padvance */
2214
2215static char *
2216padvance(const char **path, const char *name)
2217{
2218 const char *p;
2219 char *q;
2220 const char *start;
2221 size_t len;
2222
2223 if (*path == NULL)
2224 return NULL;
2225 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002226 for (p = start; *p && *p != ':' && *p != '%'; p++)
2227 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002228 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2229 while (stackblocksize() < len)
2230 growstackblock();
2231 q = stackblock();
2232 if (p != start) {
2233 memcpy(q, start, p - start);
2234 q += p - start;
2235 *q++ = '/';
2236 }
2237 strcpy(q, name);
2238 pathopt = NULL;
2239 if (*p == '%') {
2240 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002241 while (*p && *p != ':')
2242 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002243 }
2244 if (*p == ':')
2245 *path = p + 1;
2246 else
2247 *path = NULL;
2248 return stalloc(len);
2249}
2250
2251
2252/* ============ Prompt */
2253
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002254static smallint doprompt; /* if set, prompt the user */
2255static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256
2257#if ENABLE_FEATURE_EDITING
2258static line_input_t *line_input_state;
2259static const char *cmdedit_prompt;
2260static void
2261putprompt(const char *s)
2262{
2263 if (ENABLE_ASH_EXPAND_PRMT) {
2264 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002265 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002266 return;
2267 }
2268 cmdedit_prompt = s;
2269}
2270#else
2271static void
2272putprompt(const char *s)
2273{
2274 out2str(s);
2275}
2276#endif
2277
2278#if ENABLE_ASH_EXPAND_PRMT
2279/* expandstr() needs parsing machinery, so it is far away ahead... */
2280static const char *expandstr(const char *ps);
2281#else
2282#define expandstr(s) s
2283#endif
2284
2285static void
2286setprompt(int whichprompt)
2287{
2288 const char *prompt;
2289#if ENABLE_ASH_EXPAND_PRMT
2290 struct stackmark smark;
2291#endif
2292
2293 needprompt = 0;
2294
2295 switch (whichprompt) {
2296 case 1:
2297 prompt = ps1val();
2298 break;
2299 case 2:
2300 prompt = ps2val();
2301 break;
2302 default: /* 0 */
2303 prompt = nullstr;
2304 }
2305#if ENABLE_ASH_EXPAND_PRMT
2306 setstackmark(&smark);
2307 stalloc(stackblocksize());
2308#endif
2309 putprompt(expandstr(prompt));
2310#if ENABLE_ASH_EXPAND_PRMT
2311 popstackmark(&smark);
2312#endif
2313}
2314
2315
2316/* ============ The cd and pwd commands */
2317
2318#define CD_PHYSICAL 1
2319#define CD_PRINT 2
2320
2321static int docd(const char *, int);
2322
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002323static int
2324cdopt(void)
2325{
2326 int flags = 0;
2327 int i, j;
2328
2329 j = 'L';
2330 while ((i = nextopt("LP"))) {
2331 if (i != j) {
2332 flags ^= CD_PHYSICAL;
2333 j = i;
2334 }
2335 }
2336
2337 return flags;
2338}
2339
2340/*
2341 * Update curdir (the name of the current directory) in response to a
2342 * cd command.
2343 */
2344static const char *
2345updatepwd(const char *dir)
2346{
2347 char *new;
2348 char *p;
2349 char *cdcomppath;
2350 const char *lim;
2351
2352 cdcomppath = ststrdup(dir);
2353 STARTSTACKSTR(new);
2354 if (*dir != '/') {
2355 if (curdir == nullstr)
2356 return 0;
2357 new = stack_putstr(curdir, new);
2358 }
2359 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002360 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002361 if (*dir != '/') {
2362 if (new[-1] != '/')
2363 USTPUTC('/', new);
2364 if (new > lim && *lim == '/')
2365 lim++;
2366 } else {
2367 USTPUTC('/', new);
2368 cdcomppath++;
2369 if (dir[1] == '/' && dir[2] != '/') {
2370 USTPUTC('/', new);
2371 cdcomppath++;
2372 lim++;
2373 }
2374 }
2375 p = strtok(cdcomppath, "/");
2376 while (p) {
2377 switch (*p) {
2378 case '.':
2379 if (p[1] == '.' && p[2] == '\0') {
2380 while (new > lim) {
2381 STUNPUTC(new);
2382 if (new[-1] == '/')
2383 break;
2384 }
2385 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002386 }
2387 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002388 break;
2389 /* fall through */
2390 default:
2391 new = stack_putstr(p, new);
2392 USTPUTC('/', new);
2393 }
2394 p = strtok(0, "/");
2395 }
2396 if (new > lim)
2397 STUNPUTC(new);
2398 *new = 0;
2399 return stackblock();
2400}
2401
2402/*
2403 * Find out what the current directory is. If we already know the current
2404 * directory, this routine returns immediately.
2405 */
2406static char *
2407getpwd(void)
2408{
Denis Vlasenko01631112007-12-16 17:20:38 +00002409 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002410 return dir ? dir : nullstr;
2411}
2412
2413static void
2414setpwd(const char *val, int setold)
2415{
2416 char *oldcur, *dir;
2417
2418 oldcur = dir = curdir;
2419
2420 if (setold) {
2421 setvar("OLDPWD", oldcur, VEXPORT);
2422 }
2423 INT_OFF;
2424 if (physdir != nullstr) {
2425 if (physdir != oldcur)
2426 free(physdir);
2427 physdir = nullstr;
2428 }
2429 if (oldcur == val || !val) {
2430 char *s = getpwd();
2431 physdir = s;
2432 if (!val)
2433 dir = s;
2434 } else
2435 dir = ckstrdup(val);
2436 if (oldcur != dir && oldcur != nullstr) {
2437 free(oldcur);
2438 }
2439 curdir = dir;
2440 INT_ON;
2441 setvar("PWD", dir, VEXPORT);
2442}
2443
2444static void hashcd(void);
2445
2446/*
2447 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2448 * know that the current directory has changed.
2449 */
2450static int
2451docd(const char *dest, int flags)
2452{
2453 const char *dir = 0;
2454 int err;
2455
2456 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2457
2458 INT_OFF;
2459 if (!(flags & CD_PHYSICAL)) {
2460 dir = updatepwd(dest);
2461 if (dir)
2462 dest = dir;
2463 }
2464 err = chdir(dest);
2465 if (err)
2466 goto out;
2467 setpwd(dir, 1);
2468 hashcd();
2469 out:
2470 INT_ON;
2471 return err;
2472}
2473
2474static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002475cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476{
2477 const char *dest;
2478 const char *path;
2479 const char *p;
2480 char c;
2481 struct stat statb;
2482 int flags;
2483
2484 flags = cdopt();
2485 dest = *argptr;
2486 if (!dest)
2487 dest = bltinlookup(homestr);
2488 else if (LONE_DASH(dest)) {
2489 dest = bltinlookup("OLDPWD");
2490 flags |= CD_PRINT;
2491 }
2492 if (!dest)
2493 dest = nullstr;
2494 if (*dest == '/')
2495 goto step7;
2496 if (*dest == '.') {
2497 c = dest[1];
2498 dotdot:
2499 switch (c) {
2500 case '\0':
2501 case '/':
2502 goto step6;
2503 case '.':
2504 c = dest[2];
2505 if (c != '.')
2506 goto dotdot;
2507 }
2508 }
2509 if (!*dest)
2510 dest = ".";
2511 path = bltinlookup("CDPATH");
2512 if (!path) {
2513 step6:
2514 step7:
2515 p = dest;
2516 goto docd;
2517 }
2518 do {
2519 c = *path;
2520 p = padvance(&path, dest);
2521 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2522 if (c && c != ':')
2523 flags |= CD_PRINT;
2524 docd:
2525 if (!docd(p, flags))
2526 goto out;
2527 break;
2528 }
2529 } while (path);
2530 ash_msg_and_raise_error("can't cd to %s", dest);
2531 /* NOTREACHED */
2532 out:
2533 if (flags & CD_PRINT)
2534 out1fmt(snlfmt, curdir);
2535 return 0;
2536}
2537
2538static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002539pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002540{
2541 int flags;
2542 const char *dir = curdir;
2543
2544 flags = cdopt();
2545 if (flags) {
2546 if (physdir == nullstr)
2547 setpwd(dir, 0);
2548 dir = physdir;
2549 }
2550 out1fmt(snlfmt, dir);
2551 return 0;
2552}
2553
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002554
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002555/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002556
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002557#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002558#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002559
Eric Andersenc470f442003-07-28 09:56:35 +00002560/* Syntax classes */
2561#define CWORD 0 /* character is nothing special */
2562#define CNL 1 /* newline character */
2563#define CBACK 2 /* a backslash character */
2564#define CSQUOTE 3 /* single quote */
2565#define CDQUOTE 4 /* double quote */
2566#define CENDQUOTE 5 /* a terminating quote */
2567#define CBQUOTE 6 /* backwards single quote */
2568#define CVAR 7 /* a dollar sign */
2569#define CENDVAR 8 /* a '}' character */
2570#define CLP 9 /* a left paren in arithmetic */
2571#define CRP 10 /* a right paren in arithmetic */
2572#define CENDFILE 11 /* end of file */
2573#define CCTL 12 /* like CWORD, except it must be escaped */
2574#define CSPCL 13 /* these terminate a word */
2575#define CIGN 14 /* character should be ignored */
2576
Denis Vlasenko131ae172007-02-18 13:00:19 +00002577#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002578#define SYNBASE 130
2579#define PEOF -130
2580#define PEOA -129
2581#define PEOA_OR_PEOF PEOA
2582#else
2583#define SYNBASE 129
2584#define PEOF -129
2585#define PEOA_OR_PEOF PEOF
2586#endif
2587
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002588/* number syntax index */
2589#define BASESYNTAX 0 /* not in quotes */
2590#define DQSYNTAX 1 /* in double quotes */
2591#define SQSYNTAX 2 /* in single quotes */
2592#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002593#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596#define USE_SIT_FUNCTION
2597#endif
2598
Denis Vlasenko131ae172007-02-18 13:00:19 +00002599#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002600static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002601#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002602 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002603#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002604 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2605 { CNL, CNL, CNL, CNL }, /* 2, \n */
2606 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2607 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2608 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2609 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2610 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2611 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2612 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2613 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2614 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002615#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002616 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2617 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2618 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002619#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002620};
Eric Andersenc470f442003-07-28 09:56:35 +00002621#else
2622static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002623#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002624 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002625#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002626 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2627 { CNL, CNL, CNL }, /* 2, \n */
2628 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2629 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2630 { CVAR, CVAR, CWORD }, /* 5, $ */
2631 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2632 { CSPCL, CWORD, CWORD }, /* 7, ( */
2633 { CSPCL, CWORD, CWORD }, /* 8, ) */
2634 { CBACK, CBACK, CCTL }, /* 9, \ */
2635 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2636 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002637#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002638 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2639 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2640 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002641#endif
2642};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002643#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002644
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002645#ifdef USE_SIT_FUNCTION
2646
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002647static int
2648SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002649{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002650 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002651#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002652 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002653 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2654 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2655 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2656 11, 3 /* "}~" */
2657 };
2658#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002659 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002660 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2661 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2662 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2663 10, 2 /* "}~" */
2664 };
2665#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002666 const char *s;
2667 int indx;
2668
Eric Andersenc470f442003-07-28 09:56:35 +00002669 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002671#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002672 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002674 else
2675#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002676#define U_C(c) ((unsigned char)(c))
2677
2678 if ((unsigned char)c >= (unsigned char)(CTLESC)
2679 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2680 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002681 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002682 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002683 s = strchrnul(spec_symbls, c);
2684 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002685 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002686 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002687 }
2688 return S_I_T[indx][syntax];
2689}
2690
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002691#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692
Denis Vlasenko131ae172007-02-18 13:00:19 +00002693#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694#define CSPCL_CIGN_CIGN_CIGN 0
2695#define CSPCL_CWORD_CWORD_CWORD 1
2696#define CNL_CNL_CNL_CNL 2
2697#define CWORD_CCTL_CCTL_CWORD 3
2698#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2699#define CVAR_CVAR_CWORD_CVAR 5
2700#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2701#define CSPCL_CWORD_CWORD_CLP 7
2702#define CSPCL_CWORD_CWORD_CRP 8
2703#define CBACK_CBACK_CCTL_CBACK 9
2704#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2705#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2706#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2707#define CWORD_CWORD_CWORD_CWORD 13
2708#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002709#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002710#define CSPCL_CWORD_CWORD_CWORD 0
2711#define CNL_CNL_CNL_CNL 1
2712#define CWORD_CCTL_CCTL_CWORD 2
2713#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2714#define CVAR_CVAR_CWORD_CVAR 4
2715#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2716#define CSPCL_CWORD_CWORD_CLP 6
2717#define CSPCL_CWORD_CWORD_CRP 7
2718#define CBACK_CBACK_CCTL_CBACK 8
2719#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2720#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2721#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2722#define CWORD_CWORD_CWORD_CWORD 12
2723#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002724#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002725
2726static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002727 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002728 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002729#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002730 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2731#endif
2732 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2734 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2735 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2736 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2737 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2738 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2739 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2740 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002741 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2870 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2871 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2893 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002894 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002895 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2897 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002899 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002900 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2901 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2902 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2905 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2907 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2919 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2920 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2921 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2922 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2923 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2953 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2954 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2957 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2985 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2986 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2987 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002988};
2989
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002990#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2991
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002992#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002993
Eric Andersen2870d962001-07-02 17:27:21 +00002994
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002995/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002996
Denis Vlasenko131ae172007-02-18 13:00:19 +00002997#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002998
2999#define ALIASINUSE 1
3000#define ALIASDEAD 2
3001
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003002struct alias {
3003 struct alias *next;
3004 char *name;
3005 char *val;
3006 int flag;
3007};
3008
Denis Vlasenko01631112007-12-16 17:20:38 +00003009
3010static struct alias **atab; // [ATABSIZE];
3011#define INIT_G_alias() do { \
3012 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3013} while (0)
3014
Eric Andersen2870d962001-07-02 17:27:21 +00003015
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003016static struct alias **
3017__lookupalias(const char *name) {
3018 unsigned int hashval;
3019 struct alias **app;
3020 const char *p;
3021 unsigned int ch;
3022
3023 p = name;
3024
3025 ch = (unsigned char)*p;
3026 hashval = ch << 4;
3027 while (ch) {
3028 hashval += ch;
3029 ch = (unsigned char)*++p;
3030 }
3031 app = &atab[hashval % ATABSIZE];
3032
3033 for (; *app; app = &(*app)->next) {
3034 if (strcmp(name, (*app)->name) == 0) {
3035 break;
3036 }
3037 }
3038
3039 return app;
3040}
3041
3042static struct alias *
3043lookupalias(const char *name, int check)
3044{
3045 struct alias *ap = *__lookupalias(name);
3046
3047 if (check && ap && (ap->flag & ALIASINUSE))
3048 return NULL;
3049 return ap;
3050}
3051
3052static struct alias *
3053freealias(struct alias *ap)
3054{
3055 struct alias *next;
3056
3057 if (ap->flag & ALIASINUSE) {
3058 ap->flag |= ALIASDEAD;
3059 return ap;
3060 }
3061
3062 next = ap->next;
3063 free(ap->name);
3064 free(ap->val);
3065 free(ap);
3066 return next;
3067}
Eric Andersencb57d552001-06-28 07:25:16 +00003068
Eric Andersenc470f442003-07-28 09:56:35 +00003069static void
3070setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003071{
3072 struct alias *ap, **app;
3073
3074 app = __lookupalias(name);
3075 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003076 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003077 if (ap) {
3078 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003079 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003080 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003081 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003082 ap->flag &= ~ALIASDEAD;
3083 } else {
3084 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003085 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003086 ap->name = ckstrdup(name);
3087 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003088 /*ap->flag = 0; - ckzalloc did it */
3089 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003090 *app = ap;
3091 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003092 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003093}
3094
Eric Andersenc470f442003-07-28 09:56:35 +00003095static int
3096unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003097{
Eric Andersencb57d552001-06-28 07:25:16 +00003098 struct alias **app;
3099
3100 app = __lookupalias(name);
3101
3102 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003103 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003104 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003106 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003107 }
3108
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003109 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003110}
3111
Eric Andersenc470f442003-07-28 09:56:35 +00003112static void
3113rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003114{
Eric Andersencb57d552001-06-28 07:25:16 +00003115 struct alias *ap, **app;
3116 int i;
3117
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003119 for (i = 0; i < ATABSIZE; i++) {
3120 app = &atab[i];
3121 for (ap = *app; ap; ap = *app) {
3122 *app = freealias(*app);
3123 if (ap == *app) {
3124 app = &ap->next;
3125 }
3126 }
3127 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003128 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003129}
3130
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003131static void
3132printalias(const struct alias *ap)
3133{
3134 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3135}
3136
Eric Andersencb57d552001-06-28 07:25:16 +00003137/*
3138 * TODO - sort output
3139 */
Eric Andersenc470f442003-07-28 09:56:35 +00003140static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003141aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003142{
3143 char *n, *v;
3144 int ret = 0;
3145 struct alias *ap;
3146
Denis Vlasenko68404f12008-03-17 09:00:54 +00003147 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003148 int i;
3149
Denis Vlasenko68404f12008-03-17 09:00:54 +00003150 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003151 for (ap = atab[i]; ap; ap = ap->next) {
3152 printalias(ap);
3153 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003154 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003155 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003156 }
3157 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003158 v = strchr(n+1, '=');
3159 if (v == NULL) { /* n+1: funny ksh stuff */
3160 ap = *__lookupalias(n);
3161 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003162 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003163 ret = 1;
3164 } else
3165 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003166 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003167 *v++ = '\0';
3168 setalias(n, v);
3169 }
3170 }
3171
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003172 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003173}
3174
Eric Andersenc470f442003-07-28 09:56:35 +00003175static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003176unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003177{
3178 int i;
3179
3180 while ((i = nextopt("a")) != '\0') {
3181 if (i == 'a') {
3182 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003183 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003184 }
3185 }
3186 for (i = 0; *argptr; argptr++) {
3187 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003188 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003189 i = 1;
3190 }
3191 }
3192
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003193 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003194}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003195
Denis Vlasenko131ae172007-02-18 13:00:19 +00003196#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003197
Eric Andersenc470f442003-07-28 09:56:35 +00003198
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003199/* ============ jobs.c */
3200
3201/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3202#define FORK_FG 0
3203#define FORK_BG 1
3204#define FORK_NOJOB 2
3205
3206/* mode flags for showjob(s) */
3207#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3208#define SHOW_PID 0x04 /* include process pid */
3209#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3210
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003211/*
3212 * A job structure contains information about a job. A job is either a
3213 * single process or a set of processes contained in a pipeline. In the
3214 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3215 * array of pids.
3216 */
3217
3218struct procstat {
3219 pid_t pid; /* process id */
3220 int status; /* last process status from wait() */
3221 char *cmd; /* text of command being run */
3222};
3223
3224struct job {
3225 struct procstat ps0; /* status of process */
3226 struct procstat *ps; /* status or processes when more than one */
3227#if JOBS
3228 int stopstatus; /* status of a stopped job */
3229#endif
3230 uint32_t
3231 nprocs: 16, /* number of processes */
3232 state: 8,
3233#define JOBRUNNING 0 /* at least one proc running */
3234#define JOBSTOPPED 1 /* all procs are stopped */
3235#define JOBDONE 2 /* all procs are completed */
3236#if JOBS
3237 sigint: 1, /* job was killed by SIGINT */
3238 jobctl: 1, /* job running under job control */
3239#endif
3240 waited: 1, /* true if this entry has been waited for */
3241 used: 1, /* true if this entry is in used */
3242 changed: 1; /* true if status has changed */
3243 struct job *prev_job; /* previous job */
3244};
3245
3246static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003247static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003248
Denis Vlasenko68404f12008-03-17 09:00:54 +00003249static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003250#if !JOBS
3251#define forkshell(job, node, mode) forkshell(job, mode)
3252#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003253static int forkshell(struct job *, union node *, int);
3254static int waitforjob(struct job *);
3255
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003256#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003257enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003258#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259#else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003260static smallint doing_jobctl;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003261static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003262#endif
3263
3264/*
3265 * Set the signal handler for the specified signal. The routine figures
3266 * out what it should be set to.
3267 */
3268static void
3269setsignal(int signo)
3270{
3271 int action;
3272 char *t, tsig;
3273 struct sigaction act;
3274
3275 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003276 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277 if (t == NULL)
3278 action = S_DFL;
3279 else if (*t != '\0')
3280 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003281 if (rootshell && action == S_DFL) {
3282 switch (signo) {
3283 case SIGINT:
3284 if (iflag || minusc || sflag == 0)
3285 action = S_CATCH;
3286 break;
3287 case SIGQUIT:
3288#if DEBUG
3289 if (debug)
3290 break;
3291#endif
3292 /* FALLTHROUGH */
3293 case SIGTERM:
3294 if (iflag)
3295 action = S_IGN;
3296 break;
3297#if JOBS
3298 case SIGTSTP:
3299 case SIGTTOU:
3300 if (mflag)
3301 action = S_IGN;
3302 break;
3303#endif
3304 }
3305 }
3306
3307 t = &sigmode[signo - 1];
3308 tsig = *t;
3309 if (tsig == 0) {
3310 /*
3311 * current setting unknown
3312 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003313 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314 /*
3315 * Pretend it worked; maybe we should give a warning
3316 * here, but other shells don't. We don't alter
3317 * sigmode, so that we retry every time.
3318 */
3319 return;
3320 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003321 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003322 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003323 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003324 if (mflag
3325 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3326 ) {
3327 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 }
3330 }
3331 if (tsig == S_HARD_IGN || tsig == action)
3332 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003333 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334 switch (action) {
3335 case S_CATCH:
3336 act.sa_handler = onsig;
3337 break;
3338 case S_IGN:
3339 act.sa_handler = SIG_IGN;
3340 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 }
3342 *t = action;
3343 act.sa_flags = 0;
3344 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003345 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003346}
3347
3348/* mode flags for set_curjob */
3349#define CUR_DELETE 2
3350#define CUR_RUNNING 1
3351#define CUR_STOPPED 0
3352
3353/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003354#define DOWAIT_NONBLOCK WNOHANG
3355#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356
3357#if JOBS
3358/* pgrp of shell on invocation */
3359static int initialpgrp;
3360static int ttyfd = -1;
3361#endif
3362/* array of jobs */
3363static struct job *jobtab;
3364/* size of array */
3365static unsigned njobs;
3366/* current job */
3367static struct job *curjob;
3368/* number of presumed living untracked jobs */
3369static int jobless;
3370
3371static void
3372set_curjob(struct job *jp, unsigned mode)
3373{
3374 struct job *jp1;
3375 struct job **jpp, **curp;
3376
3377 /* first remove from list */
3378 jpp = curp = &curjob;
3379 do {
3380 jp1 = *jpp;
3381 if (jp1 == jp)
3382 break;
3383 jpp = &jp1->prev_job;
3384 } while (1);
3385 *jpp = jp1->prev_job;
3386
3387 /* Then re-insert in correct position */
3388 jpp = curp;
3389 switch (mode) {
3390 default:
3391#if DEBUG
3392 abort();
3393#endif
3394 case CUR_DELETE:
3395 /* job being deleted */
3396 break;
3397 case CUR_RUNNING:
3398 /* newly created job or backgrounded job,
3399 put after all stopped jobs. */
3400 do {
3401 jp1 = *jpp;
3402#if JOBS
3403 if (!jp1 || jp1->state != JOBSTOPPED)
3404#endif
3405 break;
3406 jpp = &jp1->prev_job;
3407 } while (1);
3408 /* FALLTHROUGH */
3409#if JOBS
3410 case CUR_STOPPED:
3411#endif
3412 /* newly stopped job - becomes curjob */
3413 jp->prev_job = *jpp;
3414 *jpp = jp;
3415 break;
3416 }
3417}
3418
3419#if JOBS || DEBUG
3420static int
3421jobno(const struct job *jp)
3422{
3423 return jp - jobtab + 1;
3424}
3425#endif
3426
3427/*
3428 * Convert a job name to a job structure.
3429 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003430#if !JOBS
3431#define getjob(name, getctl) getjob(name)
3432#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003433static struct job *
3434getjob(const char *name, int getctl)
3435{
3436 struct job *jp;
3437 struct job *found;
3438 const char *err_msg = "No such job: %s";
3439 unsigned num;
3440 int c;
3441 const char *p;
3442 char *(*match)(const char *, const char *);
3443
3444 jp = curjob;
3445 p = name;
3446 if (!p)
3447 goto currentjob;
3448
3449 if (*p != '%')
3450 goto err;
3451
3452 c = *++p;
3453 if (!c)
3454 goto currentjob;
3455
3456 if (!p[1]) {
3457 if (c == '+' || c == '%') {
3458 currentjob:
3459 err_msg = "No current job";
3460 goto check;
3461 }
3462 if (c == '-') {
3463 if (jp)
3464 jp = jp->prev_job;
3465 err_msg = "No previous job";
3466 check:
3467 if (!jp)
3468 goto err;
3469 goto gotit;
3470 }
3471 }
3472
3473 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003474// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003475 num = atoi(p);
3476 if (num < njobs) {
3477 jp = jobtab + num - 1;
3478 if (jp->used)
3479 goto gotit;
3480 goto err;
3481 }
3482 }
3483
3484 match = prefix;
3485 if (*p == '?') {
3486 match = strstr;
3487 p++;
3488 }
3489
3490 found = 0;
3491 while (1) {
3492 if (!jp)
3493 goto err;
3494 if (match(jp->ps[0].cmd, p)) {
3495 if (found)
3496 goto err;
3497 found = jp;
3498 err_msg = "%s: ambiguous";
3499 }
3500 jp = jp->prev_job;
3501 }
3502
3503 gotit:
3504#if JOBS
3505 err_msg = "job %s not created under job control";
3506 if (getctl && jp->jobctl == 0)
3507 goto err;
3508#endif
3509 return jp;
3510 err:
3511 ash_msg_and_raise_error(err_msg, name);
3512}
3513
3514/*
3515 * Mark a job structure as unused.
3516 */
3517static void
3518freejob(struct job *jp)
3519{
3520 struct procstat *ps;
3521 int i;
3522
3523 INT_OFF;
3524 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3525 if (ps->cmd != nullstr)
3526 free(ps->cmd);
3527 }
3528 if (jp->ps != &jp->ps0)
3529 free(jp->ps);
3530 jp->used = 0;
3531 set_curjob(jp, CUR_DELETE);
3532 INT_ON;
3533}
3534
3535#if JOBS
3536static void
3537xtcsetpgrp(int fd, pid_t pgrp)
3538{
3539 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003540 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003541}
3542
3543/*
3544 * Turn job control on and off.
3545 *
3546 * Note: This code assumes that the third arg to ioctl is a character
3547 * pointer, which is true on Berkeley systems but not System V. Since
3548 * System V doesn't have job control yet, this isn't a problem now.
3549 *
3550 * Called with interrupts off.
3551 */
3552static void
3553setjobctl(int on)
3554{
3555 int fd;
3556 int pgrp;
3557
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003558 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003559 return;
3560 if (on) {
3561 int ofd;
3562 ofd = fd = open(_PATH_TTY, O_RDWR);
3563 if (fd < 0) {
3564 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3565 * That sometimes helps to acquire controlling tty.
3566 * Obviously, a workaround for bugs when someone
3567 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003568 fd = 2;
3569 while (!isatty(fd))
3570 if (--fd < 0)
3571 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003572 }
3573 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003574 if (ofd >= 0)
3575 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003576 if (fd < 0)
3577 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003578 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003579 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003580 do { /* while we are in the background */
3581 pgrp = tcgetpgrp(fd);
3582 if (pgrp < 0) {
3583 out:
3584 ash_msg("can't access tty; job control turned off");
3585 mflag = on = 0;
3586 goto close;
3587 }
3588 if (pgrp == getpgrp())
3589 break;
3590 killpg(0, SIGTTIN);
3591 } while (1);
3592 initialpgrp = pgrp;
3593
3594 setsignal(SIGTSTP);
3595 setsignal(SIGTTOU);
3596 setsignal(SIGTTIN);
3597 pgrp = rootpid;
3598 setpgid(0, pgrp);
3599 xtcsetpgrp(fd, pgrp);
3600 } else {
3601 /* turning job control off */
3602 fd = ttyfd;
3603 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003604 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003605 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003606 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607 setpgid(0, pgrp);
3608 setsignal(SIGTSTP);
3609 setsignal(SIGTTOU);
3610 setsignal(SIGTTIN);
3611 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003612 if (fd >= 0)
3613 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614 fd = -1;
3615 }
3616 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003617 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003618}
3619
3620static int
3621killcmd(int argc, char **argv)
3622{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003623 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003624 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003625 do {
3626 if (argv[i][0] == '%') {
3627 struct job *jp = getjob(argv[i], 0);
3628 unsigned pid = jp->ps[0].pid;
3629 /* Enough space for ' -NNN<nul>' */
3630 argv[i] = alloca(sizeof(int)*3 + 3);
3631 /* kill_main has matching code to expect
3632 * leading space. Needed to not confuse
3633 * negative pids with "kill -SIGNAL_NO" syntax */
3634 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003635 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003636 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003638 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003639}
3640
3641static void
3642showpipe(struct job *jp, FILE *out)
3643{
3644 struct procstat *sp;
3645 struct procstat *spend;
3646
3647 spend = jp->ps + jp->nprocs;
3648 for (sp = jp->ps + 1; sp < spend; sp++)
3649 fprintf(out, " | %s", sp->cmd);
3650 outcslow('\n', out);
3651 flush_stdout_stderr();
3652}
3653
3654
3655static int
3656restartjob(struct job *jp, int mode)
3657{
3658 struct procstat *ps;
3659 int i;
3660 int status;
3661 pid_t pgid;
3662
3663 INT_OFF;
3664 if (jp->state == JOBDONE)
3665 goto out;
3666 jp->state = JOBRUNNING;
3667 pgid = jp->ps->pid;
3668 if (mode == FORK_FG)
3669 xtcsetpgrp(ttyfd, pgid);
3670 killpg(pgid, SIGCONT);
3671 ps = jp->ps;
3672 i = jp->nprocs;
3673 do {
3674 if (WIFSTOPPED(ps->status)) {
3675 ps->status = -1;
3676 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003677 ps++;
3678 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003679 out:
3680 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3681 INT_ON;
3682 return status;
3683}
3684
3685static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003686fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003687{
3688 struct job *jp;
3689 FILE *out;
3690 int mode;
3691 int retval;
3692
3693 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3694 nextopt(nullstr);
3695 argv = argptr;
3696 out = stdout;
3697 do {
3698 jp = getjob(*argv, 1);
3699 if (mode == FORK_BG) {
3700 set_curjob(jp, CUR_RUNNING);
3701 fprintf(out, "[%d] ", jobno(jp));
3702 }
3703 outstr(jp->ps->cmd, out);
3704 showpipe(jp, out);
3705 retval = restartjob(jp, mode);
3706 } while (*argv && *++argv);
3707 return retval;
3708}
3709#endif
3710
3711static int
3712sprint_status(char *s, int status, int sigonly)
3713{
3714 int col;
3715 int st;
3716
3717 col = 0;
3718 if (!WIFEXITED(status)) {
3719#if JOBS
3720 if (WIFSTOPPED(status))
3721 st = WSTOPSIG(status);
3722 else
3723#endif
3724 st = WTERMSIG(status);
3725 if (sigonly) {
3726 if (st == SIGINT || st == SIGPIPE)
3727 goto out;
3728#if JOBS
3729 if (WIFSTOPPED(status))
3730 goto out;
3731#endif
3732 }
3733 st &= 0x7f;
3734 col = fmtstr(s, 32, strsignal(st));
3735 if (WCOREDUMP(status)) {
3736 col += fmtstr(s + col, 16, " (core dumped)");
3737 }
3738 } else if (!sigonly) {
3739 st = WEXITSTATUS(status);
3740 if (st)
3741 col = fmtstr(s, 16, "Done(%d)", st);
3742 else
3743 col = fmtstr(s, 16, "Done");
3744 }
3745 out:
3746 return col;
3747}
3748
3749/*
3750 * Do a wait system call. If job control is compiled in, we accept
3751 * stopped processes. If block is zero, we return a value of zero
3752 * rather than blocking.
3753 *
3754 * System V doesn't have a non-blocking wait system call. It does
3755 * have a SIGCLD signal that is sent to a process when one of it's
3756 * children dies. The obvious way to use SIGCLD would be to install
3757 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3758 * was received, and have waitproc bump another counter when it got
3759 * the status of a process. Waitproc would then know that a wait
3760 * system call would not block if the two counters were different.
3761 * This approach doesn't work because if a process has children that
3762 * have not been waited for, System V will send it a SIGCLD when it
3763 * installs a signal handler for SIGCLD. What this means is that when
3764 * a child exits, the shell will be sent SIGCLD signals continuously
3765 * until is runs out of stack space, unless it does a wait call before
3766 * restoring the signal handler. The code below takes advantage of
3767 * this (mis)feature by installing a signal handler for SIGCLD and
3768 * then checking to see whether it was called. If there are any
3769 * children to be waited for, it will be.
3770 *
3771 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3772 * waits at all. In this case, the user will not be informed when
3773 * a background process until the next time she runs a real program
3774 * (as opposed to running a builtin command or just typing return),
3775 * and the jobs command may give out of date information.
3776 */
3777static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003778waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003781 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003782 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003783#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003784 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3785 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786}
3787
3788/*
3789 * Wait for a process to terminate.
3790 */
3791static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003792dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003793{
3794 int pid;
3795 int status;
3796 struct job *jp;
3797 struct job *thisjob;
3798 int state;
3799
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003800 TRACE(("dowait(%d) called\n", wait_flags));
3801 pid = waitproc(wait_flags, &status);
3802 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003803 if (pid <= 0) {
3804 /* If we were doing blocking wait and (probably) got EINTR,
3805 * check for pending sigs received while waiting.
3806 * (NB: can be moved into callers if needed) */
3807 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3808 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003810 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003811 INT_OFF;
3812 thisjob = NULL;
3813 for (jp = curjob; jp; jp = jp->prev_job) {
3814 struct procstat *sp;
3815 struct procstat *spend;
3816 if (jp->state == JOBDONE)
3817 continue;
3818 state = JOBDONE;
3819 spend = jp->ps + jp->nprocs;
3820 sp = jp->ps;
3821 do {
3822 if (sp->pid == pid) {
3823 TRACE(("Job %d: changing status of proc %d "
3824 "from 0x%x to 0x%x\n",
3825 jobno(jp), pid, sp->status, status));
3826 sp->status = status;
3827 thisjob = jp;
3828 }
3829 if (sp->status == -1)
3830 state = JOBRUNNING;
3831#if JOBS
3832 if (state == JOBRUNNING)
3833 continue;
3834 if (WIFSTOPPED(sp->status)) {
3835 jp->stopstatus = sp->status;
3836 state = JOBSTOPPED;
3837 }
3838#endif
3839 } while (++sp < spend);
3840 if (thisjob)
3841 goto gotjob;
3842 }
3843#if JOBS
3844 if (!WIFSTOPPED(status))
3845#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003846 jobless--;
3847 goto out;
3848
3849 gotjob:
3850 if (state != JOBRUNNING) {
3851 thisjob->changed = 1;
3852
3853 if (thisjob->state != state) {
3854 TRACE(("Job %d: changing state from %d to %d\n",
3855 jobno(thisjob), thisjob->state, state));
3856 thisjob->state = state;
3857#if JOBS
3858 if (state == JOBSTOPPED) {
3859 set_curjob(thisjob, CUR_STOPPED);
3860 }
3861#endif
3862 }
3863 }
3864
3865 out:
3866 INT_ON;
3867
3868 if (thisjob && thisjob == job) {
3869 char s[48 + 1];
3870 int len;
3871
3872 len = sprint_status(s, status, 1);
3873 if (len) {
3874 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003875 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876 out2str(s);
3877 }
3878 }
3879 return pid;
3880}
3881
3882#if JOBS
3883static void
3884showjob(FILE *out, struct job *jp, int mode)
3885{
3886 struct procstat *ps;
3887 struct procstat *psend;
3888 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003889 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003890 char s[80];
3891
3892 ps = jp->ps;
3893
3894 if (mode & SHOW_PGID) {
3895 /* just output process (group) id of pipeline */
3896 fprintf(out, "%d\n", ps->pid);
3897 return;
3898 }
3899
3900 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003901 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003902
3903 if (jp == curjob)
3904 s[col - 2] = '+';
3905 else if (curjob && jp == curjob->prev_job)
3906 s[col - 2] = '-';
3907
3908 if (mode & SHOW_PID)
3909 col += fmtstr(s + col, 16, "%d ", ps->pid);
3910
3911 psend = ps + jp->nprocs;
3912
3913 if (jp->state == JOBRUNNING) {
3914 strcpy(s + col, "Running");
3915 col += sizeof("Running") - 1;
3916 } else {
3917 int status = psend[-1].status;
3918 if (jp->state == JOBSTOPPED)
3919 status = jp->stopstatus;
3920 col += sprint_status(s + col, status, 0);
3921 }
3922
3923 goto start;
3924
3925 do {
3926 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003927 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003928 start:
3929 fprintf(out, "%s%*c%s",
3930 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3931 );
3932 if (!(mode & SHOW_PID)) {
3933 showpipe(jp, out);
3934 break;
3935 }
3936 if (++ps == psend) {
3937 outcslow('\n', out);
3938 break;
3939 }
3940 } while (1);
3941
3942 jp->changed = 0;
3943
3944 if (jp->state == JOBDONE) {
3945 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3946 freejob(jp);
3947 }
3948}
3949
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003950/*
3951 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3952 * statuses have changed since the last call to showjobs.
3953 */
3954static void
3955showjobs(FILE *out, int mode)
3956{
3957 struct job *jp;
3958
3959 TRACE(("showjobs(%x) called\n", mode));
3960
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003961 /* If not even one job changed, there is nothing to do */
3962 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003963 continue;
3964
3965 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003966 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003967 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003968 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969 }
3970}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003971
3972static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003973jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003974{
3975 int mode, m;
3976
3977 mode = 0;
3978 while ((m = nextopt("lp"))) {
3979 if (m == 'l')
3980 mode = SHOW_PID;
3981 else
3982 mode = SHOW_PGID;
3983 }
3984
3985 argv = argptr;
3986 if (*argv) {
3987 do
3988 showjob(stdout, getjob(*argv,0), mode);
3989 while (*++argv);
3990 } else
3991 showjobs(stdout, mode);
3992
3993 return 0;
3994}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003995#endif /* JOBS */
3996
3997static int
3998getstatus(struct job *job)
3999{
4000 int status;
4001 int retval;
4002
4003 status = job->ps[job->nprocs - 1].status;
4004 retval = WEXITSTATUS(status);
4005 if (!WIFEXITED(status)) {
4006#if JOBS
4007 retval = WSTOPSIG(status);
4008 if (!WIFSTOPPED(status))
4009#endif
4010 {
4011 /* XXX: limits number of signals */
4012 retval = WTERMSIG(status);
4013#if JOBS
4014 if (retval == SIGINT)
4015 job->sigint = 1;
4016#endif
4017 }
4018 retval += 128;
4019 }
4020 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4021 jobno(job), job->nprocs, status, retval));
4022 return retval;
4023}
4024
4025static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004026waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004027{
4028 struct job *job;
4029 int retval;
4030 struct job *jp;
4031
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004032// exsig++;
4033// xbarrier();
4034 if (pendingsig)
4035 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004036
4037 nextopt(nullstr);
4038 retval = 0;
4039
4040 argv = argptr;
4041 if (!*argv) {
4042 /* wait for all jobs */
4043 for (;;) {
4044 jp = curjob;
4045 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004046 if (!jp) /* no running procs */
4047 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004048 if (jp->state == JOBRUNNING)
4049 break;
4050 jp->waited = 1;
4051 jp = jp->prev_job;
4052 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004053 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 }
4055 }
4056
4057 retval = 127;
4058 do {
4059 if (**argv != '%') {
4060 pid_t pid = number(*argv);
4061 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004062 while (1) {
4063 if (!job)
4064 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065 if (job->ps[job->nprocs - 1].pid == pid)
4066 break;
4067 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004068 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069 } else
4070 job = getjob(*argv, 0);
4071 /* loop until process terminated or stopped */
4072 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004073 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074 job->waited = 1;
4075 retval = getstatus(job);
4076 repeat:
4077 ;
4078 } while (*++argv);
4079
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004080 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 return retval;
4082}
4083
4084static struct job *
4085growjobtab(void)
4086{
4087 size_t len;
4088 ptrdiff_t offset;
4089 struct job *jp, *jq;
4090
4091 len = njobs * sizeof(*jp);
4092 jq = jobtab;
4093 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4094
4095 offset = (char *)jp - (char *)jq;
4096 if (offset) {
4097 /* Relocate pointers */
4098 size_t l = len;
4099
4100 jq = (struct job *)((char *)jq + l);
4101 while (l) {
4102 l -= sizeof(*jp);
4103 jq--;
4104#define joff(p) ((struct job *)((char *)(p) + l))
4105#define jmove(p) (p) = (void *)((char *)(p) + offset)
4106 if (joff(jp)->ps == &jq->ps0)
4107 jmove(joff(jp)->ps);
4108 if (joff(jp)->prev_job)
4109 jmove(joff(jp)->prev_job);
4110 }
4111 if (curjob)
4112 jmove(curjob);
4113#undef joff
4114#undef jmove
4115 }
4116
4117 njobs += 4;
4118 jobtab = jp;
4119 jp = (struct job *)((char *)jp + len);
4120 jq = jp + 3;
4121 do {
4122 jq->used = 0;
4123 } while (--jq >= jp);
4124 return jp;
4125}
4126
4127/*
4128 * Return a new job structure.
4129 * Called with interrupts off.
4130 */
4131static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004132makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133{
4134 int i;
4135 struct job *jp;
4136
4137 for (i = njobs, jp = jobtab; ; jp++) {
4138 if (--i < 0) {
4139 jp = growjobtab();
4140 break;
4141 }
4142 if (jp->used == 0)
4143 break;
4144 if (jp->state != JOBDONE || !jp->waited)
4145 continue;
4146#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004147 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004148 continue;
4149#endif
4150 freejob(jp);
4151 break;
4152 }
4153 memset(jp, 0, sizeof(*jp));
4154#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004155 /* jp->jobctl is a bitfield.
4156 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004157 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004158 jp->jobctl = 1;
4159#endif
4160 jp->prev_job = curjob;
4161 curjob = jp;
4162 jp->used = 1;
4163 jp->ps = &jp->ps0;
4164 if (nprocs > 1) {
4165 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4166 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004167 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004168 jobno(jp)));
4169 return jp;
4170}
4171
4172#if JOBS
4173/*
4174 * Return a string identifying a command (to be printed by the
4175 * jobs command).
4176 */
4177static char *cmdnextc;
4178
4179static void
4180cmdputs(const char *s)
4181{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004182 static const char vstype[VSTYPE + 1][3] = {
4183 "", "}", "-", "+", "?", "=",
4184 "%", "%%", "#", "##"
4185 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4186 };
4187
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004188 const char *p, *str;
4189 char c, cc[2] = " ";
4190 char *nextc;
4191 int subtype = 0;
4192 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004193
4194 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4195 p = s;
4196 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004197 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004198 switch (c) {
4199 case CTLESC:
4200 c = *p++;
4201 break;
4202 case CTLVAR:
4203 subtype = *p++;
4204 if ((subtype & VSTYPE) == VSLENGTH)
4205 str = "${#";
4206 else
4207 str = "${";
4208 if (!(subtype & VSQUOTE) == !(quoted & 1))
4209 goto dostr;
4210 quoted ^= 1;
4211 c = '"';
4212 break;
4213 case CTLENDVAR:
4214 str = "\"}" + !(quoted & 1);
4215 quoted >>= 1;
4216 subtype = 0;
4217 goto dostr;
4218 case CTLBACKQ:
4219 str = "$(...)";
4220 goto dostr;
4221 case CTLBACKQ+CTLQUOTE:
4222 str = "\"$(...)\"";
4223 goto dostr;
4224#if ENABLE_ASH_MATH_SUPPORT
4225 case CTLARI:
4226 str = "$((";
4227 goto dostr;
4228 case CTLENDARI:
4229 str = "))";
4230 goto dostr;
4231#endif
4232 case CTLQUOTEMARK:
4233 quoted ^= 1;
4234 c = '"';
4235 break;
4236 case '=':
4237 if (subtype == 0)
4238 break;
4239 if ((subtype & VSTYPE) != VSNORMAL)
4240 quoted <<= 1;
4241 str = vstype[subtype & VSTYPE];
4242 if (subtype & VSNUL)
4243 c = ':';
4244 else
4245 goto checkstr;
4246 break;
4247 case '\'':
4248 case '\\':
4249 case '"':
4250 case '$':
4251 /* These can only happen inside quotes */
4252 cc[0] = c;
4253 str = cc;
4254 c = '\\';
4255 break;
4256 default:
4257 break;
4258 }
4259 USTPUTC(c, nextc);
4260 checkstr:
4261 if (!str)
4262 continue;
4263 dostr:
4264 while ((c = *str++)) {
4265 USTPUTC(c, nextc);
4266 }
4267 }
4268 if (quoted & 1) {
4269 USTPUTC('"', nextc);
4270 }
4271 *nextc = 0;
4272 cmdnextc = nextc;
4273}
4274
4275/* cmdtxt() and cmdlist() call each other */
4276static void cmdtxt(union node *n);
4277
4278static void
4279cmdlist(union node *np, int sep)
4280{
4281 for (; np; np = np->narg.next) {
4282 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004283 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004284 cmdtxt(np);
4285 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004286 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004287 }
4288}
4289
4290static void
4291cmdtxt(union node *n)
4292{
4293 union node *np;
4294 struct nodelist *lp;
4295 const char *p;
4296 char s[2];
4297
4298 if (!n)
4299 return;
4300 switch (n->type) {
4301 default:
4302#if DEBUG
4303 abort();
4304#endif
4305 case NPIPE:
4306 lp = n->npipe.cmdlist;
4307 for (;;) {
4308 cmdtxt(lp->n);
4309 lp = lp->next;
4310 if (!lp)
4311 break;
4312 cmdputs(" | ");
4313 }
4314 break;
4315 case NSEMI:
4316 p = "; ";
4317 goto binop;
4318 case NAND:
4319 p = " && ";
4320 goto binop;
4321 case NOR:
4322 p = " || ";
4323 binop:
4324 cmdtxt(n->nbinary.ch1);
4325 cmdputs(p);
4326 n = n->nbinary.ch2;
4327 goto donode;
4328 case NREDIR:
4329 case NBACKGND:
4330 n = n->nredir.n;
4331 goto donode;
4332 case NNOT:
4333 cmdputs("!");
4334 n = n->nnot.com;
4335 donode:
4336 cmdtxt(n);
4337 break;
4338 case NIF:
4339 cmdputs("if ");
4340 cmdtxt(n->nif.test);
4341 cmdputs("; then ");
4342 n = n->nif.ifpart;
4343 if (n->nif.elsepart) {
4344 cmdtxt(n);
4345 cmdputs("; else ");
4346 n = n->nif.elsepart;
4347 }
4348 p = "; fi";
4349 goto dotail;
4350 case NSUBSHELL:
4351 cmdputs("(");
4352 n = n->nredir.n;
4353 p = ")";
4354 goto dotail;
4355 case NWHILE:
4356 p = "while ";
4357 goto until;
4358 case NUNTIL:
4359 p = "until ";
4360 until:
4361 cmdputs(p);
4362 cmdtxt(n->nbinary.ch1);
4363 n = n->nbinary.ch2;
4364 p = "; done";
4365 dodo:
4366 cmdputs("; do ");
4367 dotail:
4368 cmdtxt(n);
4369 goto dotail2;
4370 case NFOR:
4371 cmdputs("for ");
4372 cmdputs(n->nfor.var);
4373 cmdputs(" in ");
4374 cmdlist(n->nfor.args, 1);
4375 n = n->nfor.body;
4376 p = "; done";
4377 goto dodo;
4378 case NDEFUN:
4379 cmdputs(n->narg.text);
4380 p = "() { ... }";
4381 goto dotail2;
4382 case NCMD:
4383 cmdlist(n->ncmd.args, 1);
4384 cmdlist(n->ncmd.redirect, 0);
4385 break;
4386 case NARG:
4387 p = n->narg.text;
4388 dotail2:
4389 cmdputs(p);
4390 break;
4391 case NHERE:
4392 case NXHERE:
4393 p = "<<...";
4394 goto dotail2;
4395 case NCASE:
4396 cmdputs("case ");
4397 cmdputs(n->ncase.expr->narg.text);
4398 cmdputs(" in ");
4399 for (np = n->ncase.cases; np; np = np->nclist.next) {
4400 cmdtxt(np->nclist.pattern);
4401 cmdputs(") ");
4402 cmdtxt(np->nclist.body);
4403 cmdputs(";; ");
4404 }
4405 p = "esac";
4406 goto dotail2;
4407 case NTO:
4408 p = ">";
4409 goto redir;
4410 case NCLOBBER:
4411 p = ">|";
4412 goto redir;
4413 case NAPPEND:
4414 p = ">>";
4415 goto redir;
4416 case NTOFD:
4417 p = ">&";
4418 goto redir;
4419 case NFROM:
4420 p = "<";
4421 goto redir;
4422 case NFROMFD:
4423 p = "<&";
4424 goto redir;
4425 case NFROMTO:
4426 p = "<>";
4427 redir:
4428 s[0] = n->nfile.fd + '0';
4429 s[1] = '\0';
4430 cmdputs(s);
4431 cmdputs(p);
4432 if (n->type == NTOFD || n->type == NFROMFD) {
4433 s[0] = n->ndup.dupfd + '0';
4434 p = s;
4435 goto dotail2;
4436 }
4437 n = n->nfile.fname;
4438 goto donode;
4439 }
4440}
4441
4442static char *
4443commandtext(union node *n)
4444{
4445 char *name;
4446
4447 STARTSTACKSTR(cmdnextc);
4448 cmdtxt(n);
4449 name = stackblock();
4450 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4451 name, cmdnextc, cmdnextc));
4452 return ckstrdup(name);
4453}
4454#endif /* JOBS */
4455
4456/*
4457 * Fork off a subshell. If we are doing job control, give the subshell its
4458 * own process group. Jp is a job structure that the job is to be added to.
4459 * N is the command that will be evaluated by the child. Both jp and n may
4460 * be NULL. The mode parameter can be one of the following:
4461 * FORK_FG - Fork off a foreground process.
4462 * FORK_BG - Fork off a background process.
4463 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4464 * process group even if job control is on.
4465 *
4466 * When job control is turned off, background processes have their standard
4467 * input redirected to /dev/null (except for the second and later processes
4468 * in a pipeline).
4469 *
4470 * Called with interrupts off.
4471 */
4472/*
4473 * Clear traps on a fork.
4474 */
4475static void
4476clear_traps(void)
4477{
4478 char **tp;
4479
4480 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004481 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004482 INT_OFF;
4483 free(*tp);
4484 *tp = NULL;
4485 if (tp != &trap[0])
4486 setsignal(tp - trap);
4487 INT_ON;
4488 }
4489 }
4490}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004491
4492/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004494
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004495/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004496static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004497forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004498{
4499 int oldlvl;
4500
4501 TRACE(("Child shell %d\n", getpid()));
4502 oldlvl = shlvl;
4503 shlvl++;
4504
4505 closescript();
4506 clear_traps();
4507#if JOBS
4508 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004509 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4511 pid_t pgrp;
4512
4513 if (jp->nprocs == 0)
4514 pgrp = getpid();
4515 else
4516 pgrp = jp->ps[0].pid;
4517 /* This can fail because we are doing it in the parent also */
4518 (void)setpgid(0, pgrp);
4519 if (mode == FORK_FG)
4520 xtcsetpgrp(ttyfd, pgrp);
4521 setsignal(SIGTSTP);
4522 setsignal(SIGTTOU);
4523 } else
4524#endif
4525 if (mode == FORK_BG) {
4526 ignoresig(SIGINT);
4527 ignoresig(SIGQUIT);
4528 if (jp->nprocs == 0) {
4529 close(0);
4530 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004531 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004532 }
4533 }
4534 if (!oldlvl && iflag) {
4535 setsignal(SIGINT);
4536 setsignal(SIGQUIT);
4537 setsignal(SIGTERM);
4538 }
4539 for (jp = curjob; jp; jp = jp->prev_job)
4540 freejob(jp);
4541 jobless = 0;
4542}
4543
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004544/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004545#if !JOBS
4546#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4547#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004548static void
4549forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4550{
4551 TRACE(("In parent shell: child = %d\n", pid));
4552 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004553 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4554 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004555 jobless++;
4556 return;
4557 }
4558#if JOBS
4559 if (mode != FORK_NOJOB && jp->jobctl) {
4560 int pgrp;
4561
4562 if (jp->nprocs == 0)
4563 pgrp = pid;
4564 else
4565 pgrp = jp->ps[0].pid;
4566 /* This can fail because we are doing it in the child also */
4567 setpgid(pid, pgrp);
4568 }
4569#endif
4570 if (mode == FORK_BG) {
4571 backgndpid = pid; /* set $! */
4572 set_curjob(jp, CUR_RUNNING);
4573 }
4574 if (jp) {
4575 struct procstat *ps = &jp->ps[jp->nprocs++];
4576 ps->pid = pid;
4577 ps->status = -1;
4578 ps->cmd = nullstr;
4579#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004580 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004581 ps->cmd = commandtext(n);
4582#endif
4583 }
4584}
4585
4586static int
4587forkshell(struct job *jp, union node *n, int mode)
4588{
4589 int pid;
4590
4591 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4592 pid = fork();
4593 if (pid < 0) {
4594 TRACE(("Fork failed, errno=%d", errno));
4595 if (jp)
4596 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004597 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004598 }
4599 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004600 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004601 else
4602 forkparent(jp, n, mode, pid);
4603 return pid;
4604}
4605
4606/*
4607 * Wait for job to finish.
4608 *
4609 * Under job control we have the problem that while a child process is
4610 * running interrupts generated by the user are sent to the child but not
4611 * to the shell. This means that an infinite loop started by an inter-
4612 * active user may be hard to kill. With job control turned off, an
4613 * interactive user may place an interactive program inside a loop. If
4614 * the interactive program catches interrupts, the user doesn't want
4615 * these interrupts to also abort the loop. The approach we take here
4616 * is to have the shell ignore interrupt signals while waiting for a
4617 * foreground process to terminate, and then send itself an interrupt
4618 * signal if the child process was terminated by an interrupt signal.
4619 * Unfortunately, some programs want to do a bit of cleanup and then
4620 * exit on interrupt; unless these processes terminate themselves by
4621 * sending a signal to themselves (instead of calling exit) they will
4622 * confuse this approach.
4623 *
4624 * Called with interrupts off.
4625 */
4626static int
4627waitforjob(struct job *jp)
4628{
4629 int st;
4630
4631 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4632 while (jp->state == JOBRUNNING) {
4633 dowait(DOWAIT_BLOCK, jp);
4634 }
4635 st = getstatus(jp);
4636#if JOBS
4637 if (jp->jobctl) {
4638 xtcsetpgrp(ttyfd, rootpid);
4639 /*
4640 * This is truly gross.
4641 * If we're doing job control, then we did a TIOCSPGRP which
4642 * caused us (the shell) to no longer be in the controlling
4643 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4644 * intuit from the subprocess exit status whether a SIGINT
4645 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4646 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004647 if (jp->sigint) /* TODO: do the same with all signals */
4648 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004649 }
4650 if (jp->state == JOBDONE)
4651#endif
4652 freejob(jp);
4653 return st;
4654}
4655
4656/*
4657 * return 1 if there are stopped jobs, otherwise 0
4658 */
4659static int
4660stoppedjobs(void)
4661{
4662 struct job *jp;
4663 int retval;
4664
4665 retval = 0;
4666 if (job_warning)
4667 goto out;
4668 jp = curjob;
4669 if (jp && jp->state == JOBSTOPPED) {
4670 out2str("You have stopped jobs.\n");
4671 job_warning = 2;
4672 retval++;
4673 }
4674 out:
4675 return retval;
4676}
4677
4678
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004679/* ============ redir.c
4680 *
4681 * Code for dealing with input/output redirection.
4682 */
4683
4684#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004685#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004686#ifndef PIPE_BUF
4687# define PIPESIZE 4096 /* amount of buffering in a pipe */
4688#else
4689# define PIPESIZE PIPE_BUF
4690#endif
4691
4692/*
4693 * Open a file in noclobber mode.
4694 * The code was copied from bash.
4695 */
4696static int
4697noclobberopen(const char *fname)
4698{
4699 int r, fd;
4700 struct stat finfo, finfo2;
4701
4702 /*
4703 * If the file exists and is a regular file, return an error
4704 * immediately.
4705 */
4706 r = stat(fname, &finfo);
4707 if (r == 0 && S_ISREG(finfo.st_mode)) {
4708 errno = EEXIST;
4709 return -1;
4710 }
4711
4712 /*
4713 * If the file was not present (r != 0), make sure we open it
4714 * exclusively so that if it is created before we open it, our open
4715 * will fail. Make sure that we do not truncate an existing file.
4716 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4717 * file was not a regular file, we leave O_EXCL off.
4718 */
4719 if (r != 0)
4720 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4721 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4722
4723 /* If the open failed, return the file descriptor right away. */
4724 if (fd < 0)
4725 return fd;
4726
4727 /*
4728 * OK, the open succeeded, but the file may have been changed from a
4729 * non-regular file to a regular file between the stat and the open.
4730 * We are assuming that the O_EXCL open handles the case where FILENAME
4731 * did not exist and is symlinked to an existing file between the stat
4732 * and open.
4733 */
4734
4735 /*
4736 * If we can open it and fstat the file descriptor, and neither check
4737 * revealed that it was a regular file, and the file has not been
4738 * replaced, return the file descriptor.
4739 */
4740 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4741 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4742 return fd;
4743
4744 /* The file has been replaced. badness. */
4745 close(fd);
4746 errno = EEXIST;
4747 return -1;
4748}
4749
4750/*
4751 * Handle here documents. Normally we fork off a process to write the
4752 * data to a pipe. If the document is short, we can stuff the data in
4753 * the pipe without forking.
4754 */
4755/* openhere needs this forward reference */
4756static void expandhere(union node *arg, int fd);
4757static int
4758openhere(union node *redir)
4759{
4760 int pip[2];
4761 size_t len = 0;
4762
4763 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004764 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004765 if (redir->type == NHERE) {
4766 len = strlen(redir->nhere.doc->narg.text);
4767 if (len <= PIPESIZE) {
4768 full_write(pip[1], redir->nhere.doc->narg.text, len);
4769 goto out;
4770 }
4771 }
4772 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4773 close(pip[0]);
4774 signal(SIGINT, SIG_IGN);
4775 signal(SIGQUIT, SIG_IGN);
4776 signal(SIGHUP, SIG_IGN);
4777#ifdef SIGTSTP
4778 signal(SIGTSTP, SIG_IGN);
4779#endif
4780 signal(SIGPIPE, SIG_DFL);
4781 if (redir->type == NHERE)
4782 full_write(pip[1], redir->nhere.doc->narg.text, len);
4783 else
4784 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004785 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004786 }
4787 out:
4788 close(pip[1]);
4789 return pip[0];
4790}
4791
4792static int
4793openredirect(union node *redir)
4794{
4795 char *fname;
4796 int f;
4797
4798 switch (redir->nfile.type) {
4799 case NFROM:
4800 fname = redir->nfile.expfname;
4801 f = open(fname, O_RDONLY);
4802 if (f < 0)
4803 goto eopen;
4804 break;
4805 case NFROMTO:
4806 fname = redir->nfile.expfname;
4807 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4808 if (f < 0)
4809 goto ecreate;
4810 break;
4811 case NTO:
4812 /* Take care of noclobber mode. */
4813 if (Cflag) {
4814 fname = redir->nfile.expfname;
4815 f = noclobberopen(fname);
4816 if (f < 0)
4817 goto ecreate;
4818 break;
4819 }
4820 /* FALLTHROUGH */
4821 case NCLOBBER:
4822 fname = redir->nfile.expfname;
4823 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4824 if (f < 0)
4825 goto ecreate;
4826 break;
4827 case NAPPEND:
4828 fname = redir->nfile.expfname;
4829 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4830 if (f < 0)
4831 goto ecreate;
4832 break;
4833 default:
4834#if DEBUG
4835 abort();
4836#endif
4837 /* Fall through to eliminate warning. */
4838 case NTOFD:
4839 case NFROMFD:
4840 f = -1;
4841 break;
4842 case NHERE:
4843 case NXHERE:
4844 f = openhere(redir);
4845 break;
4846 }
4847
4848 return f;
4849 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004850 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004851 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004852 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004853}
4854
4855/*
4856 * Copy a file descriptor to be >= to. Returns -1
4857 * if the source file descriptor is closed, EMPTY if there are no unused
4858 * file descriptors left.
4859 */
4860static int
4861copyfd(int from, int to)
4862{
4863 int newfd;
4864
4865 newfd = fcntl(from, F_DUPFD, to);
4866 if (newfd < 0) {
4867 if (errno == EMFILE)
4868 return EMPTY;
4869 ash_msg_and_raise_error("%d: %m", from);
4870 }
4871 return newfd;
4872}
4873
4874static void
4875dupredirect(union node *redir, int f)
4876{
4877 int fd = redir->nfile.fd;
4878
4879 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4880 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4881 copyfd(redir->ndup.dupfd, fd);
4882 }
4883 return;
4884 }
4885
4886 if (f != fd) {
4887 copyfd(f, fd);
4888 close(f);
4889 }
4890}
4891
4892/*
4893 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4894 * old file descriptors are stashed away so that the redirection can be
4895 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4896 * standard output, and the standard error if it becomes a duplicate of
4897 * stdout, is saved in memory.
4898 */
4899/* flags passed to redirect */
4900#define REDIR_PUSH 01 /* save previous values of file descriptors */
4901#define REDIR_SAVEFD2 03 /* set preverrout */
4902static void
4903redirect(union node *redir, int flags)
4904{
4905 union node *n;
4906 struct redirtab *sv;
4907 int i;
4908 int fd;
4909 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004910
Denis Vlasenko01631112007-12-16 17:20:38 +00004911 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004912 if (!redir) {
4913 return;
4914 }
4915 sv = NULL;
4916 INT_OFF;
4917 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004918 sv = ckmalloc(sizeof(*sv));
4919 sv->next = redirlist;
4920 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004921 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004922 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004923 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004924 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004925 }
4926 n = redir;
4927 do {
4928 fd = n->nfile.fd;
4929 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4930 && n->ndup.dupfd == fd)
4931 continue; /* redirect from/to same file descriptor */
4932
4933 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004934 if (fd == newfd) {
4935 /* Descriptor wasn't open before redirect.
4936 * Mark it for close in the future */
4937 if (sv && sv->renamed[fd] == EMPTY)
4938 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004939 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004940 }
4941 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004942 i = fcntl(fd, F_DUPFD, 10);
4943
4944 if (i == -1) {
4945 i = errno;
4946 if (i != EBADF) {
4947 close(newfd);
4948 errno = i;
4949 ash_msg_and_raise_error("%d: %m", fd);
4950 /* NOTREACHED */
4951 }
4952 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004953 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004954 close(fd);
4955 }
4956 } else {
4957 close(fd);
4958 }
4959 dupredirect(n, newfd);
4960 } while ((n = n->nfile.next));
4961 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004962 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004963 preverrout_fd = sv->renamed[2];
4964}
4965
4966/*
4967 * Undo the effects of the last redirection.
4968 */
4969static void
4970popredir(int drop)
4971{
4972 struct redirtab *rp;
4973 int i;
4974
Denis Vlasenko01631112007-12-16 17:20:38 +00004975 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004976 return;
4977 INT_OFF;
4978 rp = redirlist;
4979 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004980 if (rp->renamed[i] == CLOSED) {
4981 if (!drop)
4982 close(i);
4983 continue;
4984 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004985 if (rp->renamed[i] != EMPTY) {
4986 if (!drop) {
4987 close(i);
4988 copyfd(rp->renamed[i], i);
4989 }
4990 close(rp->renamed[i]);
4991 }
4992 }
4993 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004994 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004995 free(rp);
4996 INT_ON;
4997}
4998
4999/*
5000 * Undo all redirections. Called on error or interrupt.
5001 */
5002
5003/*
5004 * Discard all saved file descriptors.
5005 */
5006static void
5007clearredir(int drop)
5008{
5009 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005010 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005011 if (!redirlist)
5012 break;
5013 popredir(drop);
5014 }
5015}
5016
5017static int
5018redirectsafe(union node *redir, int flags)
5019{
5020 int err;
5021 volatile int saveint;
5022 struct jmploc *volatile savehandler = exception_handler;
5023 struct jmploc jmploc;
5024
5025 SAVE_INT(saveint);
5026 err = setjmp(jmploc.loc) * 2;
5027 if (!err) {
5028 exception_handler = &jmploc;
5029 redirect(redir, flags);
5030 }
5031 exception_handler = savehandler;
5032 if (err && exception != EXERROR)
5033 longjmp(exception_handler->loc, 1);
5034 RESTORE_INT(saveint);
5035 return err;
5036}
5037
5038
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005039/* ============ Routines to expand arguments to commands
5040 *
5041 * We have to deal with backquotes, shell variables, and file metacharacters.
5042 */
5043
5044/*
5045 * expandarg flags
5046 */
5047#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5048#define EXP_TILDE 0x2 /* do normal tilde expansion */
5049#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5050#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5051#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5052#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5053#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5054#define EXP_WORD 0x80 /* expand word in parameter expansion */
5055#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5056/*
5057 * _rmescape() flags
5058 */
5059#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5060#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5061#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5062#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5063#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5064
5065/*
5066 * Structure specifying which parts of the string should be searched
5067 * for IFS characters.
5068 */
5069struct ifsregion {
5070 struct ifsregion *next; /* next region in list */
5071 int begoff; /* offset of start of region */
5072 int endoff; /* offset of end of region */
5073 int nulonly; /* search for nul bytes only */
5074};
5075
5076struct arglist {
5077 struct strlist *list;
5078 struct strlist **lastp;
5079};
5080
5081/* output of current string */
5082static char *expdest;
5083/* list of back quote expressions */
5084static struct nodelist *argbackq;
5085/* first struct in list of ifs regions */
5086static struct ifsregion ifsfirst;
5087/* last struct in list */
5088static struct ifsregion *ifslastp;
5089/* holds expanded arg list */
5090static struct arglist exparg;
5091
5092/*
5093 * Our own itoa().
5094 */
5095static int
5096cvtnum(arith_t num)
5097{
5098 int len;
5099
5100 expdest = makestrspace(32, expdest);
5101#if ENABLE_ASH_MATH_SUPPORT_64
5102 len = fmtstr(expdest, 32, "%lld", (long long) num);
5103#else
5104 len = fmtstr(expdest, 32, "%ld", num);
5105#endif
5106 STADJUST(len, expdest);
5107 return len;
5108}
5109
5110static size_t
5111esclen(const char *start, const char *p)
5112{
5113 size_t esc = 0;
5114
5115 while (p > start && *--p == CTLESC) {
5116 esc++;
5117 }
5118 return esc;
5119}
5120
5121/*
5122 * Remove any CTLESC characters from a string.
5123 */
5124static char *
5125_rmescapes(char *str, int flag)
5126{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005127 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005128
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005129 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005130 unsigned inquotes;
5131 int notescaped;
5132 int globbing;
5133
5134 p = strpbrk(str, qchars);
5135 if (!p) {
5136 return str;
5137 }
5138 q = p;
5139 r = str;
5140 if (flag & RMESCAPE_ALLOC) {
5141 size_t len = p - str;
5142 size_t fulllen = len + strlen(p) + 1;
5143
5144 if (flag & RMESCAPE_GROW) {
5145 r = makestrspace(fulllen, expdest);
5146 } else if (flag & RMESCAPE_HEAP) {
5147 r = ckmalloc(fulllen);
5148 } else {
5149 r = stalloc(fulllen);
5150 }
5151 q = r;
5152 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005153 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005154 }
5155 }
5156 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5157 globbing = flag & RMESCAPE_GLOB;
5158 notescaped = globbing;
5159 while (*p) {
5160 if (*p == CTLQUOTEMARK) {
5161 inquotes = ~inquotes;
5162 p++;
5163 notescaped = globbing;
5164 continue;
5165 }
5166 if (*p == '\\') {
5167 /* naked back slash */
5168 notescaped = 0;
5169 goto copy;
5170 }
5171 if (*p == CTLESC) {
5172 p++;
5173 if (notescaped && inquotes && *p != '/') {
5174 *q++ = '\\';
5175 }
5176 }
5177 notescaped = globbing;
5178 copy:
5179 *q++ = *p++;
5180 }
5181 *q = '\0';
5182 if (flag & RMESCAPE_GROW) {
5183 expdest = r;
5184 STADJUST(q - r + 1, expdest);
5185 }
5186 return r;
5187}
5188#define rmescapes(p) _rmescapes((p), 0)
5189
5190#define pmatch(a, b) !fnmatch((a), (b), 0)
5191
5192/*
5193 * Prepare a pattern for a expmeta (internal glob(3)) call.
5194 *
5195 * Returns an stalloced string.
5196 */
5197static char *
5198preglob(const char *pattern, int quoted, int flag)
5199{
5200 flag |= RMESCAPE_GLOB;
5201 if (quoted) {
5202 flag |= RMESCAPE_QUOTED;
5203 }
5204 return _rmescapes((char *)pattern, flag);
5205}
5206
5207/*
5208 * Put a string on the stack.
5209 */
5210static void
5211memtodest(const char *p, size_t len, int syntax, int quotes)
5212{
5213 char *q = expdest;
5214
5215 q = makestrspace(len * 2, q);
5216
5217 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005218 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005219 if (!c)
5220 continue;
5221 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5222 USTPUTC(CTLESC, q);
5223 USTPUTC(c, q);
5224 }
5225
5226 expdest = q;
5227}
5228
5229static void
5230strtodest(const char *p, int syntax, int quotes)
5231{
5232 memtodest(p, strlen(p), syntax, quotes);
5233}
5234
5235/*
5236 * Record the fact that we have to scan this region of the
5237 * string for IFS characters.
5238 */
5239static void
5240recordregion(int start, int end, int nulonly)
5241{
5242 struct ifsregion *ifsp;
5243
5244 if (ifslastp == NULL) {
5245 ifsp = &ifsfirst;
5246 } else {
5247 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005248 ifsp = ckzalloc(sizeof(*ifsp));
5249 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005250 ifslastp->next = ifsp;
5251 INT_ON;
5252 }
5253 ifslastp = ifsp;
5254 ifslastp->begoff = start;
5255 ifslastp->endoff = end;
5256 ifslastp->nulonly = nulonly;
5257}
5258
5259static void
5260removerecordregions(int endoff)
5261{
5262 if (ifslastp == NULL)
5263 return;
5264
5265 if (ifsfirst.endoff > endoff) {
5266 while (ifsfirst.next != NULL) {
5267 struct ifsregion *ifsp;
5268 INT_OFF;
5269 ifsp = ifsfirst.next->next;
5270 free(ifsfirst.next);
5271 ifsfirst.next = ifsp;
5272 INT_ON;
5273 }
5274 if (ifsfirst.begoff > endoff)
5275 ifslastp = NULL;
5276 else {
5277 ifslastp = &ifsfirst;
5278 ifsfirst.endoff = endoff;
5279 }
5280 return;
5281 }
5282
5283 ifslastp = &ifsfirst;
5284 while (ifslastp->next && ifslastp->next->begoff < endoff)
5285 ifslastp=ifslastp->next;
5286 while (ifslastp->next != NULL) {
5287 struct ifsregion *ifsp;
5288 INT_OFF;
5289 ifsp = ifslastp->next->next;
5290 free(ifslastp->next);
5291 ifslastp->next = ifsp;
5292 INT_ON;
5293 }
5294 if (ifslastp->endoff > endoff)
5295 ifslastp->endoff = endoff;
5296}
5297
5298static char *
5299exptilde(char *startp, char *p, int flag)
5300{
5301 char c;
5302 char *name;
5303 struct passwd *pw;
5304 const char *home;
5305 int quotes = flag & (EXP_FULL | EXP_CASE);
5306 int startloc;
5307
5308 name = p + 1;
5309
5310 while ((c = *++p) != '\0') {
5311 switch (c) {
5312 case CTLESC:
5313 return startp;
5314 case CTLQUOTEMARK:
5315 return startp;
5316 case ':':
5317 if (flag & EXP_VARTILDE)
5318 goto done;
5319 break;
5320 case '/':
5321 case CTLENDVAR:
5322 goto done;
5323 }
5324 }
5325 done:
5326 *p = '\0';
5327 if (*name == '\0') {
5328 home = lookupvar(homestr);
5329 } else {
5330 pw = getpwnam(name);
5331 if (pw == NULL)
5332 goto lose;
5333 home = pw->pw_dir;
5334 }
5335 if (!home || !*home)
5336 goto lose;
5337 *p = c;
5338 startloc = expdest - (char *)stackblock();
5339 strtodest(home, SQSYNTAX, quotes);
5340 recordregion(startloc, expdest - (char *)stackblock(), 0);
5341 return p;
5342 lose:
5343 *p = c;
5344 return startp;
5345}
5346
5347/*
5348 * Execute a command inside back quotes. If it's a builtin command, we
5349 * want to save its output in a block obtained from malloc. Otherwise
5350 * we fork off a subprocess and get the output of the command via a pipe.
5351 * Should be called with interrupts off.
5352 */
5353struct backcmd { /* result of evalbackcmd */
5354 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005355 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005356 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005357 struct job *jp; /* job structure for command */
5358};
5359
5360/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005361static smalluint back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005362#define EV_EXIT 01 /* exit after evaluating tree */
5363static void evaltree(union node *, int);
5364
5365static void
5366evalbackcmd(union node *n, struct backcmd *result)
5367{
5368 int saveherefd;
5369
5370 result->fd = -1;
5371 result->buf = NULL;
5372 result->nleft = 0;
5373 result->jp = NULL;
5374 if (n == NULL) {
5375 goto out;
5376 }
5377
5378 saveherefd = herefd;
5379 herefd = -1;
5380
5381 {
5382 int pip[2];
5383 struct job *jp;
5384
5385 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005386 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005387 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005388 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5389 FORCE_INT_ON;
5390 close(pip[0]);
5391 if (pip[1] != 1) {
5392 close(1);
5393 copyfd(pip[1], 1);
5394 close(pip[1]);
5395 }
5396 eflag = 0;
5397 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5398 /* NOTREACHED */
5399 }
5400 close(pip[1]);
5401 result->fd = pip[0];
5402 result->jp = jp;
5403 }
5404 herefd = saveherefd;
5405 out:
5406 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5407 result->fd, result->buf, result->nleft, result->jp));
5408}
5409
5410/*
5411 * Expand stuff in backwards quotes.
5412 */
5413static void
5414expbackq(union node *cmd, int quoted, int quotes)
5415{
5416 struct backcmd in;
5417 int i;
5418 char buf[128];
5419 char *p;
5420 char *dest;
5421 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005422 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005423 struct stackmark smark;
5424
5425 INT_OFF;
5426 setstackmark(&smark);
5427 dest = expdest;
5428 startloc = dest - (char *)stackblock();
5429 grabstackstr(dest);
5430 evalbackcmd(cmd, &in);
5431 popstackmark(&smark);
5432
5433 p = in.buf;
5434 i = in.nleft;
5435 if (i == 0)
5436 goto read;
5437 for (;;) {
5438 memtodest(p, i, syntax, quotes);
5439 read:
5440 if (in.fd < 0)
5441 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005442 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005443 TRACE(("expbackq: read returns %d\n", i));
5444 if (i <= 0)
5445 break;
5446 p = buf;
5447 }
5448
Denis Vlasenko60818682007-09-28 22:07:23 +00005449 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005450 if (in.fd >= 0) {
5451 close(in.fd);
5452 back_exitstatus = waitforjob(in.jp);
5453 }
5454 INT_ON;
5455
5456 /* Eat all trailing newlines */
5457 dest = expdest;
5458 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5459 STUNPUTC(dest);
5460 expdest = dest;
5461
5462 if (quoted == 0)
5463 recordregion(startloc, dest - (char *)stackblock(), 0);
5464 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5465 (dest - (char *)stackblock()) - startloc,
5466 (dest - (char *)stackblock()) - startloc,
5467 stackblock() + startloc));
5468}
5469
5470#if ENABLE_ASH_MATH_SUPPORT
5471/*
5472 * Expand arithmetic expression. Backup to start of expression,
5473 * evaluate, place result in (backed up) result, adjust string position.
5474 */
5475static void
5476expari(int quotes)
5477{
5478 char *p, *start;
5479 int begoff;
5480 int flag;
5481 int len;
5482
5483 /* ifsfree(); */
5484
5485 /*
5486 * This routine is slightly over-complicated for
5487 * efficiency. Next we scan backwards looking for the
5488 * start of arithmetic.
5489 */
5490 start = stackblock();
5491 p = expdest - 1;
5492 *p = '\0';
5493 p--;
5494 do {
5495 int esc;
5496
5497 while (*p != CTLARI) {
5498 p--;
5499#if DEBUG
5500 if (p < start) {
5501 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5502 }
5503#endif
5504 }
5505
5506 esc = esclen(start, p);
5507 if (!(esc % 2)) {
5508 break;
5509 }
5510
5511 p -= esc + 1;
5512 } while (1);
5513
5514 begoff = p - start;
5515
5516 removerecordregions(begoff);
5517
5518 flag = p[1];
5519
5520 expdest = p;
5521
5522 if (quotes)
5523 rmescapes(p + 2);
5524
5525 len = cvtnum(dash_arith(p + 2));
5526
5527 if (flag != '"')
5528 recordregion(begoff, begoff + len, 0);
5529}
5530#endif
5531
5532/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005533static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005534
5535/*
5536 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5537 * characters to allow for further processing. Otherwise treat
5538 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005539 *
5540 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5541 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5542 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005543 */
5544static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005545argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005546{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005547 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005548 '=',
5549 ':',
5550 CTLQUOTEMARK,
5551 CTLENDVAR,
5552 CTLESC,
5553 CTLVAR,
5554 CTLBACKQ,
5555 CTLBACKQ | CTLQUOTE,
5556#if ENABLE_ASH_MATH_SUPPORT
5557 CTLENDARI,
5558#endif
5559 0
5560 };
5561 const char *reject = spclchars;
5562 int c;
5563 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5564 int breakall = flag & EXP_WORD;
5565 int inquotes;
5566 size_t length;
5567 int startloc;
5568
5569 if (!(flag & EXP_VARTILDE)) {
5570 reject += 2;
5571 } else if (flag & EXP_VARTILDE2) {
5572 reject++;
5573 }
5574 inquotes = 0;
5575 length = 0;
5576 if (flag & EXP_TILDE) {
5577 char *q;
5578
5579 flag &= ~EXP_TILDE;
5580 tilde:
5581 q = p;
5582 if (*q == CTLESC && (flag & EXP_QWORD))
5583 q++;
5584 if (*q == '~')
5585 p = exptilde(p, q, flag);
5586 }
5587 start:
5588 startloc = expdest - (char *)stackblock();
5589 for (;;) {
5590 length += strcspn(p + length, reject);
5591 c = p[length];
5592 if (c && (!(c & 0x80)
5593#if ENABLE_ASH_MATH_SUPPORT
5594 || c == CTLENDARI
5595#endif
5596 )) {
5597 /* c == '=' || c == ':' || c == CTLENDARI */
5598 length++;
5599 }
5600 if (length > 0) {
5601 int newloc;
5602 expdest = stack_nputstr(p, length, expdest);
5603 newloc = expdest - (char *)stackblock();
5604 if (breakall && !inquotes && newloc > startloc) {
5605 recordregion(startloc, newloc, 0);
5606 }
5607 startloc = newloc;
5608 }
5609 p += length + 1;
5610 length = 0;
5611
5612 switch (c) {
5613 case '\0':
5614 goto breakloop;
5615 case '=':
5616 if (flag & EXP_VARTILDE2) {
5617 p--;
5618 continue;
5619 }
5620 flag |= EXP_VARTILDE2;
5621 reject++;
5622 /* fall through */
5623 case ':':
5624 /*
5625 * sort of a hack - expand tildes in variable
5626 * assignments (after the first '=' and after ':'s).
5627 */
5628 if (*--p == '~') {
5629 goto tilde;
5630 }
5631 continue;
5632 }
5633
5634 switch (c) {
5635 case CTLENDVAR: /* ??? */
5636 goto breakloop;
5637 case CTLQUOTEMARK:
5638 /* "$@" syntax adherence hack */
5639 if (
5640 !inquotes &&
5641 !memcmp(p, dolatstr, 4) &&
5642 (p[4] == CTLQUOTEMARK || (
5643 p[4] == CTLENDVAR &&
5644 p[5] == CTLQUOTEMARK
5645 ))
5646 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005647 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648 goto start;
5649 }
5650 inquotes = !inquotes;
5651 addquote:
5652 if (quotes) {
5653 p--;
5654 length++;
5655 startloc++;
5656 }
5657 break;
5658 case CTLESC:
5659 startloc++;
5660 length++;
5661 goto addquote;
5662 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005663 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005664 goto start;
5665 case CTLBACKQ:
5666 c = 0;
5667 case CTLBACKQ|CTLQUOTE:
5668 expbackq(argbackq->n, c, quotes);
5669 argbackq = argbackq->next;
5670 goto start;
5671#if ENABLE_ASH_MATH_SUPPORT
5672 case CTLENDARI:
5673 p--;
5674 expari(quotes);
5675 goto start;
5676#endif
5677 }
5678 }
5679 breakloop:
5680 ;
5681}
5682
5683static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005684scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005685 int zero)
5686{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005687// This commented out code was added by James Simmons <jsimmons@infradead.org>
5688// as part of a larger change when he added support for ${var/a/b}.
5689// However, it broke # and % operators:
5690//
5691//var=ababcdcd
5692// ok bad
5693//echo ${var#ab} abcdcd abcdcd
5694//echo ${var##ab} abcdcd abcdcd
5695//echo ${var#a*b} abcdcd ababcdcd (!)
5696//echo ${var##a*b} cdcd cdcd
5697//echo ${var#?} babcdcd ababcdcd (!)
5698//echo ${var##?} babcdcd babcdcd
5699//echo ${var#*} ababcdcd babcdcd (!)
5700//echo ${var##*}
5701//echo ${var%cd} ababcd ababcd
5702//echo ${var%%cd} ababcd abab (!)
5703//echo ${var%c*d} ababcd ababcd
5704//echo ${var%%c*d} abab ababcdcd (!)
5705//echo ${var%?} ababcdc ababcdc
5706//echo ${var%%?} ababcdc ababcdcd (!)
5707//echo ${var%*} ababcdcd ababcdcd
5708//echo ${var%%*}
5709//
5710// Commenting it back out helped. Remove it completely if it really
5711// is not needed.
5712
5713 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005714 char c;
5715
5716 loc = startp;
5717 loc2 = rmesc;
5718 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005719 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005720 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005721
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722 c = *loc2;
5723 if (zero) {
5724 *loc2 = '\0';
5725 s = rmesc;
5726 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005727 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005728
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005729// // chop off end if its '*'
5730// full = strrchr(str, '*');
5731// if (full && full != str)
5732// match--;
5733//
5734// // If str starts with '*' replace with s.
5735// if ((*str == '*') && strlen(s) >= match) {
5736// full = xstrdup(s);
5737// strncpy(full+strlen(s)-match+1, str+1, match-1);
5738// } else
5739// full = xstrndup(str, match);
5740// match = strncmp(s, full, strlen(full));
5741// free(full);
5742//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005743 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005744 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005745 return loc;
5746 if (quotes && *loc == CTLESC)
5747 loc++;
5748 loc++;
5749 loc2++;
5750 } while (c);
5751 return 0;
5752}
5753
5754static char *
5755scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5756 int zero)
5757{
5758 int esc = 0;
5759 char *loc;
5760 char *loc2;
5761
5762 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5763 int match;
5764 char c = *loc2;
5765 const char *s = loc2;
5766 if (zero) {
5767 *loc2 = '\0';
5768 s = rmesc;
5769 }
5770 match = pmatch(str, s);
5771 *loc2 = c;
5772 if (match)
5773 return loc;
5774 loc--;
5775 if (quotes) {
5776 if (--esc < 0) {
5777 esc = esclen(startp, loc);
5778 }
5779 if (esc % 2) {
5780 esc--;
5781 loc--;
5782 }
5783 }
5784 }
5785 return 0;
5786}
5787
5788static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5789static void
5790varunset(const char *end, const char *var, const char *umsg, int varflags)
5791{
5792 const char *msg;
5793 const char *tail;
5794
5795 tail = nullstr;
5796 msg = "parameter not set";
5797 if (umsg) {
5798 if (*end == CTLENDVAR) {
5799 if (varflags & VSNUL)
5800 tail = " or null";
5801 } else
5802 msg = umsg;
5803 }
5804 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5805}
5806
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005807#if ENABLE_ASH_BASH_COMPAT
5808static char *
5809parse_sub_pattern(char *arg, int inquotes)
5810{
5811 char *idx, *repl = NULL;
5812 unsigned char c;
5813
Denis Vlasenko2659c632008-06-14 06:04:59 +00005814 idx = arg;
5815 while (1) {
5816 c = *arg;
5817 if (!c)
5818 break;
5819 if (c == '/') {
5820 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005821 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005822 repl = idx + 1;
5823 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005824 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005825 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005826 *idx++ = c;
5827 if (!inquotes && c == '\\' && arg[1] == '\\')
5828 arg++; /* skip both \\, not just first one */
5829 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005830 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005831 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005832
5833 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005834}
5835#endif /* ENABLE_ASH_BASH_COMPAT */
5836
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005838subevalvar(char *p, char *str, int strloc, int subtype,
5839 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005840{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005841 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005842 char *startp;
5843 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005845 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5846 USE_ASH_BASH_COMPAT(char null = '\0';)
5847 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5848 int saveherefd = herefd;
5849 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005851 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005852
5853 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005854 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5855 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856 STPUTC('\0', expdest);
5857 herefd = saveherefd;
5858 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005859 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860
5861 switch (subtype) {
5862 case VSASSIGN:
5863 setvar(str, startp, 0);
5864 amount = startp - expdest;
5865 STADJUST(amount, expdest);
5866 return startp;
5867
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005868#if ENABLE_ASH_BASH_COMPAT
5869 case VSSUBSTR:
5870 loc = str = stackblock() + strloc;
5871// TODO: number() instead? It does error checking...
5872 pos = atoi(loc);
5873 len = str - startp - 1;
5874
5875 /* *loc != '\0', guaranteed by parser */
5876 if (quotes) {
5877 char *ptr;
5878
5879 /* We must adjust the length by the number of escapes we find. */
5880 for (ptr = startp; ptr < (str - 1); ptr++) {
5881 if(*ptr == CTLESC) {
5882 len--;
5883 ptr++;
5884 }
5885 }
5886 }
5887 orig_len = len;
5888
5889 if (*loc++ == ':') {
5890// TODO: number() instead? It does error checking...
5891 len = atoi(loc);
5892 } else {
5893 len = orig_len;
5894 while (*loc && *loc != ':')
5895 loc++;
5896 if (*loc++ == ':')
5897// TODO: number() instead? It does error checking...
5898 len = atoi(loc);
5899 }
5900 if (pos >= orig_len) {
5901 pos = 0;
5902 len = 0;
5903 }
5904 if (len > (orig_len - pos))
5905 len = orig_len - pos;
5906
5907 for (str = startp; pos; str++, pos--) {
5908 if (quotes && *str == CTLESC)
5909 str++;
5910 }
5911 for (loc = startp; len; len--) {
5912 if (quotes && *str == CTLESC)
5913 *loc++ = *str++;
5914 *loc++ = *str++;
5915 }
5916 *loc = '\0';
5917 amount = loc - expdest;
5918 STADJUST(amount, expdest);
5919 return loc;
5920#endif
5921
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 case VSQUESTION:
5923 varunset(p, str, startp, varflags);
5924 /* NOTREACHED */
5925 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005926 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005927
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005928 /* We'll comeback here if we grow the stack while handling
5929 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5930 * stack will need rebasing, and we'll need to remove our work
5931 * areas each time
5932 */
5933 USE_ASH_BASH_COMPAT(restart:)
5934
5935 amount = expdest - ((char *)stackblock() + resetloc);
5936 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005937 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005938
5939 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005940 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005941 if (quotes) {
5942 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5943 if (rmesc != startp) {
5944 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005945 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946 }
5947 }
5948 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005949 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005951 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005952
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953#if ENABLE_ASH_BASH_COMPAT
5954 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5955 char *idx, *end, *restart_detect;
5956
5957 if(!repl) {
5958 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5959 if (!repl)
5960 repl = &null;
5961 }
5962
5963 /* If there's no pattern to match, return the expansion unmolested */
5964 if (*str == '\0')
5965 return 0;
5966
5967 len = 0;
5968 idx = startp;
5969 end = str - 1;
5970 while (idx < end) {
5971 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5972 if (!loc) {
5973 /* No match, advance */
5974 restart_detect = stackblock();
5975 STPUTC(*idx, expdest);
5976 if (quotes && *idx == CTLESC) {
5977 idx++;
5978 len++;
5979 STPUTC(*idx, expdest);
5980 }
5981 if (stackblock() != restart_detect)
5982 goto restart;
5983 idx++;
5984 len++;
5985 rmesc++;
5986 continue;
5987 }
5988
5989 if (subtype == VSREPLACEALL) {
5990 while (idx < loc) {
5991 if (quotes && *idx == CTLESC)
5992 idx++;
5993 idx++;
5994 rmesc++;
5995 }
5996 } else
5997 idx = loc;
5998
5999 for (loc = repl; *loc; loc++) {
6000 restart_detect = stackblock();
6001 STPUTC(*loc, expdest);
6002 if (stackblock() != restart_detect)
6003 goto restart;
6004 len++;
6005 }
6006
6007 if (subtype == VSREPLACE) {
6008 while (*idx) {
6009 restart_detect = stackblock();
6010 STPUTC(*idx, expdest);
6011 if (stackblock() != restart_detect)
6012 goto restart;
6013 len++;
6014 idx++;
6015 }
6016 break;
6017 }
6018 }
6019
6020 /* We've put the replaced text into a buffer at workloc, now
6021 * move it to the right place and adjust the stack.
6022 */
6023 startp = stackblock() + startloc;
6024 STPUTC('\0', expdest);
6025 memmove(startp, stackblock() + workloc, len);
6026 startp[len++] = '\0';
6027 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6028 STADJUST(-amount, expdest);
6029 return startp;
6030 }
6031#endif /* ENABLE_ASH_BASH_COMPAT */
6032
6033 subtype -= VSTRIMRIGHT;
6034#if DEBUG
6035 if (subtype < 0 || subtype > 7)
6036 abort();
6037#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006038 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6039 zero = subtype >> 1;
6040 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6041 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6042
6043 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6044 if (loc) {
6045 if (zero) {
6046 memmove(startp, loc, str - loc);
6047 loc = startp + (str - loc) - 1;
6048 }
6049 *loc = '\0';
6050 amount = loc - expdest;
6051 STADJUST(amount, expdest);
6052 }
6053 return loc;
6054}
6055
6056/*
6057 * Add the value of a specialized variable to the stack string.
6058 */
6059static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006060varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061{
6062 int num;
6063 char *p;
6064 int i;
6065 int sep = 0;
6066 int sepq = 0;
6067 ssize_t len = 0;
6068 char **ap;
6069 int syntax;
6070 int quoted = varflags & VSQUOTE;
6071 int subtype = varflags & VSTYPE;
6072 int quotes = flags & (EXP_FULL | EXP_CASE);
6073
6074 if (quoted && (flags & EXP_FULL))
6075 sep = 1 << CHAR_BIT;
6076
6077 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6078 switch (*name) {
6079 case '$':
6080 num = rootpid;
6081 goto numvar;
6082 case '?':
6083 num = exitstatus;
6084 goto numvar;
6085 case '#':
6086 num = shellparam.nparam;
6087 goto numvar;
6088 case '!':
6089 num = backgndpid;
6090 if (num == 0)
6091 return -1;
6092 numvar:
6093 len = cvtnum(num);
6094 break;
6095 case '-':
6096 p = makestrspace(NOPTS, expdest);
6097 for (i = NOPTS - 1; i >= 0; i--) {
6098 if (optlist[i]) {
6099 USTPUTC(optletters(i), p);
6100 len++;
6101 }
6102 }
6103 expdest = p;
6104 break;
6105 case '@':
6106 if (sep)
6107 goto param;
6108 /* fall through */
6109 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006110 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6112 sepq = 1;
6113 param:
6114 ap = shellparam.p;
6115 if (!ap)
6116 return -1;
6117 while ((p = *ap++)) {
6118 size_t partlen;
6119
6120 partlen = strlen(p);
6121 len += partlen;
6122
6123 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6124 memtodest(p, partlen, syntax, quotes);
6125
6126 if (*ap && sep) {
6127 char *q;
6128
6129 len++;
6130 if (subtype == VSPLUS || subtype == VSLENGTH) {
6131 continue;
6132 }
6133 q = expdest;
6134 if (sepq)
6135 STPUTC(CTLESC, q);
6136 STPUTC(sep, q);
6137 expdest = q;
6138 }
6139 }
6140 return len;
6141 case '0':
6142 case '1':
6143 case '2':
6144 case '3':
6145 case '4':
6146 case '5':
6147 case '6':
6148 case '7':
6149 case '8':
6150 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006151// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006152 num = atoi(name);
6153 if (num < 0 || num > shellparam.nparam)
6154 return -1;
6155 p = num ? shellparam.p[num - 1] : arg0;
6156 goto value;
6157 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006158 /* NB: name has form "VAR=..." */
6159
6160 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6161 * which should be considered before we check variables. */
6162 if (var_str_list) {
6163 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6164 p = NULL;
6165 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006166 char *str, *eq;
6167 str = var_str_list->text;
6168 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006169 if (!eq) /* stop at first non-assignment */
6170 break;
6171 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006172 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006173 && strncmp(str, name, name_len) == 0) {
6174 p = eq;
6175 /* goto value; - WRONG! */
6176 /* think "A=1 A=2 B=$A" */
6177 }
6178 var_str_list = var_str_list->next;
6179 } while (var_str_list);
6180 if (p)
6181 goto value;
6182 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 p = lookupvar(name);
6184 value:
6185 if (!p)
6186 return -1;
6187
6188 len = strlen(p);
6189 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6190 memtodest(p, len, syntax, quotes);
6191 return len;
6192 }
6193
6194 if (subtype == VSPLUS || subtype == VSLENGTH)
6195 STADJUST(-len, expdest);
6196 return len;
6197}
6198
6199/*
6200 * Expand a variable, and return a pointer to the next character in the
6201 * input string.
6202 */
6203static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006204evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006205{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006206 char varflags;
6207 char subtype;
6208 char quoted;
6209 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210 char *var;
6211 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006212 int startloc;
6213 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006214
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215 varflags = *p++;
6216 subtype = varflags & VSTYPE;
6217 quoted = varflags & VSQUOTE;
6218 var = p;
6219 easy = (!quoted || (*var == '@' && shellparam.nparam));
6220 startloc = expdest - (char *)stackblock();
6221 p = strchr(p, '=') + 1;
6222
6223 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006224 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006225 if (varflags & VSNUL)
6226 varlen--;
6227
6228 if (subtype == VSPLUS) {
6229 varlen = -1 - varlen;
6230 goto vsplus;
6231 }
6232
6233 if (subtype == VSMINUS) {
6234 vsplus:
6235 if (varlen < 0) {
6236 argstr(
6237 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006238 (quoted ? EXP_QWORD : EXP_WORD),
6239 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006240 );
6241 goto end;
6242 }
6243 if (easy)
6244 goto record;
6245 goto end;
6246 }
6247
6248 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6249 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006250 if (subevalvar(p, var, /* strloc: */ 0,
6251 subtype, startloc, varflags,
6252 /* quotes: */ 0,
6253 var_str_list)
6254 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006255 varflags &= ~VSNUL;
6256 /*
6257 * Remove any recorded regions beyond
6258 * start of variable
6259 */
6260 removerecordregions(startloc);
6261 goto again;
6262 }
6263 goto end;
6264 }
6265 if (easy)
6266 goto record;
6267 goto end;
6268 }
6269
6270 if (varlen < 0 && uflag)
6271 varunset(p, var, 0, 0);
6272
6273 if (subtype == VSLENGTH) {
6274 cvtnum(varlen > 0 ? varlen : 0);
6275 goto record;
6276 }
6277
6278 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006279 if (easy)
6280 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281 goto end;
6282 }
6283
6284#if DEBUG
6285 switch (subtype) {
6286 case VSTRIMLEFT:
6287 case VSTRIMLEFTMAX:
6288 case VSTRIMRIGHT:
6289 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006290#if ENABLE_ASH_BASH_COMPAT
6291 case VSSUBSTR:
6292 case VSREPLACE:
6293 case VSREPLACEALL:
6294#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006295 break;
6296 default:
6297 abort();
6298 }
6299#endif
6300
6301 if (varlen >= 0) {
6302 /*
6303 * Terminate the string and start recording the pattern
6304 * right after it
6305 */
6306 STPUTC('\0', expdest);
6307 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006308 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6309 startloc, varflags,
6310 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6311 var_str_list)
6312 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006313 int amount = expdest - (
6314 (char *)stackblock() + patloc - 1
6315 );
6316 STADJUST(-amount, expdest);
6317 }
6318 /* Remove any recorded regions beyond start of variable */
6319 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006320 record:
6321 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322 }
6323
6324 end:
6325 if (subtype != VSNORMAL) { /* skip to end of alternative */
6326 int nesting = 1;
6327 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006328 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329 if (c == CTLESC)
6330 p++;
6331 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6332 if (varlen >= 0)
6333 argbackq = argbackq->next;
6334 } else if (c == CTLVAR) {
6335 if ((*p++ & VSTYPE) != VSNORMAL)
6336 nesting++;
6337 } else if (c == CTLENDVAR) {
6338 if (--nesting == 0)
6339 break;
6340 }
6341 }
6342 }
6343 return p;
6344}
6345
6346/*
6347 * Break the argument string into pieces based upon IFS and add the
6348 * strings to the argument list. The regions of the string to be
6349 * searched for IFS characters have been stored by recordregion.
6350 */
6351static void
6352ifsbreakup(char *string, struct arglist *arglist)
6353{
6354 struct ifsregion *ifsp;
6355 struct strlist *sp;
6356 char *start;
6357 char *p;
6358 char *q;
6359 const char *ifs, *realifs;
6360 int ifsspc;
6361 int nulonly;
6362
6363 start = string;
6364 if (ifslastp != NULL) {
6365 ifsspc = 0;
6366 nulonly = 0;
6367 realifs = ifsset() ? ifsval() : defifs;
6368 ifsp = &ifsfirst;
6369 do {
6370 p = string + ifsp->begoff;
6371 nulonly = ifsp->nulonly;
6372 ifs = nulonly ? nullstr : realifs;
6373 ifsspc = 0;
6374 while (p < string + ifsp->endoff) {
6375 q = p;
6376 if (*p == CTLESC)
6377 p++;
6378 if (!strchr(ifs, *p)) {
6379 p++;
6380 continue;
6381 }
6382 if (!nulonly)
6383 ifsspc = (strchr(defifs, *p) != NULL);
6384 /* Ignore IFS whitespace at start */
6385 if (q == start && ifsspc) {
6386 p++;
6387 start = p;
6388 continue;
6389 }
6390 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006391 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006392 sp->text = start;
6393 *arglist->lastp = sp;
6394 arglist->lastp = &sp->next;
6395 p++;
6396 if (!nulonly) {
6397 for (;;) {
6398 if (p >= string + ifsp->endoff) {
6399 break;
6400 }
6401 q = p;
6402 if (*p == CTLESC)
6403 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006404 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405 p = q;
6406 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006407 }
6408 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006409 if (ifsspc) {
6410 p++;
6411 ifsspc = 0;
6412 } else {
6413 p = q;
6414 break;
6415 }
6416 } else
6417 p++;
6418 }
6419 }
6420 start = p;
6421 } /* while */
6422 ifsp = ifsp->next;
6423 } while (ifsp != NULL);
6424 if (nulonly)
6425 goto add;
6426 }
6427
6428 if (!*start)
6429 return;
6430
6431 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006432 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433 sp->text = start;
6434 *arglist->lastp = sp;
6435 arglist->lastp = &sp->next;
6436}
6437
6438static void
6439ifsfree(void)
6440{
6441 struct ifsregion *p;
6442
6443 INT_OFF;
6444 p = ifsfirst.next;
6445 do {
6446 struct ifsregion *ifsp;
6447 ifsp = p->next;
6448 free(p);
6449 p = ifsp;
6450 } while (p);
6451 ifslastp = NULL;
6452 ifsfirst.next = NULL;
6453 INT_ON;
6454}
6455
6456/*
6457 * Add a file name to the list.
6458 */
6459static void
6460addfname(const char *name)
6461{
6462 struct strlist *sp;
6463
Denis Vlasenko597906c2008-02-20 16:38:54 +00006464 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006465 sp->text = ststrdup(name);
6466 *exparg.lastp = sp;
6467 exparg.lastp = &sp->next;
6468}
6469
6470static char *expdir;
6471
6472/*
6473 * Do metacharacter (i.e. *, ?, [...]) expansion.
6474 */
6475static void
6476expmeta(char *enddir, char *name)
6477{
6478 char *p;
6479 const char *cp;
6480 char *start;
6481 char *endname;
6482 int metaflag;
6483 struct stat statb;
6484 DIR *dirp;
6485 struct dirent *dp;
6486 int atend;
6487 int matchdot;
6488
6489 metaflag = 0;
6490 start = name;
6491 for (p = name; *p; p++) {
6492 if (*p == '*' || *p == '?')
6493 metaflag = 1;
6494 else if (*p == '[') {
6495 char *q = p + 1;
6496 if (*q == '!')
6497 q++;
6498 for (;;) {
6499 if (*q == '\\')
6500 q++;
6501 if (*q == '/' || *q == '\0')
6502 break;
6503 if (*++q == ']') {
6504 metaflag = 1;
6505 break;
6506 }
6507 }
6508 } else if (*p == '\\')
6509 p++;
6510 else if (*p == '/') {
6511 if (metaflag)
6512 goto out;
6513 start = p + 1;
6514 }
6515 }
6516 out:
6517 if (metaflag == 0) { /* we've reached the end of the file name */
6518 if (enddir != expdir)
6519 metaflag++;
6520 p = name;
6521 do {
6522 if (*p == '\\')
6523 p++;
6524 *enddir++ = *p;
6525 } while (*p++);
6526 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6527 addfname(expdir);
6528 return;
6529 }
6530 endname = p;
6531 if (name < start) {
6532 p = name;
6533 do {
6534 if (*p == '\\')
6535 p++;
6536 *enddir++ = *p++;
6537 } while (p < start);
6538 }
6539 if (enddir == expdir) {
6540 cp = ".";
6541 } else if (enddir == expdir + 1 && *expdir == '/') {
6542 cp = "/";
6543 } else {
6544 cp = expdir;
6545 enddir[-1] = '\0';
6546 }
6547 dirp = opendir(cp);
6548 if (dirp == NULL)
6549 return;
6550 if (enddir != expdir)
6551 enddir[-1] = '/';
6552 if (*endname == 0) {
6553 atend = 1;
6554 } else {
6555 atend = 0;
6556 *endname++ = '\0';
6557 }
6558 matchdot = 0;
6559 p = start;
6560 if (*p == '\\')
6561 p++;
6562 if (*p == '.')
6563 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006564 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006565 if (dp->d_name[0] == '.' && ! matchdot)
6566 continue;
6567 if (pmatch(start, dp->d_name)) {
6568 if (atend) {
6569 strcpy(enddir, dp->d_name);
6570 addfname(expdir);
6571 } else {
6572 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6573 continue;
6574 p[-1] = '/';
6575 expmeta(p, endname);
6576 }
6577 }
6578 }
6579 closedir(dirp);
6580 if (! atend)
6581 endname[-1] = '/';
6582}
6583
6584static struct strlist *
6585msort(struct strlist *list, int len)
6586{
6587 struct strlist *p, *q = NULL;
6588 struct strlist **lpp;
6589 int half;
6590 int n;
6591
6592 if (len <= 1)
6593 return list;
6594 half = len >> 1;
6595 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006596 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 q = p;
6598 p = p->next;
6599 }
6600 q->next = NULL; /* terminate first half of list */
6601 q = msort(list, half); /* sort first half of list */
6602 p = msort(p, len - half); /* sort second half */
6603 lpp = &list;
6604 for (;;) {
6605#if ENABLE_LOCALE_SUPPORT
6606 if (strcoll(p->text, q->text) < 0)
6607#else
6608 if (strcmp(p->text, q->text) < 0)
6609#endif
6610 {
6611 *lpp = p;
6612 lpp = &p->next;
6613 p = *lpp;
6614 if (p == NULL) {
6615 *lpp = q;
6616 break;
6617 }
6618 } else {
6619 *lpp = q;
6620 lpp = &q->next;
6621 q = *lpp;
6622 if (q == NULL) {
6623 *lpp = p;
6624 break;
6625 }
6626 }
6627 }
6628 return list;
6629}
6630
6631/*
6632 * Sort the results of file name expansion. It calculates the number of
6633 * strings to sort and then calls msort (short for merge sort) to do the
6634 * work.
6635 */
6636static struct strlist *
6637expsort(struct strlist *str)
6638{
6639 int len;
6640 struct strlist *sp;
6641
6642 len = 0;
6643 for (sp = str; sp; sp = sp->next)
6644 len++;
6645 return msort(str, len);
6646}
6647
6648static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006649expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006650{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006651 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006652 '*', '?', '[', 0
6653 };
6654 /* TODO - EXP_REDIR */
6655
6656 while (str) {
6657 struct strlist **savelastp;
6658 struct strlist *sp;
6659 char *p;
6660
6661 if (fflag)
6662 goto nometa;
6663 if (!strpbrk(str->text, metachars))
6664 goto nometa;
6665 savelastp = exparg.lastp;
6666
6667 INT_OFF;
6668 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6669 {
6670 int i = strlen(str->text);
6671 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6672 }
6673
6674 expmeta(expdir, p);
6675 free(expdir);
6676 if (p != str->text)
6677 free(p);
6678 INT_ON;
6679 if (exparg.lastp == savelastp) {
6680 /*
6681 * no matches
6682 */
6683 nometa:
6684 *exparg.lastp = str;
6685 rmescapes(str->text);
6686 exparg.lastp = &str->next;
6687 } else {
6688 *exparg.lastp = NULL;
6689 *savelastp = sp = expsort(*savelastp);
6690 while (sp->next != NULL)
6691 sp = sp->next;
6692 exparg.lastp = &sp->next;
6693 }
6694 str = str->next;
6695 }
6696}
6697
6698/*
6699 * Perform variable substitution and command substitution on an argument,
6700 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6701 * perform splitting and file name expansion. When arglist is NULL, perform
6702 * here document expansion.
6703 */
6704static void
6705expandarg(union node *arg, struct arglist *arglist, int flag)
6706{
6707 struct strlist *sp;
6708 char *p;
6709
6710 argbackq = arg->narg.backquote;
6711 STARTSTACKSTR(expdest);
6712 ifsfirst.next = NULL;
6713 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006714 argstr(arg->narg.text, flag,
6715 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716 p = _STPUTC('\0', expdest);
6717 expdest = p - 1;
6718 if (arglist == NULL) {
6719 return; /* here document expanded */
6720 }
6721 p = grabstackstr(p);
6722 exparg.lastp = &exparg.list;
6723 /*
6724 * TODO - EXP_REDIR
6725 */
6726 if (flag & EXP_FULL) {
6727 ifsbreakup(p, &exparg);
6728 *exparg.lastp = NULL;
6729 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006730 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006731 } else {
6732 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6733 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006734 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006735 sp->text = p;
6736 *exparg.lastp = sp;
6737 exparg.lastp = &sp->next;
6738 }
6739 if (ifsfirst.next)
6740 ifsfree();
6741 *exparg.lastp = NULL;
6742 if (exparg.list) {
6743 *arglist->lastp = exparg.list;
6744 arglist->lastp = exparg.lastp;
6745 }
6746}
6747
6748/*
6749 * Expand shell variables and backquotes inside a here document.
6750 */
6751static void
6752expandhere(union node *arg, int fd)
6753{
6754 herefd = fd;
6755 expandarg(arg, (struct arglist *)NULL, 0);
6756 full_write(fd, stackblock(), expdest - (char *)stackblock());
6757}
6758
6759/*
6760 * Returns true if the pattern matches the string.
6761 */
6762static int
6763patmatch(char *pattern, const char *string)
6764{
6765 return pmatch(preglob(pattern, 0, 0), string);
6766}
6767
6768/*
6769 * See if a pattern matches in a case statement.
6770 */
6771static int
6772casematch(union node *pattern, char *val)
6773{
6774 struct stackmark smark;
6775 int result;
6776
6777 setstackmark(&smark);
6778 argbackq = pattern->narg.backquote;
6779 STARTSTACKSTR(expdest);
6780 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006781 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6782 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006783 STACKSTRNUL(expdest);
6784 result = patmatch(stackblock(), val);
6785 popstackmark(&smark);
6786 return result;
6787}
6788
6789
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006790/* ============ find_command */
6791
6792struct builtincmd {
6793 const char *name;
6794 int (*builtin)(int, char **);
6795 /* unsigned flags; */
6796};
6797#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006798/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006799 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006800#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006801#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006802
6803struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006804 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006805 union param {
6806 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006807 /* index >= 0 for commands without path (slashes) */
6808 /* (TODO: what exactly does the value mean? PATH position?) */
6809 /* index == -1 for commands with slashes */
6810 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006811 const struct builtincmd *cmd;
6812 struct funcnode *func;
6813 } u;
6814};
6815/* values of cmdtype */
6816#define CMDUNKNOWN -1 /* no entry in table for command */
6817#define CMDNORMAL 0 /* command is an executable program */
6818#define CMDFUNCTION 1 /* command is a shell function */
6819#define CMDBUILTIN 2 /* command is a shell builtin */
6820
6821/* action to find_command() */
6822#define DO_ERR 0x01 /* prints errors */
6823#define DO_ABS 0x02 /* checks absolute paths */
6824#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6825#define DO_ALTPATH 0x08 /* using alternate path */
6826#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6827
6828static void find_command(char *, struct cmdentry *, int, const char *);
6829
6830
6831/* ============ Hashing commands */
6832
6833/*
6834 * When commands are first encountered, they are entered in a hash table.
6835 * This ensures that a full path search will not have to be done for them
6836 * on each invocation.
6837 *
6838 * We should investigate converting to a linear search, even though that
6839 * would make the command name "hash" a misnomer.
6840 */
6841
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006842struct tblentry {
6843 struct tblentry *next; /* next entry in hash chain */
6844 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006845 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006846 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006847 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006848};
6849
Denis Vlasenko01631112007-12-16 17:20:38 +00006850static struct tblentry **cmdtable;
6851#define INIT_G_cmdtable() do { \
6852 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6853} while (0)
6854
6855static int builtinloc = -1; /* index in path of %builtin, or -1 */
6856
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006857
6858static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006859tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860{
6861 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006862
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006863#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006864 if (applet_no >= 0) {
6865 if (APPLET_IS_NOEXEC(applet_no))
6866 run_applet_no_and_exit(applet_no, argv);
6867 /* re-exec ourselves with the new arguments */
6868 execve(bb_busybox_exec_path, argv, envp);
6869 /* If they called chroot or otherwise made the binary no longer
6870 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006871 }
6872#endif
6873
6874 repeat:
6875#ifdef SYSV
6876 do {
6877 execve(cmd, argv, envp);
6878 } while (errno == EINTR);
6879#else
6880 execve(cmd, argv, envp);
6881#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006882 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006883 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006884 return;
6885 }
6886 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006887 char **ap;
6888 char **new;
6889
6890 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006891 continue;
6892 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006893 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006894 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006895 ap += 2;
6896 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006897 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006898 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006899 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006900 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901 goto repeat;
6902 }
6903}
6904
6905/*
6906 * Exec a program. Never returns. If you change this routine, you may
6907 * have to change the find_command routine as well.
6908 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006909static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6910static void
6911shellexec(char **argv, const char *path, int idx)
6912{
6913 char *cmdname;
6914 int e;
6915 char **envp;
6916 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006917#if ENABLE_FEATURE_SH_STANDALONE
6918 int applet_no = -1;
6919#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006920
6921 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006922 envp = listvars(VEXPORT, VUNSET, 0);
6923 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006924#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006925 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006926#endif
6927 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006928 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006929 e = errno;
6930 } else {
6931 e = ENOENT;
6932 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6933 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006934 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006935 if (errno != ENOENT && errno != ENOTDIR)
6936 e = errno;
6937 }
6938 stunalloc(cmdname);
6939 }
6940 }
6941
6942 /* Map to POSIX errors */
6943 switch (e) {
6944 case EACCES:
6945 exerrno = 126;
6946 break;
6947 case ENOENT:
6948 exerrno = 127;
6949 break;
6950 default:
6951 exerrno = 2;
6952 break;
6953 }
6954 exitstatus = exerrno;
6955 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006956 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6958 /* NOTREACHED */
6959}
6960
6961static void
6962printentry(struct tblentry *cmdp)
6963{
6964 int idx;
6965 const char *path;
6966 char *name;
6967
6968 idx = cmdp->param.index;
6969 path = pathval();
6970 do {
6971 name = padvance(&path, cmdp->cmdname);
6972 stunalloc(name);
6973 } while (--idx >= 0);
6974 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6975}
6976
6977/*
6978 * Clear out command entries. The argument specifies the first entry in
6979 * PATH which has changed.
6980 */
6981static void
6982clearcmdentry(int firstchange)
6983{
6984 struct tblentry **tblp;
6985 struct tblentry **pp;
6986 struct tblentry *cmdp;
6987
6988 INT_OFF;
6989 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6990 pp = tblp;
6991 while ((cmdp = *pp) != NULL) {
6992 if ((cmdp->cmdtype == CMDNORMAL &&
6993 cmdp->param.index >= firstchange)
6994 || (cmdp->cmdtype == CMDBUILTIN &&
6995 builtinloc >= firstchange)
6996 ) {
6997 *pp = cmdp->next;
6998 free(cmdp);
6999 } else {
7000 pp = &cmdp->next;
7001 }
7002 }
7003 }
7004 INT_ON;
7005}
7006
7007/*
7008 * Locate a command in the command hash table. If "add" is nonzero,
7009 * add the command to the table if it is not already present. The
7010 * variable "lastcmdentry" is set to point to the address of the link
7011 * pointing to the entry, so that delete_cmd_entry can delete the
7012 * entry.
7013 *
7014 * Interrupts must be off if called with add != 0.
7015 */
7016static struct tblentry **lastcmdentry;
7017
7018static struct tblentry *
7019cmdlookup(const char *name, int add)
7020{
7021 unsigned int hashval;
7022 const char *p;
7023 struct tblentry *cmdp;
7024 struct tblentry **pp;
7025
7026 p = name;
7027 hashval = (unsigned char)*p << 4;
7028 while (*p)
7029 hashval += (unsigned char)*p++;
7030 hashval &= 0x7FFF;
7031 pp = &cmdtable[hashval % CMDTABLESIZE];
7032 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7033 if (strcmp(cmdp->cmdname, name) == 0)
7034 break;
7035 pp = &cmdp->next;
7036 }
7037 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007038 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7039 + strlen(name)
7040 /* + 1 - already done because
7041 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007042 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043 cmdp->cmdtype = CMDUNKNOWN;
7044 strcpy(cmdp->cmdname, name);
7045 }
7046 lastcmdentry = pp;
7047 return cmdp;
7048}
7049
7050/*
7051 * Delete the command entry returned on the last lookup.
7052 */
7053static void
7054delete_cmd_entry(void)
7055{
7056 struct tblentry *cmdp;
7057
7058 INT_OFF;
7059 cmdp = *lastcmdentry;
7060 *lastcmdentry = cmdp->next;
7061 if (cmdp->cmdtype == CMDFUNCTION)
7062 freefunc(cmdp->param.func);
7063 free(cmdp);
7064 INT_ON;
7065}
7066
7067/*
7068 * Add a new command entry, replacing any existing command entry for
7069 * the same name - except special builtins.
7070 */
7071static void
7072addcmdentry(char *name, struct cmdentry *entry)
7073{
7074 struct tblentry *cmdp;
7075
7076 cmdp = cmdlookup(name, 1);
7077 if (cmdp->cmdtype == CMDFUNCTION) {
7078 freefunc(cmdp->param.func);
7079 }
7080 cmdp->cmdtype = entry->cmdtype;
7081 cmdp->param = entry->u;
7082 cmdp->rehash = 0;
7083}
7084
7085static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007086hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007087{
7088 struct tblentry **pp;
7089 struct tblentry *cmdp;
7090 int c;
7091 struct cmdentry entry;
7092 char *name;
7093
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007094 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007095 clearcmdentry(0);
7096 return 0;
7097 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007098
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007099 if (*argptr == NULL) {
7100 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7101 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7102 if (cmdp->cmdtype == CMDNORMAL)
7103 printentry(cmdp);
7104 }
7105 }
7106 return 0;
7107 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007108
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007109 c = 0;
7110 while ((name = *argptr) != NULL) {
7111 cmdp = cmdlookup(name, 0);
7112 if (cmdp != NULL
7113 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007114 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7115 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007116 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007117 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007118 find_command(name, &entry, DO_ERR, pathval());
7119 if (entry.cmdtype == CMDUNKNOWN)
7120 c = 1;
7121 argptr++;
7122 }
7123 return c;
7124}
7125
7126/*
7127 * Called when a cd is done. Marks all commands so the next time they
7128 * are executed they will be rehashed.
7129 */
7130static void
7131hashcd(void)
7132{
7133 struct tblentry **pp;
7134 struct tblentry *cmdp;
7135
7136 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7137 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007138 if (cmdp->cmdtype == CMDNORMAL
7139 || (cmdp->cmdtype == CMDBUILTIN
7140 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7141 && builtinloc > 0)
7142 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007143 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007144 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007145 }
7146 }
7147}
7148
7149/*
7150 * Fix command hash table when PATH changed.
7151 * Called before PATH is changed. The argument is the new value of PATH;
7152 * pathval() still returns the old value at this point.
7153 * Called with interrupts off.
7154 */
7155static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007156changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007157{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007158 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007159 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007160 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007161 int idx_bltin;
7162
7163 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007164 firstchange = 9999; /* assume no change */
7165 idx = 0;
7166 idx_bltin = -1;
7167 for (;;) {
7168 if (*old != *new) {
7169 firstchange = idx;
7170 if ((*old == '\0' && *new == ':')
7171 || (*old == ':' && *new == '\0'))
7172 firstchange++;
7173 old = new; /* ignore subsequent differences */
7174 }
7175 if (*new == '\0')
7176 break;
7177 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7178 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007179 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007180 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007181 new++, old++;
7182 }
7183 if (builtinloc < 0 && idx_bltin >= 0)
7184 builtinloc = idx_bltin; /* zap builtins */
7185 if (builtinloc >= 0 && idx_bltin < 0)
7186 firstchange = 0;
7187 clearcmdentry(firstchange);
7188 builtinloc = idx_bltin;
7189}
7190
7191#define TEOF 0
7192#define TNL 1
7193#define TREDIR 2
7194#define TWORD 3
7195#define TSEMI 4
7196#define TBACKGND 5
7197#define TAND 6
7198#define TOR 7
7199#define TPIPE 8
7200#define TLP 9
7201#define TRP 10
7202#define TENDCASE 11
7203#define TENDBQUOTE 12
7204#define TNOT 13
7205#define TCASE 14
7206#define TDO 15
7207#define TDONE 16
7208#define TELIF 17
7209#define TELSE 18
7210#define TESAC 19
7211#define TFI 20
7212#define TFOR 21
7213#define TIF 22
7214#define TIN 23
7215#define TTHEN 24
7216#define TUNTIL 25
7217#define TWHILE 26
7218#define TBEGIN 27
7219#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007220typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007221
7222/* first char is indicating which tokens mark the end of a list */
7223static const char *const tokname_array[] = {
7224 "\1end of file",
7225 "\0newline",
7226 "\0redirection",
7227 "\0word",
7228 "\0;",
7229 "\0&",
7230 "\0&&",
7231 "\0||",
7232 "\0|",
7233 "\0(",
7234 "\1)",
7235 "\1;;",
7236 "\1`",
7237#define KWDOFFSET 13
7238 /* the following are keywords */
7239 "\0!",
7240 "\0case",
7241 "\1do",
7242 "\1done",
7243 "\1elif",
7244 "\1else",
7245 "\1esac",
7246 "\1fi",
7247 "\0for",
7248 "\0if",
7249 "\0in",
7250 "\1then",
7251 "\0until",
7252 "\0while",
7253 "\0{",
7254 "\1}",
7255};
7256
7257static const char *
7258tokname(int tok)
7259{
7260 static char buf[16];
7261
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007262//try this:
7263//if (tok < TSEMI) return tokname_array[tok] + 1;
7264//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7265//return buf;
7266
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007267 if (tok >= TSEMI)
7268 buf[0] = '"';
7269 sprintf(buf + (tok >= TSEMI), "%s%c",
7270 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7271 return buf;
7272}
7273
7274/* Wrapper around strcmp for qsort/bsearch/... */
7275static int
7276pstrcmp(const void *a, const void *b)
7277{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007278 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007279}
7280
7281static const char *const *
7282findkwd(const char *s)
7283{
7284 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007285 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7286 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287}
7288
7289/*
7290 * Locate and print what a word is...
7291 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007292static int
7293describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294{
7295 struct cmdentry entry;
7296 struct tblentry *cmdp;
7297#if ENABLE_ASH_ALIAS
7298 const struct alias *ap;
7299#endif
7300 const char *path = pathval();
7301
7302 if (describe_command_verbose) {
7303 out1str(command);
7304 }
7305
7306 /* First look at the keywords */
7307 if (findkwd(command)) {
7308 out1str(describe_command_verbose ? " is a shell keyword" : command);
7309 goto out;
7310 }
7311
7312#if ENABLE_ASH_ALIAS
7313 /* Then look at the aliases */
7314 ap = lookupalias(command, 0);
7315 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007316 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007317 out1str("alias ");
7318 printalias(ap);
7319 return 0;
7320 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007321 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007322 goto out;
7323 }
7324#endif
7325 /* Then check if it is a tracked alias */
7326 cmdp = cmdlookup(command, 0);
7327 if (cmdp != NULL) {
7328 entry.cmdtype = cmdp->cmdtype;
7329 entry.u = cmdp->param;
7330 } else {
7331 /* Finally use brute force */
7332 find_command(command, &entry, DO_ABS, path);
7333 }
7334
7335 switch (entry.cmdtype) {
7336 case CMDNORMAL: {
7337 int j = entry.u.index;
7338 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007339 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340 p = command;
7341 } else {
7342 do {
7343 p = padvance(&path, command);
7344 stunalloc(p);
7345 } while (--j >= 0);
7346 }
7347 if (describe_command_verbose) {
7348 out1fmt(" is%s %s",
7349 (cmdp ? " a tracked alias for" : nullstr), p
7350 );
7351 } else {
7352 out1str(p);
7353 }
7354 break;
7355 }
7356
7357 case CMDFUNCTION:
7358 if (describe_command_verbose) {
7359 out1str(" is a shell function");
7360 } else {
7361 out1str(command);
7362 }
7363 break;
7364
7365 case CMDBUILTIN:
7366 if (describe_command_verbose) {
7367 out1fmt(" is a %sshell builtin",
7368 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7369 "special " : nullstr
7370 );
7371 } else {
7372 out1str(command);
7373 }
7374 break;
7375
7376 default:
7377 if (describe_command_verbose) {
7378 out1str(": not found\n");
7379 }
7380 return 127;
7381 }
7382 out:
7383 outstr("\n", stdout);
7384 return 0;
7385}
7386
7387static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007388typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007390 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007392 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393
Denis Vlasenko46846e22007-05-20 13:08:31 +00007394 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007395 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007396 i++;
7397 verbose = 0;
7398 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007399 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007400 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 }
7402 return err;
7403}
7404
7405#if ENABLE_ASH_CMDCMD
7406static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007407commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408{
7409 int c;
7410 enum {
7411 VERIFY_BRIEF = 1,
7412 VERIFY_VERBOSE = 2,
7413 } verify = 0;
7414
7415 while ((c = nextopt("pvV")) != '\0')
7416 if (c == 'V')
7417 verify |= VERIFY_VERBOSE;
7418 else if (c == 'v')
7419 verify |= VERIFY_BRIEF;
7420#if DEBUG
7421 else if (c != 'p')
7422 abort();
7423#endif
7424 if (verify)
7425 return describe_command(*argptr, verify - VERIFY_BRIEF);
7426
7427 return 0;
7428}
7429#endif
7430
7431
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007432/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007433
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007434static int funcblocksize; /* size of structures in function */
7435static int funcstringsize; /* size of strings in node */
7436static void *funcblock; /* block to allocate function from */
7437static char *funcstring; /* block to allocate strings from */
7438
Eric Andersencb57d552001-06-28 07:25:16 +00007439/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007440#define EV_EXIT 01 /* exit after evaluating tree */
7441#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7442#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007444static const short nodesize[26] = {
7445 SHELL_ALIGN(sizeof(struct ncmd)),
7446 SHELL_ALIGN(sizeof(struct npipe)),
7447 SHELL_ALIGN(sizeof(struct nredir)),
7448 SHELL_ALIGN(sizeof(struct nredir)),
7449 SHELL_ALIGN(sizeof(struct nredir)),
7450 SHELL_ALIGN(sizeof(struct nbinary)),
7451 SHELL_ALIGN(sizeof(struct nbinary)),
7452 SHELL_ALIGN(sizeof(struct nbinary)),
7453 SHELL_ALIGN(sizeof(struct nif)),
7454 SHELL_ALIGN(sizeof(struct nbinary)),
7455 SHELL_ALIGN(sizeof(struct nbinary)),
7456 SHELL_ALIGN(sizeof(struct nfor)),
7457 SHELL_ALIGN(sizeof(struct ncase)),
7458 SHELL_ALIGN(sizeof(struct nclist)),
7459 SHELL_ALIGN(sizeof(struct narg)),
7460 SHELL_ALIGN(sizeof(struct narg)),
7461 SHELL_ALIGN(sizeof(struct nfile)),
7462 SHELL_ALIGN(sizeof(struct nfile)),
7463 SHELL_ALIGN(sizeof(struct nfile)),
7464 SHELL_ALIGN(sizeof(struct nfile)),
7465 SHELL_ALIGN(sizeof(struct nfile)),
7466 SHELL_ALIGN(sizeof(struct ndup)),
7467 SHELL_ALIGN(sizeof(struct ndup)),
7468 SHELL_ALIGN(sizeof(struct nhere)),
7469 SHELL_ALIGN(sizeof(struct nhere)),
7470 SHELL_ALIGN(sizeof(struct nnot)),
7471};
7472
7473static void calcsize(union node *n);
7474
7475static void
7476sizenodelist(struct nodelist *lp)
7477{
7478 while (lp) {
7479 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7480 calcsize(lp->n);
7481 lp = lp->next;
7482 }
7483}
7484
7485static void
7486calcsize(union node *n)
7487{
7488 if (n == NULL)
7489 return;
7490 funcblocksize += nodesize[n->type];
7491 switch (n->type) {
7492 case NCMD:
7493 calcsize(n->ncmd.redirect);
7494 calcsize(n->ncmd.args);
7495 calcsize(n->ncmd.assign);
7496 break;
7497 case NPIPE:
7498 sizenodelist(n->npipe.cmdlist);
7499 break;
7500 case NREDIR:
7501 case NBACKGND:
7502 case NSUBSHELL:
7503 calcsize(n->nredir.redirect);
7504 calcsize(n->nredir.n);
7505 break;
7506 case NAND:
7507 case NOR:
7508 case NSEMI:
7509 case NWHILE:
7510 case NUNTIL:
7511 calcsize(n->nbinary.ch2);
7512 calcsize(n->nbinary.ch1);
7513 break;
7514 case NIF:
7515 calcsize(n->nif.elsepart);
7516 calcsize(n->nif.ifpart);
7517 calcsize(n->nif.test);
7518 break;
7519 case NFOR:
7520 funcstringsize += strlen(n->nfor.var) + 1;
7521 calcsize(n->nfor.body);
7522 calcsize(n->nfor.args);
7523 break;
7524 case NCASE:
7525 calcsize(n->ncase.cases);
7526 calcsize(n->ncase.expr);
7527 break;
7528 case NCLIST:
7529 calcsize(n->nclist.body);
7530 calcsize(n->nclist.pattern);
7531 calcsize(n->nclist.next);
7532 break;
7533 case NDEFUN:
7534 case NARG:
7535 sizenodelist(n->narg.backquote);
7536 funcstringsize += strlen(n->narg.text) + 1;
7537 calcsize(n->narg.next);
7538 break;
7539 case NTO:
7540 case NCLOBBER:
7541 case NFROM:
7542 case NFROMTO:
7543 case NAPPEND:
7544 calcsize(n->nfile.fname);
7545 calcsize(n->nfile.next);
7546 break;
7547 case NTOFD:
7548 case NFROMFD:
7549 calcsize(n->ndup.vname);
7550 calcsize(n->ndup.next);
7551 break;
7552 case NHERE:
7553 case NXHERE:
7554 calcsize(n->nhere.doc);
7555 calcsize(n->nhere.next);
7556 break;
7557 case NNOT:
7558 calcsize(n->nnot.com);
7559 break;
7560 };
7561}
7562
7563static char *
7564nodeckstrdup(char *s)
7565{
7566 char *rtn = funcstring;
7567
7568 strcpy(funcstring, s);
7569 funcstring += strlen(s) + 1;
7570 return rtn;
7571}
7572
7573static union node *copynode(union node *);
7574
7575static struct nodelist *
7576copynodelist(struct nodelist *lp)
7577{
7578 struct nodelist *start;
7579 struct nodelist **lpp;
7580
7581 lpp = &start;
7582 while (lp) {
7583 *lpp = funcblock;
7584 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7585 (*lpp)->n = copynode(lp->n);
7586 lp = lp->next;
7587 lpp = &(*lpp)->next;
7588 }
7589 *lpp = NULL;
7590 return start;
7591}
7592
7593static union node *
7594copynode(union node *n)
7595{
7596 union node *new;
7597
7598 if (n == NULL)
7599 return NULL;
7600 new = funcblock;
7601 funcblock = (char *) funcblock + nodesize[n->type];
7602
7603 switch (n->type) {
7604 case NCMD:
7605 new->ncmd.redirect = copynode(n->ncmd.redirect);
7606 new->ncmd.args = copynode(n->ncmd.args);
7607 new->ncmd.assign = copynode(n->ncmd.assign);
7608 break;
7609 case NPIPE:
7610 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7611 new->npipe.backgnd = n->npipe.backgnd;
7612 break;
7613 case NREDIR:
7614 case NBACKGND:
7615 case NSUBSHELL:
7616 new->nredir.redirect = copynode(n->nredir.redirect);
7617 new->nredir.n = copynode(n->nredir.n);
7618 break;
7619 case NAND:
7620 case NOR:
7621 case NSEMI:
7622 case NWHILE:
7623 case NUNTIL:
7624 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7625 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7626 break;
7627 case NIF:
7628 new->nif.elsepart = copynode(n->nif.elsepart);
7629 new->nif.ifpart = copynode(n->nif.ifpart);
7630 new->nif.test = copynode(n->nif.test);
7631 break;
7632 case NFOR:
7633 new->nfor.var = nodeckstrdup(n->nfor.var);
7634 new->nfor.body = copynode(n->nfor.body);
7635 new->nfor.args = copynode(n->nfor.args);
7636 break;
7637 case NCASE:
7638 new->ncase.cases = copynode(n->ncase.cases);
7639 new->ncase.expr = copynode(n->ncase.expr);
7640 break;
7641 case NCLIST:
7642 new->nclist.body = copynode(n->nclist.body);
7643 new->nclist.pattern = copynode(n->nclist.pattern);
7644 new->nclist.next = copynode(n->nclist.next);
7645 break;
7646 case NDEFUN:
7647 case NARG:
7648 new->narg.backquote = copynodelist(n->narg.backquote);
7649 new->narg.text = nodeckstrdup(n->narg.text);
7650 new->narg.next = copynode(n->narg.next);
7651 break;
7652 case NTO:
7653 case NCLOBBER:
7654 case NFROM:
7655 case NFROMTO:
7656 case NAPPEND:
7657 new->nfile.fname = copynode(n->nfile.fname);
7658 new->nfile.fd = n->nfile.fd;
7659 new->nfile.next = copynode(n->nfile.next);
7660 break;
7661 case NTOFD:
7662 case NFROMFD:
7663 new->ndup.vname = copynode(n->ndup.vname);
7664 new->ndup.dupfd = n->ndup.dupfd;
7665 new->ndup.fd = n->ndup.fd;
7666 new->ndup.next = copynode(n->ndup.next);
7667 break;
7668 case NHERE:
7669 case NXHERE:
7670 new->nhere.doc = copynode(n->nhere.doc);
7671 new->nhere.fd = n->nhere.fd;
7672 new->nhere.next = copynode(n->nhere.next);
7673 break;
7674 case NNOT:
7675 new->nnot.com = copynode(n->nnot.com);
7676 break;
7677 };
7678 new->type = n->type;
7679 return new;
7680}
7681
7682/*
7683 * Make a copy of a parse tree.
7684 */
7685static struct funcnode *
7686copyfunc(union node *n)
7687{
7688 struct funcnode *f;
7689 size_t blocksize;
7690
7691 funcblocksize = offsetof(struct funcnode, n);
7692 funcstringsize = 0;
7693 calcsize(n);
7694 blocksize = funcblocksize;
7695 f = ckmalloc(blocksize + funcstringsize);
7696 funcblock = (char *) f + offsetof(struct funcnode, n);
7697 funcstring = (char *) f + blocksize;
7698 copynode(n);
7699 f->count = 0;
7700 return f;
7701}
7702
7703/*
7704 * Define a shell function.
7705 */
7706static void
7707defun(char *name, union node *func)
7708{
7709 struct cmdentry entry;
7710
7711 INT_OFF;
7712 entry.cmdtype = CMDFUNCTION;
7713 entry.u.func = copyfunc(func);
7714 addcmdentry(name, &entry);
7715 INT_ON;
7716}
7717
7718static int evalskip; /* set if we are skipping commands */
7719/* reasons for skipping commands (see comment on breakcmd routine) */
7720#define SKIPBREAK (1 << 0)
7721#define SKIPCONT (1 << 1)
7722#define SKIPFUNC (1 << 2)
7723#define SKIPFILE (1 << 3)
7724#define SKIPEVAL (1 << 4)
7725static int skipcount; /* number of levels to skip */
7726static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007727static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007728
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007729/* forward decl way out to parsing code - dotrap needs it */
7730static int evalstring(char *s, int mask);
7731
7732/*
7733 * Called to execute a trap. Perhaps we should avoid entering new trap
7734 * handlers while we are executing a trap handler.
7735 */
7736static int
7737dotrap(void)
7738{
7739 char *p;
7740 char *q;
7741 int i;
7742 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007743 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007744
7745 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007746 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007747 xbarrier();
7748
7749 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7750 if (!*q)
7751 continue;
7752 *q = '\0';
7753
7754 p = trap[i + 1];
7755 if (!p)
7756 continue;
7757 skip = evalstring(p, SKIPEVAL);
7758 exitstatus = savestatus;
7759 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007760 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007761 }
7762
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007763 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007764}
7765
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007766/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007767static void evalloop(union node *, int);
7768static void evalfor(union node *, int);
7769static void evalcase(union node *, int);
7770static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007771static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007772static void evalpipe(union node *, int);
7773static void evalcommand(union node *, int);
7774static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007775static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007776
Eric Andersen62483552001-07-10 06:09:16 +00007777/*
Eric Andersenc470f442003-07-28 09:56:35 +00007778 * Evaluate a parse tree. The value is left in the global variable
7779 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007780 */
Eric Andersenc470f442003-07-28 09:56:35 +00007781static void
7782evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007783{
Eric Andersenc470f442003-07-28 09:56:35 +00007784 int checkexit = 0;
7785 void (*evalfn)(union node *, int);
7786 unsigned isor;
7787 int status;
7788 if (n == NULL) {
7789 TRACE(("evaltree(NULL) called\n"));
7790 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007791 }
Eric Andersenc470f442003-07-28 09:56:35 +00007792 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007793 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007794 switch (n->type) {
7795 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007796#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007797 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007798 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007799 break;
7800#endif
7801 case NNOT:
7802 evaltree(n->nnot.com, EV_TESTED);
7803 status = !exitstatus;
7804 goto setstatus;
7805 case NREDIR:
7806 expredir(n->nredir.redirect);
7807 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7808 if (!status) {
7809 evaltree(n->nredir.n, flags & EV_TESTED);
7810 status = exitstatus;
7811 }
7812 popredir(0);
7813 goto setstatus;
7814 case NCMD:
7815 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007816 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007817 if (eflag && !(flags & EV_TESTED))
7818 checkexit = ~0;
7819 goto calleval;
7820 case NFOR:
7821 evalfn = evalfor;
7822 goto calleval;
7823 case NWHILE:
7824 case NUNTIL:
7825 evalfn = evalloop;
7826 goto calleval;
7827 case NSUBSHELL:
7828 case NBACKGND:
7829 evalfn = evalsubshell;
7830 goto calleval;
7831 case NPIPE:
7832 evalfn = evalpipe;
7833 goto checkexit;
7834 case NCASE:
7835 evalfn = evalcase;
7836 goto calleval;
7837 case NAND:
7838 case NOR:
7839 case NSEMI:
7840#if NAND + 1 != NOR
7841#error NAND + 1 != NOR
7842#endif
7843#if NOR + 1 != NSEMI
7844#error NOR + 1 != NSEMI
7845#endif
7846 isor = n->type - NAND;
7847 evaltree(
7848 n->nbinary.ch1,
7849 (flags | ((isor >> 1) - 1)) & EV_TESTED
7850 );
7851 if (!exitstatus == isor)
7852 break;
7853 if (!evalskip) {
7854 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007855 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007856 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007857 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007858 evalfn(n, flags);
7859 break;
7860 }
7861 break;
7862 case NIF:
7863 evaltree(n->nif.test, EV_TESTED);
7864 if (evalskip)
7865 break;
7866 if (exitstatus == 0) {
7867 n = n->nif.ifpart;
7868 goto evaln;
7869 } else if (n->nif.elsepart) {
7870 n = n->nif.elsepart;
7871 goto evaln;
7872 }
7873 goto success;
7874 case NDEFUN:
7875 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007876 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007877 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007878 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007879 exitstatus = status;
7880 break;
7881 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007882 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007883 if ((checkexit & exitstatus))
7884 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007885 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007886 goto exexit;
7887
7888 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007889 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007890 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007891 }
Eric Andersen62483552001-07-10 06:09:16 +00007892}
7893
Eric Andersenc470f442003-07-28 09:56:35 +00007894#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7895static
7896#endif
7897void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7898
Eric Andersenc470f442003-07-28 09:56:35 +00007899static void
7900evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007901{
7902 int status;
7903
7904 loopnest++;
7905 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007906 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007907 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007908 int i;
7909
Eric Andersencb57d552001-06-28 07:25:16 +00007910 evaltree(n->nbinary.ch1, EV_TESTED);
7911 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007912 skipping:
7913 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007914 evalskip = 0;
7915 continue;
7916 }
7917 if (evalskip == SKIPBREAK && --skipcount <= 0)
7918 evalskip = 0;
7919 break;
7920 }
Eric Andersenc470f442003-07-28 09:56:35 +00007921 i = exitstatus;
7922 if (n->type != NWHILE)
7923 i = !i;
7924 if (i != 0)
7925 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007926 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007927 status = exitstatus;
7928 if (evalskip)
7929 goto skipping;
7930 }
7931 loopnest--;
7932 exitstatus = status;
7933}
7934
Eric Andersenc470f442003-07-28 09:56:35 +00007935static void
7936evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007937{
7938 struct arglist arglist;
7939 union node *argp;
7940 struct strlist *sp;
7941 struct stackmark smark;
7942
7943 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007944 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007945 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007946 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007947 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007948 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007949 if (evalskip)
7950 goto out;
7951 }
7952 *arglist.lastp = NULL;
7953
7954 exitstatus = 0;
7955 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007956 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007957 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007958 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007959 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007960 if (evalskip) {
7961 if (evalskip == SKIPCONT && --skipcount <= 0) {
7962 evalskip = 0;
7963 continue;
7964 }
7965 if (evalskip == SKIPBREAK && --skipcount <= 0)
7966 evalskip = 0;
7967 break;
7968 }
7969 }
7970 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007971 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007972 popstackmark(&smark);
7973}
7974
Eric Andersenc470f442003-07-28 09:56:35 +00007975static void
7976evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007977{
7978 union node *cp;
7979 union node *patp;
7980 struct arglist arglist;
7981 struct stackmark smark;
7982
7983 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007984 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007985 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007986 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007988 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7989 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007990 if (casematch(patp, arglist.list->text)) {
7991 if (evalskip == 0) {
7992 evaltree(cp->nclist.body, flags);
7993 }
7994 goto out;
7995 }
7996 }
7997 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007998 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007999 popstackmark(&smark);
8000}
8001
Eric Andersenc470f442003-07-28 09:56:35 +00008002/*
8003 * Kick off a subshell to evaluate a tree.
8004 */
Eric Andersenc470f442003-07-28 09:56:35 +00008005static void
8006evalsubshell(union node *n, int flags)
8007{
8008 struct job *jp;
8009 int backgnd = (n->type == NBACKGND);
8010 int status;
8011
8012 expredir(n->nredir.redirect);
8013 if (!backgnd && flags & EV_EXIT && !trap[0])
8014 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008015 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008016 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008017 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008018 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008019 flags |= EV_EXIT;
8020 if (backgnd)
8021 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008022 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008023 redirect(n->nredir.redirect, 0);
8024 evaltreenr(n->nredir.n, flags);
8025 /* never returns */
8026 }
8027 status = 0;
8028 if (! backgnd)
8029 status = waitforjob(jp);
8030 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008031 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008032}
8033
Eric Andersenc470f442003-07-28 09:56:35 +00008034/*
8035 * Compute the names of the files in a redirection list.
8036 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008037static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008038static void
8039expredir(union node *n)
8040{
8041 union node *redir;
8042
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008043 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008044 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008045
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008046 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008047 fn.lastp = &fn.list;
8048 switch (redir->type) {
8049 case NFROMTO:
8050 case NFROM:
8051 case NTO:
8052 case NCLOBBER:
8053 case NAPPEND:
8054 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8055 redir->nfile.expfname = fn.list->text;
8056 break;
8057 case NFROMFD:
8058 case NTOFD:
8059 if (redir->ndup.vname) {
8060 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008061 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008062 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008063 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008064 }
8065 break;
8066 }
8067 }
8068}
8069
Eric Andersencb57d552001-06-28 07:25:16 +00008070/*
Eric Andersencb57d552001-06-28 07:25:16 +00008071 * Evaluate a pipeline. All the processes in the pipeline are children
8072 * of the process creating the pipeline. (This differs from some versions
8073 * of the shell, which make the last process in a pipeline the parent
8074 * of all the rest.)
8075 */
Eric Andersenc470f442003-07-28 09:56:35 +00008076static void
8077evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008078{
8079 struct job *jp;
8080 struct nodelist *lp;
8081 int pipelen;
8082 int prevfd;
8083 int pip[2];
8084
Eric Andersenc470f442003-07-28 09:56:35 +00008085 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008086 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008087 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008088 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008089 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008090 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008091 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008092 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008093 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008094 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008095 pip[1] = -1;
8096 if (lp->next) {
8097 if (pipe(pip) < 0) {
8098 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008099 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008100 }
8101 }
8102 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008103 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008104 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008105 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008106 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008107 if (prevfd > 0) {
8108 dup2(prevfd, 0);
8109 close(prevfd);
8110 }
8111 if (pip[1] > 1) {
8112 dup2(pip[1], 1);
8113 close(pip[1]);
8114 }
Eric Andersenc470f442003-07-28 09:56:35 +00008115 evaltreenr(lp->n, flags);
8116 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008117 }
8118 if (prevfd >= 0)
8119 close(prevfd);
8120 prevfd = pip[0];
8121 close(pip[1]);
8122 }
Eric Andersencb57d552001-06-28 07:25:16 +00008123 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008124 exitstatus = waitforjob(jp);
8125 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008126 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008127 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008128}
8129
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008130/*
8131 * Controls whether the shell is interactive or not.
8132 */
8133static void
8134setinteractive(int on)
8135{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008136 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008137
8138 if (++on == is_interactive)
8139 return;
8140 is_interactive = on;
8141 setsignal(SIGINT);
8142 setsignal(SIGQUIT);
8143 setsignal(SIGTERM);
8144#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8145 if (is_interactive > 1) {
8146 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008147 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008148
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008149 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008150 out1fmt(
8151 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008152 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008153 "Enter 'help' for a list of built-in commands."
8154 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008155 bb_banner);
8156 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008157 }
8158 }
8159#endif
8160}
8161
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008162static void
8163optschanged(void)
8164{
8165#if DEBUG
8166 opentrace();
8167#endif
8168 setinteractive(iflag);
8169 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008170#if ENABLE_FEATURE_EDITING_VI
8171 if (viflag)
8172 line_input_state->flags |= VI_MODE;
8173 else
8174 line_input_state->flags &= ~VI_MODE;
8175#else
8176 viflag = 0; /* forcibly keep the option off */
8177#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008178}
8179
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008180static struct localvar *localvars;
8181
8182/*
8183 * Called after a function returns.
8184 * Interrupts must be off.
8185 */
8186static void
8187poplocalvars(void)
8188{
8189 struct localvar *lvp;
8190 struct var *vp;
8191
8192 while ((lvp = localvars) != NULL) {
8193 localvars = lvp->next;
8194 vp = lvp->vp;
8195 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8196 if (vp == NULL) { /* $- saved */
8197 memcpy(optlist, lvp->text, sizeof(optlist));
8198 free((char*)lvp->text);
8199 optschanged();
8200 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8201 unsetvar(vp->text);
8202 } else {
8203 if (vp->func)
8204 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8205 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8206 free((char*)vp->text);
8207 vp->flags = lvp->flags;
8208 vp->text = lvp->text;
8209 }
8210 free(lvp);
8211 }
8212}
8213
8214static int
8215evalfun(struct funcnode *func, int argc, char **argv, int flags)
8216{
8217 volatile struct shparam saveparam;
8218 struct localvar *volatile savelocalvars;
8219 struct jmploc *volatile savehandler;
8220 struct jmploc jmploc;
8221 int e;
8222
8223 saveparam = shellparam;
8224 savelocalvars = localvars;
8225 e = setjmp(jmploc.loc);
8226 if (e) {
8227 goto funcdone;
8228 }
8229 INT_OFF;
8230 savehandler = exception_handler;
8231 exception_handler = &jmploc;
8232 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008233 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008234 func->count++;
8235 funcnest++;
8236 INT_ON;
8237 shellparam.nparam = argc - 1;
8238 shellparam.p = argv + 1;
8239#if ENABLE_ASH_GETOPTS
8240 shellparam.optind = 1;
8241 shellparam.optoff = -1;
8242#endif
8243 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008244 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008245 INT_OFF;
8246 funcnest--;
8247 freefunc(func);
8248 poplocalvars();
8249 localvars = savelocalvars;
8250 freeparam(&shellparam);
8251 shellparam = saveparam;
8252 exception_handler = savehandler;
8253 INT_ON;
8254 evalskip &= ~SKIPFUNC;
8255 return e;
8256}
8257
Denis Vlasenko131ae172007-02-18 13:00:19 +00008258#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008259static char **
8260parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008261{
8262 char *cp, c;
8263
8264 for (;;) {
8265 cp = *++argv;
8266 if (!cp)
8267 return 0;
8268 if (*cp++ != '-')
8269 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008270 c = *cp++;
8271 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008272 break;
8273 if (c == '-' && !*cp) {
8274 argv++;
8275 break;
8276 }
8277 do {
8278 switch (c) {
8279 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008280 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008281 break;
8282 default:
8283 /* run 'typecmd' for other options */
8284 return 0;
8285 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008286 c = *cp++;
8287 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008288 }
8289 return argv;
8290}
8291#endif
8292
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008293/*
8294 * Make a variable a local variable. When a variable is made local, it's
8295 * value and flags are saved in a localvar structure. The saved values
8296 * will be restored when the shell function returns. We handle the name
8297 * "-" as a special case.
8298 */
8299static void
8300mklocal(char *name)
8301{
8302 struct localvar *lvp;
8303 struct var **vpp;
8304 struct var *vp;
8305
8306 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008307 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008308 if (LONE_DASH(name)) {
8309 char *p;
8310 p = ckmalloc(sizeof(optlist));
8311 lvp->text = memcpy(p, optlist, sizeof(optlist));
8312 vp = NULL;
8313 } else {
8314 char *eq;
8315
8316 vpp = hashvar(name);
8317 vp = *findvar(vpp, name);
8318 eq = strchr(name, '=');
8319 if (vp == NULL) {
8320 if (eq)
8321 setvareq(name, VSTRFIXED);
8322 else
8323 setvar(name, NULL, VSTRFIXED);
8324 vp = *vpp; /* the new variable */
8325 lvp->flags = VUNSET;
8326 } else {
8327 lvp->text = vp->text;
8328 lvp->flags = vp->flags;
8329 vp->flags |= VSTRFIXED|VTEXTFIXED;
8330 if (eq)
8331 setvareq(name, 0);
8332 }
8333 }
8334 lvp->vp = vp;
8335 lvp->next = localvars;
8336 localvars = lvp;
8337 INT_ON;
8338}
8339
8340/*
8341 * The "local" command.
8342 */
8343static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008344localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008345{
8346 char *name;
8347
8348 argv = argptr;
8349 while ((name = *argv++) != NULL) {
8350 mklocal(name);
8351 }
8352 return 0;
8353}
8354
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008355static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008356falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008357{
8358 return 1;
8359}
8360
8361static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008362truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008363{
8364 return 0;
8365}
8366
8367static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008368execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008369{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008370 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008371 iflag = 0; /* exit on error */
8372 mflag = 0;
8373 optschanged();
8374 shellexec(argv + 1, pathval(), 0);
8375 }
8376 return 0;
8377}
8378
8379/*
8380 * The return command.
8381 */
8382static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008383returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008384{
8385 /*
8386 * If called outside a function, do what ksh does;
8387 * skip the rest of the file.
8388 */
8389 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8390 return argv[1] ? number(argv[1]) : exitstatus;
8391}
8392
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008393/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008394static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008395static int dotcmd(int, char **);
8396static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008397static int exitcmd(int, char **);
8398static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008399#if ENABLE_ASH_GETOPTS
8400static int getoptscmd(int, char **);
8401#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008402#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008403static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008404#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008405#if ENABLE_ASH_MATH_SUPPORT
8406static int letcmd(int, char **);
8407#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008408static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008409static int setcmd(int, char **);
8410static int shiftcmd(int, char **);
8411static int timescmd(int, char **);
8412static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008413static int umaskcmd(int, char **);
8414static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008416
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008417#define BUILTIN_NOSPEC "0"
8418#define BUILTIN_SPECIAL "1"
8419#define BUILTIN_REGULAR "2"
8420#define BUILTIN_SPEC_REG "3"
8421#define BUILTIN_ASSIGN "4"
8422#define BUILTIN_SPEC_ASSG "5"
8423#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008424#define BUILTIN_SPEC_REG_ASSG "7"
8425
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008426/* We do not handle [[ expr ]] bashism bash-compatibly,
8427 * we make it a synonym of [ expr ].
8428 * Basically, word splitting and pathname expansion should NOT be performed
8429 * Examples:
8430 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8431 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8432 * Additional operators:
8433 * || and && should work as -o and -a
8434 * =~ regexp match
8435 * Apart from the above, [[ expr ]] should work as [ expr ]
8436 */
8437
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008438#define echocmd echo_main
8439#define printfcmd printf_main
8440#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008441
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008442/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008443static const struct builtincmd builtintab[] = {
8444 { BUILTIN_SPEC_REG ".", dotcmd },
8445 { BUILTIN_SPEC_REG ":", truecmd },
8446#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008447 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008448#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008449 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008450#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008451#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008452#if ENABLE_ASH_ALIAS
8453 { BUILTIN_REG_ASSG "alias", aliascmd },
8454#endif
8455#if JOBS
8456 { BUILTIN_REGULAR "bg", fg_bgcmd },
8457#endif
8458 { BUILTIN_SPEC_REG "break", breakcmd },
8459 { BUILTIN_REGULAR "cd", cdcmd },
8460 { BUILTIN_NOSPEC "chdir", cdcmd },
8461#if ENABLE_ASH_CMDCMD
8462 { BUILTIN_REGULAR "command", commandcmd },
8463#endif
8464 { BUILTIN_SPEC_REG "continue", breakcmd },
8465#if ENABLE_ASH_BUILTIN_ECHO
8466 { BUILTIN_REGULAR "echo", echocmd },
8467#endif
8468 { BUILTIN_SPEC_REG "eval", evalcmd },
8469 { BUILTIN_SPEC_REG "exec", execcmd },
8470 { BUILTIN_SPEC_REG "exit", exitcmd },
8471 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8472 { BUILTIN_REGULAR "false", falsecmd },
8473#if JOBS
8474 { BUILTIN_REGULAR "fg", fg_bgcmd },
8475#endif
8476#if ENABLE_ASH_GETOPTS
8477 { BUILTIN_REGULAR "getopts", getoptscmd },
8478#endif
8479 { BUILTIN_NOSPEC "hash", hashcmd },
8480#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8481 { BUILTIN_NOSPEC "help", helpcmd },
8482#endif
8483#if JOBS
8484 { BUILTIN_REGULAR "jobs", jobscmd },
8485 { BUILTIN_REGULAR "kill", killcmd },
8486#endif
8487#if ENABLE_ASH_MATH_SUPPORT
8488 { BUILTIN_NOSPEC "let", letcmd },
8489#endif
8490 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008491#if ENABLE_ASH_BUILTIN_PRINTF
8492 { BUILTIN_REGULAR "printf", printfcmd },
8493#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008494 { BUILTIN_NOSPEC "pwd", pwdcmd },
8495 { BUILTIN_REGULAR "read", readcmd },
8496 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8497 { BUILTIN_SPEC_REG "return", returncmd },
8498 { BUILTIN_SPEC_REG "set", setcmd },
8499 { BUILTIN_SPEC_REG "shift", shiftcmd },
8500 { BUILTIN_SPEC_REG "source", dotcmd },
8501#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008502 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008503#endif
8504 { BUILTIN_SPEC_REG "times", timescmd },
8505 { BUILTIN_SPEC_REG "trap", trapcmd },
8506 { BUILTIN_REGULAR "true", truecmd },
8507 { BUILTIN_NOSPEC "type", typecmd },
8508 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8509 { BUILTIN_REGULAR "umask", umaskcmd },
8510#if ENABLE_ASH_ALIAS
8511 { BUILTIN_REGULAR "unalias", unaliascmd },
8512#endif
8513 { BUILTIN_SPEC_REG "unset", unsetcmd },
8514 { BUILTIN_REGULAR "wait", waitcmd },
8515};
8516
Denis Vlasenko80591b02008-03-25 07:49:43 +00008517/* Should match the above table! */
8518#define COMMANDCMD (builtintab + \
8519 2 + \
8520 1 * ENABLE_ASH_BUILTIN_TEST + \
8521 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8522 1 * ENABLE_ASH_ALIAS + \
8523 1 * ENABLE_ASH_JOB_CONTROL + \
8524 3)
8525#define EXECCMD (builtintab + \
8526 2 + \
8527 1 * ENABLE_ASH_BUILTIN_TEST + \
8528 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8529 1 * ENABLE_ASH_ALIAS + \
8530 1 * ENABLE_ASH_JOB_CONTROL + \
8531 3 + \
8532 1 * ENABLE_ASH_CMDCMD + \
8533 1 + \
8534 ENABLE_ASH_BUILTIN_ECHO + \
8535 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008536
8537/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008538 * Search the table of builtin commands.
8539 */
8540static struct builtincmd *
8541find_builtin(const char *name)
8542{
8543 struct builtincmd *bp;
8544
8545 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008546 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008547 pstrcmp
8548 );
8549 return bp;
8550}
8551
8552/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008553 * Execute a simple command.
8554 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008555static int
8556isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008557{
8558 const char *q = endofname(p);
8559 if (p == q)
8560 return 0;
8561 return *q == '=';
8562}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008563static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008564bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565{
8566 /* Preserve exitstatus of a previous possible redirection
8567 * as POSIX mandates */
8568 return back_exitstatus;
8569}
Eric Andersenc470f442003-07-28 09:56:35 +00008570static void
8571evalcommand(union node *cmd, int flags)
8572{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008573 static const struct builtincmd null_bltin = {
8574 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008575 };
Eric Andersenc470f442003-07-28 09:56:35 +00008576 struct stackmark smark;
8577 union node *argp;
8578 struct arglist arglist;
8579 struct arglist varlist;
8580 char **argv;
8581 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008582 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008583 struct cmdentry cmdentry;
8584 struct job *jp;
8585 char *lastarg;
8586 const char *path;
8587 int spclbltin;
8588 int cmd_is_exec;
8589 int status;
8590 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008591 struct builtincmd *bcmd;
8592 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008593
8594 /* First expand the arguments. */
8595 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8596 setstackmark(&smark);
8597 back_exitstatus = 0;
8598
8599 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008600 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008601 varlist.lastp = &varlist.list;
8602 *varlist.lastp = NULL;
8603 arglist.lastp = &arglist.list;
8604 *arglist.lastp = NULL;
8605
8606 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008607 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008608 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8609 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8610 }
8611
Eric Andersenc470f442003-07-28 09:56:35 +00008612 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8613 struct strlist **spp;
8614
8615 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008616 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008617 expandarg(argp, &arglist, EXP_VARTILDE);
8618 else
8619 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8620
Eric Andersenc470f442003-07-28 09:56:35 +00008621 for (sp = *spp; sp; sp = sp->next)
8622 argc++;
8623 }
8624
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008625 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008626 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008627 TRACE(("evalcommand arg: %s\n", sp->text));
8628 *nargv++ = sp->text;
8629 }
8630 *nargv = NULL;
8631
8632 lastarg = NULL;
8633 if (iflag && funcnest == 0 && argc > 0)
8634 lastarg = nargv[-1];
8635
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008636 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008637 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008638 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008639
8640 path = vpath.text;
8641 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8642 struct strlist **spp;
8643 char *p;
8644
8645 spp = varlist.lastp;
8646 expandarg(argp, &varlist, EXP_VARTILDE);
8647
8648 /*
8649 * Modify the command lookup path, if a PATH= assignment
8650 * is present
8651 */
8652 p = (*spp)->text;
8653 if (varequal(p, path))
8654 path = p;
8655 }
8656
8657 /* Print the command if xflag is set. */
8658 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008659 int n;
8660 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008661
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008662 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008663 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008664
8665 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008666 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008667 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008668 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008669 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008670 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008671 p--;
8672 }
8673 }
8674 sp = arglist.list;
8675 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008676 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008677 }
8678
8679 cmd_is_exec = 0;
8680 spclbltin = -1;
8681
8682 /* Now locate the command. */
8683 if (argc) {
8684 const char *oldpath;
8685 int cmd_flag = DO_ERR;
8686
8687 path += 5;
8688 oldpath = path;
8689 for (;;) {
8690 find_command(argv[0], &cmdentry, cmd_flag, path);
8691 if (cmdentry.cmdtype == CMDUNKNOWN) {
8692 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008693 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008694 goto bail;
8695 }
8696
8697 /* implement bltin and command here */
8698 if (cmdentry.cmdtype != CMDBUILTIN)
8699 break;
8700 if (spclbltin < 0)
8701 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8702 if (cmdentry.u.cmd == EXECCMD)
8703 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008704#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008705 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008706 path = oldpath;
8707 nargv = parse_command_args(argv, &path);
8708 if (!nargv)
8709 break;
8710 argc -= nargv - argv;
8711 argv = nargv;
8712 cmd_flag |= DO_NOFUNC;
8713 } else
8714#endif
8715 break;
8716 }
8717 }
8718
8719 if (status) {
8720 /* We have a redirection error. */
8721 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008722 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008723 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008724 exitstatus = status;
8725 goto out;
8726 }
8727
8728 /* Execute the command. */
8729 switch (cmdentry.cmdtype) {
8730 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008731#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008732 {
8733 /* find_command() encodes applet_no as (-2 - applet_no) */
8734 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008735 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008736 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008737 /* run <applet>_main() */
8738 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008739 break;
8740 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008741 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008742#endif
8743
Eric Andersenc470f442003-07-28 09:56:35 +00008744 /* Fork off a child process if necessary. */
8745 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008746 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008747 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008748 if (forkshell(jp, cmd, FORK_FG) != 0) {
8749 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008750 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008751 break;
8752 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008753 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008754 }
8755 listsetvar(varlist.list, VEXPORT|VSTACK);
8756 shellexec(argv, path, cmdentry.u.index);
8757 /* NOTREACHED */
8758
8759 case CMDBUILTIN:
8760 cmdenviron = varlist.list;
8761 if (cmdenviron) {
8762 struct strlist *list = cmdenviron;
8763 int i = VNOSET;
8764 if (spclbltin > 0 || argc == 0) {
8765 i = 0;
8766 if (cmd_is_exec && argc > 1)
8767 i = VEXPORT;
8768 }
8769 listsetvar(list, i);
8770 }
8771 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8772 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008773 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008774 if (i == EXEXIT)
8775 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008776 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008777 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008778 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008779 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008780 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008781 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008782 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008783 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008784 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008785 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008786 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008787 }
8788 break;
8789
8790 case CMDFUNCTION:
8791 listsetvar(varlist.list, 0);
8792 if (evalfun(cmdentry.u.func, argc, argv, flags))
8793 goto raise;
8794 break;
8795 }
8796
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008797 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008798 popredir(cmd_is_exec);
8799 if (lastarg)
8800 /* dsl: I think this is intended to be used to support
8801 * '_' in 'vi' command mode during line editing...
8802 * However I implemented that within libedit itself.
8803 */
8804 setvar("_", lastarg, 0);
8805 popstackmark(&smark);
8806}
8807
8808static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008809evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8810{
Eric Andersenc470f442003-07-28 09:56:35 +00008811 char *volatile savecmdname;
8812 struct jmploc *volatile savehandler;
8813 struct jmploc jmploc;
8814 int i;
8815
8816 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008817 i = setjmp(jmploc.loc);
8818 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008819 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008820 savehandler = exception_handler;
8821 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008822 commandname = argv[0];
8823 argptr = argv + 1;
8824 optptr = NULL; /* initialize nextopt */
8825 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008826 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008827 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008828 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008829 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008830 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008831// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008832 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008833
8834 return i;
8835}
8836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008837static int
8838goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008839{
8840 return !*endofname(p);
8841}
8842
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008843
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008844/*
8845 * Search for a command. This is called before we fork so that the
8846 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008847 * the child. The check for "goodname" is an overly conservative
8848 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008849 */
Eric Andersenc470f442003-07-28 09:56:35 +00008850static void
8851prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008852{
8853 struct cmdentry entry;
8854
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008855 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8856 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008857}
8858
Eric Andersencb57d552001-06-28 07:25:16 +00008859
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008860/* ============ Builtin commands
8861 *
8862 * Builtin commands whose functions are closely tied to evaluation
8863 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008864 */
8865
8866/*
Eric Andersencb57d552001-06-28 07:25:16 +00008867 * Handle break and continue commands. Break, continue, and return are
8868 * all handled by setting the evalskip flag. The evaluation routines
8869 * above all check this flag, and if it is set they start skipping
8870 * commands rather than executing them. The variable skipcount is
8871 * the number of loops to break/continue, or the number of function
8872 * levels to return. (The latter is always 1.) It should probably
8873 * be an error to break out of more loops than exist, but it isn't
8874 * in the standard shell so we don't make it one here.
8875 */
Eric Andersenc470f442003-07-28 09:56:35 +00008876static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008877breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008878{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008879 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008880
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008881 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008883 if (n > loopnest)
8884 n = loopnest;
8885 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008886 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008887 skipcount = n;
8888 }
8889 return 0;
8890}
8891
Eric Andersenc470f442003-07-28 09:56:35 +00008892
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008893/* ============ input.c
8894 *
Eric Andersen90898442003-08-06 11:20:52 +00008895 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008896 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008897
Eric Andersenc470f442003-07-28 09:56:35 +00008898#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008899
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008900enum {
8901 INPUT_PUSH_FILE = 1,
8902 INPUT_NOFILE_OK = 2,
8903};
Eric Andersencb57d552001-06-28 07:25:16 +00008904
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008905static int plinno = 1; /* input line number */
8906/* number of characters left in input buffer */
8907static int parsenleft; /* copy of parsefile->nleft */
8908static int parselleft; /* copy of parsefile->lleft */
8909/* next character in input buffer */
8910static char *parsenextc; /* copy of parsefile->nextc */
8911
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008912static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008913/* values of checkkwd variable */
8914#define CHKALIAS 0x1
8915#define CHKKWD 0x2
8916#define CHKNL 0x4
8917
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008918static void
8919popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008920{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008921 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008922
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008923 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008924#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008925 if (sp->ap) {
8926 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8927 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008928 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008929 if (sp->string != sp->ap->val) {
8930 free(sp->string);
8931 }
8932 sp->ap->flag &= ~ALIASINUSE;
8933 if (sp->ap->flag & ALIASDEAD) {
8934 unalias(sp->ap->name);
8935 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008936 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008937#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008938 parsenextc = sp->prevstring;
8939 parsenleft = sp->prevnleft;
8940/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008941 g_parsefile->strpush = sp->prev;
8942 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008943 free(sp);
8944 INT_ON;
8945}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008946
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008947static int
8948preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008949{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008950 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008951 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008952 parsenextc = buf;
8953
Denis Vlasenko38f63192007-01-22 09:03:07 +00008954#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008955 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008956 if (!iflag || g_parsefile->fd)
8957 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008958 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008959#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008960 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008961#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008962 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8963 if (nr == 0) {
8964 /* Ctrl+C pressed */
8965 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008966 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008967 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008968 raise(SIGINT);
8969 return 1;
8970 }
Eric Andersenc470f442003-07-28 09:56:35 +00008971 goto retry;
8972 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008973 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008974 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008975 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008976 }
Eric Andersencb57d552001-06-28 07:25:16 +00008977 }
8978#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008979 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008980#endif
8981
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008982#if 0
8983/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008984 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008985 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008986 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008987 if (flags >= 0 && (flags & O_NONBLOCK)) {
8988 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008989 if (fcntl(0, F_SETFL, flags) >= 0) {
8990 out2str("sh: turning off NDELAY mode\n");
8991 goto retry;
8992 }
8993 }
8994 }
8995 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008996#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008997 return nr;
8998}
8999
9000/*
9001 * Refill the input buffer and return the next input character:
9002 *
9003 * 1) If a string was pushed back on the input, pop it;
9004 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9005 * from a string so we can't refill the buffer, return EOF.
9006 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9007 * 4) Process input up to the next newline, deleting nul characters.
9008 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009009static int
Eric Andersenc470f442003-07-28 09:56:35 +00009010preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009011{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009012 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009013 int more;
9014 char savec;
9015
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009016 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009017#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009018 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009019 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009020 return PEOA;
9021 }
Eric Andersen2870d962001-07-02 17:27:21 +00009022#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009023 popstring();
9024 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009025 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009026 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009027 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009028 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009029 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009030
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009031 more = parselleft;
9032 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009033 again:
9034 more = preadfd();
9035 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009036 parselleft = parsenleft = EOF_NLEFT;
9037 return PEOF;
9038 }
9039 }
9040
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009041 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009042
9043 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009044 for (;;) {
9045 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009046
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009047 more--;
9048 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009049
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009050 if (!c)
9051 memmove(q, q + 1, more);
9052 else {
9053 q++;
9054 if (c == '\n') {
9055 parsenleft = q - parsenextc - 1;
9056 break;
9057 }
Eric Andersencb57d552001-06-28 07:25:16 +00009058 }
9059
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009060 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009061 parsenleft = q - parsenextc - 1;
9062 if (parsenleft < 0)
9063 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009064 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009065 }
9066 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009067 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009068
9069 savec = *q;
9070 *q = '\0';
9071
9072 if (vflag) {
9073 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009074 }
9075
9076 *q = savec;
9077
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009078 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009079}
9080
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009081#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009082static int
9083pgetc(void)
9084{
9085 return pgetc_as_macro();
9086}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009087
9088#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9089#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009090#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009091#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092#endif
9093
9094/*
9095 * Same as pgetc(), but ignores PEOA.
9096 */
9097#if ENABLE_ASH_ALIAS
9098static int
9099pgetc2(void)
9100{
9101 int c;
9102
9103 do {
9104 c = pgetc_macro();
9105 } while (c == PEOA);
9106 return c;
9107}
9108#else
9109static int
9110pgetc2(void)
9111{
9112 return pgetc_macro();
9113}
9114#endif
9115
9116/*
9117 * Read a line from the script.
9118 */
9119static char *
9120pfgets(char *line, int len)
9121{
9122 char *p = line;
9123 int nleft = len;
9124 int c;
9125
9126 while (--nleft > 0) {
9127 c = pgetc2();
9128 if (c == PEOF) {
9129 if (p == line)
9130 return NULL;
9131 break;
9132 }
9133 *p++ = c;
9134 if (c == '\n')
9135 break;
9136 }
9137 *p = '\0';
9138 return line;
9139}
9140
Eric Andersenc470f442003-07-28 09:56:35 +00009141/*
9142 * Undo the last call to pgetc. Only one character may be pushed back.
9143 * PEOF may be pushed back.
9144 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009145static void
Eric Andersenc470f442003-07-28 09:56:35 +00009146pungetc(void)
9147{
9148 parsenleft++;
9149 parsenextc--;
9150}
Eric Andersencb57d552001-06-28 07:25:16 +00009151
9152/*
9153 * Push a string back onto the input at this current parsefile level.
9154 * We handle aliases this way.
9155 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009156#if !ENABLE_ASH_ALIAS
9157#define pushstring(s, ap) pushstring(s)
9158#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009159static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009160pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009161{
Eric Andersencb57d552001-06-28 07:25:16 +00009162 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009163 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009164
Eric Andersenc470f442003-07-28 09:56:35 +00009165 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009166 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009167/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009168 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009169 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009170 sp->prev = g_parsefile->strpush;
9171 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009172 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009173 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009174 sp->prevstring = parsenextc;
9175 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009176#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009177 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009178 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009179 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009180 sp->string = s;
9181 }
Eric Andersen2870d962001-07-02 17:27:21 +00009182#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009183 parsenextc = s;
9184 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009185 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009186}
9187
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009188/*
9189 * To handle the "." command, a stack of input files is used. Pushfile
9190 * adds a new entry to the stack and popfile restores the previous level.
9191 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009192static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009193pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009194{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009195 struct parsefile *pf;
9196
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009197 g_parsefile->nleft = parsenleft;
9198 g_parsefile->lleft = parselleft;
9199 g_parsefile->nextc = parsenextc;
9200 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009201 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009202 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009204 /*pf->strpush = NULL; - ckzalloc did it */
9205 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009206 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009207}
9208
9209static void
9210popfile(void)
9211{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009212 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009213
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215 if (pf->fd >= 0)
9216 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009217 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218 while (pf->strpush)
9219 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009220 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009221 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009222 parsenleft = g_parsefile->nleft;
9223 parselleft = g_parsefile->lleft;
9224 parsenextc = g_parsefile->nextc;
9225 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009226 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009227}
9228
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009229/*
9230 * Return to top level.
9231 */
9232static void
9233popallfiles(void)
9234{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009235 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009236 popfile();
9237}
9238
9239/*
9240 * Close the file(s) that the shell is reading commands from. Called
9241 * after a fork is done.
9242 */
9243static void
9244closescript(void)
9245{
9246 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009247 if (g_parsefile->fd > 0) {
9248 close(g_parsefile->fd);
9249 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009250 }
9251}
9252
9253/*
9254 * Like setinputfile, but takes an open file descriptor. Call this with
9255 * interrupts off.
9256 */
9257static void
9258setinputfd(int fd, int push)
9259{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009260 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009261 if (push) {
9262 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009263 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009264 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009265 g_parsefile->fd = fd;
9266 if (g_parsefile->buf == NULL)
9267 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009268 parselleft = parsenleft = 0;
9269 plinno = 1;
9270}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009271
Eric Andersenc470f442003-07-28 09:56:35 +00009272/*
9273 * Set the input to take input from a file. If push is set, push the
9274 * old input onto the stack first.
9275 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009276static int
9277setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009278{
9279 int fd;
9280 int fd2;
9281
Denis Vlasenkob012b102007-02-19 22:43:01 +00009282 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009283 fd = open(fname, O_RDONLY);
9284 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009285 if (flags & INPUT_NOFILE_OK)
9286 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009287 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009288 }
Eric Andersenc470f442003-07-28 09:56:35 +00009289 if (fd < 10) {
9290 fd2 = copyfd(fd, 10);
9291 close(fd);
9292 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009293 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009294 fd = fd2;
9295 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009296 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009297 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009298 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009299 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009300}
9301
Eric Andersencb57d552001-06-28 07:25:16 +00009302/*
9303 * Like setinputfile, but takes input from a string.
9304 */
Eric Andersenc470f442003-07-28 09:56:35 +00009305static void
9306setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009307{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009309 pushfile();
9310 parsenextc = string;
9311 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009312 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009313 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009314 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009315}
9316
9317
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009318/* ============ mail.c
9319 *
9320 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009321 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009322
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009323#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009324
Eric Andersencb57d552001-06-28 07:25:16 +00009325#define MAXMBOXES 10
9326
Eric Andersenc470f442003-07-28 09:56:35 +00009327/* times of mailboxes */
9328static time_t mailtime[MAXMBOXES];
9329/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009330static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Eric Andersencb57d552001-06-28 07:25:16 +00009332/*
Eric Andersenc470f442003-07-28 09:56:35 +00009333 * Print appropriate message(s) if mail has arrived.
9334 * If mail_var_path_changed is set,
9335 * then the value of MAIL has mail_var_path_changed,
9336 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009337 */
Eric Andersenc470f442003-07-28 09:56:35 +00009338static void
9339chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009340{
Eric Andersencb57d552001-06-28 07:25:16 +00009341 const char *mpath;
9342 char *p;
9343 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009344 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009345 struct stackmark smark;
9346 struct stat statb;
9347
Eric Andersencb57d552001-06-28 07:25:16 +00009348 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009349 mpath = mpathset() ? mpathval() : mailval();
9350 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009351 p = padvance(&mpath, nullstr);
9352 if (p == NULL)
9353 break;
9354 if (*p == '\0')
9355 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009356 for (q = p; *q; q++)
9357 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009358#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009359 if (q[-1] != '/')
9360 abort();
9361#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009362 q[-1] = '\0'; /* delete trailing '/' */
9363 if (stat(p, &statb) < 0) {
9364 *mtp = 0;
9365 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009366 }
Eric Andersenc470f442003-07-28 09:56:35 +00009367 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9368 fprintf(
9369 stderr, snlfmt,
9370 pathopt ? pathopt : "you have mail"
9371 );
9372 }
9373 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009374 }
Eric Andersenc470f442003-07-28 09:56:35 +00009375 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009376 popstackmark(&smark);
9377}
Eric Andersencb57d552001-06-28 07:25:16 +00009378
Eric Andersenc470f442003-07-28 09:56:35 +00009379static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009380changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009381{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009382 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009383}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009384
Denis Vlasenko131ae172007-02-18 13:00:19 +00009385#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009386
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009387
9388/* ============ ??? */
9389
Eric Andersencb57d552001-06-28 07:25:16 +00009390/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009391 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009392 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009393static void
9394setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009395{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009396 char **newparam;
9397 char **ap;
9398 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009399
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009400 for (nparam = 0; argv[nparam]; nparam++)
9401 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009402 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9403 while (*argv) {
9404 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009405 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009406 *ap = NULL;
9407 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009408 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009409 shellparam.nparam = nparam;
9410 shellparam.p = newparam;
9411#if ENABLE_ASH_GETOPTS
9412 shellparam.optind = 1;
9413 shellparam.optoff = -1;
9414#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009415}
9416
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009417/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009418 * Process shell options. The global variable argptr contains a pointer
9419 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009420 *
9421 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9422 * For a non-interactive shell, an error condition encountered
9423 * by a special built-in ... shall cause the shell to write a diagnostic message
9424 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009425 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009426 * ...
9427 * Utility syntax error (option or operand error) Shall exit
9428 * ...
9429 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9430 * we see that bash does not do that (set "finishes" with error code 1 instead,
9431 * and shell continues), and people rely on this behavior!
9432 * Testcase:
9433 * set -o barfoo 2>/dev/null
9434 * echo $?
9435 *
9436 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009437 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009438static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009439plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009440{
9441 int i;
9442
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009443 if (name) {
9444 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009445 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009447 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009448 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009449 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009450 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009451 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009452 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009453 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009454 if (val) {
9455 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9456 } else {
9457 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9458 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009459 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009460 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009461}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009462static void
9463setoption(int flag, int val)
9464{
9465 int i;
9466
9467 for (i = 0; i < NOPTS; i++) {
9468 if (optletters(i) == flag) {
9469 optlist[i] = val;
9470 return;
9471 }
9472 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009473 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009474 /* NOTREACHED */
9475}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009476static int
Eric Andersenc470f442003-07-28 09:56:35 +00009477options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009478{
9479 char *p;
9480 int val;
9481 int c;
9482
9483 if (cmdline)
9484 minusc = NULL;
9485 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009486 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009487 if (c != '-' && c != '+')
9488 break;
9489 argptr++;
9490 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009491 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009492 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009493 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009494 if (!cmdline) {
9495 /* "-" means turn off -x and -v */
9496 if (p[0] == '\0')
9497 xflag = vflag = 0;
9498 /* "--" means reset params */
9499 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009500 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009501 }
Eric Andersenc470f442003-07-28 09:56:35 +00009502 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009503 }
Eric Andersencb57d552001-06-28 07:25:16 +00009504 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009505 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009506 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009507 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009508 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009509 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009510 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009511 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009512 /* it already printed err message */
9513 return 1; /* error */
9514 }
Eric Andersencb57d552001-06-28 07:25:16 +00009515 if (*argptr)
9516 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009517 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9518 isloginsh = 1;
9519 /* bash does not accept +-login, we also won't */
9520 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009521 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009522 isloginsh = 1;
9523 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009524 } else {
9525 setoption(c, val);
9526 }
9527 }
9528 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009529 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009530}
9531
Eric Andersencb57d552001-06-28 07:25:16 +00009532/*
Eric Andersencb57d552001-06-28 07:25:16 +00009533 * The shift builtin command.
9534 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009535static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009536shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009537{
9538 int n;
9539 char **ap1, **ap2;
9540
9541 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009542 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009543 n = number(argv[1]);
9544 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009545 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009546 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009547 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009548 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009549 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009550 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009551 }
9552 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009553 while ((*ap2++ = *ap1++) != NULL)
9554 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009555#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009556 shellparam.optind = 1;
9557 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009558#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009559 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009560 return 0;
9561}
9562
Eric Andersencb57d552001-06-28 07:25:16 +00009563/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009564 * POSIX requires that 'set' (but not export or readonly) output the
9565 * variables in lexicographic order - by the locale's collating order (sigh).
9566 * Maybe we could keep them in an ordered balanced binary tree
9567 * instead of hashed lists.
9568 * For now just roll 'em through qsort for printing...
9569 */
9570static int
9571showvars(const char *sep_prefix, int on, int off)
9572{
9573 const char *sep;
9574 char **ep, **epend;
9575
9576 ep = listvars(on, off, &epend);
9577 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9578
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009579 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009580
9581 for (; ep < epend; ep++) {
9582 const char *p;
9583 const char *q;
9584
9585 p = strchrnul(*ep, '=');
9586 q = nullstr;
9587 if (*p)
9588 q = single_quote(++p);
9589 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9590 }
9591 return 0;
9592}
9593
9594/*
Eric Andersencb57d552001-06-28 07:25:16 +00009595 * The set command builtin.
9596 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009597static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009598setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009599{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009600 int retval;
9601
Denis Vlasenko68404f12008-03-17 09:00:54 +00009602 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009603 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009604 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009605 retval = 1;
9606 if (!options(0)) { /* if no parse error... */
9607 retval = 0;
9608 optschanged();
9609 if (*argptr != NULL) {
9610 setparam(argptr);
9611 }
Eric Andersencb57d552001-06-28 07:25:16 +00009612 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009613 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009614 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009615}
9616
Denis Vlasenko131ae172007-02-18 13:00:19 +00009617#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009618/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009619static void
9620change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009621{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009622 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009623 /* "get", generate */
9624 char buf[16];
9625
9626 rseed = rseed * 1103515245 + 12345;
9627 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9628 /* set without recursion */
9629 setvar(vrandom.text, buf, VNOFUNC);
9630 vrandom.flags &= ~VNOFUNC;
9631 } else {
9632 /* set/reset */
9633 rseed = strtoul(value, (char **)NULL, 10);
9634 }
Eric Andersenef02f822004-03-11 13:34:24 +00009635}
Eric Andersen16767e22004-03-16 05:14:10 +00009636#endif
9637
Denis Vlasenko131ae172007-02-18 13:00:19 +00009638#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009639static int
Eric Andersenc470f442003-07-28 09:56:35 +00009640getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009641{
9642 char *p, *q;
9643 char c = '?';
9644 int done = 0;
9645 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009646 char s[12];
9647 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009648
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009649 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009650 return 1;
9651 optnext = optfirst + *param_optind - 1;
9652
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009653 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009654 p = NULL;
9655 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009656 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009657 if (p == NULL || *p == '\0') {
9658 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009659 p = *optnext;
9660 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009661 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009662 p = NULL;
9663 done = 1;
9664 goto out;
9665 }
9666 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009667 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009668 goto atend;
9669 }
9670
9671 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009672 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009673 if (*q == '\0') {
9674 if (optstr[0] == ':') {
9675 s[0] = c;
9676 s[1] = '\0';
9677 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009678 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009679 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009680 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009681 }
9682 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009683 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009684 }
9685 if (*++q == ':')
9686 q++;
9687 }
9688
9689 if (*++q == ':') {
9690 if (*p == '\0' && (p = *optnext) == NULL) {
9691 if (optstr[0] == ':') {
9692 s[0] = c;
9693 s[1] = '\0';
9694 err |= setvarsafe("OPTARG", s, 0);
9695 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009696 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009697 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009698 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009699 c = '?';
9700 }
Eric Andersenc470f442003-07-28 09:56:35 +00009701 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009702 }
9703
9704 if (p == *optnext)
9705 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009706 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009707 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009708 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009709 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009710 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009711 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009712 *param_optind = optnext - optfirst + 1;
9713 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009714 err |= setvarsafe("OPTIND", s, VNOFUNC);
9715 s[0] = c;
9716 s[1] = '\0';
9717 err |= setvarsafe(optvar, s, 0);
9718 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009719 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009720 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009721 flush_stdout_stderr();
9722 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
9724 return done;
9725}
Eric Andersenc470f442003-07-28 09:56:35 +00009726
9727/*
9728 * The getopts builtin. Shellparam.optnext points to the next argument
9729 * to be processed. Shellparam.optptr points to the next character to
9730 * be processed in the current argument. If shellparam.optnext is NULL,
9731 * then it's the first time getopts has been called.
9732 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009733static int
Eric Andersenc470f442003-07-28 09:56:35 +00009734getoptscmd(int argc, char **argv)
9735{
9736 char **optbase;
9737
9738 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009739 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009740 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009741 optbase = shellparam.p;
9742 if (shellparam.optind > shellparam.nparam + 1) {
9743 shellparam.optind = 1;
9744 shellparam.optoff = -1;
9745 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009746 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009747 optbase = &argv[3];
9748 if (shellparam.optind > argc - 2) {
9749 shellparam.optind = 1;
9750 shellparam.optoff = -1;
9751 }
9752 }
9753
9754 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009755 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009756}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009757#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009758
Eric Andersencb57d552001-06-28 07:25:16 +00009759
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009760/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009761
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009762struct heredoc {
9763 struct heredoc *next; /* next here document in list */
9764 union node *here; /* redirection node */
9765 char *eofmark; /* string indicating end of input */
9766 smallint striptabs; /* if set, strip leading tabs */
9767};
9768
9769static smallint tokpushback; /* last token pushed back */
9770static smallint parsebackquote; /* nonzero if we are inside backquotes */
9771static smallint quoteflag; /* set if (part of) last token was quoted */
9772static token_id_t lasttoken; /* last token read (integer id Txxx) */
9773static struct heredoc *heredoclist; /* list of here documents to read */
9774static char *wordtext; /* text of last word returned by readtoken */
9775static struct nodelist *backquotelist;
9776static union node *redirnode;
9777static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009778/*
9779 * NEOF is returned by parsecmd when it encounters an end of file. It
9780 * must be distinct from NULL, so we use the address of a variable that
9781 * happens to be handy.
9782 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009783#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009784
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009785static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9786static void
9787raise_error_syntax(const char *msg)
9788{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009789 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009790 /* NOTREACHED */
9791}
9792
9793/*
9794 * Called when an unexpected token is read during the parse. The argument
9795 * is the token that is expected, or -1 if more than one type of token can
9796 * occur at this point.
9797 */
9798static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9799static void
9800raise_error_unexpected_syntax(int token)
9801{
9802 char msg[64];
9803 int l;
9804
9805 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9806 if (token >= 0)
9807 sprintf(msg + l, " (expecting %s)", tokname(token));
9808 raise_error_syntax(msg);
9809 /* NOTREACHED */
9810}
Eric Andersencb57d552001-06-28 07:25:16 +00009811
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009812#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009813
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009814/* parsing is heavily cross-recursive, need these forward decls */
9815static union node *andor(void);
9816static union node *pipeline(void);
9817static union node *parse_command(void);
9818static void parseheredoc(void);
9819static char peektoken(void);
9820static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009821
Eric Andersenc470f442003-07-28 09:56:35 +00009822static union node *
9823list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009824{
9825 union node *n1, *n2, *n3;
9826 int tok;
9827
Eric Andersenc470f442003-07-28 09:56:35 +00009828 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9829 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009830 return NULL;
9831 n1 = NULL;
9832 for (;;) {
9833 n2 = andor();
9834 tok = readtoken();
9835 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009836 if (n2->type == NPIPE) {
9837 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009838 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009839 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009840 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009841 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009842 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009843 n2 = n3;
9844 }
9845 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009846 }
9847 }
9848 if (n1 == NULL) {
9849 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009850 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009851 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009852 n3->type = NSEMI;
9853 n3->nbinary.ch1 = n1;
9854 n3->nbinary.ch2 = n2;
9855 n1 = n3;
9856 }
9857 switch (tok) {
9858 case TBACKGND:
9859 case TSEMI:
9860 tok = readtoken();
9861 /* fall through */
9862 case TNL:
9863 if (tok == TNL) {
9864 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009865 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009866 return n1;
9867 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009868 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009869 }
Eric Andersenc470f442003-07-28 09:56:35 +00009870 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009871 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009872 return n1;
9873 break;
9874 case TEOF:
9875 if (heredoclist)
9876 parseheredoc();
9877 else
Eric Andersenc470f442003-07-28 09:56:35 +00009878 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009879 return n1;
9880 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009881 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009882 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009883 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009884 return n1;
9885 }
9886 }
9887}
9888
Eric Andersenc470f442003-07-28 09:56:35 +00009889static union node *
9890andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009891{
Eric Andersencb57d552001-06-28 07:25:16 +00009892 union node *n1, *n2, *n3;
9893 int t;
9894
Eric Andersencb57d552001-06-28 07:25:16 +00009895 n1 = pipeline();
9896 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897 t = readtoken();
9898 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009899 t = NAND;
9900 } else if (t == TOR) {
9901 t = NOR;
9902 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009903 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 return n1;
9905 }
Eric Andersenc470f442003-07-28 09:56:35 +00009906 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009908 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009909 n3->type = t;
9910 n3->nbinary.ch1 = n1;
9911 n3->nbinary.ch2 = n2;
9912 n1 = n3;
9913 }
9914}
9915
Eric Andersenc470f442003-07-28 09:56:35 +00009916static union node *
9917pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009918{
Eric Andersencb57d552001-06-28 07:25:16 +00009919 union node *n1, *n2, *pipenode;
9920 struct nodelist *lp, *prev;
9921 int negate;
9922
9923 negate = 0;
9924 TRACE(("pipeline: entered\n"));
9925 if (readtoken() == TNOT) {
9926 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009927 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009928 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009929 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009930 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009931 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009932 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009933 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009934 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009935 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009936 pipenode->npipe.cmdlist = lp;
9937 lp->n = n1;
9938 do {
9939 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009940 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009941 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009942 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009943 prev->next = lp;
9944 } while (readtoken() == TPIPE);
9945 lp->next = NULL;
9946 n1 = pipenode;
9947 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009948 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009949 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009950 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009951 n2->type = NNOT;
9952 n2->nnot.com = n1;
9953 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009954 }
9955 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009956}
9957
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009958static union node *
9959makename(void)
9960{
9961 union node *n;
9962
Denis Vlasenko597906c2008-02-20 16:38:54 +00009963 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009964 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009965 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009966 n->narg.text = wordtext;
9967 n->narg.backquote = backquotelist;
9968 return n;
9969}
9970
9971static void
9972fixredir(union node *n, const char *text, int err)
9973{
9974 TRACE(("Fix redir %s %d\n", text, err));
9975 if (!err)
9976 n->ndup.vname = NULL;
9977
9978 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009979 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009980 else if (LONE_DASH(text))
9981 n->ndup.dupfd = -1;
9982 else {
9983 if (err)
9984 raise_error_syntax("Bad fd number");
9985 n->ndup.vname = makename();
9986 }
9987}
9988
9989/*
9990 * Returns true if the text contains nothing to expand (no dollar signs
9991 * or backquotes).
9992 */
9993static int
9994noexpand(char *text)
9995{
9996 char *p;
9997 char c;
9998
9999 p = text;
10000 while ((c = *p++) != '\0') {
10001 if (c == CTLQUOTEMARK)
10002 continue;
10003 if (c == CTLESC)
10004 p++;
10005 else if (SIT(c, BASESYNTAX) == CCTL)
10006 return 0;
10007 }
10008 return 1;
10009}
10010
10011static void
10012parsefname(void)
10013{
10014 union node *n = redirnode;
10015
10016 if (readtoken() != TWORD)
10017 raise_error_unexpected_syntax(-1);
10018 if (n->type == NHERE) {
10019 struct heredoc *here = heredoc;
10020 struct heredoc *p;
10021 int i;
10022
10023 if (quoteflag == 0)
10024 n->type = NXHERE;
10025 TRACE(("Here document %d\n", n->type));
10026 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10027 raise_error_syntax("Illegal eof marker for << redirection");
10028 rmescapes(wordtext);
10029 here->eofmark = wordtext;
10030 here->next = NULL;
10031 if (heredoclist == NULL)
10032 heredoclist = here;
10033 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010034 for (p = heredoclist; p->next; p = p->next)
10035 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010036 p->next = here;
10037 }
10038 } else if (n->type == NTOFD || n->type == NFROMFD) {
10039 fixredir(n, wordtext, 0);
10040 } else {
10041 n->nfile.fname = makename();
10042 }
10043}
Eric Andersencb57d552001-06-28 07:25:16 +000010044
Eric Andersenc470f442003-07-28 09:56:35 +000010045static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010046simplecmd(void)
10047{
10048 union node *args, **app;
10049 union node *n = NULL;
10050 union node *vars, **vpp;
10051 union node **rpp, *redir;
10052 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010053#if ENABLE_ASH_BASH_COMPAT
10054 smallint double_brackets_flag = 0;
10055#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010056
10057 args = NULL;
10058 app = &args;
10059 vars = NULL;
10060 vpp = &vars;
10061 redir = NULL;
10062 rpp = &redir;
10063
10064 savecheckkwd = CHKALIAS;
10065 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010066 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010067 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010068 t = readtoken();
10069 switch (t) {
10070#if ENABLE_ASH_BASH_COMPAT
10071 case TAND: /* "&&" */
10072 case TOR: /* "||" */
10073 if (!double_brackets_flag) {
10074 tokpushback = 1;
10075 goto out;
10076 }
10077 wordtext = (char *) (t == TAND ? "-a" : "-o");
10078#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010079 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010080 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010081 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010082 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010083 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010084#if ENABLE_ASH_BASH_COMPAT
10085 if (strcmp("[[", wordtext) == 0)
10086 double_brackets_flag = 1;
10087 else if (strcmp("]]", wordtext) == 0)
10088 double_brackets_flag = 0;
10089#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010090 n->narg.backquote = backquotelist;
10091 if (savecheckkwd && isassignment(wordtext)) {
10092 *vpp = n;
10093 vpp = &n->narg.next;
10094 } else {
10095 *app = n;
10096 app = &n->narg.next;
10097 savecheckkwd = 0;
10098 }
10099 break;
10100 case TREDIR:
10101 *rpp = n = redirnode;
10102 rpp = &n->nfile.next;
10103 parsefname(); /* read name of redirection file */
10104 break;
10105 case TLP:
10106 if (args && app == &args->narg.next
10107 && !vars && !redir
10108 ) {
10109 struct builtincmd *bcmd;
10110 const char *name;
10111
10112 /* We have a function */
10113 if (readtoken() != TRP)
10114 raise_error_unexpected_syntax(TRP);
10115 name = n->narg.text;
10116 if (!goodname(name)
10117 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10118 ) {
10119 raise_error_syntax("Bad function name");
10120 }
10121 n->type = NDEFUN;
10122 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10123 n->narg.next = parse_command();
10124 return n;
10125 }
10126 /* fall through */
10127 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010128 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010129 goto out;
10130 }
10131 }
10132 out:
10133 *app = NULL;
10134 *vpp = NULL;
10135 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010136 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010137 n->type = NCMD;
10138 n->ncmd.args = args;
10139 n->ncmd.assign = vars;
10140 n->ncmd.redirect = redir;
10141 return n;
10142}
10143
10144static union node *
10145parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010146{
Eric Andersencb57d552001-06-28 07:25:16 +000010147 union node *n1, *n2;
10148 union node *ap, **app;
10149 union node *cp, **cpp;
10150 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010151 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010152 int t;
10153
10154 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010155 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010156
Eric Andersencb57d552001-06-28 07:25:16 +000010157 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010158 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010159 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010160 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010161 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010162 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010163 n1->type = NIF;
10164 n1->nif.test = list(0);
10165 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010166 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010167 n1->nif.ifpart = list(0);
10168 n2 = n1;
10169 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010170 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010171 n2 = n2->nif.elsepart;
10172 n2->type = NIF;
10173 n2->nif.test = list(0);
10174 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010175 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010176 n2->nif.ifpart = list(0);
10177 }
10178 if (lasttoken == TELSE)
10179 n2->nif.elsepart = list(0);
10180 else {
10181 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010182 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010183 }
Eric Andersenc470f442003-07-28 09:56:35 +000010184 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010185 break;
10186 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010187 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010188 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010189 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010190 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010191 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010192 got = readtoken();
10193 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010194 TRACE(("expecting DO got %s %s\n", tokname(got),
10195 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010196 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010197 }
10198 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010199 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010200 break;
10201 }
10202 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010203 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010204 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010205 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010206 n1->type = NFOR;
10207 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010208 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010209 if (readtoken() == TIN) {
10210 app = &ap;
10211 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010212 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010213 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010214 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010215 n2->narg.text = wordtext;
10216 n2->narg.backquote = backquotelist;
10217 *app = n2;
10218 app = &n2->narg.next;
10219 }
10220 *app = NULL;
10221 n1->nfor.args = ap;
10222 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010223 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010224 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010225 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010226 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010227 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010228 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010229 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010230 n1->nfor.args = n2;
10231 /*
10232 * Newline or semicolon here is optional (but note
10233 * that the original Bourne shell only allowed NL).
10234 */
10235 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010236 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010237 }
Eric Andersenc470f442003-07-28 09:56:35 +000010238 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010239 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010240 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010241 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010242 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010243 break;
10244 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010245 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010246 n1->type = NCASE;
10247 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010248 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010249 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010250 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010251 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010252 n2->narg.text = wordtext;
10253 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010254 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010255 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010256 } while (readtoken() == TNL);
10257 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010258 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010259 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010260 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010261 checkkwd = CHKNL | CHKKWD;
10262 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010263 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010264 if (lasttoken == TLP)
10265 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010266 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010267 cp->type = NCLIST;
10268 app = &cp->nclist.pattern;
10269 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010270 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010271 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010272 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010273 ap->narg.text = wordtext;
10274 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010275 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010276 break;
10277 app = &ap->narg.next;
10278 readtoken();
10279 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010280 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010282 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010283 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010284
Eric Andersenc470f442003-07-28 09:56:35 +000010285 cpp = &cp->nclist.next;
10286
10287 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010288 t = readtoken();
10289 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010290 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010291 raise_error_unexpected_syntax(TENDCASE);
10292 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010293 }
Eric Andersenc470f442003-07-28 09:56:35 +000010294 }
Eric Andersencb57d552001-06-28 07:25:16 +000010295 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010296 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010297 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010298 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010299 n1->type = NSUBSHELL;
10300 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010301 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010302 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010303 break;
10304 case TBEGIN:
10305 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010306 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010307 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010308 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010309 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010310 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010311 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010312 }
10313
Eric Andersenc470f442003-07-28 09:56:35 +000010314 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010315 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010316
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010317 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010318 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010319 checkkwd = CHKKWD | CHKALIAS;
10320 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010321 while (readtoken() == TREDIR) {
10322 *rpp = n2 = redirnode;
10323 rpp = &n2->nfile.next;
10324 parsefname();
10325 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010326 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010327 *rpp = NULL;
10328 if (redir) {
10329 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010330 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010331 n2->type = NREDIR;
10332 n2->nredir.n = n1;
10333 n1 = n2;
10334 }
10335 n1->nredir.redirect = redir;
10336 }
Eric Andersencb57d552001-06-28 07:25:16 +000010337 return n1;
10338}
10339
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010340#if ENABLE_ASH_BASH_COMPAT
10341static int decode_dollar_squote(void)
10342{
10343 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10344 int c, cnt;
10345 char *p;
10346 char buf[4];
10347
10348 c = pgetc();
10349 p = strchr(C_escapes, c);
10350 if (p) {
10351 buf[0] = c;
10352 p = buf;
10353 cnt = 3;
10354 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10355 do {
10356 c = pgetc();
10357 *++p = c;
10358 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10359 pungetc();
10360 } else if (c == 'x') { /* \xHH */
10361 do {
10362 c = pgetc();
10363 *++p = c;
10364 } while (isxdigit(c) && --cnt);
10365 pungetc();
10366 if (cnt == 3) { /* \x but next char is "bad" */
10367 c = 'x';
10368 goto unrecognized;
10369 }
10370 } else { /* simple seq like \\ or \t */
10371 p++;
10372 }
10373 *p = '\0';
10374 p = buf;
10375 c = bb_process_escape_sequence((void*)&p);
10376 } else { /* unrecognized "\z": print both chars unless ' or " */
10377 if (c != '\'' && c != '"') {
10378 unrecognized:
10379 c |= 0x100; /* "please encode \, then me" */
10380 }
10381 }
10382 return c;
10383}
10384#endif
10385
Eric Andersencb57d552001-06-28 07:25:16 +000010386/*
10387 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10388 * is not NULL, read a here document. In the latter case, eofmark is the
10389 * word which marks the end of the document and striptabs is true if
10390 * leading tabs should be stripped from the document. The argument firstc
10391 * is the first character of the input token or document.
10392 *
10393 * Because C does not have internal subroutines, I have simulated them
10394 * using goto's to implement the subroutine linkage. The following macros
10395 * will run code that appears at the end of readtoken1.
10396 */
Eric Andersen2870d962001-07-02 17:27:21 +000010397#define CHECKEND() {goto checkend; checkend_return:;}
10398#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10399#define PARSESUB() {goto parsesub; parsesub_return:;}
10400#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10401#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10402#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010403static int
Eric Andersenc470f442003-07-28 09:56:35 +000010404readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010405{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010406 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010407 int c = firstc;
10408 char *out;
10409 int len;
10410 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010411 struct nodelist *bqlist;
10412 smallint quotef;
10413 smallint dblquote;
10414 smallint oldstyle;
10415 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010416#if ENABLE_ASH_EXPAND_PRMT
10417 smallint pssyntax; /* we are expanding a prompt string */
10418#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010419 int varnest; /* levels of variables expansion */
10420 int arinest; /* levels of arithmetic expansion */
10421 int parenlevel; /* levels of parens in arithmetic */
10422 int dqvarnest; /* levels of variables expansion within double quotes */
10423
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010424 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10425
Eric Andersencb57d552001-06-28 07:25:16 +000010426#if __GNUC__
10427 /* Avoid longjmp clobbering */
10428 (void) &out;
10429 (void) &quotef;
10430 (void) &dblquote;
10431 (void) &varnest;
10432 (void) &arinest;
10433 (void) &parenlevel;
10434 (void) &dqvarnest;
10435 (void) &oldstyle;
10436 (void) &prevsyntax;
10437 (void) &syntax;
10438#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010439 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010440 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010441 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010442 oldstyle = 0;
10443 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010444#if ENABLE_ASH_EXPAND_PRMT
10445 pssyntax = (syntax == PSSYNTAX);
10446 if (pssyntax)
10447 syntax = DQSYNTAX;
10448#endif
10449 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010450 varnest = 0;
10451 arinest = 0;
10452 parenlevel = 0;
10453 dqvarnest = 0;
10454
10455 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010456 loop: { /* for each line, until end of word */
10457 CHECKEND(); /* set c to PEOF if at end of here document */
10458 for (;;) { /* until end of line or end of word */
10459 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010460 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010461 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010462 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010463 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010464 USTPUTC(c, out);
10465 plinno++;
10466 if (doprompt)
10467 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010468 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010469 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010470 case CWORD:
10471 USTPUTC(c, out);
10472 break;
10473 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010474 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010475 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010476#if ENABLE_ASH_BASH_COMPAT
10477 if (c == '\\' && bash_dollar_squote) {
10478 c = decode_dollar_squote();
10479 if (c & 0x100) {
10480 USTPUTC('\\', out);
10481 c = (unsigned char)c;
10482 }
10483 }
10484#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010485 USTPUTC(c, out);
10486 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010487 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010488 c = pgetc2();
10489 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010490 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010491 USTPUTC('\\', out);
10492 pungetc();
10493 } else if (c == '\n') {
10494 if (doprompt)
10495 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010496 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010497#if ENABLE_ASH_EXPAND_PRMT
10498 if (c == '$' && pssyntax) {
10499 USTPUTC(CTLESC, out);
10500 USTPUTC('\\', out);
10501 }
10502#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010503 if (dblquote && c != '\\'
10504 && c != '`' && c != '$'
10505 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010506 ) {
10507 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010508 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010509 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010510 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010511 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010512 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010513 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010514 }
10515 break;
10516 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010517 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010518 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010519 if (eofmark == NULL) {
10520 USTPUTC(CTLQUOTEMARK, out);
10521 }
Eric Andersencb57d552001-06-28 07:25:16 +000010522 break;
10523 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010524 syntax = DQSYNTAX;
10525 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010526 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010527 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010528 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010529 if (eofmark != NULL && arinest == 0
10530 && varnest == 0
10531 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010532 USTPUTC(c, out);
10533 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010534 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010535 syntax = BASESYNTAX;
10536 dblquote = 0;
10537 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010538 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010539 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010540 }
10541 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010542 case CVAR: /* '$' */
10543 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010544 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010545 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010546 if (varnest > 0) {
10547 varnest--;
10548 if (dqvarnest > 0) {
10549 dqvarnest--;
10550 }
10551 USTPUTC(CTLENDVAR, out);
10552 } else {
10553 USTPUTC(c, out);
10554 }
10555 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010556#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010557 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010558 parenlevel++;
10559 USTPUTC(c, out);
10560 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010561 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010562 if (parenlevel > 0) {
10563 USTPUTC(c, out);
10564 --parenlevel;
10565 } else {
10566 if (pgetc() == ')') {
10567 if (--arinest == 0) {
10568 USTPUTC(CTLENDARI, out);
10569 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010570 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010571 } else
10572 USTPUTC(')', out);
10573 } else {
10574 /*
10575 * unbalanced parens
10576 * (don't 2nd guess - no error)
10577 */
10578 pungetc();
10579 USTPUTC(')', out);
10580 }
10581 }
10582 break;
10583#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010584 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010585 PARSEBACKQOLD();
10586 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010587 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010588 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010589 case CIGN:
10590 break;
10591 default:
10592 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010593 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010594#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010595 if (c != PEOA)
10596#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010597 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010598
Eric Andersencb57d552001-06-28 07:25:16 +000010599 }
10600 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010601 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010602 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010603 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010604#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010605 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010606 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010607#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010608 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010609 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010610 if (varnest != 0) {
10611 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010612 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010613 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010614 }
10615 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010616 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010617 out = stackblock();
10618 if (eofmark == NULL) {
10619 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010620 && quotef == 0
10621 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010622 && (*out == '\0' || isdigit(*out))
10623 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010624 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010625 lasttoken = TREDIR;
10626 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010627 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010628 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010629 }
10630 quoteflag = quotef;
10631 backquotelist = bqlist;
10632 grabstackblock(len);
10633 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010634 lasttoken = TWORD;
10635 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010636/* end of readtoken routine */
10637
Eric Andersencb57d552001-06-28 07:25:16 +000010638/*
10639 * Check to see whether we are at the end of the here document. When this
10640 * is called, c is set to the first character of the next input line. If
10641 * we are at the end of the here document, this routine sets the c to PEOF.
10642 */
Eric Andersenc470f442003-07-28 09:56:35 +000010643checkend: {
10644 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010645#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010646 if (c == PEOA) {
10647 c = pgetc2();
10648 }
10649#endif
10650 if (striptabs) {
10651 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010652 c = pgetc2();
10653 }
Eric Andersenc470f442003-07-28 09:56:35 +000010654 }
10655 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010656 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010657 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010658
Eric Andersenc470f442003-07-28 09:56:35 +000010659 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010660 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10661 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010662 if (*p == '\n' && *q == '\0') {
10663 c = PEOF;
10664 plinno++;
10665 needprompt = doprompt;
10666 } else {
10667 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010668 }
10669 }
10670 }
10671 }
Eric Andersenc470f442003-07-28 09:56:35 +000010672 goto checkend_return;
10673}
Eric Andersencb57d552001-06-28 07:25:16 +000010674
Eric Andersencb57d552001-06-28 07:25:16 +000010675/*
10676 * Parse a redirection operator. The variable "out" points to a string
10677 * specifying the fd to be redirected. The variable "c" contains the
10678 * first character of the redirection operator.
10679 */
Eric Andersenc470f442003-07-28 09:56:35 +000010680parseredir: {
10681 char fd = *out;
10682 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010683
Denis Vlasenko597906c2008-02-20 16:38:54 +000010684 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010685 if (c == '>') {
10686 np->nfile.fd = 1;
10687 c = pgetc();
10688 if (c == '>')
10689 np->type = NAPPEND;
10690 else if (c == '|')
10691 np->type = NCLOBBER;
10692 else if (c == '&')
10693 np->type = NTOFD;
10694 else {
10695 np->type = NTO;
10696 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010697 }
Eric Andersenc470f442003-07-28 09:56:35 +000010698 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010699 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010700 c = pgetc();
10701 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010702 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010703 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010704 np = stzalloc(sizeof(struct nhere));
10705 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010706 }
10707 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010708 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010709 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010710 c = pgetc();
10711 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010712 heredoc->striptabs = 1;
10713 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010714 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010715 pungetc();
10716 }
10717 break;
10718
10719 case '&':
10720 np->type = NFROMFD;
10721 break;
10722
10723 case '>':
10724 np->type = NFROMTO;
10725 break;
10726
10727 default:
10728 np->type = NFROM;
10729 pungetc();
10730 break;
10731 }
Eric Andersencb57d552001-06-28 07:25:16 +000010732 }
Eric Andersenc470f442003-07-28 09:56:35 +000010733 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010734 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010735 redirnode = np;
10736 goto parseredir_return;
10737}
Eric Andersencb57d552001-06-28 07:25:16 +000010738
Eric Andersencb57d552001-06-28 07:25:16 +000010739/*
10740 * Parse a substitution. At this point, we have read the dollar sign
10741 * and nothing else.
10742 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010743
10744/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10745 * (assuming ascii char codes, as the original implementation did) */
10746#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010747 (((unsigned)(c) - 33 < 32) \
10748 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010749parsesub: {
10750 int subtype;
10751 int typeloc;
10752 int flags;
10753 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010754 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010755
Eric Andersenc470f442003-07-28 09:56:35 +000010756 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010757 if (c <= PEOA_OR_PEOF
10758 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010759 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010760#if ENABLE_ASH_BASH_COMPAT
10761 if (c == '\'')
10762 bash_dollar_squote = 1;
10763 else
10764#endif
10765 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010766 pungetc();
10767 } else if (c == '(') { /* $(command) or $((arith)) */
10768 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010769#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010770 PARSEARITH();
10771#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010772 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010773#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010774 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010775 pungetc();
10776 PARSEBACKQNEW();
10777 }
10778 } else {
10779 USTPUTC(CTLVAR, out);
10780 typeloc = out - (char *)stackblock();
10781 USTPUTC(VSNORMAL, out);
10782 subtype = VSNORMAL;
10783 if (c == '{') {
10784 c = pgetc();
10785 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010786 c = pgetc();
10787 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010788 c = '#';
10789 else
10790 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010791 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010792 subtype = 0;
10793 }
10794 if (c > PEOA_OR_PEOF && is_name(c)) {
10795 do {
10796 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010797 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010798 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010799 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010800 do {
10801 STPUTC(c, out);
10802 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010803 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010804 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010805 USTPUTC(c, out);
10806 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010807 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010808 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010809
Eric Andersenc470f442003-07-28 09:56:35 +000010810 STPUTC('=', out);
10811 flags = 0;
10812 if (subtype == 0) {
10813 switch (c) {
10814 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010815 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010816#if ENABLE_ASH_BASH_COMPAT
10817 if (c == ':' || c == '$' || isdigit(c)) {
10818 pungetc();
10819 subtype = VSSUBSTR;
10820 break;
10821 }
10822#endif
10823 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010824 /*FALLTHROUGH*/
10825 default:
10826 p = strchr(types, c);
10827 if (p == NULL)
10828 goto badsub;
10829 subtype = p - types + VSNORMAL;
10830 break;
10831 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010832 case '#': {
10833 int cc = c;
10834 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10835 c = pgetc();
10836 if (c == cc)
10837 subtype++;
10838 else
10839 pungetc();
10840 break;
10841 }
10842#if ENABLE_ASH_BASH_COMPAT
10843 case '/':
10844 subtype = VSREPLACE;
10845 c = pgetc();
10846 if (c == '/')
10847 subtype++; /* VSREPLACEALL */
10848 else
10849 pungetc();
10850 break;
10851#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010852 }
Eric Andersenc470f442003-07-28 09:56:35 +000010853 } else {
10854 pungetc();
10855 }
10856 if (dblquote || arinest)
10857 flags |= VSQUOTE;
10858 *((char *)stackblock() + typeloc) = subtype | flags;
10859 if (subtype != VSNORMAL) {
10860 varnest++;
10861 if (dblquote || arinest) {
10862 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010863 }
10864 }
10865 }
Eric Andersenc470f442003-07-28 09:56:35 +000010866 goto parsesub_return;
10867}
Eric Andersencb57d552001-06-28 07:25:16 +000010868
Eric Andersencb57d552001-06-28 07:25:16 +000010869/*
10870 * Called to parse command substitutions. Newstyle is set if the command
10871 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10872 * list of commands (passed by reference), and savelen is the number of
10873 * characters on the top of the stack which must be preserved.
10874 */
Eric Andersenc470f442003-07-28 09:56:35 +000010875parsebackq: {
10876 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010877 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010878 union node *n;
10879 char *volatile str;
10880 struct jmploc jmploc;
10881 struct jmploc *volatile savehandler;
10882 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010883 smallint saveprompt = 0;
10884
Eric Andersencb57d552001-06-28 07:25:16 +000010885#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010886 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010887#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010888 savepbq = parsebackquote;
10889 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010890 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010891 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010892 exception_handler = savehandler;
10893 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010894 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010895 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010896 str = NULL;
10897 savelen = out - (char *)stackblock();
10898 if (savelen > 0) {
10899 str = ckmalloc(savelen);
10900 memcpy(str, stackblock(), savelen);
10901 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010902 savehandler = exception_handler;
10903 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010904 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010905 if (oldstyle) {
10906 /* We must read until the closing backquote, giving special
10907 treatment to some slashes, and then push the string and
10908 reread it as input, interpreting it normally. */
10909 char *pout;
10910 int pc;
10911 size_t psavelen;
10912 char *pstr;
10913
10914
10915 STARTSTACKSTR(pout);
10916 for (;;) {
10917 if (needprompt) {
10918 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010919 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010920 pc = pgetc();
10921 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010922 case '`':
10923 goto done;
10924
10925 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010926 pc = pgetc();
10927 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010928 plinno++;
10929 if (doprompt)
10930 setprompt(2);
10931 /*
10932 * If eating a newline, avoid putting
10933 * the newline into the new character
10934 * stream (via the STPUTC after the
10935 * switch).
10936 */
10937 continue;
10938 }
10939 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010940 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010941 STPUTC('\\', pout);
10942 if (pc > PEOA_OR_PEOF) {
10943 break;
10944 }
10945 /* fall through */
10946
10947 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010948#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010949 case PEOA:
10950#endif
10951 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010952 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010953
10954 case '\n':
10955 plinno++;
10956 needprompt = doprompt;
10957 break;
10958
10959 default:
10960 break;
10961 }
10962 STPUTC(pc, pout);
10963 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010964 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010965 STPUTC('\0', pout);
10966 psavelen = pout - (char *)stackblock();
10967 if (psavelen > 0) {
10968 pstr = grabstackstr(pout);
10969 setinputstring(pstr);
10970 }
10971 }
10972 nlpp = &bqlist;
10973 while (*nlpp)
10974 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010975 *nlpp = stzalloc(sizeof(**nlpp));
10976 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010977 parsebackquote = oldstyle;
10978
10979 if (oldstyle) {
10980 saveprompt = doprompt;
10981 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010982 }
10983
Eric Andersenc470f442003-07-28 09:56:35 +000010984 n = list(2);
10985
10986 if (oldstyle)
10987 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010988 else if (readtoken() != TRP)
10989 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010990
10991 (*nlpp)->n = n;
10992 if (oldstyle) {
10993 /*
10994 * Start reading from old file again, ignoring any pushed back
10995 * tokens left from the backquote parsing
10996 */
10997 popfile();
10998 tokpushback = 0;
10999 }
11000 while (stackblocksize() <= savelen)
11001 growstackblock();
11002 STARTSTACKSTR(out);
11003 if (str) {
11004 memcpy(out, str, savelen);
11005 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011006 INT_OFF;
11007 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011008 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011009 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011010 }
11011 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011012 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011013 if (arinest || dblquote)
11014 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11015 else
11016 USTPUTC(CTLBACKQ, out);
11017 if (oldstyle)
11018 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011019 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011020}
11021
Denis Vlasenko131ae172007-02-18 13:00:19 +000011022#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011023/*
11024 * Parse an arithmetic expansion (indicate start of one and set state)
11025 */
Eric Andersenc470f442003-07-28 09:56:35 +000011026parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011027 if (++arinest == 1) {
11028 prevsyntax = syntax;
11029 syntax = ARISYNTAX;
11030 USTPUTC(CTLARI, out);
11031 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011032 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011033 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011034 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011035 } else {
11036 /*
11037 * we collapse embedded arithmetic expansion to
11038 * parenthesis, which should be equivalent
11039 */
11040 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011041 }
Eric Andersenc470f442003-07-28 09:56:35 +000011042 goto parsearith_return;
11043}
11044#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011045
Eric Andersenc470f442003-07-28 09:56:35 +000011046} /* end of readtoken */
11047
Eric Andersencb57d552001-06-28 07:25:16 +000011048/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011049 * Read the next input token.
11050 * If the token is a word, we set backquotelist to the list of cmds in
11051 * backquotes. We set quoteflag to true if any part of the word was
11052 * quoted.
11053 * If the token is TREDIR, then we set redirnode to a structure containing
11054 * the redirection.
11055 * In all cases, the variable startlinno is set to the number of the line
11056 * on which the token starts.
11057 *
11058 * [Change comment: here documents and internal procedures]
11059 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11060 * word parsing code into a separate routine. In this case, readtoken
11061 * doesn't need to have any internal procedures, but parseword does.
11062 * We could also make parseoperator in essence the main routine, and
11063 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011064 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011065#define NEW_xxreadtoken
11066#ifdef NEW_xxreadtoken
11067/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011068static const char xxreadtoken_chars[7] ALIGN1 = {
11069 '\n', '(', ')', '&', '|', ';', 0
11070};
Eric Andersencb57d552001-06-28 07:25:16 +000011071
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011072static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011073 TNL, TLP, TRP, /* only single occurrence allowed */
11074 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11075 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011076 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011077};
11078
11079#define xxreadtoken_doubles \
11080 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11081#define xxreadtoken_singles \
11082 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11083
11084static int
11085xxreadtoken(void)
11086{
11087 int c;
11088
11089 if (tokpushback) {
11090 tokpushback = 0;
11091 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011092 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011093 if (needprompt) {
11094 setprompt(2);
11095 }
11096 startlinno = plinno;
11097 for (;;) { /* until token or start of word found */
11098 c = pgetc_macro();
11099
11100 if ((c != ' ') && (c != '\t')
11101#if ENABLE_ASH_ALIAS
11102 && (c != PEOA)
11103#endif
11104 ) {
11105 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011106 while ((c = pgetc()) != '\n' && c != PEOF)
11107 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011108 pungetc();
11109 } else if (c == '\\') {
11110 if (pgetc() != '\n') {
11111 pungetc();
11112 goto READTOKEN1;
11113 }
11114 startlinno = ++plinno;
11115 if (doprompt)
11116 setprompt(2);
11117 } else {
11118 const char *p
11119 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11120
11121 if (c != PEOF) {
11122 if (c == '\n') {
11123 plinno++;
11124 needprompt = doprompt;
11125 }
11126
11127 p = strchr(xxreadtoken_chars, c);
11128 if (p == NULL) {
11129 READTOKEN1:
11130 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11131 }
11132
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011133 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011134 if (pgetc() == *p) { /* double occurrence? */
11135 p += xxreadtoken_doubles + 1;
11136 } else {
11137 pungetc();
11138 }
11139 }
11140 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011141 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11142 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011143 }
11144 }
11145 } /* for */
11146}
11147#else
11148#define RETURN(token) return lasttoken = token
11149static int
11150xxreadtoken(void)
11151{
11152 int c;
11153
11154 if (tokpushback) {
11155 tokpushback = 0;
11156 return lasttoken;
11157 }
11158 if (needprompt) {
11159 setprompt(2);
11160 }
11161 startlinno = plinno;
11162 for (;;) { /* until token or start of word found */
11163 c = pgetc_macro();
11164 switch (c) {
11165 case ' ': case '\t':
11166#if ENABLE_ASH_ALIAS
11167 case PEOA:
11168#endif
11169 continue;
11170 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011171 while ((c = pgetc()) != '\n' && c != PEOF)
11172 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011173 pungetc();
11174 continue;
11175 case '\\':
11176 if (pgetc() == '\n') {
11177 startlinno = ++plinno;
11178 if (doprompt)
11179 setprompt(2);
11180 continue;
11181 }
11182 pungetc();
11183 goto breakloop;
11184 case '\n':
11185 plinno++;
11186 needprompt = doprompt;
11187 RETURN(TNL);
11188 case PEOF:
11189 RETURN(TEOF);
11190 case '&':
11191 if (pgetc() == '&')
11192 RETURN(TAND);
11193 pungetc();
11194 RETURN(TBACKGND);
11195 case '|':
11196 if (pgetc() == '|')
11197 RETURN(TOR);
11198 pungetc();
11199 RETURN(TPIPE);
11200 case ';':
11201 if (pgetc() == ';')
11202 RETURN(TENDCASE);
11203 pungetc();
11204 RETURN(TSEMI);
11205 case '(':
11206 RETURN(TLP);
11207 case ')':
11208 RETURN(TRP);
11209 default:
11210 goto breakloop;
11211 }
11212 }
11213 breakloop:
11214 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11215#undef RETURN
11216}
11217#endif /* NEW_xxreadtoken */
11218
11219static int
11220readtoken(void)
11221{
11222 int t;
11223#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011224 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011225#endif
11226
11227#if ENABLE_ASH_ALIAS
11228 top:
11229#endif
11230
11231 t = xxreadtoken();
11232
11233 /*
11234 * eat newlines
11235 */
11236 if (checkkwd & CHKNL) {
11237 while (t == TNL) {
11238 parseheredoc();
11239 t = xxreadtoken();
11240 }
11241 }
11242
11243 if (t != TWORD || quoteflag) {
11244 goto out;
11245 }
11246
11247 /*
11248 * check for keywords
11249 */
11250 if (checkkwd & CHKKWD) {
11251 const char *const *pp;
11252
11253 pp = findkwd(wordtext);
11254 if (pp) {
11255 lasttoken = t = pp - tokname_array;
11256 TRACE(("keyword %s recognized\n", tokname(t)));
11257 goto out;
11258 }
11259 }
11260
11261 if (checkkwd & CHKALIAS) {
11262#if ENABLE_ASH_ALIAS
11263 struct alias *ap;
11264 ap = lookupalias(wordtext, 1);
11265 if (ap != NULL) {
11266 if (*ap->val) {
11267 pushstring(ap->val, ap);
11268 }
11269 goto top;
11270 }
11271#endif
11272 }
11273 out:
11274 checkkwd = 0;
11275#if DEBUG
11276 if (!alreadyseen)
11277 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11278 else
11279 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11280#endif
11281 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011282}
11283
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011284static char
11285peektoken(void)
11286{
11287 int t;
11288
11289 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011290 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011291 return tokname_array[t][0];
11292}
Eric Andersencb57d552001-06-28 07:25:16 +000011293
11294/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011295 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11296 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011297 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011298static union node *
11299parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011300{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011301 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011302
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011303 tokpushback = 0;
11304 doprompt = interact;
11305 if (doprompt)
11306 setprompt(doprompt);
11307 needprompt = 0;
11308 t = readtoken();
11309 if (t == TEOF)
11310 return NEOF;
11311 if (t == TNL)
11312 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011313 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011314 return list(1);
11315}
11316
11317/*
11318 * Input any here documents.
11319 */
11320static void
11321parseheredoc(void)
11322{
11323 struct heredoc *here;
11324 union node *n;
11325
11326 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011327 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011328
11329 while (here) {
11330 if (needprompt) {
11331 setprompt(2);
11332 }
11333 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11334 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011335 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011336 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011337 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011338 n->narg.text = wordtext;
11339 n->narg.backquote = backquotelist;
11340 here->here->nhere.doc = n;
11341 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011342 }
Eric Andersencb57d552001-06-28 07:25:16 +000011343}
11344
11345
11346/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011347 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011348 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011349#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011350static const char *
11351expandstr(const char *ps)
11352{
11353 union node n;
11354
11355 /* XXX Fix (char *) cast. */
11356 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011357 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011358 popfile();
11359
11360 n.narg.type = NARG;
11361 n.narg.next = NULL;
11362 n.narg.text = wordtext;
11363 n.narg.backquote = backquotelist;
11364
11365 expandarg(&n, NULL, 0);
11366 return stackblock();
11367}
11368#endif
11369
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011370/*
11371 * Execute a command or commands contained in a string.
11372 */
11373static int
11374evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011375{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011376 union node *n;
11377 struct stackmark smark;
11378 int skip;
11379
11380 setinputstring(s);
11381 setstackmark(&smark);
11382
11383 skip = 0;
11384 while ((n = parsecmd(0)) != NEOF) {
11385 evaltree(n, 0);
11386 popstackmark(&smark);
11387 skip = evalskip;
11388 if (skip)
11389 break;
11390 }
11391 popfile();
11392
11393 skip &= mask;
11394 evalskip = skip;
11395 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011396}
11397
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011398/*
11399 * The eval command.
11400 */
11401static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011402evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011403{
11404 char *p;
11405 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011406
Denis Vlasenko68404f12008-03-17 09:00:54 +000011407 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011408 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011409 argv += 2;
11410 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011411 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011412 for (;;) {
11413 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011414 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011415 if (p == NULL)
11416 break;
11417 STPUTC(' ', concat);
11418 }
11419 STPUTC('\0', concat);
11420 p = grabstackstr(concat);
11421 }
11422 evalstring(p, ~SKIPEVAL);
11423
11424 }
11425 return exitstatus;
11426}
11427
11428/*
11429 * Read and execute commands. "Top" is nonzero for the top level command
11430 * loop; it turns on prompting if the shell is interactive.
11431 */
11432static int
11433cmdloop(int top)
11434{
11435 union node *n;
11436 struct stackmark smark;
11437 int inter;
11438 int numeof = 0;
11439
11440 TRACE(("cmdloop(%d) called\n", top));
11441 for (;;) {
11442 int skip;
11443
11444 setstackmark(&smark);
11445#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011446 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011447 showjobs(stderr, SHOW_CHANGED);
11448#endif
11449 inter = 0;
11450 if (iflag && top) {
11451 inter++;
11452#if ENABLE_ASH_MAIL
11453 chkmail();
11454#endif
11455 }
11456 n = parsecmd(inter);
11457 /* showtree(n); DEBUG */
11458 if (n == NEOF) {
11459 if (!top || numeof >= 50)
11460 break;
11461 if (!stoppedjobs()) {
11462 if (!Iflag)
11463 break;
11464 out2str("\nUse \"exit\" to leave shell.\n");
11465 }
11466 numeof++;
11467 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011468 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11469 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011470 numeof = 0;
11471 evaltree(n, 0);
11472 }
11473 popstackmark(&smark);
11474 skip = evalskip;
11475
11476 if (skip) {
11477 evalskip = 0;
11478 return skip & SKIPEVAL;
11479 }
11480 }
11481 return 0;
11482}
11483
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011484/*
11485 * Take commands from a file. To be compatible we should do a path
11486 * search for the file, which is necessary to find sub-commands.
11487 */
11488static char *
11489find_dot_file(char *name)
11490{
11491 char *fullname;
11492 const char *path = pathval();
11493 struct stat statb;
11494
11495 /* don't try this for absolute or relative paths */
11496 if (strchr(name, '/'))
11497 return name;
11498
11499 while ((fullname = padvance(&path, name)) != NULL) {
11500 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11501 /*
11502 * Don't bother freeing here, since it will
11503 * be freed by the caller.
11504 */
11505 return fullname;
11506 }
11507 stunalloc(fullname);
11508 }
11509
11510 /* not found in the PATH */
11511 ash_msg_and_raise_error("%s: not found", name);
11512 /* NOTREACHED */
11513}
11514
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011515static int
11516dotcmd(int argc, char **argv)
11517{
11518 struct strlist *sp;
11519 volatile struct shparam saveparam;
11520 int status = 0;
11521
11522 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011523 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011524
Denis Vlasenko68404f12008-03-17 09:00:54 +000011525 if (argv[1]) { /* That's what SVR2 does */
11526 char *fullname = find_dot_file(argv[1]);
11527 argv += 2;
11528 argc -= 2;
11529 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011530 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011531 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011532 shellparam.nparam = argc;
11533 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011534 };
11535
11536 setinputfile(fullname, INPUT_PUSH_FILE);
11537 commandname = fullname;
11538 cmdloop(0);
11539 popfile();
11540
Denis Vlasenko68404f12008-03-17 09:00:54 +000011541 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011542 freeparam(&shellparam);
11543 shellparam = saveparam;
11544 };
11545 status = exitstatus;
11546 }
11547 return status;
11548}
11549
11550static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011551exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011552{
11553 if (stoppedjobs())
11554 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011555 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011556 exitstatus = number(argv[1]);
11557 raise_exception(EXEXIT);
11558 /* NOTREACHED */
11559}
11560
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011561/*
11562 * Read a file containing shell functions.
11563 */
11564static void
11565readcmdfile(char *name)
11566{
11567 setinputfile(name, INPUT_PUSH_FILE);
11568 cmdloop(0);
11569 popfile();
11570}
11571
11572
Denis Vlasenkocc571512007-02-23 21:10:35 +000011573/* ============ find_command inplementation */
11574
11575/*
11576 * Resolve a command name. If you change this routine, you may have to
11577 * change the shellexec routine as well.
11578 */
11579static void
11580find_command(char *name, struct cmdentry *entry, int act, const char *path)
11581{
11582 struct tblentry *cmdp;
11583 int idx;
11584 int prev;
11585 char *fullname;
11586 struct stat statb;
11587 int e;
11588 int updatetbl;
11589 struct builtincmd *bcmd;
11590
11591 /* If name contains a slash, don't use PATH or hash table */
11592 if (strchr(name, '/') != NULL) {
11593 entry->u.index = -1;
11594 if (act & DO_ABS) {
11595 while (stat(name, &statb) < 0) {
11596#ifdef SYSV
11597 if (errno == EINTR)
11598 continue;
11599#endif
11600 entry->cmdtype = CMDUNKNOWN;
11601 return;
11602 }
11603 }
11604 entry->cmdtype = CMDNORMAL;
11605 return;
11606 }
11607
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011608/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011609
11610 updatetbl = (path == pathval());
11611 if (!updatetbl) {
11612 act |= DO_ALTPATH;
11613 if (strstr(path, "%builtin") != NULL)
11614 act |= DO_ALTBLTIN;
11615 }
11616
11617 /* If name is in the table, check answer will be ok */
11618 cmdp = cmdlookup(name, 0);
11619 if (cmdp != NULL) {
11620 int bit;
11621
11622 switch (cmdp->cmdtype) {
11623 default:
11624#if DEBUG
11625 abort();
11626#endif
11627 case CMDNORMAL:
11628 bit = DO_ALTPATH;
11629 break;
11630 case CMDFUNCTION:
11631 bit = DO_NOFUNC;
11632 break;
11633 case CMDBUILTIN:
11634 bit = DO_ALTBLTIN;
11635 break;
11636 }
11637 if (act & bit) {
11638 updatetbl = 0;
11639 cmdp = NULL;
11640 } else if (cmdp->rehash == 0)
11641 /* if not invalidated by cd, we're done */
11642 goto success;
11643 }
11644
11645 /* If %builtin not in path, check for builtin next */
11646 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011647 if (bcmd) {
11648 if (IS_BUILTIN_REGULAR(bcmd))
11649 goto builtin_success;
11650 if (act & DO_ALTPATH) {
11651 if (!(act & DO_ALTBLTIN))
11652 goto builtin_success;
11653 } else if (builtinloc <= 0) {
11654 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011655 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011656 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011657
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011658#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011659 {
11660 int applet_no = find_applet_by_name(name);
11661 if (applet_no >= 0) {
11662 entry->cmdtype = CMDNORMAL;
11663 entry->u.index = -2 - applet_no;
11664 return;
11665 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011666 }
11667#endif
11668
Denis Vlasenkocc571512007-02-23 21:10:35 +000011669 /* We have to search path. */
11670 prev = -1; /* where to start */
11671 if (cmdp && cmdp->rehash) { /* doing a rehash */
11672 if (cmdp->cmdtype == CMDBUILTIN)
11673 prev = builtinloc;
11674 else
11675 prev = cmdp->param.index;
11676 }
11677
11678 e = ENOENT;
11679 idx = -1;
11680 loop:
11681 while ((fullname = padvance(&path, name)) != NULL) {
11682 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011683 /* NB: code below will still use fullname
11684 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011685 idx++;
11686 if (pathopt) {
11687 if (prefix(pathopt, "builtin")) {
11688 if (bcmd)
11689 goto builtin_success;
11690 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011691 }
11692 if ((act & DO_NOFUNC)
11693 || !prefix(pathopt, "func")
11694 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011695 continue;
11696 }
11697 }
11698 /* if rehash, don't redo absolute path names */
11699 if (fullname[0] == '/' && idx <= prev) {
11700 if (idx < prev)
11701 continue;
11702 TRACE(("searchexec \"%s\": no change\n", name));
11703 goto success;
11704 }
11705 while (stat(fullname, &statb) < 0) {
11706#ifdef SYSV
11707 if (errno == EINTR)
11708 continue;
11709#endif
11710 if (errno != ENOENT && errno != ENOTDIR)
11711 e = errno;
11712 goto loop;
11713 }
11714 e = EACCES; /* if we fail, this will be the error */
11715 if (!S_ISREG(statb.st_mode))
11716 continue;
11717 if (pathopt) { /* this is a %func directory */
11718 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011719 /* NB: stalloc will return space pointed by fullname
11720 * (because we don't have any intervening allocations
11721 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011722 readcmdfile(fullname);
11723 cmdp = cmdlookup(name, 0);
11724 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11725 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11726 stunalloc(fullname);
11727 goto success;
11728 }
11729 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11730 if (!updatetbl) {
11731 entry->cmdtype = CMDNORMAL;
11732 entry->u.index = idx;
11733 return;
11734 }
11735 INT_OFF;
11736 cmdp = cmdlookup(name, 1);
11737 cmdp->cmdtype = CMDNORMAL;
11738 cmdp->param.index = idx;
11739 INT_ON;
11740 goto success;
11741 }
11742
11743 /* We failed. If there was an entry for this command, delete it */
11744 if (cmdp && updatetbl)
11745 delete_cmd_entry();
11746 if (act & DO_ERR)
11747 ash_msg("%s: %s", name, errmsg(e, "not found"));
11748 entry->cmdtype = CMDUNKNOWN;
11749 return;
11750
11751 builtin_success:
11752 if (!updatetbl) {
11753 entry->cmdtype = CMDBUILTIN;
11754 entry->u.cmd = bcmd;
11755 return;
11756 }
11757 INT_OFF;
11758 cmdp = cmdlookup(name, 1);
11759 cmdp->cmdtype = CMDBUILTIN;
11760 cmdp->param.cmd = bcmd;
11761 INT_ON;
11762 success:
11763 cmdp->rehash = 0;
11764 entry->cmdtype = cmdp->cmdtype;
11765 entry->u = cmdp->param;
11766}
11767
11768
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011769/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011770
Eric Andersencb57d552001-06-28 07:25:16 +000011771/*
Eric Andersencb57d552001-06-28 07:25:16 +000011772 * The trap builtin.
11773 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011774static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011775trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011776{
11777 char *action;
11778 char **ap;
11779 int signo;
11780
Eric Andersenc470f442003-07-28 09:56:35 +000011781 nextopt(nullstr);
11782 ap = argptr;
11783 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011784 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011785 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011786 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011787
Rob Landleyc9c1a412006-07-12 19:17:55 +000011788 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011789 out1fmt("trap -- %s %s\n",
11790 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011791 }
11792 }
11793 return 0;
11794 }
Eric Andersenc470f442003-07-28 09:56:35 +000011795 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011796 action = NULL;
11797 else
11798 action = *ap++;
11799 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011800 signo = get_signum(*ap);
11801 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011802 ash_msg_and_raise_error("%s: bad trap", *ap);
11803 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011804 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011805 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011806 action = NULL;
11807 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011808 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011809 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011810 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011811 trap[signo] = action;
11812 if (signo != 0)
11813 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011814 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011815 ap++;
11816 }
11817 return 0;
11818}
11819
Eric Andersenc470f442003-07-28 09:56:35 +000011820
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011821/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011822
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011823#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011824/*
11825 * Lists available builtins
11826 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011827static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011828helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011829{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011830 unsigned col;
11831 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011832
11833 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011834 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011835 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011836 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011837 if (col > 60) {
11838 out1fmt("\n");
11839 col = 0;
11840 }
11841 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011842#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011843 {
11844 const char *a = applet_names;
11845 while (*a) {
11846 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11847 if (col > 60) {
11848 out1fmt("\n");
11849 col = 0;
11850 }
11851 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011852 }
11853 }
11854#endif
11855 out1fmt("\n\n");
11856 return EXIT_SUCCESS;
11857}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011858#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011859
Eric Andersencb57d552001-06-28 07:25:16 +000011860/*
Eric Andersencb57d552001-06-28 07:25:16 +000011861 * The export and readonly commands.
11862 */
Eric Andersenc470f442003-07-28 09:56:35 +000011863static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011864exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011865{
11866 struct var *vp;
11867 char *name;
11868 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011869 char **aptr;
11870 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011871
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011872 if (nextopt("p") != 'p') {
11873 aptr = argptr;
11874 name = *aptr;
11875 if (name) {
11876 do {
11877 p = strchr(name, '=');
11878 if (p != NULL) {
11879 p++;
11880 } else {
11881 vp = *findvar(hashvar(name), name);
11882 if (vp) {
11883 vp->flags |= flag;
11884 continue;
11885 }
Eric Andersencb57d552001-06-28 07:25:16 +000011886 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011887 setvar(name, p, flag);
11888 } while ((name = *++aptr) != NULL);
11889 return 0;
11890 }
Eric Andersencb57d552001-06-28 07:25:16 +000011891 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011892 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011893 return 0;
11894}
11895
Eric Andersencb57d552001-06-28 07:25:16 +000011896/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011897 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011898 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011899static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011900unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011901{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011902 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011903
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011904 cmdp = cmdlookup(name, 0);
11905 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11906 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011907}
11908
Eric Andersencb57d552001-06-28 07:25:16 +000011909/*
Eric Andersencb57d552001-06-28 07:25:16 +000011910 * The unset builtin command. We unset the function before we unset the
11911 * variable to allow a function to be unset when there is a readonly variable
11912 * with the same name.
11913 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011914static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011915unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011916{
11917 char **ap;
11918 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011919 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011920 int ret = 0;
11921
11922 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011923 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011924 }
Eric Andersencb57d552001-06-28 07:25:16 +000011925
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011926 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011927 if (flag != 'f') {
11928 i = unsetvar(*ap);
11929 ret |= i;
11930 if (!(i & 2))
11931 continue;
11932 }
11933 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011934 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011935 }
Eric Andersenc470f442003-07-28 09:56:35 +000011936 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011937}
11938
11939
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011940/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011941
Eric Andersenc470f442003-07-28 09:56:35 +000011942#include <sys/times.h>
11943
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011944static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011945 ' ', offsetof(struct tms, tms_utime),
11946 '\n', offsetof(struct tms, tms_stime),
11947 ' ', offsetof(struct tms, tms_cutime),
11948 '\n', offsetof(struct tms, tms_cstime),
11949 0
11950};
Eric Andersencb57d552001-06-28 07:25:16 +000011951
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011952static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011953timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011954{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011955 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011956 const unsigned char *p;
11957 struct tms buf;
11958
11959 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011960 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011961
11962 p = timescmd_str;
11963 do {
11964 t = *(clock_t *)(((char *) &buf) + p[1]);
11965 s = t / clk_tck;
11966 out1fmt("%ldm%ld.%.3lds%c",
11967 s/60, s%60,
11968 ((t - s * clk_tck) * 1000) / clk_tck,
11969 p[0]);
11970 } while (*(p += 2));
11971
Eric Andersencb57d552001-06-28 07:25:16 +000011972 return 0;
11973}
11974
Denis Vlasenko131ae172007-02-18 13:00:19 +000011975#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011976static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011977dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011978{
Eric Andersened9ecf72004-06-22 08:29:45 +000011979 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011980 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011981
Denis Vlasenkob012b102007-02-19 22:43:01 +000011982 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011983 result = arith(s, &errcode);
11984 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011985 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011986 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011987 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011988 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011989 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011990 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011991 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011992 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011993 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011994
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011995 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011996}
Eric Andersenc470f442003-07-28 09:56:35 +000011997
Eric Andersenc470f442003-07-28 09:56:35 +000011998/*
Eric Andersen90898442003-08-06 11:20:52 +000011999 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12000 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12001 *
12002 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012003 */
12004static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012005letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012006{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012007 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012008
Denis Vlasenko68404f12008-03-17 09:00:54 +000012009 argv++;
12010 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012011 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012012 do {
12013 i = dash_arith(*argv);
12014 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012015
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012016 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012017}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012018#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012019
Eric Andersenc470f442003-07-28 09:56:35 +000012020
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012021/* ============ miscbltin.c
12022 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012023 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012024 */
12025
12026#undef rflag
12027
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012028#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012029typedef enum __rlimit_resource rlim_t;
12030#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012031
Eric Andersenc470f442003-07-28 09:56:35 +000012032/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012033 * The read builtin. Options:
12034 * -r Do not interpret '\' specially
12035 * -s Turn off echo (tty only)
12036 * -n NCHARS Read NCHARS max
12037 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12038 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12039 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012040 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012041 * TODO: bash also has:
12042 * -a ARRAY Read into array[0],[1],etc
12043 * -d DELIM End on DELIM char, not newline
12044 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012045 */
Eric Andersenc470f442003-07-28 09:56:35 +000012046static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012047readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012048{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012049 static const char *const arg_REPLY[] = { "REPLY", NULL };
12050
Eric Andersenc470f442003-07-28 09:56:35 +000012051 char **ap;
12052 int backslash;
12053 char c;
12054 int rflag;
12055 char *prompt;
12056 const char *ifs;
12057 char *p;
12058 int startword;
12059 int status;
12060 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012061 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012062#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012063 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012064 int silent = 0;
12065 struct termios tty, old_tty;
12066#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012067#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012068 unsigned end_ms = 0;
12069 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012070#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012071
12072 rflag = 0;
12073 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012074 while ((i = nextopt("p:u:r"
12075 USE_ASH_READ_TIMEOUT("t:")
12076 USE_ASH_READ_NCHARS("n:s")
12077 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012078 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012079 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012080 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012081 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012082#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012083 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012084 nchars = bb_strtou(optionarg, NULL, 10);
12085 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012086 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012087 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012088 break;
12089 case 's':
12090 silent = 1;
12091 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012092#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012093#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012094 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012095 timeout = bb_strtou(optionarg, NULL, 10);
12096 if (errno || timeout > UINT_MAX / 2048)
12097 ash_msg_and_raise_error("invalid timeout");
12098 timeout *= 1000;
12099#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012100 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012101 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012102 /* EINVAL means number is ok, but not terminated by NUL */
12103 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012104 char *p2;
12105 if (*++p) {
12106 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012107 ts.tv_usec = bb_strtou(p, &p2, 10);
12108 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012109 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012110 scale = p2 - p;
12111 /* normalize to usec */
12112 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012113 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012114 while (scale++ < 6)
12115 ts.tv_usec *= 10;
12116 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012117 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012118 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012119 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012120 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012121 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012122 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012123#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012124 break;
12125#endif
12126 case 'r':
12127 rflag = 1;
12128 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012129 case 'u':
12130 fd = bb_strtou(optionarg, NULL, 10);
12131 if (fd < 0 || errno)
12132 ash_msg_and_raise_error("invalid file descriptor");
12133 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012134 default:
12135 break;
12136 }
Eric Andersenc470f442003-07-28 09:56:35 +000012137 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012138 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012139 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012140 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012141 ap = argptr;
12142 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012143 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012144 ifs = bltinlookup("IFS");
12145 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012146 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012147#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012148 tcgetattr(fd, &tty);
12149 old_tty = tty;
12150 if (nchars || silent) {
12151 if (nchars) {
12152 tty.c_lflag &= ~ICANON;
12153 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012154 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012155 if (silent) {
12156 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12157 }
12158 /* if tcgetattr failed, tcsetattr will fail too.
12159 * Ignoring, it's harmless. */
12160 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012161 }
12162#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012163
Eric Andersenc470f442003-07-28 09:56:35 +000012164 status = 0;
12165 startword = 1;
12166 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012167#if ENABLE_ASH_READ_TIMEOUT
12168 if (timeout) /* NB: ensuring end_ms is nonzero */
12169 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12170#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012171 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012172 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012173#if ENABLE_ASH_READ_TIMEOUT
12174 if (end_ms) {
12175 struct pollfd pfd[1];
12176 pfd[0].fd = fd;
12177 pfd[0].events = POLLIN;
12178 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12179 if ((int)timeout <= 0 /* already late? */
12180 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12181 ) { /* timed out! */
12182#if ENABLE_ASH_READ_NCHARS
12183 tcsetattr(fd, TCSANOW, &old_tty);
12184#endif
12185 return 1;
12186 }
12187 }
12188#endif
12189 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012190 status = 1;
12191 break;
12192 }
12193 if (c == '\0')
12194 continue;
12195 if (backslash) {
12196 backslash = 0;
12197 if (c != '\n')
12198 goto put;
12199 continue;
12200 }
12201 if (!rflag && c == '\\') {
12202 backslash++;
12203 continue;
12204 }
12205 if (c == '\n')
12206 break;
12207 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12208 continue;
12209 }
12210 startword = 0;
12211 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12212 STACKSTRNUL(p);
12213 setvar(*ap, stackblock(), 0);
12214 ap++;
12215 startword = 1;
12216 STARTSTACKSTR(p);
12217 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012218 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012219 STPUTC(c, p);
12220 }
12221 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012222/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012223#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012224 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012225#else
12226 while (1);
12227#endif
12228
12229#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012230 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012231#endif
12232
Eric Andersenc470f442003-07-28 09:56:35 +000012233 STACKSTRNUL(p);
12234 /* Remove trailing blanks */
12235 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12236 *p = '\0';
12237 setvar(*ap, stackblock(), 0);
12238 while (*++ap != NULL)
12239 setvar(*ap, nullstr, 0);
12240 return status;
12241}
12242
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012243static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012244umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012245{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012246 static const char permuser[3] ALIGN1 = "ugo";
12247 static const char permmode[3] ALIGN1 = "rwx";
12248 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012249 S_IRUSR, S_IWUSR, S_IXUSR,
12250 S_IRGRP, S_IWGRP, S_IXGRP,
12251 S_IROTH, S_IWOTH, S_IXOTH
12252 };
12253
12254 char *ap;
12255 mode_t mask;
12256 int i;
12257 int symbolic_mode = 0;
12258
12259 while (nextopt("S") != '\0') {
12260 symbolic_mode = 1;
12261 }
12262
Denis Vlasenkob012b102007-02-19 22:43:01 +000012263 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012264 mask = umask(0);
12265 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012266 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012267
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012268 ap = *argptr;
12269 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012270 if (symbolic_mode) {
12271 char buf[18];
12272 char *p = buf;
12273
12274 for (i = 0; i < 3; i++) {
12275 int j;
12276
12277 *p++ = permuser[i];
12278 *p++ = '=';
12279 for (j = 0; j < 3; j++) {
12280 if ((mask & permmask[3 * i + j]) == 0) {
12281 *p++ = permmode[j];
12282 }
12283 }
12284 *p++ = ',';
12285 }
12286 *--p = 0;
12287 puts(buf);
12288 } else {
12289 out1fmt("%.4o\n", mask);
12290 }
12291 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012292 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012293 mask = 0;
12294 do {
12295 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012296 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012297 mask = (mask << 3) + (*ap - '0');
12298 } while (*++ap != '\0');
12299 umask(mask);
12300 } else {
12301 mask = ~mask & 0777;
12302 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012303 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012304 }
12305 umask(~mask & 0777);
12306 }
12307 }
12308 return 0;
12309}
12310
12311/*
12312 * ulimit builtin
12313 *
12314 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12315 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12316 * ash by J.T. Conklin.
12317 *
12318 * Public domain.
12319 */
12320
12321struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012322 uint8_t cmd; /* RLIMIT_xxx fit into it */
12323 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012324 char option;
12325};
12326
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012327static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012328#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012329 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012330#endif
12331#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012332 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012333#endif
12334#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012335 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012336#endif
12337#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012338 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012339#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012340#ifdef RLIMIT_CORE
12341 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012342#endif
12343#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012344 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012345#endif
12346#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012347 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012348#endif
12349#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012350 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012351#endif
12352#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012353 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012354#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012355#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012356 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012357#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012358#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012359 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012360#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012361};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012362static const char limits_name[] =
12363#ifdef RLIMIT_CPU
12364 "time(seconds)" "\0"
12365#endif
12366#ifdef RLIMIT_FSIZE
12367 "file(blocks)" "\0"
12368#endif
12369#ifdef RLIMIT_DATA
12370 "data(kb)" "\0"
12371#endif
12372#ifdef RLIMIT_STACK
12373 "stack(kb)" "\0"
12374#endif
12375#ifdef RLIMIT_CORE
12376 "coredump(blocks)" "\0"
12377#endif
12378#ifdef RLIMIT_RSS
12379 "memory(kb)" "\0"
12380#endif
12381#ifdef RLIMIT_MEMLOCK
12382 "locked memory(kb)" "\0"
12383#endif
12384#ifdef RLIMIT_NPROC
12385 "process" "\0"
12386#endif
12387#ifdef RLIMIT_NOFILE
12388 "nofiles" "\0"
12389#endif
12390#ifdef RLIMIT_AS
12391 "vmemory(kb)" "\0"
12392#endif
12393#ifdef RLIMIT_LOCKS
12394 "locks" "\0"
12395#endif
12396;
Eric Andersenc470f442003-07-28 09:56:35 +000012397
Glenn L McGrath76620622004-01-13 10:19:37 +000012398enum limtype { SOFT = 0x1, HARD = 0x2 };
12399
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012400static void
12401printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012402 const struct limits *l)
12403{
12404 rlim_t val;
12405
12406 val = limit->rlim_max;
12407 if (how & SOFT)
12408 val = limit->rlim_cur;
12409
12410 if (val == RLIM_INFINITY)
12411 out1fmt("unlimited\n");
12412 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012413 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012414 out1fmt("%lld\n", (long long) val);
12415 }
12416}
12417
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012418static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012419ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012420{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012421 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012422 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012423 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012424 const struct limits *l;
12425 int set, all = 0;
12426 int optc, what;
12427 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012428
12429 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012430 while ((optc = nextopt("HSa"
12431#ifdef RLIMIT_CPU
12432 "t"
12433#endif
12434#ifdef RLIMIT_FSIZE
12435 "f"
12436#endif
12437#ifdef RLIMIT_DATA
12438 "d"
12439#endif
12440#ifdef RLIMIT_STACK
12441 "s"
12442#endif
12443#ifdef RLIMIT_CORE
12444 "c"
12445#endif
12446#ifdef RLIMIT_RSS
12447 "m"
12448#endif
12449#ifdef RLIMIT_MEMLOCK
12450 "l"
12451#endif
12452#ifdef RLIMIT_NPROC
12453 "p"
12454#endif
12455#ifdef RLIMIT_NOFILE
12456 "n"
12457#endif
12458#ifdef RLIMIT_AS
12459 "v"
12460#endif
12461#ifdef RLIMIT_LOCKS
12462 "w"
12463#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012464 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012465 switch (optc) {
12466 case 'H':
12467 how = HARD;
12468 break;
12469 case 'S':
12470 how = SOFT;
12471 break;
12472 case 'a':
12473 all = 1;
12474 break;
12475 default:
12476 what = optc;
12477 }
12478
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012479 for (l = limits_tbl; l->option != what; l++)
12480 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012481
12482 set = *argptr ? 1 : 0;
12483 if (set) {
12484 char *p = *argptr;
12485
12486 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012487 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012488 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012489 val = RLIM_INFINITY;
12490 else {
12491 val = (rlim_t) 0;
12492
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012493 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012494 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012495 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012496 if (val < (rlim_t) 0)
12497 break;
12498 }
12499 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012500 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012501 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012502 }
12503 }
12504 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012505 const char *lname = limits_name;
12506 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012507 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012508 out1fmt("%-20s ", lname);
12509 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012510 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012511 }
12512 return 0;
12513 }
12514
12515 getrlimit(l->cmd, &limit);
12516 if (set) {
12517 if (how & HARD)
12518 limit.rlim_max = val;
12519 if (how & SOFT)
12520 limit.rlim_cur = val;
12521 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012522 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012523 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012524 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012525 }
12526 return 0;
12527}
12528
Eric Andersen90898442003-08-06 11:20:52 +000012529
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012530/* ============ Math support */
12531
Denis Vlasenko131ae172007-02-18 13:00:19 +000012532#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012533
12534/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12535
12536 Permission is hereby granted, free of charge, to any person obtaining
12537 a copy of this software and associated documentation files (the
12538 "Software"), to deal in the Software without restriction, including
12539 without limitation the rights to use, copy, modify, merge, publish,
12540 distribute, sublicense, and/or sell copies of the Software, and to
12541 permit persons to whom the Software is furnished to do so, subject to
12542 the following conditions:
12543
12544 The above copyright notice and this permission notice shall be
12545 included in all copies or substantial portions of the Software.
12546
12547 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12548 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12549 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12550 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12551 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12552 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12553 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12554*/
12555
12556/* This is my infix parser/evaluator. It is optimized for size, intended
12557 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012558 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012559 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012560 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012561 * be that which POSIX specifies for shells. */
12562
12563/* The code uses a simple two-stack algorithm. See
12564 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012565 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012566 * this is based (this code differs in that it applies operators immediately
12567 * to the stack instead of adding them to a queue to end up with an
12568 * expression). */
12569
12570/* To use the routine, call it with an expression string and error return
12571 * pointer */
12572
12573/*
12574 * Aug 24, 2001 Manuel Novoa III
12575 *
12576 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12577 *
12578 * 1) In arith_apply():
12579 * a) Cached values of *numptr and &(numptr[-1]).
12580 * b) Removed redundant test for zero denominator.
12581 *
12582 * 2) In arith():
12583 * a) Eliminated redundant code for processing operator tokens by moving
12584 * to a table-based implementation. Also folded handling of parens
12585 * into the table.
12586 * b) Combined all 3 loops which called arith_apply to reduce generated
12587 * code size at the cost of speed.
12588 *
12589 * 3) The following expressions were treated as valid by the original code:
12590 * 1() , 0! , 1 ( *3 ) .
12591 * These bugs have been fixed by internally enclosing the expression in
12592 * parens and then checking that all binary ops and right parens are
12593 * preceded by a valid expression (NUM_TOKEN).
12594 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012595 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012596 * ctype's isspace() if it is used by another busybox applet or if additional
12597 * whitespace chars should be considered. Look below the "#include"s for a
12598 * precompiler test.
12599 */
12600
12601/*
12602 * Aug 26, 2001 Manuel Novoa III
12603 *
12604 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12605 *
12606 * Merge in Aaron's comments previously posted to the busybox list,
12607 * modified slightly to take account of my changes to the code.
12608 *
12609 */
12610
12611/*
12612 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12613 *
12614 * - allow access to variable,
12615 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12616 * - realize assign syntax (VAR=expr, +=, *= etc)
12617 * - realize exponentiation (** operator)
12618 * - realize comma separated - expr, expr
12619 * - realise ++expr --expr expr++ expr--
12620 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012621 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012622 * - was restored loses XOR operator
12623 * - remove one goto label, added three ;-)
12624 * - protect $((num num)) as true zero expr (Manuel`s error)
12625 * - always use special isspace(), see comment from bash ;-)
12626 */
12627
Eric Andersen90898442003-08-06 11:20:52 +000012628#define arith_isspace(arithval) \
12629 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12630
Eric Andersen90898442003-08-06 11:20:52 +000012631typedef unsigned char operator;
12632
12633/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012634 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012635 * precedence. The ID portion is so that multiple operators can have the
12636 * same precedence, ensuring that the leftmost one is evaluated first.
12637 * Consider * and /. */
12638
12639#define tok_decl(prec,id) (((id)<<5)|(prec))
12640#define PREC(op) ((op) & 0x1F)
12641
12642#define TOK_LPAREN tok_decl(0,0)
12643
12644#define TOK_COMMA tok_decl(1,0)
12645
12646#define TOK_ASSIGN tok_decl(2,0)
12647#define TOK_AND_ASSIGN tok_decl(2,1)
12648#define TOK_OR_ASSIGN tok_decl(2,2)
12649#define TOK_XOR_ASSIGN tok_decl(2,3)
12650#define TOK_PLUS_ASSIGN tok_decl(2,4)
12651#define TOK_MINUS_ASSIGN tok_decl(2,5)
12652#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12653#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12654
12655#define TOK_MUL_ASSIGN tok_decl(3,0)
12656#define TOK_DIV_ASSIGN tok_decl(3,1)
12657#define TOK_REM_ASSIGN tok_decl(3,2)
12658
12659/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012660#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012661
12662/* conditional is right associativity too */
12663#define TOK_CONDITIONAL tok_decl(4,0)
12664#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12665
12666#define TOK_OR tok_decl(5,0)
12667
12668#define TOK_AND tok_decl(6,0)
12669
12670#define TOK_BOR tok_decl(7,0)
12671
12672#define TOK_BXOR tok_decl(8,0)
12673
12674#define TOK_BAND tok_decl(9,0)
12675
12676#define TOK_EQ tok_decl(10,0)
12677#define TOK_NE tok_decl(10,1)
12678
12679#define TOK_LT tok_decl(11,0)
12680#define TOK_GT tok_decl(11,1)
12681#define TOK_GE tok_decl(11,2)
12682#define TOK_LE tok_decl(11,3)
12683
12684#define TOK_LSHIFT tok_decl(12,0)
12685#define TOK_RSHIFT tok_decl(12,1)
12686
12687#define TOK_ADD tok_decl(13,0)
12688#define TOK_SUB tok_decl(13,1)
12689
12690#define TOK_MUL tok_decl(14,0)
12691#define TOK_DIV tok_decl(14,1)
12692#define TOK_REM tok_decl(14,2)
12693
12694/* exponent is right associativity */
12695#define TOK_EXPONENT tok_decl(15,1)
12696
12697/* For now unary operators. */
12698#define UNARYPREC 16
12699#define TOK_BNOT tok_decl(UNARYPREC,0)
12700#define TOK_NOT tok_decl(UNARYPREC,1)
12701
12702#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12703#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12704
12705#define PREC_PRE (UNARYPREC+2)
12706
12707#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12708#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12709
12710#define PREC_POST (UNARYPREC+3)
12711
12712#define TOK_POST_INC tok_decl(PREC_POST, 0)
12713#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12714
12715#define SPEC_PREC (UNARYPREC+4)
12716
12717#define TOK_NUM tok_decl(SPEC_PREC, 0)
12718#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12719
12720#define NUMPTR (*numstackptr)
12721
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012722static int
12723tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012724{
12725 operator prec = PREC(op);
12726
12727 convert_prec_is_assing(prec);
12728 return (prec == PREC(TOK_ASSIGN) ||
12729 prec == PREC_PRE || prec == PREC_POST);
12730}
12731
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012732static int
12733is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012734{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012735 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12736 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012737}
12738
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012739typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012740 arith_t val;
12741 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012742 char contidional_second_val_initialized;
12743 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012744 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012745} v_n_t;
12746
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012747typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012748 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012749 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012750} chk_var_recursive_looped_t;
12751
12752static chk_var_recursive_looped_t *prev_chk_var_recursive;
12753
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012754static int
12755arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012756{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012757 if (t->var) {
12758 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012759
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012760 if (p) {
12761 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012762
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012763 /* recursive try as expression */
12764 chk_var_recursive_looped_t *cur;
12765 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012766
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012767 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12768 if (strcmp(cur->var, t->var) == 0) {
12769 /* expression recursion loop detected */
12770 return -5;
12771 }
12772 }
12773 /* save current lookuped var name */
12774 cur = prev_chk_var_recursive;
12775 cur_save.var = t->var;
12776 cur_save.next = cur;
12777 prev_chk_var_recursive = &cur_save;
12778
12779 t->val = arith (p, &errcode);
12780 /* restore previous ptr after recursiving */
12781 prev_chk_var_recursive = cur;
12782 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012783 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012784 /* allow undefined var as 0 */
12785 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012786 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012787 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012788}
12789
12790/* "applying" a token means performing it on the top elements on the integer
12791 * stack. For a unary operator it will only change the top element, but a
12792 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012793static int
12794arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012795{
Eric Andersen90898442003-08-06 11:20:52 +000012796 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012797 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012798 int ret_arith_lookup_val;
12799
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012800 /* There is no operator that can work without arguments */
12801 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012802 numptr_m1 = NUMPTR - 1;
12803
12804 /* check operand is var with noninteger value */
12805 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012806 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012807 return ret_arith_lookup_val;
12808
12809 rez = numptr_m1->val;
12810 if (op == TOK_UMINUS)
12811 rez *= -1;
12812 else if (op == TOK_NOT)
12813 rez = !rez;
12814 else if (op == TOK_BNOT)
12815 rez = ~rez;
12816 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12817 rez++;
12818 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12819 rez--;
12820 else if (op != TOK_UPLUS) {
12821 /* Binary operators */
12822
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012823 /* check and binary operators need two arguments */
12824 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012825
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012826 /* ... and they pop one */
12827 --NUMPTR;
12828 numptr_val = rez;
12829 if (op == TOK_CONDITIONAL) {
12830 if (! numptr_m1->contidional_second_val_initialized) {
12831 /* protect $((expr1 ? expr2)) without ": expr" */
12832 goto err;
12833 }
12834 rez = numptr_m1->contidional_second_val;
12835 } else if (numptr_m1->contidional_second_val_initialized) {
12836 /* protect $((expr1 : expr2)) without "expr ? " */
12837 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012838 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012839 numptr_m1 = NUMPTR - 1;
12840 if (op != TOK_ASSIGN) {
12841 /* check operand is var with noninteger value for not '=' */
12842 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12843 if (ret_arith_lookup_val)
12844 return ret_arith_lookup_val;
12845 }
12846 if (op == TOK_CONDITIONAL) {
12847 numptr_m1->contidional_second_val = rez;
12848 }
12849 rez = numptr_m1->val;
12850 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012851 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012852 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012853 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012854 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012855 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012856 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012857 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012858 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012859 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012860 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012861 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012862 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012863 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012864 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012865 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012866 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012867 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012868 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012869 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012870 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012871 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012872 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012873 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012874 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012875 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012876 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012877 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012878 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012879 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012880 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012881 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012882 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012883 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012884 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012885 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012886 /* protect $((expr : expr)) without "expr ? " */
12887 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012888 }
12889 numptr_m1->contidional_second_val_initialized = op;
12890 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012891 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012892 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012893 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012894 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012895 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012896 return -3; /* exponent less than 0 */
12897 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012898 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012899
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012900 if (numptr_val)
12901 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012902 c *= rez;
12903 rez = c;
12904 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012905 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012906 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012908 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012909 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012910 rez %= numptr_val;
12911 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012912 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012913 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012914
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012915 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012916 /* Hmm, 1=2 ? */
12917 goto err;
12918 }
12919 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012920#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012921 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012922#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012923 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012924#endif
Eric Andersen90898442003-08-06 11:20:52 +000012925 setvar(numptr_m1->var, buf, 0);
12926 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012927 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012928 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012929 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012930 rez++;
12931 }
12932 numptr_m1->val = rez;
12933 /* protect geting var value, is number now */
12934 numptr_m1->var = NULL;
12935 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012936 err:
12937 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012938}
12939
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012940/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012941static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012942 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12943 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12944 '<','<', 0, TOK_LSHIFT,
12945 '>','>', 0, TOK_RSHIFT,
12946 '|','|', 0, TOK_OR,
12947 '&','&', 0, TOK_AND,
12948 '!','=', 0, TOK_NE,
12949 '<','=', 0, TOK_LE,
12950 '>','=', 0, TOK_GE,
12951 '=','=', 0, TOK_EQ,
12952 '|','=', 0, TOK_OR_ASSIGN,
12953 '&','=', 0, TOK_AND_ASSIGN,
12954 '*','=', 0, TOK_MUL_ASSIGN,
12955 '/','=', 0, TOK_DIV_ASSIGN,
12956 '%','=', 0, TOK_REM_ASSIGN,
12957 '+','=', 0, TOK_PLUS_ASSIGN,
12958 '-','=', 0, TOK_MINUS_ASSIGN,
12959 '-','-', 0, TOK_POST_DEC,
12960 '^','=', 0, TOK_XOR_ASSIGN,
12961 '+','+', 0, TOK_POST_INC,
12962 '*','*', 0, TOK_EXPONENT,
12963 '!', 0, TOK_NOT,
12964 '<', 0, TOK_LT,
12965 '>', 0, TOK_GT,
12966 '=', 0, TOK_ASSIGN,
12967 '|', 0, TOK_BOR,
12968 '&', 0, TOK_BAND,
12969 '*', 0, TOK_MUL,
12970 '/', 0, TOK_DIV,
12971 '%', 0, TOK_REM,
12972 '+', 0, TOK_ADD,
12973 '-', 0, TOK_SUB,
12974 '^', 0, TOK_BXOR,
12975 /* uniq */
12976 '~', 0, TOK_BNOT,
12977 ',', 0, TOK_COMMA,
12978 '?', 0, TOK_CONDITIONAL,
12979 ':', 0, TOK_CONDITIONAL_SEP,
12980 ')', 0, TOK_RPAREN,
12981 '(', 0, TOK_LPAREN,
12982 0
12983};
12984/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012985#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000012986
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012987static arith_t
12988arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012989{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012990 char arithval; /* Current character under analysis */
12991 operator lasttok, op;
12992 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000012993 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012994 const char *p = endexpression;
12995 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000012996 v_n_t *numstack, *numstackptr;
12997 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012998
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012999 /* Stack of integers */
13000 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13001 * in any given correct or incorrect expression is left as an exercise to
13002 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013003 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013004 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013005 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013006
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013007 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13008 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013009
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013010 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013011 arithval = *expr;
13012 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013013 if (p == endexpression) {
13014 /* Null expression. */
13015 return 0;
13016 }
13017
13018 /* This is only reached after all tokens have been extracted from the
13019 * input stream. If there are still tokens on the operator stack, they
13020 * are to be applied in order. At the end, there should be a final
13021 * result on the integer stack */
13022
13023 if (expr != endexpression + 1) {
13024 /* If we haven't done so already, */
13025 /* append a closing right paren */
13026 expr = endexpression;
13027 /* and let the loop process it. */
13028 continue;
13029 }
13030 /* At this point, we're done with the expression. */
13031 if (numstackptr != numstack+1) {
13032 /* ... but if there isn't, it's bad */
13033 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013034 *perrcode = -1;
13035 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013036 }
13037 if (numstack->var) {
13038 /* expression is $((var)) only, lookup now */
13039 errcode = arith_lookup_val(numstack);
13040 }
13041 ret:
13042 *perrcode = errcode;
13043 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013044 }
13045
Eric Andersen90898442003-08-06 11:20:52 +000013046 /* Continue processing the expression. */
13047 if (arith_isspace(arithval)) {
13048 /* Skip whitespace */
13049 goto prologue;
13050 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013051 p = endofname(expr);
13052 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013053 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013054
13055 numstackptr->var = alloca(var_name_size);
13056 safe_strncpy(numstackptr->var, expr, var_name_size);
13057 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013058 num:
Eric Andersen90898442003-08-06 11:20:52 +000013059 numstackptr->contidional_second_val_initialized = 0;
13060 numstackptr++;
13061 lasttok = TOK_NUM;
13062 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013063 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013064 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013065 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013066#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013067 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013068#else
13069 numstackptr->val = strtol(expr, (char **) &expr, 0);
13070#endif
Eric Andersen90898442003-08-06 11:20:52 +000013071 goto num;
13072 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013073 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013074 const char *o;
13075
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013076 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013077 /* strange operator not found */
13078 goto err;
13079 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013080 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013081 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013082 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013083 /* found */
13084 expr = o - 1;
13085 break;
13086 }
13087 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013089 p++;
13090 /* skip zero delim */
13091 p++;
13092 }
13093 op = p[1];
13094
13095 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13097 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013098
13099 /* Plus and minus are binary (not unary) _only_ if the last
13100 * token was as number, or a right paren (which pretends to be
13101 * a number, since it evaluates to one). Think about it.
13102 * It makes sense. */
13103 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013104 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013105 case TOK_ADD:
13106 op = TOK_UPLUS;
13107 break;
13108 case TOK_SUB:
13109 op = TOK_UMINUS;
13110 break;
13111 case TOK_POST_INC:
13112 op = TOK_PRE_INC;
13113 break;
13114 case TOK_POST_DEC:
13115 op = TOK_PRE_DEC;
13116 break;
Eric Andersen90898442003-08-06 11:20:52 +000013117 }
13118 }
13119 /* We don't want a unary operator to cause recursive descent on the
13120 * stack, because there can be many in a row and it could cause an
13121 * operator to be evaluated before its argument is pushed onto the
13122 * integer stack. */
13123 /* But for binary operators, "apply" everything on the operator
13124 * stack until we find an operator with a lesser priority than the
13125 * one we have just extracted. */
13126 /* Left paren is given the lowest priority so it will never be
13127 * "applied" in this way.
13128 * if associativity is right and priority eq, applied also skip
13129 */
13130 prec = PREC(op);
13131 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13132 /* not left paren or unary */
13133 if (lasttok != TOK_NUM) {
13134 /* binary op must be preceded by a num */
13135 goto err;
13136 }
13137 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013138 if (op == TOK_RPAREN) {
13139 /* The algorithm employed here is simple: while we don't
13140 * hit an open paren nor the bottom of the stack, pop
13141 * tokens and apply them */
13142 if (stackptr[-1] == TOK_LPAREN) {
13143 --stackptr;
13144 /* Any operator directly after a */
13145 lasttok = TOK_NUM;
13146 /* close paren should consider itself binary */
13147 goto prologue;
13148 }
13149 } else {
13150 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013151
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013152 convert_prec_is_assing(prec);
13153 convert_prec_is_assing(prev_prec);
13154 if (prev_prec < prec)
13155 break;
13156 /* check right assoc */
13157 if (prev_prec == prec && is_right_associativity(prec))
13158 break;
13159 }
13160 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13161 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013162 }
13163 if (op == TOK_RPAREN) {
13164 goto err;
13165 }
13166 }
13167
13168 /* Push this operator to the stack and remember it. */
13169 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013170 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013171 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013172 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013173}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013174#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013175
13176
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013177/* ============ main() and helpers */
13178
13179/*
13180 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013181 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013182static void exitshell(void) ATTRIBUTE_NORETURN;
13183static void
13184exitshell(void)
13185{
13186 struct jmploc loc;
13187 char *p;
13188 int status;
13189
13190 status = exitstatus;
13191 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13192 if (setjmp(loc.loc)) {
13193 if (exception == EXEXIT)
13194/* dash bug: it just does _exit(exitstatus) here
13195 * but we have to do setjobctl(0) first!
13196 * (bug is still not fixed in dash-0.5.3 - if you run dash
13197 * under Midnight Commander, on exit from dash MC is backgrounded) */
13198 status = exitstatus;
13199 goto out;
13200 }
13201 exception_handler = &loc;
13202 p = trap[0];
13203 if (p) {
13204 trap[0] = NULL;
13205 evalstring(p, 0);
13206 }
13207 flush_stdout_stderr();
13208 out:
13209 setjobctl(0);
13210 _exit(status);
13211 /* NOTREACHED */
13212}
13213
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013214static void
13215init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013216{
13217 /* from input.c: */
13218 basepf.nextc = basepf.buf = basebuf;
13219
13220 /* from trap.c: */
13221 signal(SIGCHLD, SIG_DFL);
13222
13223 /* from var.c: */
13224 {
13225 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013226 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013227 const char *p;
13228 struct stat st1, st2;
13229
13230 initvar();
13231 for (envp = environ; envp && *envp; envp++) {
13232 if (strchr(*envp, '=')) {
13233 setvareq(*envp, VEXPORT|VTEXTFIXED);
13234 }
13235 }
13236
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013237 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013238 setvar("PPID", ppid, 0);
13239
13240 p = lookupvar("PWD");
13241 if (p)
13242 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13243 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13244 p = '\0';
13245 setpwd(p, 0);
13246 }
13247}
13248
13249/*
13250 * Process the shell command line arguments.
13251 */
13252static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013253procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013254{
13255 int i;
13256 const char *xminusc;
13257 char **xargv;
13258
13259 xargv = argv;
13260 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013261 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013262 xargv++;
13263 for (i = 0; i < NOPTS; i++)
13264 optlist[i] = 2;
13265 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013266 if (options(1)) {
13267 /* it already printed err message */
13268 raise_exception(EXERROR);
13269 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013270 xargv = argptr;
13271 xminusc = minusc;
13272 if (*xargv == NULL) {
13273 if (xminusc)
13274 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13275 sflag = 1;
13276 }
13277 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13278 iflag = 1;
13279 if (mflag == 2)
13280 mflag = iflag;
13281 for (i = 0; i < NOPTS; i++)
13282 if (optlist[i] == 2)
13283 optlist[i] = 0;
13284#if DEBUG == 2
13285 debug = 1;
13286#endif
13287 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13288 if (xminusc) {
13289 minusc = *xargv++;
13290 if (*xargv)
13291 goto setarg0;
13292 } else if (!sflag) {
13293 setinputfile(*xargv, 0);
13294 setarg0:
13295 arg0 = *xargv++;
13296 commandname = arg0;
13297 }
13298
13299 shellparam.p = xargv;
13300#if ENABLE_ASH_GETOPTS
13301 shellparam.optind = 1;
13302 shellparam.optoff = -1;
13303#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013304 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013305 while (*xargv) {
13306 shellparam.nparam++;
13307 xargv++;
13308 }
13309 optschanged();
13310}
13311
13312/*
13313 * Read /etc/profile or .profile.
13314 */
13315static void
13316read_profile(const char *name)
13317{
13318 int skip;
13319
13320 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13321 return;
13322 skip = cmdloop(0);
13323 popfile();
13324 if (skip)
13325 exitshell();
13326}
13327
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013328/*
13329 * This routine is called when an error or an interrupt occurs in an
13330 * interactive shell and control is returned to the main command loop.
13331 */
13332static void
13333reset(void)
13334{
13335 /* from eval.c: */
13336 evalskip = 0;
13337 loopnest = 0;
13338 /* from input.c: */
13339 parselleft = parsenleft = 0; /* clear input buffer */
13340 popallfiles();
13341 /* from parser.c: */
13342 tokpushback = 0;
13343 checkkwd = 0;
13344 /* from redir.c: */
13345 clearredir(0);
13346}
13347
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013348#if PROFILE
13349static short profile_buf[16384];
13350extern int etext();
13351#endif
13352
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013353/*
13354 * Main routine. We initialize things, parse the arguments, execute
13355 * profiles if we're a login shell, and then call cmdloop to execute
13356 * commands. The setjmp call sets up the location to jump to when an
13357 * exception occurs. When an exception occurs the variable "state"
13358 * is used to figure out how far we had gotten.
13359 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013360int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013361int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013362{
13363 char *shinit;
13364 volatile int state;
13365 struct jmploc jmploc;
13366 struct stackmark smark;
13367
Denis Vlasenko01631112007-12-16 17:20:38 +000013368 /* Initialize global data */
13369 INIT_G_misc();
13370 INIT_G_memstack();
13371 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013372#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013373 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013374#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013375 INIT_G_cmdtable();
13376
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013377#if PROFILE
13378 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13379#endif
13380
13381#if ENABLE_FEATURE_EDITING
13382 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13383#endif
13384 state = 0;
13385 if (setjmp(jmploc.loc)) {
13386 int e;
13387 int s;
13388
13389 reset();
13390
13391 e = exception;
13392 if (e == EXERROR)
13393 exitstatus = 2;
13394 s = state;
13395 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13396 exitshell();
13397
13398 if (e == EXINT) {
13399 outcslow('\n', stderr);
13400 }
13401 popstackmark(&smark);
13402 FORCE_INT_ON; /* enable interrupts */
13403 if (s == 1)
13404 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013405 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013406 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013407 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013408 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013409 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013410 }
13411 exception_handler = &jmploc;
13412#if DEBUG
13413 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013414 trace_puts("Shell args: ");
13415 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013416#endif
13417 rootpid = getpid();
13418
13419#if ENABLE_ASH_RANDOM_SUPPORT
13420 rseed = rootpid + time(NULL);
13421#endif
13422 init();
13423 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013424 procargs(argv);
13425
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013426#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13427 if (iflag) {
13428 const char *hp = lookupvar("HISTFILE");
13429
13430 if (hp == NULL) {
13431 hp = lookupvar("HOME");
13432 if (hp != NULL) {
13433 char *defhp = concat_path_file(hp, ".ash_history");
13434 setvar("HISTFILE", defhp, 0);
13435 free(defhp);
13436 }
13437 }
13438 }
13439#endif
13440 if (argv[0] && argv[0][0] == '-')
13441 isloginsh = 1;
13442 if (isloginsh) {
13443 state = 1;
13444 read_profile("/etc/profile");
13445 state1:
13446 state = 2;
13447 read_profile(".profile");
13448 }
13449 state2:
13450 state = 3;
13451 if (
13452#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013453 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013454#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013455 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013456 ) {
13457 shinit = lookupvar("ENV");
13458 if (shinit != NULL && *shinit != '\0') {
13459 read_profile(shinit);
13460 }
13461 }
13462 state3:
13463 state = 4;
13464 if (minusc)
13465 evalstring(minusc, 0);
13466
13467 if (sflag || minusc == NULL) {
13468#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013469 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013470 const char *hp = lookupvar("HISTFILE");
13471
13472 if (hp != NULL)
13473 line_input_state->hist_file = hp;
13474 }
13475#endif
13476 state4: /* XXX ??? - why isn't this before the "if" statement */
13477 cmdloop(1);
13478 }
13479#if PROFILE
13480 monitor(0);
13481#endif
13482#ifdef GPROF
13483 {
13484 extern void _mcleanup(void);
13485 _mcleanup();
13486 }
13487#endif
13488 exitshell();
13489 /* NOTREACHED */
13490}
13491
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013492#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013493const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013494int main(int argc, char **argv)
13495{
13496 return ash_main(argc, argv);
13497}
13498#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013499
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013500
Eric Andersendf82f612001-06-28 07:46:40 +000013501/*-
13502 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013503 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013504 *
13505 * This code is derived from software contributed to Berkeley by
13506 * Kenneth Almquist.
13507 *
13508 * Redistribution and use in source and binary forms, with or without
13509 * modification, are permitted provided that the following conditions
13510 * are met:
13511 * 1. Redistributions of source code must retain the above copyright
13512 * notice, this list of conditions and the following disclaimer.
13513 * 2. Redistributions in binary form must reproduce the above copyright
13514 * notice, this list of conditions and the following disclaimer in the
13515 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013516 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013517 * may be used to endorse or promote products derived from this software
13518 * without specific prior written permission.
13519 *
13520 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13521 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13522 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13523 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13524 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13525 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13526 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13527 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13528 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13529 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13530 * SUCH DAMAGE.
13531 */