blob: 8c99e30efb7eb41e4b05df2a6497beaa8733af3b [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 Vlasenko448d30e2008-06-27 00:24:11 +0000204
205 /* Rarely referenced stuff */
206#if ENABLE_ASH_RANDOM_SUPPORT
207 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak) */
208 uint32_t random_LCG; /* LCG1 (fast but weak) */
209#endif
210 pid_t backgndpid; /* pid of last background process */
211 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000212};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000213extern struct globals_misc *const ash_ptr_to_globals_misc;
214#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko01631112007-12-16 17:20:38 +0000215#define rootpid (G_misc.rootpid )
216#define shlvl (G_misc.shlvl )
217#define minusc (G_misc.minusc )
218#define curdir (G_misc.curdir )
219#define physdir (G_misc.physdir )
220#define arg0 (G_misc.arg0 )
221#define exception_handler (G_misc.exception_handler)
222#define exception (G_misc.exception )
223#define suppressint (G_misc.suppressint )
224#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000225//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000226#define pendingsig (G_misc.pendingsig )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000227#define isloginsh (G_misc.isloginsh )
228#define nullstr (G_misc.nullstr )
229#define optlist (G_misc.optlist )
230#define sigmode (G_misc.sigmode )
231#define gotsig (G_misc.gotsig )
232#define trap (G_misc.trap )
233#define random_galois_LFSR (G_misc.random_galois_LFSR)
234#define random_LCG (G_misc.random_LCG )
235#define backgndpid (G_misc.backgndpid )
236#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000237#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000238 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
239 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000240 curdir = nullstr; \
241 physdir = nullstr; \
242} while (0)
243
244
245/* ============ Interrupts / exceptions */
246
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000247/*
Eric Andersen2870d962001-07-02 17:27:21 +0000248 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000249 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000250 * much more efficient and portable. (But hacking the kernel is so much
251 * more fun than worrying about efficiency and portability. :-))
252 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000253#define INT_OFF do { \
254 suppressint++; \
255 xbarrier(); \
256} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000257
258/*
259 * Called to raise an exception. Since C doesn't include exceptions, we
260 * just do a longjmp to the exception handler. The type of exception is
261 * stored in the global variable "exception".
262 */
263static void raise_exception(int) ATTRIBUTE_NORETURN;
264static void
265raise_exception(int e)
266{
267#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000268 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000269 abort();
270#endif
271 INT_OFF;
272 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000273 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000274}
275
276/*
277 * Called from trap.c when a SIGINT is received. (If the user specifies
278 * that SIGINT is to be trapped or ignored using the trap builtin, then
279 * this routine is not called.) Suppressint is nonzero when interrupts
280 * are held using the INT_OFF macro. (The test for iflag is just
281 * defensive programming.)
282 */
283static void raise_interrupt(void) ATTRIBUTE_NORETURN;
284static void
285raise_interrupt(void)
286{
287 int i;
288
289 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000290 /* Signal is not automatically unmasked after it is raised,
291 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000292 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000293 /* pendingsig = 0; - now done in onsig() */
294
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295 i = EXSIG;
296 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
297 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000298 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299 signal(SIGINT, SIG_DFL);
300 raise(SIGINT);
301 }
302 i = EXINT;
303 }
304 raise_exception(i);
305 /* NOTREACHED */
306}
307
308#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000309static void
310int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000311{
312 if (--suppressint == 0 && intpending) {
313 raise_interrupt();
314 }
315}
316#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000317static void
318force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000319{
320 suppressint = 0;
321 if (intpending)
322 raise_interrupt();
323}
324#define FORCE_INT_ON force_int_on()
325#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000326#define INT_ON do { \
327 xbarrier(); \
328 if (--suppressint == 0 && intpending) \
329 raise_interrupt(); \
330} while (0)
331#define FORCE_INT_ON do { \
332 xbarrier(); \
333 suppressint = 0; \
334 if (intpending) \
335 raise_interrupt(); \
336} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000337#endif /* ASH_OPTIMIZE_FOR_SIZE */
338
339#define SAVE_INT(v) ((v) = suppressint)
340
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000341#define RESTORE_INT(v) do { \
342 xbarrier(); \
343 suppressint = (v); \
344 if (suppressint == 0 && intpending) \
345 raise_interrupt(); \
346} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000347
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000348/*
349 * Ignore a signal. Only one usage site - in forkchild()
350 */
351static void
352ignoresig(int signo)
353{
354 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
355 signal(signo, SIG_IGN);
356 }
357 sigmode[signo - 1] = S_HARD_IGN;
358}
359
360/*
361 * Signal handler. Only one usage site - in setsignal()
362 */
363static void
364onsig(int signo)
365{
366 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000367 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000368
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000369 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000370 if (!suppressint) {
371 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000372 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000373 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000374 intpending = 1;
375 }
376}
377
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000378
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000379/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000380
Eric Andersenc470f442003-07-28 09:56:35 +0000381static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000382outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000383{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000384 INT_OFF;
385 fputs(p, file);
386 INT_ON;
387}
388
389static void
390flush_stdout_stderr(void)
391{
392 INT_OFF;
393 fflush(stdout);
394 fflush(stderr);
395 INT_ON;
396}
397
398static void
399flush_stderr(void)
400{
401 INT_OFF;
402 fflush(stderr);
403 INT_ON;
404}
405
406static void
407outcslow(int c, FILE *dest)
408{
409 INT_OFF;
410 putc(c, dest);
411 fflush(dest);
412 INT_ON;
413}
414
415static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
416static int
417out1fmt(const char *fmt, ...)
418{
419 va_list ap;
420 int r;
421
422 INT_OFF;
423 va_start(ap, fmt);
424 r = vprintf(fmt, ap);
425 va_end(ap);
426 INT_ON;
427 return r;
428}
429
430static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
431static int
432fmtstr(char *outbuf, size_t length, const char *fmt, ...)
433{
434 va_list ap;
435 int ret;
436
437 va_start(ap, fmt);
438 INT_OFF;
439 ret = vsnprintf(outbuf, length, fmt, ap);
440 va_end(ap);
441 INT_ON;
442 return ret;
443}
444
445static void
446out1str(const char *p)
447{
448 outstr(p, stdout);
449}
450
451static void
452out2str(const char *p)
453{
454 outstr(p, stderr);
455 flush_stderr();
456}
457
458
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000459/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000460
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000461/* control characters in argument strings */
462#define CTLESC '\201' /* escape next character */
463#define CTLVAR '\202' /* variable defn */
464#define CTLENDVAR '\203'
465#define CTLBACKQ '\204'
466#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
467/* CTLBACKQ | CTLQUOTE == '\205' */
468#define CTLARI '\206' /* arithmetic expression */
469#define CTLENDARI '\207'
470#define CTLQUOTEMARK '\210'
471
472/* variable substitution byte (follows CTLVAR) */
473#define VSTYPE 0x0f /* type of variable substitution */
474#define VSNUL 0x10 /* colon--treat the empty string as unset */
475#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
476
477/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000478#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
479#define VSMINUS 0x2 /* ${var-text} */
480#define VSPLUS 0x3 /* ${var+text} */
481#define VSQUESTION 0x4 /* ${var?message} */
482#define VSASSIGN 0x5 /* ${var=text} */
483#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
484#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
485#define VSTRIMLEFT 0x8 /* ${var#pattern} */
486#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
487#define VSLENGTH 0xa /* ${#var} */
488#if ENABLE_ASH_BASH_COMPAT
489#define VSSUBSTR 0xc /* ${var:position:length} */
490#define VSREPLACE 0xd /* ${var/pattern/replacement} */
491#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
492#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000493
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000494static const char dolatstr[] ALIGN1 = {
495 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
496};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000497
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000498#define NCMD 0
499#define NPIPE 1
500#define NREDIR 2
501#define NBACKGND 3
502#define NSUBSHELL 4
503#define NAND 5
504#define NOR 6
505#define NSEMI 7
506#define NIF 8
507#define NWHILE 9
508#define NUNTIL 10
509#define NFOR 11
510#define NCASE 12
511#define NCLIST 13
512#define NDEFUN 14
513#define NARG 15
514#define NTO 16
515#define NCLOBBER 17
516#define NFROM 18
517#define NFROMTO 19
518#define NAPPEND 20
519#define NTOFD 21
520#define NFROMFD 22
521#define NHERE 23
522#define NXHERE 24
523#define NNOT 25
524
525union node;
526
527struct ncmd {
528 int type;
529 union node *assign;
530 union node *args;
531 union node *redirect;
532};
533
534struct npipe {
535 int type;
536 int backgnd;
537 struct nodelist *cmdlist;
538};
539
540struct nredir {
541 int type;
542 union node *n;
543 union node *redirect;
544};
545
546struct nbinary {
547 int type;
548 union node *ch1;
549 union node *ch2;
550};
551
552struct nif {
553 int type;
554 union node *test;
555 union node *ifpart;
556 union node *elsepart;
557};
558
559struct nfor {
560 int type;
561 union node *args;
562 union node *body;
563 char *var;
564};
565
566struct ncase {
567 int type;
568 union node *expr;
569 union node *cases;
570};
571
572struct nclist {
573 int type;
574 union node *next;
575 union node *pattern;
576 union node *body;
577};
578
579struct narg {
580 int type;
581 union node *next;
582 char *text;
583 struct nodelist *backquote;
584};
585
586struct nfile {
587 int type;
588 union node *next;
589 int fd;
590 union node *fname;
591 char *expfname;
592};
593
594struct ndup {
595 int type;
596 union node *next;
597 int fd;
598 int dupfd;
599 union node *vname;
600};
601
602struct nhere {
603 int type;
604 union node *next;
605 int fd;
606 union node *doc;
607};
608
609struct nnot {
610 int type;
611 union node *com;
612};
613
614union node {
615 int type;
616 struct ncmd ncmd;
617 struct npipe npipe;
618 struct nredir nredir;
619 struct nbinary nbinary;
620 struct nif nif;
621 struct nfor nfor;
622 struct ncase ncase;
623 struct nclist nclist;
624 struct narg narg;
625 struct nfile nfile;
626 struct ndup ndup;
627 struct nhere nhere;
628 struct nnot nnot;
629};
630
631struct nodelist {
632 struct nodelist *next;
633 union node *n;
634};
635
636struct funcnode {
637 int count;
638 union node n;
639};
640
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000641/*
642 * Free a parse tree.
643 */
644static void
645freefunc(struct funcnode *f)
646{
647 if (f && --f->count < 0)
648 free(f);
649}
650
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000651
652/* ============ Debugging output */
653
654#if DEBUG
655
656static FILE *tracefile;
657
658static void
659trace_printf(const char *fmt, ...)
660{
661 va_list va;
662
663 if (debug != 1)
664 return;
665 va_start(va, fmt);
666 vfprintf(tracefile, fmt, va);
667 va_end(va);
668}
669
670static void
671trace_vprintf(const char *fmt, va_list va)
672{
673 if (debug != 1)
674 return;
675 vfprintf(tracefile, fmt, va);
676}
677
678static void
679trace_puts(const char *s)
680{
681 if (debug != 1)
682 return;
683 fputs(s, tracefile);
684}
685
686static void
687trace_puts_quoted(char *s)
688{
689 char *p;
690 char c;
691
692 if (debug != 1)
693 return;
694 putc('"', tracefile);
695 for (p = s; *p; p++) {
696 switch (*p) {
697 case '\n': c = 'n'; goto backslash;
698 case '\t': c = 't'; goto backslash;
699 case '\r': c = 'r'; goto backslash;
700 case '"': c = '"'; goto backslash;
701 case '\\': c = '\\'; goto backslash;
702 case CTLESC: c = 'e'; goto backslash;
703 case CTLVAR: c = 'v'; goto backslash;
704 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
705 case CTLBACKQ: c = 'q'; goto backslash;
706 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
707 backslash:
708 putc('\\', tracefile);
709 putc(c, tracefile);
710 break;
711 default:
712 if (*p >= ' ' && *p <= '~')
713 putc(*p, tracefile);
714 else {
715 putc('\\', tracefile);
716 putc(*p >> 6 & 03, tracefile);
717 putc(*p >> 3 & 07, tracefile);
718 putc(*p & 07, tracefile);
719 }
720 break;
721 }
722 }
723 putc('"', tracefile);
724}
725
726static void
727trace_puts_args(char **ap)
728{
729 if (debug != 1)
730 return;
731 if (!*ap)
732 return;
733 while (1) {
734 trace_puts_quoted(*ap);
735 if (!*++ap) {
736 putc('\n', tracefile);
737 break;
738 }
739 putc(' ', tracefile);
740 }
741}
742
743static void
744opentrace(void)
745{
746 char s[100];
747#ifdef O_APPEND
748 int flags;
749#endif
750
751 if (debug != 1) {
752 if (tracefile)
753 fflush(tracefile);
754 /* leave open because libedit might be using it */
755 return;
756 }
757 strcpy(s, "./trace");
758 if (tracefile) {
759 if (!freopen(s, "a", tracefile)) {
760 fprintf(stderr, "Can't re-open %s\n", s);
761 debug = 0;
762 return;
763 }
764 } else {
765 tracefile = fopen(s, "a");
766 if (tracefile == NULL) {
767 fprintf(stderr, "Can't open %s\n", s);
768 debug = 0;
769 return;
770 }
771 }
772#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000773 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000774 if (flags >= 0)
775 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
776#endif
777 setlinebuf(tracefile);
778 fputs("\nTracing started.\n", tracefile);
779}
780
781static void
782indent(int amount, char *pfx, FILE *fp)
783{
784 int i;
785
786 for (i = 0; i < amount; i++) {
787 if (pfx && i == amount - 1)
788 fputs(pfx, fp);
789 putc('\t', fp);
790 }
791}
792
793/* little circular references here... */
794static void shtree(union node *n, int ind, char *pfx, FILE *fp);
795
796static void
797sharg(union node *arg, FILE *fp)
798{
799 char *p;
800 struct nodelist *bqlist;
801 int subtype;
802
803 if (arg->type != NARG) {
804 out1fmt("<node type %d>\n", arg->type);
805 abort();
806 }
807 bqlist = arg->narg.backquote;
808 for (p = arg->narg.text; *p; p++) {
809 switch (*p) {
810 case CTLESC:
811 putc(*++p, fp);
812 break;
813 case CTLVAR:
814 putc('$', fp);
815 putc('{', fp);
816 subtype = *++p;
817 if (subtype == VSLENGTH)
818 putc('#', fp);
819
820 while (*p != '=')
821 putc(*p++, fp);
822
823 if (subtype & VSNUL)
824 putc(':', fp);
825
826 switch (subtype & VSTYPE) {
827 case VSNORMAL:
828 putc('}', fp);
829 break;
830 case VSMINUS:
831 putc('-', fp);
832 break;
833 case VSPLUS:
834 putc('+', fp);
835 break;
836 case VSQUESTION:
837 putc('?', fp);
838 break;
839 case VSASSIGN:
840 putc('=', fp);
841 break;
842 case VSTRIMLEFT:
843 putc('#', fp);
844 break;
845 case VSTRIMLEFTMAX:
846 putc('#', fp);
847 putc('#', fp);
848 break;
849 case VSTRIMRIGHT:
850 putc('%', fp);
851 break;
852 case VSTRIMRIGHTMAX:
853 putc('%', fp);
854 putc('%', fp);
855 break;
856 case VSLENGTH:
857 break;
858 default:
859 out1fmt("<subtype %d>", subtype);
860 }
861 break;
862 case CTLENDVAR:
863 putc('}', fp);
864 break;
865 case CTLBACKQ:
866 case CTLBACKQ|CTLQUOTE:
867 putc('$', fp);
868 putc('(', fp);
869 shtree(bqlist->n, -1, NULL, fp);
870 putc(')', fp);
871 break;
872 default:
873 putc(*p, fp);
874 break;
875 }
876 }
877}
878
879static void
880shcmd(union node *cmd, FILE *fp)
881{
882 union node *np;
883 int first;
884 const char *s;
885 int dftfd;
886
887 first = 1;
888 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000889 if (!first)
890 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000891 sharg(np, fp);
892 first = 0;
893 }
894 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000895 if (!first)
896 putc(' ', fp);
897 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000898 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000899 case NTO: s = ">>"+1; dftfd = 1; break;
900 case NCLOBBER: s = ">|"; dftfd = 1; break;
901 case NAPPEND: s = ">>"; dftfd = 1; break;
902 case NTOFD: s = ">&"; dftfd = 1; break;
903 case NFROM: s = "<"; break;
904 case NFROMFD: s = "<&"; break;
905 case NFROMTO: s = "<>"; break;
906 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000907 }
908 if (np->nfile.fd != dftfd)
909 fprintf(fp, "%d", np->nfile.fd);
910 fputs(s, fp);
911 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
912 fprintf(fp, "%d", np->ndup.dupfd);
913 } else {
914 sharg(np->nfile.fname, fp);
915 }
916 first = 0;
917 }
918}
919
920static void
921shtree(union node *n, int ind, char *pfx, FILE *fp)
922{
923 struct nodelist *lp;
924 const char *s;
925
926 if (n == NULL)
927 return;
928
929 indent(ind, pfx, fp);
930 switch (n->type) {
931 case NSEMI:
932 s = "; ";
933 goto binop;
934 case NAND:
935 s = " && ";
936 goto binop;
937 case NOR:
938 s = " || ";
939 binop:
940 shtree(n->nbinary.ch1, ind, NULL, fp);
941 /* if (ind < 0) */
942 fputs(s, fp);
943 shtree(n->nbinary.ch2, ind, NULL, fp);
944 break;
945 case NCMD:
946 shcmd(n, fp);
947 if (ind >= 0)
948 putc('\n', fp);
949 break;
950 case NPIPE:
951 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
952 shcmd(lp->n, fp);
953 if (lp->next)
954 fputs(" | ", fp);
955 }
956 if (n->npipe.backgnd)
957 fputs(" &", fp);
958 if (ind >= 0)
959 putc('\n', fp);
960 break;
961 default:
962 fprintf(fp, "<node type %d>", n->type);
963 if (ind >= 0)
964 putc('\n', fp);
965 break;
966 }
967}
968
969static void
970showtree(union node *n)
971{
972 trace_puts("showtree called\n");
973 shtree(n, 1, NULL, stdout);
974}
975
976#define TRACE(param) trace_printf param
977#define TRACEV(param) trace_vprintf param
978
979#else
980
981#define TRACE(param)
982#define TRACEV(param)
983
984#endif /* DEBUG */
985
986
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000987/* ============ Parser data */
988
989/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000990 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
991 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000992struct strlist {
993 struct strlist *next;
994 char *text;
995};
996
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000997struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000998
Denis Vlasenkob012b102007-02-19 22:43:01 +0000999struct strpush {
1000 struct strpush *prev; /* preceding string on stack */
1001 char *prevstring;
1002 int prevnleft;
1003#if ENABLE_ASH_ALIAS
1004 struct alias *ap; /* if push was associated with an alias */
1005#endif
1006 char *string; /* remember the string since it may change */
1007};
1008
1009struct parsefile {
1010 struct parsefile *prev; /* preceding file on stack */
1011 int linno; /* current line */
1012 int fd; /* file descriptor (or -1 if string) */
1013 int nleft; /* number of chars left in this line */
1014 int lleft; /* number of chars left in this buffer */
1015 char *nextc; /* next char in buffer */
1016 char *buf; /* input buffer */
1017 struct strpush *strpush; /* for pushing strings at this level */
1018 struct strpush basestrpush; /* so pushing one is fast */
1019};
1020
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001021static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001022static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001023static int startlinno; /* line # where last token started */
1024static char *commandname; /* currently executing command */
1025static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001026static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001027
1028
1029/* ============ Message printing */
1030
1031static void
1032ash_vmsg(const char *msg, va_list ap)
1033{
1034 fprintf(stderr, "%s: ", arg0);
1035 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001036 if (strcmp(arg0, commandname))
1037 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001038 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001039 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001040 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001041 vfprintf(stderr, msg, ap);
1042 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001043}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001044
1045/*
1046 * Exverror is called to raise the error exception. If the second argument
1047 * is not NULL then error prints an error message using printf style
1048 * formatting. It then raises the error exception.
1049 */
1050static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1051static void
1052ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001053{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001054#if DEBUG
1055 if (msg) {
1056 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1057 TRACEV((msg, ap));
1058 TRACE(("\") pid=%d\n", getpid()));
1059 } else
1060 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1061 if (msg)
1062#endif
1063 ash_vmsg(msg, ap);
1064
1065 flush_stdout_stderr();
1066 raise_exception(cond);
1067 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001068}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001069
1070static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1071static void
1072ash_msg_and_raise_error(const char *msg, ...)
1073{
1074 va_list ap;
1075
1076 va_start(ap, msg);
1077 ash_vmsg_and_raise(EXERROR, msg, ap);
1078 /* NOTREACHED */
1079 va_end(ap);
1080}
1081
1082static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1083static void
1084ash_msg_and_raise(int cond, const char *msg, ...)
1085{
1086 va_list ap;
1087
1088 va_start(ap, msg);
1089 ash_vmsg_and_raise(cond, msg, ap);
1090 /* NOTREACHED */
1091 va_end(ap);
1092}
1093
1094/*
1095 * error/warning routines for external builtins
1096 */
1097static void
1098ash_msg(const char *fmt, ...)
1099{
1100 va_list ap;
1101
1102 va_start(ap, fmt);
1103 ash_vmsg(fmt, ap);
1104 va_end(ap);
1105}
1106
1107/*
1108 * Return a string describing an error. The returned string may be a
1109 * pointer to a static buffer that will be overwritten on the next call.
1110 * Action describes the operation that got the error.
1111 */
1112static const char *
1113errmsg(int e, const char *em)
1114{
1115 if (e == ENOENT || e == ENOTDIR) {
1116 return em;
1117 }
1118 return strerror(e);
1119}
1120
1121
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001122/* ============ Memory allocation */
1123
1124/*
1125 * It appears that grabstackstr() will barf with such alignments
1126 * because stalloc() will return a string allocated in a new stackblock.
1127 */
1128#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1129enum {
1130 /* Most machines require the value returned from malloc to be aligned
1131 * in some way. The following macro will get this right
1132 * on many machines. */
1133 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1134 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001135 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001136};
1137
1138struct stack_block {
1139 struct stack_block *prev;
1140 char space[MINSIZE];
1141};
1142
1143struct stackmark {
1144 struct stack_block *stackp;
1145 char *stacknxt;
1146 size_t stacknleft;
1147 struct stackmark *marknext;
1148};
1149
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001150
Denis Vlasenko01631112007-12-16 17:20:38 +00001151struct globals_memstack {
1152 struct stack_block *g_stackp; // = &stackbase;
1153 struct stackmark *markp;
1154 char *g_stacknxt; // = stackbase.space;
1155 char *sstrend; // = stackbase.space + MINSIZE;
1156 size_t g_stacknleft; // = MINSIZE;
1157 int herefd; // = -1;
1158 struct stack_block stackbase;
1159};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001160extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1161#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001162#define g_stackp (G_memstack.g_stackp )
1163#define markp (G_memstack.markp )
1164#define g_stacknxt (G_memstack.g_stacknxt )
1165#define sstrend (G_memstack.sstrend )
1166#define g_stacknleft (G_memstack.g_stacknleft)
1167#define herefd (G_memstack.herefd )
1168#define stackbase (G_memstack.stackbase )
1169#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001170 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1171 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001172 g_stackp = &stackbase; \
1173 g_stacknxt = stackbase.space; \
1174 g_stacknleft = MINSIZE; \
1175 sstrend = stackbase.space + MINSIZE; \
1176 herefd = -1; \
1177} while (0)
1178
1179#define stackblock() ((void *)g_stacknxt)
1180#define stackblocksize() g_stacknleft
1181
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001182
1183static void *
1184ckrealloc(void * p, size_t nbytes)
1185{
1186 p = realloc(p, nbytes);
1187 if (!p)
1188 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1189 return p;
1190}
1191
1192static void *
1193ckmalloc(size_t nbytes)
1194{
1195 return ckrealloc(NULL, nbytes);
1196}
1197
Denis Vlasenko597906c2008-02-20 16:38:54 +00001198static void *
1199ckzalloc(size_t nbytes)
1200{
1201 return memset(ckmalloc(nbytes), 0, nbytes);
1202}
1203
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001204/*
1205 * Make a copy of a string in safe storage.
1206 */
1207static char *
1208ckstrdup(const char *s)
1209{
1210 char *p = strdup(s);
1211 if (!p)
1212 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1213 return p;
1214}
1215
1216/*
1217 * Parse trees for commands are allocated in lifo order, so we use a stack
1218 * to make this more efficient, and also to avoid all sorts of exception
1219 * handling code to handle interrupts in the middle of a parse.
1220 *
1221 * The size 504 was chosen because the Ultrix malloc handles that size
1222 * well.
1223 */
1224static void *
1225stalloc(size_t nbytes)
1226{
1227 char *p;
1228 size_t aligned;
1229
1230 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001231 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001232 size_t len;
1233 size_t blocksize;
1234 struct stack_block *sp;
1235
1236 blocksize = aligned;
1237 if (blocksize < MINSIZE)
1238 blocksize = MINSIZE;
1239 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1240 if (len < blocksize)
1241 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1242 INT_OFF;
1243 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001244 sp->prev = g_stackp;
1245 g_stacknxt = sp->space;
1246 g_stacknleft = blocksize;
1247 sstrend = g_stacknxt + blocksize;
1248 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001249 INT_ON;
1250 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001251 p = g_stacknxt;
1252 g_stacknxt += aligned;
1253 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001254 return p;
1255}
1256
Denis Vlasenko597906c2008-02-20 16:38:54 +00001257static void *
1258stzalloc(size_t nbytes)
1259{
1260 return memset(stalloc(nbytes), 0, nbytes);
1261}
1262
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001263static void
1264stunalloc(void *p)
1265{
1266#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001267 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001268 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001269 abort();
1270 }
1271#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001272 g_stacknleft += g_stacknxt - (char *)p;
1273 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274}
1275
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001276/*
1277 * Like strdup but works with the ash stack.
1278 */
1279static char *
1280ststrdup(const char *p)
1281{
1282 size_t len = strlen(p) + 1;
1283 return memcpy(stalloc(len), p, len);
1284}
1285
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001286static void
1287setstackmark(struct stackmark *mark)
1288{
Denis Vlasenko01631112007-12-16 17:20:38 +00001289 mark->stackp = g_stackp;
1290 mark->stacknxt = g_stacknxt;
1291 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001292 mark->marknext = markp;
1293 markp = mark;
1294}
1295
1296static void
1297popstackmark(struct stackmark *mark)
1298{
1299 struct stack_block *sp;
1300
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001301 if (!mark->stackp)
1302 return;
1303
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001304 INT_OFF;
1305 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001306 while (g_stackp != mark->stackp) {
1307 sp = g_stackp;
1308 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001309 free(sp);
1310 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001311 g_stacknxt = mark->stacknxt;
1312 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001313 sstrend = mark->stacknxt + mark->stacknleft;
1314 INT_ON;
1315}
1316
1317/*
1318 * When the parser reads in a string, it wants to stick the string on the
1319 * stack and only adjust the stack pointer when it knows how big the
1320 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1321 * of space on top of the stack and stackblocklen returns the length of
1322 * this block. Growstackblock will grow this space by at least one byte,
1323 * possibly moving it (like realloc). Grabstackblock actually allocates the
1324 * part of the block that has been used.
1325 */
1326static void
1327growstackblock(void)
1328{
1329 size_t newlen;
1330
Denis Vlasenko01631112007-12-16 17:20:38 +00001331 newlen = g_stacknleft * 2;
1332 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1334 if (newlen < 128)
1335 newlen += 128;
1336
Denis Vlasenko01631112007-12-16 17:20:38 +00001337 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338 struct stack_block *oldstackp;
1339 struct stackmark *xmark;
1340 struct stack_block *sp;
1341 struct stack_block *prevstackp;
1342 size_t grosslen;
1343
1344 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001345 oldstackp = g_stackp;
1346 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347 prevstackp = sp->prev;
1348 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1349 sp = ckrealloc(sp, grosslen);
1350 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001351 g_stackp = sp;
1352 g_stacknxt = sp->space;
1353 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001354 sstrend = sp->space + newlen;
1355
1356 /*
1357 * Stack marks pointing to the start of the old block
1358 * must be relocated to point to the new block
1359 */
1360 xmark = markp;
1361 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 xmark->stackp = g_stackp;
1363 xmark->stacknxt = g_stacknxt;
1364 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001365 xmark = xmark->marknext;
1366 }
1367 INT_ON;
1368 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001370 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001371 char *p = stalloc(newlen);
1372
1373 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001374 g_stacknxt = memcpy(p, oldspace, oldlen);
1375 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001376 }
1377}
1378
1379static void
1380grabstackblock(size_t len)
1381{
1382 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001383 g_stacknxt += len;
1384 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001385}
1386
1387/*
1388 * The following routines are somewhat easier to use than the above.
1389 * The user declares a variable of type STACKSTR, which may be declared
1390 * to be a register. The macro STARTSTACKSTR initializes things. Then
1391 * the user uses the macro STPUTC to add characters to the string. In
1392 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1393 * grown as necessary. When the user is done, she can just leave the
1394 * string there and refer to it using stackblock(). Or she can allocate
1395 * the space for it using grabstackstr(). If it is necessary to allow
1396 * someone else to use the stack temporarily and then continue to grow
1397 * the string, the user should use grabstack to allocate the space, and
1398 * then call ungrabstr(p) to return to the previous mode of operation.
1399 *
1400 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1401 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1402 * is space for at least one character.
1403 */
1404static void *
1405growstackstr(void)
1406{
1407 size_t len = stackblocksize();
1408 if (herefd >= 0 && len >= 1024) {
1409 full_write(herefd, stackblock(), len);
1410 return stackblock();
1411 }
1412 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001413 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001414}
1415
1416/*
1417 * Called from CHECKSTRSPACE.
1418 */
1419static char *
1420makestrspace(size_t newlen, char *p)
1421{
Denis Vlasenko01631112007-12-16 17:20:38 +00001422 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423 size_t size = stackblocksize();
1424
1425 for (;;) {
1426 size_t nleft;
1427
1428 size = stackblocksize();
1429 nleft = size - len;
1430 if (nleft >= newlen)
1431 break;
1432 growstackblock();
1433 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001434 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001435}
1436
1437static char *
1438stack_nputstr(const char *s, size_t n, char *p)
1439{
1440 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001441 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001442 return p;
1443}
1444
1445static char *
1446stack_putstr(const char *s, char *p)
1447{
1448 return stack_nputstr(s, strlen(s), p);
1449}
1450
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001451static char *
1452_STPUTC(int c, char *p)
1453{
1454 if (p == sstrend)
1455 p = growstackstr();
1456 *p++ = c;
1457 return p;
1458}
1459
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001460#define STARTSTACKSTR(p) ((p) = stackblock())
1461#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001462#define CHECKSTRSPACE(n, p) do { \
1463 char *q = (p); \
1464 size_t l = (n); \
1465 size_t m = sstrend - q; \
1466 if (l > m) \
1467 (p) = makestrspace(l, q); \
1468} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001469#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001470#define STACKSTRNUL(p) do { \
1471 if ((p) == sstrend) \
1472 (p) = growstackstr(); \
1473 *(p) = '\0'; \
1474} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001475#define STUNPUTC(p) (--(p))
1476#define STTOPC(p) ((p)[-1])
1477#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001478
1479#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001480#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001481#define stackstrend() ((void *)sstrend)
1482
1483
1484/* ============ String helpers */
1485
1486/*
1487 * prefix -- see if pfx is a prefix of string.
1488 */
1489static char *
1490prefix(const char *string, const char *pfx)
1491{
1492 while (*pfx) {
1493 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001494 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001495 }
1496 return (char *) string;
1497}
1498
1499/*
1500 * Check for a valid number. This should be elsewhere.
1501 */
1502static int
1503is_number(const char *p)
1504{
1505 do {
1506 if (!isdigit(*p))
1507 return 0;
1508 } while (*++p != '\0');
1509 return 1;
1510}
1511
1512/*
1513 * Convert a string of digits to an integer, printing an error message on
1514 * failure.
1515 */
1516static int
1517number(const char *s)
1518{
1519 if (!is_number(s))
1520 ash_msg_and_raise_error(illnum, s);
1521 return atoi(s);
1522}
1523
1524/*
1525 * Produce a possibly single quoted string suitable as input to the shell.
1526 * The return string is allocated on the stack.
1527 */
1528static char *
1529single_quote(const char *s)
1530{
1531 char *p;
1532
1533 STARTSTACKSTR(p);
1534
1535 do {
1536 char *q;
1537 size_t len;
1538
1539 len = strchrnul(s, '\'') - s;
1540
1541 q = p = makestrspace(len + 3, p);
1542
1543 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001544 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001545 *q++ = '\'';
1546 s += len;
1547
1548 STADJUST(q - p, p);
1549
1550 len = strspn(s, "'");
1551 if (!len)
1552 break;
1553
1554 q = p = makestrspace(len + 3, p);
1555
1556 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001557 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001558 *q++ = '"';
1559 s += len;
1560
1561 STADJUST(q - p, p);
1562 } while (*s);
1563
1564 USTPUTC(0, p);
1565
1566 return stackblock();
1567}
1568
1569
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001570/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001571
1572static char **argptr; /* argument list for builtin commands */
1573static char *optionarg; /* set by nextopt (like getopt) */
1574static char *optptr; /* used by nextopt */
1575
1576/*
1577 * XXX - should get rid of. have all builtins use getopt(3). the
1578 * library getopt must have the BSD extension static variable "optreset"
1579 * otherwise it can't be used within the shell safely.
1580 *
1581 * Standard option processing (a la getopt) for builtin routines. The
1582 * only argument that is passed to nextopt is the option string; the
1583 * other arguments are unnecessary. It return the character, or '\0' on
1584 * end of input.
1585 */
1586static int
1587nextopt(const char *optstring)
1588{
1589 char *p;
1590 const char *q;
1591 char c;
1592
1593 p = optptr;
1594 if (p == NULL || *p == '\0') {
1595 p = *argptr;
1596 if (p == NULL || *p != '-' || *++p == '\0')
1597 return '\0';
1598 argptr++;
1599 if (LONE_DASH(p)) /* check for "--" */
1600 return '\0';
1601 }
1602 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001603 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001604 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001605 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001606 if (*++q == ':')
1607 q++;
1608 }
1609 if (*++q == ':') {
1610 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001611 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001612 optionarg = p;
1613 p = NULL;
1614 }
1615 optptr = p;
1616 return c;
1617}
1618
1619
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001620/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001621
Denis Vlasenko01631112007-12-16 17:20:38 +00001622/*
1623 * The parsefile structure pointed to by the global variable parsefile
1624 * contains information about the current file being read.
1625 */
1626struct redirtab {
1627 struct redirtab *next;
1628 int renamed[10];
1629 int nullredirs;
1630};
1631
1632struct shparam {
1633 int nparam; /* # of positional parameters (without $0) */
1634#if ENABLE_ASH_GETOPTS
1635 int optind; /* next parameter to be processed by getopts */
1636 int optoff; /* used by getopts */
1637#endif
1638 unsigned char malloced; /* if parameter list dynamically allocated */
1639 char **p; /* parameter list */
1640};
1641
1642/*
1643 * Free the list of positional parameters.
1644 */
1645static void
1646freeparam(volatile struct shparam *param)
1647{
1648 char **ap;
1649
1650 if (param->malloced) {
1651 for (ap = param->p; *ap; ap++)
1652 free(*ap);
1653 free(param->p);
1654 }
1655}
1656
1657#if ENABLE_ASH_GETOPTS
1658static void getoptsreset(const char *value);
1659#endif
1660
1661struct var {
1662 struct var *next; /* next entry in hash list */
1663 int flags; /* flags are defined above */
1664 const char *text; /* name=value */
1665 void (*func)(const char *); /* function to be called when */
1666 /* the variable gets set/unset */
1667};
1668
1669struct localvar {
1670 struct localvar *next; /* next local variable in list */
1671 struct var *vp; /* the variable that was made local */
1672 int flags; /* saved flags */
1673 const char *text; /* saved text */
1674};
1675
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001676/* flags */
1677#define VEXPORT 0x01 /* variable is exported */
1678#define VREADONLY 0x02 /* variable cannot be modified */
1679#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1680#define VTEXTFIXED 0x08 /* text is statically allocated */
1681#define VSTACK 0x10 /* text is allocated on the stack */
1682#define VUNSET 0x20 /* the variable is not set */
1683#define VNOFUNC 0x40 /* don't call the callback function */
1684#define VNOSET 0x80 /* do not set variable - just readonly test */
1685#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001686#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001687# define VDYNAMIC 0x200 /* dynamic variable */
1688#else
1689# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690#endif
1691
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001693static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001694#define defifs (defifsvar + 4)
1695#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001696static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001697#endif
1698
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001699
Denis Vlasenko01631112007-12-16 17:20:38 +00001700/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001701#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001702static void
1703change_lc_all(const char *value)
1704{
1705 if (value && *value != '\0')
1706 setlocale(LC_ALL, value);
1707}
1708static void
1709change_lc_ctype(const char *value)
1710{
1711 if (value && *value != '\0')
1712 setlocale(LC_CTYPE, value);
1713}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001714#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715#if ENABLE_ASH_MAIL
1716static void chkmail(void);
1717static void changemail(const char *);
1718#endif
1719static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720#if ENABLE_ASH_RANDOM_SUPPORT
1721static void change_random(const char *);
1722#endif
1723
Denis Vlasenko01631112007-12-16 17:20:38 +00001724static const struct {
1725 int flags;
1726 const char *text;
1727 void (*func)(const char *);
1728} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001730 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001732 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001735 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1736 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001738 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1739 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1740 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1741 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#endif
1745#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001746 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#endif
1748#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1750 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#endif
1752#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754#endif
1755};
1756
Denis Vlasenko01631112007-12-16 17:20:38 +00001757
1758struct globals_var {
1759 struct shparam shellparam; /* $@ current positional parameters */
1760 struct redirtab *redirlist;
1761 int g_nullredirs;
1762 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1763 struct var *vartab[VTABSIZE];
1764 struct var varinit[ARRAY_SIZE(varinit_data)];
1765};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001766extern struct globals_var *const ash_ptr_to_globals_var;
1767#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001768#define shellparam (G_var.shellparam )
1769#define redirlist (G_var.redirlist )
1770#define g_nullredirs (G_var.g_nullredirs )
1771#define preverrout_fd (G_var.preverrout_fd)
1772#define vartab (G_var.vartab )
1773#define varinit (G_var.varinit )
1774#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001775 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001776 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1777 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001778 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1779 varinit[i].flags = varinit_data[i].flags; \
1780 varinit[i].text = varinit_data[i].text; \
1781 varinit[i].func = varinit_data[i].func; \
1782 } \
1783} while (0)
1784
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001785#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001787# define vmail (&vifs)[1]
1788# define vmpath (&vmail)[1]
1789# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001791# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001792#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001793#define vps1 (&vpath)[1]
1794#define vps2 (&vps1)[1]
1795#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001797# define voptind (&vps4)[1]
1798# if ENABLE_ASH_RANDOM_SUPPORT
1799# define vrandom (&voptind)[1]
1800# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001801#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001802# if ENABLE_ASH_RANDOM_SUPPORT
1803# define vrandom (&vps4)[1]
1804# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001806
1807/*
1808 * The following macros access the values of the above variables.
1809 * They have to skip over the name. They return the null string
1810 * for unset variables.
1811 */
1812#define ifsval() (vifs.text + 4)
1813#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001814#if ENABLE_ASH_MAIL
1815# define mailval() (vmail.text + 5)
1816# define mpathval() (vmpath.text + 9)
1817# define mpathset() ((vmpath.flags & VUNSET) == 0)
1818#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819#define pathval() (vpath.text + 5)
1820#define ps1val() (vps1.text + 4)
1821#define ps2val() (vps2.text + 4)
1822#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001823#if ENABLE_ASH_GETOPTS
1824# define optindval() (voptind.text + 7)
1825#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001826
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001827
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001828#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1829#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1830
Denis Vlasenko01631112007-12-16 17:20:38 +00001831#if ENABLE_ASH_GETOPTS
1832static void
1833getoptsreset(const char *value)
1834{
1835 shellparam.optind = number(value);
1836 shellparam.optoff = -1;
1837}
1838#endif
1839
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840/*
1841 * Return of a legal variable name (a letter or underscore followed by zero or
1842 * more letters, underscores, and digits).
1843 */
1844static char *
1845endofname(const char *name)
1846{
1847 char *p;
1848
1849 p = (char *) name;
1850 if (!is_name(*p))
1851 return p;
1852 while (*++p) {
1853 if (!is_in_name(*p))
1854 break;
1855 }
1856 return p;
1857}
1858
1859/*
1860 * Compares two strings up to the first = or '\0'. The first
1861 * string must be terminated by '='; the second may be terminated by
1862 * either '=' or '\0'.
1863 */
1864static int
1865varcmp(const char *p, const char *q)
1866{
1867 int c, d;
1868
1869 while ((c = *p) == (d = *q)) {
1870 if (!c || c == '=')
1871 goto out;
1872 p++;
1873 q++;
1874 }
1875 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001876 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001877 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001878 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879 out:
1880 return c - d;
1881}
1882
1883static int
1884varequal(const char *a, const char *b)
1885{
1886 return !varcmp(a, b);
1887}
1888
1889/*
1890 * Find the appropriate entry in the hash table from the name.
1891 */
1892static struct var **
1893hashvar(const char *p)
1894{
1895 unsigned hashval;
1896
1897 hashval = ((unsigned char) *p) << 4;
1898 while (*p && *p != '=')
1899 hashval += (unsigned char) *p++;
1900 return &vartab[hashval % VTABSIZE];
1901}
1902
1903static int
1904vpcmp(const void *a, const void *b)
1905{
1906 return varcmp(*(const char **)a, *(const char **)b);
1907}
1908
1909/*
1910 * This routine initializes the builtin variables.
1911 */
1912static void
1913initvar(void)
1914{
1915 struct var *vp;
1916 struct var *end;
1917 struct var **vpp;
1918
1919 /*
1920 * PS1 depends on uid
1921 */
1922#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1923 vps1.text = "PS1=\\w \\$ ";
1924#else
1925 if (!geteuid())
1926 vps1.text = "PS1=# ";
1927#endif
1928 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001929 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001930 do {
1931 vpp = hashvar(vp->text);
1932 vp->next = *vpp;
1933 *vpp = vp;
1934 } while (++vp < end);
1935}
1936
1937static struct var **
1938findvar(struct var **vpp, const char *name)
1939{
1940 for (; *vpp; vpp = &(*vpp)->next) {
1941 if (varequal((*vpp)->text, name)) {
1942 break;
1943 }
1944 }
1945 return vpp;
1946}
1947
1948/*
1949 * Find the value of a variable. Returns NULL if not set.
1950 */
1951static char *
1952lookupvar(const char *name)
1953{
1954 struct var *v;
1955
1956 v = *findvar(hashvar(name), name);
1957 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001958#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959 /*
1960 * Dynamic variables are implemented roughly the same way they are
1961 * in bash. Namely, they're "special" so long as they aren't unset.
1962 * As soon as they're unset, they're no longer dynamic, and dynamic
1963 * lookup will no longer happen at that point. -- PFM.
1964 */
1965 if ((v->flags & VDYNAMIC))
1966 (*v->func)(NULL);
1967#endif
1968 if (!(v->flags & VUNSET))
1969 return strchrnul(v->text, '=') + 1;
1970 }
1971 return NULL;
1972}
1973
1974/*
1975 * Search the environment of a builtin command.
1976 */
1977static char *
1978bltinlookup(const char *name)
1979{
1980 struct strlist *sp;
1981
1982 for (sp = cmdenviron; sp; sp = sp->next) {
1983 if (varequal(sp->text, name))
1984 return strchrnul(sp->text, '=') + 1;
1985 }
1986 return lookupvar(name);
1987}
1988
1989/*
1990 * Same as setvar except that the variable and value are passed in
1991 * the first argument as name=value. Since the first argument will
1992 * be actually stored in the table, it should not be a string that
1993 * will go away.
1994 * Called with interrupts off.
1995 */
1996static void
1997setvareq(char *s, int flags)
1998{
1999 struct var *vp, **vpp;
2000
2001 vpp = hashvar(s);
2002 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2003 vp = *findvar(vpp, s);
2004 if (vp) {
2005 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2006 const char *n;
2007
2008 if (flags & VNOSAVE)
2009 free(s);
2010 n = vp->text;
2011 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2012 }
2013
2014 if (flags & VNOSET)
2015 return;
2016
2017 if (vp->func && (flags & VNOFUNC) == 0)
2018 (*vp->func)(strchrnul(s, '=') + 1);
2019
2020 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2021 free((char*)vp->text);
2022
2023 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2024 } else {
2025 if (flags & VNOSET)
2026 return;
2027 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002028 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002029 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002030 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002031 *vpp = vp;
2032 }
2033 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2034 s = ckstrdup(s);
2035 vp->text = s;
2036 vp->flags = flags;
2037}
2038
2039/*
2040 * Set the value of a variable. The flags argument is ored with the
2041 * flags of the variable. If val is NULL, the variable is unset.
2042 */
2043static void
2044setvar(const char *name, const char *val, int flags)
2045{
2046 char *p, *q;
2047 size_t namelen;
2048 char *nameeq;
2049 size_t vallen;
2050
2051 q = endofname(name);
2052 p = strchrnul(q, '=');
2053 namelen = p - name;
2054 if (!namelen || p != q)
2055 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2056 vallen = 0;
2057 if (val == NULL) {
2058 flags |= VUNSET;
2059 } else {
2060 vallen = strlen(val);
2061 }
2062 INT_OFF;
2063 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002064 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002065 if (val) {
2066 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002067 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002068 }
2069 *p = '\0';
2070 setvareq(nameeq, flags | VNOSAVE);
2071 INT_ON;
2072}
2073
2074#if ENABLE_ASH_GETOPTS
2075/*
2076 * Safe version of setvar, returns 1 on success 0 on failure.
2077 */
2078static int
2079setvarsafe(const char *name, const char *val, int flags)
2080{
2081 int err;
2082 volatile int saveint;
2083 struct jmploc *volatile savehandler = exception_handler;
2084 struct jmploc jmploc;
2085
2086 SAVE_INT(saveint);
2087 if (setjmp(jmploc.loc))
2088 err = 1;
2089 else {
2090 exception_handler = &jmploc;
2091 setvar(name, val, flags);
2092 err = 0;
2093 }
2094 exception_handler = savehandler;
2095 RESTORE_INT(saveint);
2096 return err;
2097}
2098#endif
2099
2100/*
2101 * Unset the specified variable.
2102 */
2103static int
2104unsetvar(const char *s)
2105{
2106 struct var **vpp;
2107 struct var *vp;
2108 int retval;
2109
2110 vpp = findvar(hashvar(s), s);
2111 vp = *vpp;
2112 retval = 2;
2113 if (vp) {
2114 int flags = vp->flags;
2115
2116 retval = 1;
2117 if (flags & VREADONLY)
2118 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002119#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002120 vp->flags &= ~VDYNAMIC;
2121#endif
2122 if (flags & VUNSET)
2123 goto ok;
2124 if ((flags & VSTRFIXED) == 0) {
2125 INT_OFF;
2126 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2127 free((char*)vp->text);
2128 *vpp = vp->next;
2129 free(vp);
2130 INT_ON;
2131 } else {
2132 setvar(s, 0, 0);
2133 vp->flags &= ~VEXPORT;
2134 }
2135 ok:
2136 retval = 0;
2137 }
2138 out:
2139 return retval;
2140}
2141
2142/*
2143 * Process a linked list of variable assignments.
2144 */
2145static void
2146listsetvar(struct strlist *list_set_var, int flags)
2147{
2148 struct strlist *lp = list_set_var;
2149
2150 if (!lp)
2151 return;
2152 INT_OFF;
2153 do {
2154 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002155 lp = lp->next;
2156 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002157 INT_ON;
2158}
2159
2160/*
2161 * Generate a list of variables satisfying the given conditions.
2162 */
2163static char **
2164listvars(int on, int off, char ***end)
2165{
2166 struct var **vpp;
2167 struct var *vp;
2168 char **ep;
2169 int mask;
2170
2171 STARTSTACKSTR(ep);
2172 vpp = vartab;
2173 mask = on | off;
2174 do {
2175 for (vp = *vpp; vp; vp = vp->next) {
2176 if ((vp->flags & mask) == on) {
2177 if (ep == stackstrend())
2178 ep = growstackstr();
2179 *ep++ = (char *) vp->text;
2180 }
2181 }
2182 } while (++vpp < vartab + VTABSIZE);
2183 if (ep == stackstrend())
2184 ep = growstackstr();
2185 if (end)
2186 *end = ep;
2187 *ep++ = NULL;
2188 return grabstackstr(ep);
2189}
2190
2191
2192/* ============ Path search helper
2193 *
2194 * The variable path (passed by reference) should be set to the start
2195 * of the path before the first call; padvance will update
2196 * this value as it proceeds. Successive calls to padvance will return
2197 * the possible path expansions in sequence. If an option (indicated by
2198 * a percent sign) appears in the path entry then the global variable
2199 * pathopt will be set to point to it; otherwise pathopt will be set to
2200 * NULL.
2201 */
2202static const char *pathopt; /* set by padvance */
2203
2204static char *
2205padvance(const char **path, const char *name)
2206{
2207 const char *p;
2208 char *q;
2209 const char *start;
2210 size_t len;
2211
2212 if (*path == NULL)
2213 return NULL;
2214 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002215 for (p = start; *p && *p != ':' && *p != '%'; p++)
2216 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2218 while (stackblocksize() < len)
2219 growstackblock();
2220 q = stackblock();
2221 if (p != start) {
2222 memcpy(q, start, p - start);
2223 q += p - start;
2224 *q++ = '/';
2225 }
2226 strcpy(q, name);
2227 pathopt = NULL;
2228 if (*p == '%') {
2229 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002230 while (*p && *p != ':')
2231 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002232 }
2233 if (*p == ':')
2234 *path = p + 1;
2235 else
2236 *path = NULL;
2237 return stalloc(len);
2238}
2239
2240
2241/* ============ Prompt */
2242
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002243static smallint doprompt; /* if set, prompt the user */
2244static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002245
2246#if ENABLE_FEATURE_EDITING
2247static line_input_t *line_input_state;
2248static const char *cmdedit_prompt;
2249static void
2250putprompt(const char *s)
2251{
2252 if (ENABLE_ASH_EXPAND_PRMT) {
2253 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002254 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002255 return;
2256 }
2257 cmdedit_prompt = s;
2258}
2259#else
2260static void
2261putprompt(const char *s)
2262{
2263 out2str(s);
2264}
2265#endif
2266
2267#if ENABLE_ASH_EXPAND_PRMT
2268/* expandstr() needs parsing machinery, so it is far away ahead... */
2269static const char *expandstr(const char *ps);
2270#else
2271#define expandstr(s) s
2272#endif
2273
2274static void
2275setprompt(int whichprompt)
2276{
2277 const char *prompt;
2278#if ENABLE_ASH_EXPAND_PRMT
2279 struct stackmark smark;
2280#endif
2281
2282 needprompt = 0;
2283
2284 switch (whichprompt) {
2285 case 1:
2286 prompt = ps1val();
2287 break;
2288 case 2:
2289 prompt = ps2val();
2290 break;
2291 default: /* 0 */
2292 prompt = nullstr;
2293 }
2294#if ENABLE_ASH_EXPAND_PRMT
2295 setstackmark(&smark);
2296 stalloc(stackblocksize());
2297#endif
2298 putprompt(expandstr(prompt));
2299#if ENABLE_ASH_EXPAND_PRMT
2300 popstackmark(&smark);
2301#endif
2302}
2303
2304
2305/* ============ The cd and pwd commands */
2306
2307#define CD_PHYSICAL 1
2308#define CD_PRINT 2
2309
2310static int docd(const char *, int);
2311
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002312static int
2313cdopt(void)
2314{
2315 int flags = 0;
2316 int i, j;
2317
2318 j = 'L';
2319 while ((i = nextopt("LP"))) {
2320 if (i != j) {
2321 flags ^= CD_PHYSICAL;
2322 j = i;
2323 }
2324 }
2325
2326 return flags;
2327}
2328
2329/*
2330 * Update curdir (the name of the current directory) in response to a
2331 * cd command.
2332 */
2333static const char *
2334updatepwd(const char *dir)
2335{
2336 char *new;
2337 char *p;
2338 char *cdcomppath;
2339 const char *lim;
2340
2341 cdcomppath = ststrdup(dir);
2342 STARTSTACKSTR(new);
2343 if (*dir != '/') {
2344 if (curdir == nullstr)
2345 return 0;
2346 new = stack_putstr(curdir, new);
2347 }
2348 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002349 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002350 if (*dir != '/') {
2351 if (new[-1] != '/')
2352 USTPUTC('/', new);
2353 if (new > lim && *lim == '/')
2354 lim++;
2355 } else {
2356 USTPUTC('/', new);
2357 cdcomppath++;
2358 if (dir[1] == '/' && dir[2] != '/') {
2359 USTPUTC('/', new);
2360 cdcomppath++;
2361 lim++;
2362 }
2363 }
2364 p = strtok(cdcomppath, "/");
2365 while (p) {
2366 switch (*p) {
2367 case '.':
2368 if (p[1] == '.' && p[2] == '\0') {
2369 while (new > lim) {
2370 STUNPUTC(new);
2371 if (new[-1] == '/')
2372 break;
2373 }
2374 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002375 }
2376 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002377 break;
2378 /* fall through */
2379 default:
2380 new = stack_putstr(p, new);
2381 USTPUTC('/', new);
2382 }
2383 p = strtok(0, "/");
2384 }
2385 if (new > lim)
2386 STUNPUTC(new);
2387 *new = 0;
2388 return stackblock();
2389}
2390
2391/*
2392 * Find out what the current directory is. If we already know the current
2393 * directory, this routine returns immediately.
2394 */
2395static char *
2396getpwd(void)
2397{
Denis Vlasenko01631112007-12-16 17:20:38 +00002398 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002399 return dir ? dir : nullstr;
2400}
2401
2402static void
2403setpwd(const char *val, int setold)
2404{
2405 char *oldcur, *dir;
2406
2407 oldcur = dir = curdir;
2408
2409 if (setold) {
2410 setvar("OLDPWD", oldcur, VEXPORT);
2411 }
2412 INT_OFF;
2413 if (physdir != nullstr) {
2414 if (physdir != oldcur)
2415 free(physdir);
2416 physdir = nullstr;
2417 }
2418 if (oldcur == val || !val) {
2419 char *s = getpwd();
2420 physdir = s;
2421 if (!val)
2422 dir = s;
2423 } else
2424 dir = ckstrdup(val);
2425 if (oldcur != dir && oldcur != nullstr) {
2426 free(oldcur);
2427 }
2428 curdir = dir;
2429 INT_ON;
2430 setvar("PWD", dir, VEXPORT);
2431}
2432
2433static void hashcd(void);
2434
2435/*
2436 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2437 * know that the current directory has changed.
2438 */
2439static int
2440docd(const char *dest, int flags)
2441{
2442 const char *dir = 0;
2443 int err;
2444
2445 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2446
2447 INT_OFF;
2448 if (!(flags & CD_PHYSICAL)) {
2449 dir = updatepwd(dest);
2450 if (dir)
2451 dest = dir;
2452 }
2453 err = chdir(dest);
2454 if (err)
2455 goto out;
2456 setpwd(dir, 1);
2457 hashcd();
2458 out:
2459 INT_ON;
2460 return err;
2461}
2462
2463static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002464cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002465{
2466 const char *dest;
2467 const char *path;
2468 const char *p;
2469 char c;
2470 struct stat statb;
2471 int flags;
2472
2473 flags = cdopt();
2474 dest = *argptr;
2475 if (!dest)
2476 dest = bltinlookup(homestr);
2477 else if (LONE_DASH(dest)) {
2478 dest = bltinlookup("OLDPWD");
2479 flags |= CD_PRINT;
2480 }
2481 if (!dest)
2482 dest = nullstr;
2483 if (*dest == '/')
2484 goto step7;
2485 if (*dest == '.') {
2486 c = dest[1];
2487 dotdot:
2488 switch (c) {
2489 case '\0':
2490 case '/':
2491 goto step6;
2492 case '.':
2493 c = dest[2];
2494 if (c != '.')
2495 goto dotdot;
2496 }
2497 }
2498 if (!*dest)
2499 dest = ".";
2500 path = bltinlookup("CDPATH");
2501 if (!path) {
2502 step6:
2503 step7:
2504 p = dest;
2505 goto docd;
2506 }
2507 do {
2508 c = *path;
2509 p = padvance(&path, dest);
2510 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2511 if (c && c != ':')
2512 flags |= CD_PRINT;
2513 docd:
2514 if (!docd(p, flags))
2515 goto out;
2516 break;
2517 }
2518 } while (path);
2519 ash_msg_and_raise_error("can't cd to %s", dest);
2520 /* NOTREACHED */
2521 out:
2522 if (flags & CD_PRINT)
2523 out1fmt(snlfmt, curdir);
2524 return 0;
2525}
2526
2527static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002528pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002529{
2530 int flags;
2531 const char *dir = curdir;
2532
2533 flags = cdopt();
2534 if (flags) {
2535 if (physdir == nullstr)
2536 setpwd(dir, 0);
2537 dir = physdir;
2538 }
2539 out1fmt(snlfmt, dir);
2540 return 0;
2541}
2542
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002543
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002544/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002545
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002546#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002547#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002548
Eric Andersenc470f442003-07-28 09:56:35 +00002549/* Syntax classes */
2550#define CWORD 0 /* character is nothing special */
2551#define CNL 1 /* newline character */
2552#define CBACK 2 /* a backslash character */
2553#define CSQUOTE 3 /* single quote */
2554#define CDQUOTE 4 /* double quote */
2555#define CENDQUOTE 5 /* a terminating quote */
2556#define CBQUOTE 6 /* backwards single quote */
2557#define CVAR 7 /* a dollar sign */
2558#define CENDVAR 8 /* a '}' character */
2559#define CLP 9 /* a left paren in arithmetic */
2560#define CRP 10 /* a right paren in arithmetic */
2561#define CENDFILE 11 /* end of file */
2562#define CCTL 12 /* like CWORD, except it must be escaped */
2563#define CSPCL 13 /* these terminate a word */
2564#define CIGN 14 /* character should be ignored */
2565
Denis Vlasenko131ae172007-02-18 13:00:19 +00002566#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002567#define SYNBASE 130
2568#define PEOF -130
2569#define PEOA -129
2570#define PEOA_OR_PEOF PEOA
2571#else
2572#define SYNBASE 129
2573#define PEOF -129
2574#define PEOA_OR_PEOF PEOF
2575#endif
2576
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002577/* number syntax index */
2578#define BASESYNTAX 0 /* not in quotes */
2579#define DQSYNTAX 1 /* in double quotes */
2580#define SQSYNTAX 2 /* in single quotes */
2581#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002582#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002583
Denis Vlasenko131ae172007-02-18 13:00:19 +00002584#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002585#define USE_SIT_FUNCTION
2586#endif
2587
Denis Vlasenko131ae172007-02-18 13:00:19 +00002588#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002589static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002590#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002592#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002593 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2594 { CNL, CNL, CNL, CNL }, /* 2, \n */
2595 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2596 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2597 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2598 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2599 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2600 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2601 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2602 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2603 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002604#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002605 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2606 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2607 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002608#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002609};
Eric Andersenc470f442003-07-28 09:56:35 +00002610#else
2611static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002612#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002613 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002614#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002615 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2616 { CNL, CNL, CNL }, /* 2, \n */
2617 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2618 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2619 { CVAR, CVAR, CWORD }, /* 5, $ */
2620 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2621 { CSPCL, CWORD, CWORD }, /* 7, ( */
2622 { CSPCL, CWORD, CWORD }, /* 8, ) */
2623 { CBACK, CBACK, CCTL }, /* 9, \ */
2624 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2625 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002626#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002627 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2628 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2629 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002630#endif
2631};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002632#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002633
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002634#ifdef USE_SIT_FUNCTION
2635
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002636static int
2637SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002638{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002639 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002640#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002641 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002642 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2643 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2644 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2645 11, 3 /* "}~" */
2646 };
2647#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002648 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002649 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2650 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2651 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2652 10, 2 /* "}~" */
2653 };
2654#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002655 const char *s;
2656 int indx;
2657
Eric Andersenc470f442003-07-28 09:56:35 +00002658 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002659 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002660#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002661 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002662 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002663 else
2664#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002665#define U_C(c) ((unsigned char)(c))
2666
2667 if ((unsigned char)c >= (unsigned char)(CTLESC)
2668 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2669 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002670 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002671 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002672 s = strchrnul(spec_symbls, c);
2673 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002674 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002675 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002676 }
2677 return S_I_T[indx][syntax];
2678}
2679
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002680#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002681
Denis Vlasenko131ae172007-02-18 13:00:19 +00002682#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002683#define CSPCL_CIGN_CIGN_CIGN 0
2684#define CSPCL_CWORD_CWORD_CWORD 1
2685#define CNL_CNL_CNL_CNL 2
2686#define CWORD_CCTL_CCTL_CWORD 3
2687#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2688#define CVAR_CVAR_CWORD_CVAR 5
2689#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2690#define CSPCL_CWORD_CWORD_CLP 7
2691#define CSPCL_CWORD_CWORD_CRP 8
2692#define CBACK_CBACK_CCTL_CBACK 9
2693#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2694#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2695#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2696#define CWORD_CWORD_CWORD_CWORD 13
2697#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002698#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002699#define CSPCL_CWORD_CWORD_CWORD 0
2700#define CNL_CNL_CNL_CNL 1
2701#define CWORD_CCTL_CCTL_CWORD 2
2702#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2703#define CVAR_CVAR_CWORD_CVAR 4
2704#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2705#define CSPCL_CWORD_CWORD_CLP 6
2706#define CSPCL_CWORD_CWORD_CRP 7
2707#define CBACK_CBACK_CCTL_CBACK 8
2708#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2709#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2710#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2711#define CWORD_CWORD_CWORD_CWORD 12
2712#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002713#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002714
2715static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002716 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002717 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002718#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002719 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2720#endif
2721 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2723 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2724 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2725 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2726 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2727 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2728 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2729 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002730 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2859 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2860 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2882 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002883 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002884 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2886 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002888 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002889 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2890 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2891 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2894 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2895 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2896 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2909 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2911 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2942 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2946 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2974 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2975 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2976 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002977};
2978
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002979#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2980
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002981#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002982
Eric Andersen2870d962001-07-02 17:27:21 +00002983
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002984/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002985
Denis Vlasenko131ae172007-02-18 13:00:19 +00002986#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002987
2988#define ALIASINUSE 1
2989#define ALIASDEAD 2
2990
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002991struct alias {
2992 struct alias *next;
2993 char *name;
2994 char *val;
2995 int flag;
2996};
2997
Denis Vlasenko01631112007-12-16 17:20:38 +00002998
2999static struct alias **atab; // [ATABSIZE];
3000#define INIT_G_alias() do { \
3001 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3002} while (0)
3003
Eric Andersen2870d962001-07-02 17:27:21 +00003004
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003005static struct alias **
3006__lookupalias(const char *name) {
3007 unsigned int hashval;
3008 struct alias **app;
3009 const char *p;
3010 unsigned int ch;
3011
3012 p = name;
3013
3014 ch = (unsigned char)*p;
3015 hashval = ch << 4;
3016 while (ch) {
3017 hashval += ch;
3018 ch = (unsigned char)*++p;
3019 }
3020 app = &atab[hashval % ATABSIZE];
3021
3022 for (; *app; app = &(*app)->next) {
3023 if (strcmp(name, (*app)->name) == 0) {
3024 break;
3025 }
3026 }
3027
3028 return app;
3029}
3030
3031static struct alias *
3032lookupalias(const char *name, int check)
3033{
3034 struct alias *ap = *__lookupalias(name);
3035
3036 if (check && ap && (ap->flag & ALIASINUSE))
3037 return NULL;
3038 return ap;
3039}
3040
3041static struct alias *
3042freealias(struct alias *ap)
3043{
3044 struct alias *next;
3045
3046 if (ap->flag & ALIASINUSE) {
3047 ap->flag |= ALIASDEAD;
3048 return ap;
3049 }
3050
3051 next = ap->next;
3052 free(ap->name);
3053 free(ap->val);
3054 free(ap);
3055 return next;
3056}
Eric Andersencb57d552001-06-28 07:25:16 +00003057
Eric Andersenc470f442003-07-28 09:56:35 +00003058static void
3059setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003060{
3061 struct alias *ap, **app;
3062
3063 app = __lookupalias(name);
3064 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003065 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003066 if (ap) {
3067 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003068 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003069 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003070 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003071 ap->flag &= ~ALIASDEAD;
3072 } else {
3073 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003074 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003075 ap->name = ckstrdup(name);
3076 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003077 /*ap->flag = 0; - ckzalloc did it */
3078 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003079 *app = ap;
3080 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003081 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003082}
3083
Eric Andersenc470f442003-07-28 09:56:35 +00003084static int
3085unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003086{
Eric Andersencb57d552001-06-28 07:25:16 +00003087 struct alias **app;
3088
3089 app = __lookupalias(name);
3090
3091 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003092 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003093 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003094 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003095 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003096 }
3097
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003098 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003099}
3100
Eric Andersenc470f442003-07-28 09:56:35 +00003101static void
3102rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003103{
Eric Andersencb57d552001-06-28 07:25:16 +00003104 struct alias *ap, **app;
3105 int i;
3106
Denis Vlasenkob012b102007-02-19 22:43:01 +00003107 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003108 for (i = 0; i < ATABSIZE; i++) {
3109 app = &atab[i];
3110 for (ap = *app; ap; ap = *app) {
3111 *app = freealias(*app);
3112 if (ap == *app) {
3113 app = &ap->next;
3114 }
3115 }
3116 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003117 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003118}
3119
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003120static void
3121printalias(const struct alias *ap)
3122{
3123 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3124}
3125
Eric Andersencb57d552001-06-28 07:25:16 +00003126/*
3127 * TODO - sort output
3128 */
Eric Andersenc470f442003-07-28 09:56:35 +00003129static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003130aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003131{
3132 char *n, *v;
3133 int ret = 0;
3134 struct alias *ap;
3135
Denis Vlasenko68404f12008-03-17 09:00:54 +00003136 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003137 int i;
3138
Denis Vlasenko68404f12008-03-17 09:00:54 +00003139 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003140 for (ap = atab[i]; ap; ap = ap->next) {
3141 printalias(ap);
3142 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003143 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003144 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003145 }
3146 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003147 v = strchr(n+1, '=');
3148 if (v == NULL) { /* n+1: funny ksh stuff */
3149 ap = *__lookupalias(n);
3150 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003151 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003152 ret = 1;
3153 } else
3154 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003155 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003156 *v++ = '\0';
3157 setalias(n, v);
3158 }
3159 }
3160
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003161 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003162}
3163
Eric Andersenc470f442003-07-28 09:56:35 +00003164static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003165unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003166{
3167 int i;
3168
3169 while ((i = nextopt("a")) != '\0') {
3170 if (i == 'a') {
3171 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003172 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003173 }
3174 }
3175 for (i = 0; *argptr; argptr++) {
3176 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003177 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003178 i = 1;
3179 }
3180 }
3181
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003182 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003183}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003184
Denis Vlasenko131ae172007-02-18 13:00:19 +00003185#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003186
Eric Andersenc470f442003-07-28 09:56:35 +00003187
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003188/* ============ jobs.c */
3189
3190/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3191#define FORK_FG 0
3192#define FORK_BG 1
3193#define FORK_NOJOB 2
3194
3195/* mode flags for showjob(s) */
3196#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3197#define SHOW_PID 0x04 /* include process pid */
3198#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3199
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003200/*
3201 * A job structure contains information about a job. A job is either a
3202 * single process or a set of processes contained in a pipeline. In the
3203 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3204 * array of pids.
3205 */
3206
3207struct procstat {
3208 pid_t pid; /* process id */
3209 int status; /* last process status from wait() */
3210 char *cmd; /* text of command being run */
3211};
3212
3213struct job {
3214 struct procstat ps0; /* status of process */
3215 struct procstat *ps; /* status or processes when more than one */
3216#if JOBS
3217 int stopstatus; /* status of a stopped job */
3218#endif
3219 uint32_t
3220 nprocs: 16, /* number of processes */
3221 state: 8,
3222#define JOBRUNNING 0 /* at least one proc running */
3223#define JOBSTOPPED 1 /* all procs are stopped */
3224#define JOBDONE 2 /* all procs are completed */
3225#if JOBS
3226 sigint: 1, /* job was killed by SIGINT */
3227 jobctl: 1, /* job running under job control */
3228#endif
3229 waited: 1, /* true if this entry has been waited for */
3230 used: 1, /* true if this entry is in used */
3231 changed: 1; /* true if status has changed */
3232 struct job *prev_job; /* previous job */
3233};
3234
Denis Vlasenko68404f12008-03-17 09:00:54 +00003235static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003236#if !JOBS
3237#define forkshell(job, node, mode) forkshell(job, mode)
3238#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003239static int forkshell(struct job *, union node *, int);
3240static int waitforjob(struct job *);
3241
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003242#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003243enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003244#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003245#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003246static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003247static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003248#endif
3249
3250/*
3251 * Set the signal handler for the specified signal. The routine figures
3252 * out what it should be set to.
3253 */
3254static void
3255setsignal(int signo)
3256{
3257 int action;
3258 char *t, tsig;
3259 struct sigaction act;
3260
3261 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003262 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003263 if (t == NULL)
3264 action = S_DFL;
3265 else if (*t != '\0')
3266 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003267 if (rootshell && action == S_DFL) {
3268 switch (signo) {
3269 case SIGINT:
3270 if (iflag || minusc || sflag == 0)
3271 action = S_CATCH;
3272 break;
3273 case SIGQUIT:
3274#if DEBUG
3275 if (debug)
3276 break;
3277#endif
3278 /* FALLTHROUGH */
3279 case SIGTERM:
3280 if (iflag)
3281 action = S_IGN;
3282 break;
3283#if JOBS
3284 case SIGTSTP:
3285 case SIGTTOU:
3286 if (mflag)
3287 action = S_IGN;
3288 break;
3289#endif
3290 }
3291 }
3292
3293 t = &sigmode[signo - 1];
3294 tsig = *t;
3295 if (tsig == 0) {
3296 /*
3297 * current setting unknown
3298 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003299 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003300 /*
3301 * Pretend it worked; maybe we should give a warning
3302 * here, but other shells don't. We don't alter
3303 * sigmode, so that we retry every time.
3304 */
3305 return;
3306 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003307 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003308 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003309 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003310 if (mflag
3311 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3312 ) {
3313 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003314 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003315 }
3316 }
3317 if (tsig == S_HARD_IGN || tsig == action)
3318 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003319 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320 switch (action) {
3321 case S_CATCH:
3322 act.sa_handler = onsig;
3323 break;
3324 case S_IGN:
3325 act.sa_handler = SIG_IGN;
3326 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327 }
3328 *t = action;
3329 act.sa_flags = 0;
3330 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003331 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332}
3333
3334/* mode flags for set_curjob */
3335#define CUR_DELETE 2
3336#define CUR_RUNNING 1
3337#define CUR_STOPPED 0
3338
3339/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003340#define DOWAIT_NONBLOCK WNOHANG
3341#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342
3343#if JOBS
3344/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003345static int initialpgrp; //references:2
3346static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003347#endif
3348/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003349static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003350/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003351static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003352/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003353static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003354/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003355static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356
3357static void
3358set_curjob(struct job *jp, unsigned mode)
3359{
3360 struct job *jp1;
3361 struct job **jpp, **curp;
3362
3363 /* first remove from list */
3364 jpp = curp = &curjob;
3365 do {
3366 jp1 = *jpp;
3367 if (jp1 == jp)
3368 break;
3369 jpp = &jp1->prev_job;
3370 } while (1);
3371 *jpp = jp1->prev_job;
3372
3373 /* Then re-insert in correct position */
3374 jpp = curp;
3375 switch (mode) {
3376 default:
3377#if DEBUG
3378 abort();
3379#endif
3380 case CUR_DELETE:
3381 /* job being deleted */
3382 break;
3383 case CUR_RUNNING:
3384 /* newly created job or backgrounded job,
3385 put after all stopped jobs. */
3386 do {
3387 jp1 = *jpp;
3388#if JOBS
3389 if (!jp1 || jp1->state != JOBSTOPPED)
3390#endif
3391 break;
3392 jpp = &jp1->prev_job;
3393 } while (1);
3394 /* FALLTHROUGH */
3395#if JOBS
3396 case CUR_STOPPED:
3397#endif
3398 /* newly stopped job - becomes curjob */
3399 jp->prev_job = *jpp;
3400 *jpp = jp;
3401 break;
3402 }
3403}
3404
3405#if JOBS || DEBUG
3406static int
3407jobno(const struct job *jp)
3408{
3409 return jp - jobtab + 1;
3410}
3411#endif
3412
3413/*
3414 * Convert a job name to a job structure.
3415 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003416#if !JOBS
3417#define getjob(name, getctl) getjob(name)
3418#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003419static struct job *
3420getjob(const char *name, int getctl)
3421{
3422 struct job *jp;
3423 struct job *found;
3424 const char *err_msg = "No such job: %s";
3425 unsigned num;
3426 int c;
3427 const char *p;
3428 char *(*match)(const char *, const char *);
3429
3430 jp = curjob;
3431 p = name;
3432 if (!p)
3433 goto currentjob;
3434
3435 if (*p != '%')
3436 goto err;
3437
3438 c = *++p;
3439 if (!c)
3440 goto currentjob;
3441
3442 if (!p[1]) {
3443 if (c == '+' || c == '%') {
3444 currentjob:
3445 err_msg = "No current job";
3446 goto check;
3447 }
3448 if (c == '-') {
3449 if (jp)
3450 jp = jp->prev_job;
3451 err_msg = "No previous job";
3452 check:
3453 if (!jp)
3454 goto err;
3455 goto gotit;
3456 }
3457 }
3458
3459 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003460// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003461 num = atoi(p);
3462 if (num < njobs) {
3463 jp = jobtab + num - 1;
3464 if (jp->used)
3465 goto gotit;
3466 goto err;
3467 }
3468 }
3469
3470 match = prefix;
3471 if (*p == '?') {
3472 match = strstr;
3473 p++;
3474 }
3475
3476 found = 0;
3477 while (1) {
3478 if (!jp)
3479 goto err;
3480 if (match(jp->ps[0].cmd, p)) {
3481 if (found)
3482 goto err;
3483 found = jp;
3484 err_msg = "%s: ambiguous";
3485 }
3486 jp = jp->prev_job;
3487 }
3488
3489 gotit:
3490#if JOBS
3491 err_msg = "job %s not created under job control";
3492 if (getctl && jp->jobctl == 0)
3493 goto err;
3494#endif
3495 return jp;
3496 err:
3497 ash_msg_and_raise_error(err_msg, name);
3498}
3499
3500/*
3501 * Mark a job structure as unused.
3502 */
3503static void
3504freejob(struct job *jp)
3505{
3506 struct procstat *ps;
3507 int i;
3508
3509 INT_OFF;
3510 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3511 if (ps->cmd != nullstr)
3512 free(ps->cmd);
3513 }
3514 if (jp->ps != &jp->ps0)
3515 free(jp->ps);
3516 jp->used = 0;
3517 set_curjob(jp, CUR_DELETE);
3518 INT_ON;
3519}
3520
3521#if JOBS
3522static void
3523xtcsetpgrp(int fd, pid_t pgrp)
3524{
3525 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003526 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527}
3528
3529/*
3530 * Turn job control on and off.
3531 *
3532 * Note: This code assumes that the third arg to ioctl is a character
3533 * pointer, which is true on Berkeley systems but not System V. Since
3534 * System V doesn't have job control yet, this isn't a problem now.
3535 *
3536 * Called with interrupts off.
3537 */
3538static void
3539setjobctl(int on)
3540{
3541 int fd;
3542 int pgrp;
3543
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003544 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003545 return;
3546 if (on) {
3547 int ofd;
3548 ofd = fd = open(_PATH_TTY, O_RDWR);
3549 if (fd < 0) {
3550 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3551 * That sometimes helps to acquire controlling tty.
3552 * Obviously, a workaround for bugs when someone
3553 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003554 fd = 2;
3555 while (!isatty(fd))
3556 if (--fd < 0)
3557 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003558 }
3559 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003560 if (ofd >= 0)
3561 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003562 if (fd < 0)
3563 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003564 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003565 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003566 do { /* while we are in the background */
3567 pgrp = tcgetpgrp(fd);
3568 if (pgrp < 0) {
3569 out:
3570 ash_msg("can't access tty; job control turned off");
3571 mflag = on = 0;
3572 goto close;
3573 }
3574 if (pgrp == getpgrp())
3575 break;
3576 killpg(0, SIGTTIN);
3577 } while (1);
3578 initialpgrp = pgrp;
3579
3580 setsignal(SIGTSTP);
3581 setsignal(SIGTTOU);
3582 setsignal(SIGTTIN);
3583 pgrp = rootpid;
3584 setpgid(0, pgrp);
3585 xtcsetpgrp(fd, pgrp);
3586 } else {
3587 /* turning job control off */
3588 fd = ttyfd;
3589 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003590 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003591 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003592 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003593 setpgid(0, pgrp);
3594 setsignal(SIGTSTP);
3595 setsignal(SIGTTOU);
3596 setsignal(SIGTTIN);
3597 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003598 if (fd >= 0)
3599 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003600 fd = -1;
3601 }
3602 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003603 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003604}
3605
3606static int
3607killcmd(int argc, char **argv)
3608{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003609 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003610 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003611 do {
3612 if (argv[i][0] == '%') {
3613 struct job *jp = getjob(argv[i], 0);
3614 unsigned pid = jp->ps[0].pid;
3615 /* Enough space for ' -NNN<nul>' */
3616 argv[i] = alloca(sizeof(int)*3 + 3);
3617 /* kill_main has matching code to expect
3618 * leading space. Needed to not confuse
3619 * negative pids with "kill -SIGNAL_NO" syntax */
3620 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003622 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003623 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003624 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003625}
3626
3627static void
3628showpipe(struct job *jp, FILE *out)
3629{
3630 struct procstat *sp;
3631 struct procstat *spend;
3632
3633 spend = jp->ps + jp->nprocs;
3634 for (sp = jp->ps + 1; sp < spend; sp++)
3635 fprintf(out, " | %s", sp->cmd);
3636 outcslow('\n', out);
3637 flush_stdout_stderr();
3638}
3639
3640
3641static int
3642restartjob(struct job *jp, int mode)
3643{
3644 struct procstat *ps;
3645 int i;
3646 int status;
3647 pid_t pgid;
3648
3649 INT_OFF;
3650 if (jp->state == JOBDONE)
3651 goto out;
3652 jp->state = JOBRUNNING;
3653 pgid = jp->ps->pid;
3654 if (mode == FORK_FG)
3655 xtcsetpgrp(ttyfd, pgid);
3656 killpg(pgid, SIGCONT);
3657 ps = jp->ps;
3658 i = jp->nprocs;
3659 do {
3660 if (WIFSTOPPED(ps->status)) {
3661 ps->status = -1;
3662 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003663 ps++;
3664 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003665 out:
3666 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3667 INT_ON;
3668 return status;
3669}
3670
3671static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003672fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003673{
3674 struct job *jp;
3675 FILE *out;
3676 int mode;
3677 int retval;
3678
3679 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3680 nextopt(nullstr);
3681 argv = argptr;
3682 out = stdout;
3683 do {
3684 jp = getjob(*argv, 1);
3685 if (mode == FORK_BG) {
3686 set_curjob(jp, CUR_RUNNING);
3687 fprintf(out, "[%d] ", jobno(jp));
3688 }
3689 outstr(jp->ps->cmd, out);
3690 showpipe(jp, out);
3691 retval = restartjob(jp, mode);
3692 } while (*argv && *++argv);
3693 return retval;
3694}
3695#endif
3696
3697static int
3698sprint_status(char *s, int status, int sigonly)
3699{
3700 int col;
3701 int st;
3702
3703 col = 0;
3704 if (!WIFEXITED(status)) {
3705#if JOBS
3706 if (WIFSTOPPED(status))
3707 st = WSTOPSIG(status);
3708 else
3709#endif
3710 st = WTERMSIG(status);
3711 if (sigonly) {
3712 if (st == SIGINT || st == SIGPIPE)
3713 goto out;
3714#if JOBS
3715 if (WIFSTOPPED(status))
3716 goto out;
3717#endif
3718 }
3719 st &= 0x7f;
3720 col = fmtstr(s, 32, strsignal(st));
3721 if (WCOREDUMP(status)) {
3722 col += fmtstr(s + col, 16, " (core dumped)");
3723 }
3724 } else if (!sigonly) {
3725 st = WEXITSTATUS(status);
3726 if (st)
3727 col = fmtstr(s, 16, "Done(%d)", st);
3728 else
3729 col = fmtstr(s, 16, "Done");
3730 }
3731 out:
3732 return col;
3733}
3734
3735/*
3736 * Do a wait system call. If job control is compiled in, we accept
3737 * stopped processes. If block is zero, we return a value of zero
3738 * rather than blocking.
3739 *
3740 * System V doesn't have a non-blocking wait system call. It does
3741 * have a SIGCLD signal that is sent to a process when one of it's
3742 * children dies. The obvious way to use SIGCLD would be to install
3743 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3744 * was received, and have waitproc bump another counter when it got
3745 * the status of a process. Waitproc would then know that a wait
3746 * system call would not block if the two counters were different.
3747 * This approach doesn't work because if a process has children that
3748 * have not been waited for, System V will send it a SIGCLD when it
3749 * installs a signal handler for SIGCLD. What this means is that when
3750 * a child exits, the shell will be sent SIGCLD signals continuously
3751 * until is runs out of stack space, unless it does a wait call before
3752 * restoring the signal handler. The code below takes advantage of
3753 * this (mis)feature by installing a signal handler for SIGCLD and
3754 * then checking to see whether it was called. If there are any
3755 * children to be waited for, it will be.
3756 *
3757 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3758 * waits at all. In this case, the user will not be informed when
3759 * a background process until the next time she runs a real program
3760 * (as opposed to running a builtin command or just typing return),
3761 * and the jobs command may give out of date information.
3762 */
3763static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003764waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003767 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003768 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003769#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003770 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3771 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772}
3773
3774/*
3775 * Wait for a process to terminate.
3776 */
3777static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003778dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779{
3780 int pid;
3781 int status;
3782 struct job *jp;
3783 struct job *thisjob;
3784 int state;
3785
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003786 TRACE(("dowait(%d) called\n", wait_flags));
3787 pid = waitproc(wait_flags, &status);
3788 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003789 if (pid <= 0) {
3790 /* If we were doing blocking wait and (probably) got EINTR,
3791 * check for pending sigs received while waiting.
3792 * (NB: can be moved into callers if needed) */
3793 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3794 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003796 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003797 INT_OFF;
3798 thisjob = NULL;
3799 for (jp = curjob; jp; jp = jp->prev_job) {
3800 struct procstat *sp;
3801 struct procstat *spend;
3802 if (jp->state == JOBDONE)
3803 continue;
3804 state = JOBDONE;
3805 spend = jp->ps + jp->nprocs;
3806 sp = jp->ps;
3807 do {
3808 if (sp->pid == pid) {
3809 TRACE(("Job %d: changing status of proc %d "
3810 "from 0x%x to 0x%x\n",
3811 jobno(jp), pid, sp->status, status));
3812 sp->status = status;
3813 thisjob = jp;
3814 }
3815 if (sp->status == -1)
3816 state = JOBRUNNING;
3817#if JOBS
3818 if (state == JOBRUNNING)
3819 continue;
3820 if (WIFSTOPPED(sp->status)) {
3821 jp->stopstatus = sp->status;
3822 state = JOBSTOPPED;
3823 }
3824#endif
3825 } while (++sp < spend);
3826 if (thisjob)
3827 goto gotjob;
3828 }
3829#if JOBS
3830 if (!WIFSTOPPED(status))
3831#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832 jobless--;
3833 goto out;
3834
3835 gotjob:
3836 if (state != JOBRUNNING) {
3837 thisjob->changed = 1;
3838
3839 if (thisjob->state != state) {
3840 TRACE(("Job %d: changing state from %d to %d\n",
3841 jobno(thisjob), thisjob->state, state));
3842 thisjob->state = state;
3843#if JOBS
3844 if (state == JOBSTOPPED) {
3845 set_curjob(thisjob, CUR_STOPPED);
3846 }
3847#endif
3848 }
3849 }
3850
3851 out:
3852 INT_ON;
3853
3854 if (thisjob && thisjob == job) {
3855 char s[48 + 1];
3856 int len;
3857
3858 len = sprint_status(s, status, 1);
3859 if (len) {
3860 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003861 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003862 out2str(s);
3863 }
3864 }
3865 return pid;
3866}
3867
3868#if JOBS
3869static void
3870showjob(FILE *out, struct job *jp, int mode)
3871{
3872 struct procstat *ps;
3873 struct procstat *psend;
3874 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003875 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876 char s[80];
3877
3878 ps = jp->ps;
3879
3880 if (mode & SHOW_PGID) {
3881 /* just output process (group) id of pipeline */
3882 fprintf(out, "%d\n", ps->pid);
3883 return;
3884 }
3885
3886 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003887 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003888
3889 if (jp == curjob)
3890 s[col - 2] = '+';
3891 else if (curjob && jp == curjob->prev_job)
3892 s[col - 2] = '-';
3893
3894 if (mode & SHOW_PID)
3895 col += fmtstr(s + col, 16, "%d ", ps->pid);
3896
3897 psend = ps + jp->nprocs;
3898
3899 if (jp->state == JOBRUNNING) {
3900 strcpy(s + col, "Running");
3901 col += sizeof("Running") - 1;
3902 } else {
3903 int status = psend[-1].status;
3904 if (jp->state == JOBSTOPPED)
3905 status = jp->stopstatus;
3906 col += sprint_status(s + col, status, 0);
3907 }
3908
3909 goto start;
3910
3911 do {
3912 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003913 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003914 start:
3915 fprintf(out, "%s%*c%s",
3916 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3917 );
3918 if (!(mode & SHOW_PID)) {
3919 showpipe(jp, out);
3920 break;
3921 }
3922 if (++ps == psend) {
3923 outcslow('\n', out);
3924 break;
3925 }
3926 } while (1);
3927
3928 jp->changed = 0;
3929
3930 if (jp->state == JOBDONE) {
3931 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3932 freejob(jp);
3933 }
3934}
3935
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003936/*
3937 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3938 * statuses have changed since the last call to showjobs.
3939 */
3940static void
3941showjobs(FILE *out, int mode)
3942{
3943 struct job *jp;
3944
3945 TRACE(("showjobs(%x) called\n", mode));
3946
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003947 /* If not even one job changed, there is nothing to do */
3948 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 continue;
3950
3951 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003952 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003953 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003954 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003955 }
3956}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003957
3958static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003959jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003960{
3961 int mode, m;
3962
3963 mode = 0;
3964 while ((m = nextopt("lp"))) {
3965 if (m == 'l')
3966 mode = SHOW_PID;
3967 else
3968 mode = SHOW_PGID;
3969 }
3970
3971 argv = argptr;
3972 if (*argv) {
3973 do
3974 showjob(stdout, getjob(*argv,0), mode);
3975 while (*++argv);
3976 } else
3977 showjobs(stdout, mode);
3978
3979 return 0;
3980}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003981#endif /* JOBS */
3982
3983static int
3984getstatus(struct job *job)
3985{
3986 int status;
3987 int retval;
3988
3989 status = job->ps[job->nprocs - 1].status;
3990 retval = WEXITSTATUS(status);
3991 if (!WIFEXITED(status)) {
3992#if JOBS
3993 retval = WSTOPSIG(status);
3994 if (!WIFSTOPPED(status))
3995#endif
3996 {
3997 /* XXX: limits number of signals */
3998 retval = WTERMSIG(status);
3999#if JOBS
4000 if (retval == SIGINT)
4001 job->sigint = 1;
4002#endif
4003 }
4004 retval += 128;
4005 }
4006 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4007 jobno(job), job->nprocs, status, retval));
4008 return retval;
4009}
4010
4011static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004012waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013{
4014 struct job *job;
4015 int retval;
4016 struct job *jp;
4017
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004018// exsig++;
4019// xbarrier();
4020 if (pendingsig)
4021 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004022
4023 nextopt(nullstr);
4024 retval = 0;
4025
4026 argv = argptr;
4027 if (!*argv) {
4028 /* wait for all jobs */
4029 for (;;) {
4030 jp = curjob;
4031 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004032 if (!jp) /* no running procs */
4033 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034 if (jp->state == JOBRUNNING)
4035 break;
4036 jp->waited = 1;
4037 jp = jp->prev_job;
4038 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004039 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004040 }
4041 }
4042
4043 retval = 127;
4044 do {
4045 if (**argv != '%') {
4046 pid_t pid = number(*argv);
4047 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004048 while (1) {
4049 if (!job)
4050 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 if (job->ps[job->nprocs - 1].pid == pid)
4052 break;
4053 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004054 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 } else
4056 job = getjob(*argv, 0);
4057 /* loop until process terminated or stopped */
4058 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004059 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004060 job->waited = 1;
4061 retval = getstatus(job);
4062 repeat:
4063 ;
4064 } while (*++argv);
4065
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004066 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004067 return retval;
4068}
4069
4070static struct job *
4071growjobtab(void)
4072{
4073 size_t len;
4074 ptrdiff_t offset;
4075 struct job *jp, *jq;
4076
4077 len = njobs * sizeof(*jp);
4078 jq = jobtab;
4079 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4080
4081 offset = (char *)jp - (char *)jq;
4082 if (offset) {
4083 /* Relocate pointers */
4084 size_t l = len;
4085
4086 jq = (struct job *)((char *)jq + l);
4087 while (l) {
4088 l -= sizeof(*jp);
4089 jq--;
4090#define joff(p) ((struct job *)((char *)(p) + l))
4091#define jmove(p) (p) = (void *)((char *)(p) + offset)
4092 if (joff(jp)->ps == &jq->ps0)
4093 jmove(joff(jp)->ps);
4094 if (joff(jp)->prev_job)
4095 jmove(joff(jp)->prev_job);
4096 }
4097 if (curjob)
4098 jmove(curjob);
4099#undef joff
4100#undef jmove
4101 }
4102
4103 njobs += 4;
4104 jobtab = jp;
4105 jp = (struct job *)((char *)jp + len);
4106 jq = jp + 3;
4107 do {
4108 jq->used = 0;
4109 } while (--jq >= jp);
4110 return jp;
4111}
4112
4113/*
4114 * Return a new job structure.
4115 * Called with interrupts off.
4116 */
4117static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004118makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004119{
4120 int i;
4121 struct job *jp;
4122
4123 for (i = njobs, jp = jobtab; ; jp++) {
4124 if (--i < 0) {
4125 jp = growjobtab();
4126 break;
4127 }
4128 if (jp->used == 0)
4129 break;
4130 if (jp->state != JOBDONE || !jp->waited)
4131 continue;
4132#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004133 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 continue;
4135#endif
4136 freejob(jp);
4137 break;
4138 }
4139 memset(jp, 0, sizeof(*jp));
4140#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004141 /* jp->jobctl is a bitfield.
4142 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004143 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004144 jp->jobctl = 1;
4145#endif
4146 jp->prev_job = curjob;
4147 curjob = jp;
4148 jp->used = 1;
4149 jp->ps = &jp->ps0;
4150 if (nprocs > 1) {
4151 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4152 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004153 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004154 jobno(jp)));
4155 return jp;
4156}
4157
4158#if JOBS
4159/*
4160 * Return a string identifying a command (to be printed by the
4161 * jobs command).
4162 */
4163static char *cmdnextc;
4164
4165static void
4166cmdputs(const char *s)
4167{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004168 static const char vstype[VSTYPE + 1][3] = {
4169 "", "}", "-", "+", "?", "=",
4170 "%", "%%", "#", "##"
4171 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4172 };
4173
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174 const char *p, *str;
4175 char c, cc[2] = " ";
4176 char *nextc;
4177 int subtype = 0;
4178 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179
4180 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4181 p = s;
4182 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004183 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 switch (c) {
4185 case CTLESC:
4186 c = *p++;
4187 break;
4188 case CTLVAR:
4189 subtype = *p++;
4190 if ((subtype & VSTYPE) == VSLENGTH)
4191 str = "${#";
4192 else
4193 str = "${";
4194 if (!(subtype & VSQUOTE) == !(quoted & 1))
4195 goto dostr;
4196 quoted ^= 1;
4197 c = '"';
4198 break;
4199 case CTLENDVAR:
4200 str = "\"}" + !(quoted & 1);
4201 quoted >>= 1;
4202 subtype = 0;
4203 goto dostr;
4204 case CTLBACKQ:
4205 str = "$(...)";
4206 goto dostr;
4207 case CTLBACKQ+CTLQUOTE:
4208 str = "\"$(...)\"";
4209 goto dostr;
4210#if ENABLE_ASH_MATH_SUPPORT
4211 case CTLARI:
4212 str = "$((";
4213 goto dostr;
4214 case CTLENDARI:
4215 str = "))";
4216 goto dostr;
4217#endif
4218 case CTLQUOTEMARK:
4219 quoted ^= 1;
4220 c = '"';
4221 break;
4222 case '=':
4223 if (subtype == 0)
4224 break;
4225 if ((subtype & VSTYPE) != VSNORMAL)
4226 quoted <<= 1;
4227 str = vstype[subtype & VSTYPE];
4228 if (subtype & VSNUL)
4229 c = ':';
4230 else
4231 goto checkstr;
4232 break;
4233 case '\'':
4234 case '\\':
4235 case '"':
4236 case '$':
4237 /* These can only happen inside quotes */
4238 cc[0] = c;
4239 str = cc;
4240 c = '\\';
4241 break;
4242 default:
4243 break;
4244 }
4245 USTPUTC(c, nextc);
4246 checkstr:
4247 if (!str)
4248 continue;
4249 dostr:
4250 while ((c = *str++)) {
4251 USTPUTC(c, nextc);
4252 }
4253 }
4254 if (quoted & 1) {
4255 USTPUTC('"', nextc);
4256 }
4257 *nextc = 0;
4258 cmdnextc = nextc;
4259}
4260
4261/* cmdtxt() and cmdlist() call each other */
4262static void cmdtxt(union node *n);
4263
4264static void
4265cmdlist(union node *np, int sep)
4266{
4267 for (; np; np = np->narg.next) {
4268 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004269 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004270 cmdtxt(np);
4271 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004272 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273 }
4274}
4275
4276static void
4277cmdtxt(union node *n)
4278{
4279 union node *np;
4280 struct nodelist *lp;
4281 const char *p;
4282 char s[2];
4283
4284 if (!n)
4285 return;
4286 switch (n->type) {
4287 default:
4288#if DEBUG
4289 abort();
4290#endif
4291 case NPIPE:
4292 lp = n->npipe.cmdlist;
4293 for (;;) {
4294 cmdtxt(lp->n);
4295 lp = lp->next;
4296 if (!lp)
4297 break;
4298 cmdputs(" | ");
4299 }
4300 break;
4301 case NSEMI:
4302 p = "; ";
4303 goto binop;
4304 case NAND:
4305 p = " && ";
4306 goto binop;
4307 case NOR:
4308 p = " || ";
4309 binop:
4310 cmdtxt(n->nbinary.ch1);
4311 cmdputs(p);
4312 n = n->nbinary.ch2;
4313 goto donode;
4314 case NREDIR:
4315 case NBACKGND:
4316 n = n->nredir.n;
4317 goto donode;
4318 case NNOT:
4319 cmdputs("!");
4320 n = n->nnot.com;
4321 donode:
4322 cmdtxt(n);
4323 break;
4324 case NIF:
4325 cmdputs("if ");
4326 cmdtxt(n->nif.test);
4327 cmdputs("; then ");
4328 n = n->nif.ifpart;
4329 if (n->nif.elsepart) {
4330 cmdtxt(n);
4331 cmdputs("; else ");
4332 n = n->nif.elsepart;
4333 }
4334 p = "; fi";
4335 goto dotail;
4336 case NSUBSHELL:
4337 cmdputs("(");
4338 n = n->nredir.n;
4339 p = ")";
4340 goto dotail;
4341 case NWHILE:
4342 p = "while ";
4343 goto until;
4344 case NUNTIL:
4345 p = "until ";
4346 until:
4347 cmdputs(p);
4348 cmdtxt(n->nbinary.ch1);
4349 n = n->nbinary.ch2;
4350 p = "; done";
4351 dodo:
4352 cmdputs("; do ");
4353 dotail:
4354 cmdtxt(n);
4355 goto dotail2;
4356 case NFOR:
4357 cmdputs("for ");
4358 cmdputs(n->nfor.var);
4359 cmdputs(" in ");
4360 cmdlist(n->nfor.args, 1);
4361 n = n->nfor.body;
4362 p = "; done";
4363 goto dodo;
4364 case NDEFUN:
4365 cmdputs(n->narg.text);
4366 p = "() { ... }";
4367 goto dotail2;
4368 case NCMD:
4369 cmdlist(n->ncmd.args, 1);
4370 cmdlist(n->ncmd.redirect, 0);
4371 break;
4372 case NARG:
4373 p = n->narg.text;
4374 dotail2:
4375 cmdputs(p);
4376 break;
4377 case NHERE:
4378 case NXHERE:
4379 p = "<<...";
4380 goto dotail2;
4381 case NCASE:
4382 cmdputs("case ");
4383 cmdputs(n->ncase.expr->narg.text);
4384 cmdputs(" in ");
4385 for (np = n->ncase.cases; np; np = np->nclist.next) {
4386 cmdtxt(np->nclist.pattern);
4387 cmdputs(") ");
4388 cmdtxt(np->nclist.body);
4389 cmdputs(";; ");
4390 }
4391 p = "esac";
4392 goto dotail2;
4393 case NTO:
4394 p = ">";
4395 goto redir;
4396 case NCLOBBER:
4397 p = ">|";
4398 goto redir;
4399 case NAPPEND:
4400 p = ">>";
4401 goto redir;
4402 case NTOFD:
4403 p = ">&";
4404 goto redir;
4405 case NFROM:
4406 p = "<";
4407 goto redir;
4408 case NFROMFD:
4409 p = "<&";
4410 goto redir;
4411 case NFROMTO:
4412 p = "<>";
4413 redir:
4414 s[0] = n->nfile.fd + '0';
4415 s[1] = '\0';
4416 cmdputs(s);
4417 cmdputs(p);
4418 if (n->type == NTOFD || n->type == NFROMFD) {
4419 s[0] = n->ndup.dupfd + '0';
4420 p = s;
4421 goto dotail2;
4422 }
4423 n = n->nfile.fname;
4424 goto donode;
4425 }
4426}
4427
4428static char *
4429commandtext(union node *n)
4430{
4431 char *name;
4432
4433 STARTSTACKSTR(cmdnextc);
4434 cmdtxt(n);
4435 name = stackblock();
4436 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4437 name, cmdnextc, cmdnextc));
4438 return ckstrdup(name);
4439}
4440#endif /* JOBS */
4441
4442/*
4443 * Fork off a subshell. If we are doing job control, give the subshell its
4444 * own process group. Jp is a job structure that the job is to be added to.
4445 * N is the command that will be evaluated by the child. Both jp and n may
4446 * be NULL. The mode parameter can be one of the following:
4447 * FORK_FG - Fork off a foreground process.
4448 * FORK_BG - Fork off a background process.
4449 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4450 * process group even if job control is on.
4451 *
4452 * When job control is turned off, background processes have their standard
4453 * input redirected to /dev/null (except for the second and later processes
4454 * in a pipeline).
4455 *
4456 * Called with interrupts off.
4457 */
4458/*
4459 * Clear traps on a fork.
4460 */
4461static void
4462clear_traps(void)
4463{
4464 char **tp;
4465
4466 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004467 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004468 INT_OFF;
4469 free(*tp);
4470 *tp = NULL;
4471 if (tp != &trap[0])
4472 setsignal(tp - trap);
4473 INT_ON;
4474 }
4475 }
4476}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004477
4478/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004479static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004480
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004481/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004482static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004483forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004484{
4485 int oldlvl;
4486
4487 TRACE(("Child shell %d\n", getpid()));
4488 oldlvl = shlvl;
4489 shlvl++;
4490
4491 closescript();
4492 clear_traps();
4493#if JOBS
4494 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004495 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004496 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4497 pid_t pgrp;
4498
4499 if (jp->nprocs == 0)
4500 pgrp = getpid();
4501 else
4502 pgrp = jp->ps[0].pid;
4503 /* This can fail because we are doing it in the parent also */
4504 (void)setpgid(0, pgrp);
4505 if (mode == FORK_FG)
4506 xtcsetpgrp(ttyfd, pgrp);
4507 setsignal(SIGTSTP);
4508 setsignal(SIGTTOU);
4509 } else
4510#endif
4511 if (mode == FORK_BG) {
4512 ignoresig(SIGINT);
4513 ignoresig(SIGQUIT);
4514 if (jp->nprocs == 0) {
4515 close(0);
4516 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004517 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004518 }
4519 }
4520 if (!oldlvl && iflag) {
4521 setsignal(SIGINT);
4522 setsignal(SIGQUIT);
4523 setsignal(SIGTERM);
4524 }
4525 for (jp = curjob; jp; jp = jp->prev_job)
4526 freejob(jp);
4527 jobless = 0;
4528}
4529
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004530/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004531#if !JOBS
4532#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4533#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004534static void
4535forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4536{
4537 TRACE(("In parent shell: child = %d\n", pid));
4538 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004539 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4540 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004541 jobless++;
4542 return;
4543 }
4544#if JOBS
4545 if (mode != FORK_NOJOB && jp->jobctl) {
4546 int pgrp;
4547
4548 if (jp->nprocs == 0)
4549 pgrp = pid;
4550 else
4551 pgrp = jp->ps[0].pid;
4552 /* This can fail because we are doing it in the child also */
4553 setpgid(pid, pgrp);
4554 }
4555#endif
4556 if (mode == FORK_BG) {
4557 backgndpid = pid; /* set $! */
4558 set_curjob(jp, CUR_RUNNING);
4559 }
4560 if (jp) {
4561 struct procstat *ps = &jp->ps[jp->nprocs++];
4562 ps->pid = pid;
4563 ps->status = -1;
4564 ps->cmd = nullstr;
4565#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004566 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004567 ps->cmd = commandtext(n);
4568#endif
4569 }
4570}
4571
4572static int
4573forkshell(struct job *jp, union node *n, int mode)
4574{
4575 int pid;
4576
4577 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4578 pid = fork();
4579 if (pid < 0) {
4580 TRACE(("Fork failed, errno=%d", errno));
4581 if (jp)
4582 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004583 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004584 }
4585 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004586 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004587 else
4588 forkparent(jp, n, mode, pid);
4589 return pid;
4590}
4591
4592/*
4593 * Wait for job to finish.
4594 *
4595 * Under job control we have the problem that while a child process is
4596 * running interrupts generated by the user are sent to the child but not
4597 * to the shell. This means that an infinite loop started by an inter-
4598 * active user may be hard to kill. With job control turned off, an
4599 * interactive user may place an interactive program inside a loop. If
4600 * the interactive program catches interrupts, the user doesn't want
4601 * these interrupts to also abort the loop. The approach we take here
4602 * is to have the shell ignore interrupt signals while waiting for a
4603 * foreground process to terminate, and then send itself an interrupt
4604 * signal if the child process was terminated by an interrupt signal.
4605 * Unfortunately, some programs want to do a bit of cleanup and then
4606 * exit on interrupt; unless these processes terminate themselves by
4607 * sending a signal to themselves (instead of calling exit) they will
4608 * confuse this approach.
4609 *
4610 * Called with interrupts off.
4611 */
4612static int
4613waitforjob(struct job *jp)
4614{
4615 int st;
4616
4617 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4618 while (jp->state == JOBRUNNING) {
4619 dowait(DOWAIT_BLOCK, jp);
4620 }
4621 st = getstatus(jp);
4622#if JOBS
4623 if (jp->jobctl) {
4624 xtcsetpgrp(ttyfd, rootpid);
4625 /*
4626 * This is truly gross.
4627 * If we're doing job control, then we did a TIOCSPGRP which
4628 * caused us (the shell) to no longer be in the controlling
4629 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4630 * intuit from the subprocess exit status whether a SIGINT
4631 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4632 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004633 if (jp->sigint) /* TODO: do the same with all signals */
4634 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004635 }
4636 if (jp->state == JOBDONE)
4637#endif
4638 freejob(jp);
4639 return st;
4640}
4641
4642/*
4643 * return 1 if there are stopped jobs, otherwise 0
4644 */
4645static int
4646stoppedjobs(void)
4647{
4648 struct job *jp;
4649 int retval;
4650
4651 retval = 0;
4652 if (job_warning)
4653 goto out;
4654 jp = curjob;
4655 if (jp && jp->state == JOBSTOPPED) {
4656 out2str("You have stopped jobs.\n");
4657 job_warning = 2;
4658 retval++;
4659 }
4660 out:
4661 return retval;
4662}
4663
4664
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004665/* ============ redir.c
4666 *
4667 * Code for dealing with input/output redirection.
4668 */
4669
4670#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004671#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004672#ifndef PIPE_BUF
4673# define PIPESIZE 4096 /* amount of buffering in a pipe */
4674#else
4675# define PIPESIZE PIPE_BUF
4676#endif
4677
4678/*
4679 * Open a file in noclobber mode.
4680 * The code was copied from bash.
4681 */
4682static int
4683noclobberopen(const char *fname)
4684{
4685 int r, fd;
4686 struct stat finfo, finfo2;
4687
4688 /*
4689 * If the file exists and is a regular file, return an error
4690 * immediately.
4691 */
4692 r = stat(fname, &finfo);
4693 if (r == 0 && S_ISREG(finfo.st_mode)) {
4694 errno = EEXIST;
4695 return -1;
4696 }
4697
4698 /*
4699 * If the file was not present (r != 0), make sure we open it
4700 * exclusively so that if it is created before we open it, our open
4701 * will fail. Make sure that we do not truncate an existing file.
4702 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4703 * file was not a regular file, we leave O_EXCL off.
4704 */
4705 if (r != 0)
4706 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4707 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4708
4709 /* If the open failed, return the file descriptor right away. */
4710 if (fd < 0)
4711 return fd;
4712
4713 /*
4714 * OK, the open succeeded, but the file may have been changed from a
4715 * non-regular file to a regular file between the stat and the open.
4716 * We are assuming that the O_EXCL open handles the case where FILENAME
4717 * did not exist and is symlinked to an existing file between the stat
4718 * and open.
4719 */
4720
4721 /*
4722 * If we can open it and fstat the file descriptor, and neither check
4723 * revealed that it was a regular file, and the file has not been
4724 * replaced, return the file descriptor.
4725 */
4726 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4727 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4728 return fd;
4729
4730 /* The file has been replaced. badness. */
4731 close(fd);
4732 errno = EEXIST;
4733 return -1;
4734}
4735
4736/*
4737 * Handle here documents. Normally we fork off a process to write the
4738 * data to a pipe. If the document is short, we can stuff the data in
4739 * the pipe without forking.
4740 */
4741/* openhere needs this forward reference */
4742static void expandhere(union node *arg, int fd);
4743static int
4744openhere(union node *redir)
4745{
4746 int pip[2];
4747 size_t len = 0;
4748
4749 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004750 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004751 if (redir->type == NHERE) {
4752 len = strlen(redir->nhere.doc->narg.text);
4753 if (len <= PIPESIZE) {
4754 full_write(pip[1], redir->nhere.doc->narg.text, len);
4755 goto out;
4756 }
4757 }
4758 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4759 close(pip[0]);
4760 signal(SIGINT, SIG_IGN);
4761 signal(SIGQUIT, SIG_IGN);
4762 signal(SIGHUP, SIG_IGN);
4763#ifdef SIGTSTP
4764 signal(SIGTSTP, SIG_IGN);
4765#endif
4766 signal(SIGPIPE, SIG_DFL);
4767 if (redir->type == NHERE)
4768 full_write(pip[1], redir->nhere.doc->narg.text, len);
4769 else
4770 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004771 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772 }
4773 out:
4774 close(pip[1]);
4775 return pip[0];
4776}
4777
4778static int
4779openredirect(union node *redir)
4780{
4781 char *fname;
4782 int f;
4783
4784 switch (redir->nfile.type) {
4785 case NFROM:
4786 fname = redir->nfile.expfname;
4787 f = open(fname, O_RDONLY);
4788 if (f < 0)
4789 goto eopen;
4790 break;
4791 case NFROMTO:
4792 fname = redir->nfile.expfname;
4793 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4794 if (f < 0)
4795 goto ecreate;
4796 break;
4797 case NTO:
4798 /* Take care of noclobber mode. */
4799 if (Cflag) {
4800 fname = redir->nfile.expfname;
4801 f = noclobberopen(fname);
4802 if (f < 0)
4803 goto ecreate;
4804 break;
4805 }
4806 /* FALLTHROUGH */
4807 case NCLOBBER:
4808 fname = redir->nfile.expfname;
4809 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4810 if (f < 0)
4811 goto ecreate;
4812 break;
4813 case NAPPEND:
4814 fname = redir->nfile.expfname;
4815 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4816 if (f < 0)
4817 goto ecreate;
4818 break;
4819 default:
4820#if DEBUG
4821 abort();
4822#endif
4823 /* Fall through to eliminate warning. */
4824 case NTOFD:
4825 case NFROMFD:
4826 f = -1;
4827 break;
4828 case NHERE:
4829 case NXHERE:
4830 f = openhere(redir);
4831 break;
4832 }
4833
4834 return f;
4835 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004836 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004837 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004838 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004839}
4840
4841/*
4842 * Copy a file descriptor to be >= to. Returns -1
4843 * if the source file descriptor is closed, EMPTY if there are no unused
4844 * file descriptors left.
4845 */
4846static int
4847copyfd(int from, int to)
4848{
4849 int newfd;
4850
4851 newfd = fcntl(from, F_DUPFD, to);
4852 if (newfd < 0) {
4853 if (errno == EMFILE)
4854 return EMPTY;
4855 ash_msg_and_raise_error("%d: %m", from);
4856 }
4857 return newfd;
4858}
4859
4860static void
4861dupredirect(union node *redir, int f)
4862{
4863 int fd = redir->nfile.fd;
4864
4865 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4866 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4867 copyfd(redir->ndup.dupfd, fd);
4868 }
4869 return;
4870 }
4871
4872 if (f != fd) {
4873 copyfd(f, fd);
4874 close(f);
4875 }
4876}
4877
4878/*
4879 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4880 * old file descriptors are stashed away so that the redirection can be
4881 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4882 * standard output, and the standard error if it becomes a duplicate of
4883 * stdout, is saved in memory.
4884 */
4885/* flags passed to redirect */
4886#define REDIR_PUSH 01 /* save previous values of file descriptors */
4887#define REDIR_SAVEFD2 03 /* set preverrout */
4888static void
4889redirect(union node *redir, int flags)
4890{
4891 union node *n;
4892 struct redirtab *sv;
4893 int i;
4894 int fd;
4895 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004896
Denis Vlasenko01631112007-12-16 17:20:38 +00004897 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004898 if (!redir) {
4899 return;
4900 }
4901 sv = NULL;
4902 INT_OFF;
4903 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004904 sv = ckmalloc(sizeof(*sv));
4905 sv->next = redirlist;
4906 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004907 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004908 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004909 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004910 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004911 }
4912 n = redir;
4913 do {
4914 fd = n->nfile.fd;
4915 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4916 && n->ndup.dupfd == fd)
4917 continue; /* redirect from/to same file descriptor */
4918
4919 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004920 if (fd == newfd) {
4921 /* Descriptor wasn't open before redirect.
4922 * Mark it for close in the future */
4923 if (sv && sv->renamed[fd] == EMPTY)
4924 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004925 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004926 }
4927 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004928 i = fcntl(fd, F_DUPFD, 10);
4929
4930 if (i == -1) {
4931 i = errno;
4932 if (i != EBADF) {
4933 close(newfd);
4934 errno = i;
4935 ash_msg_and_raise_error("%d: %m", fd);
4936 /* NOTREACHED */
4937 }
4938 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004939 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004940 close(fd);
4941 }
4942 } else {
4943 close(fd);
4944 }
4945 dupredirect(n, newfd);
4946 } while ((n = n->nfile.next));
4947 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004948 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004949 preverrout_fd = sv->renamed[2];
4950}
4951
4952/*
4953 * Undo the effects of the last redirection.
4954 */
4955static void
4956popredir(int drop)
4957{
4958 struct redirtab *rp;
4959 int i;
4960
Denis Vlasenko01631112007-12-16 17:20:38 +00004961 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004962 return;
4963 INT_OFF;
4964 rp = redirlist;
4965 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004966 if (rp->renamed[i] == CLOSED) {
4967 if (!drop)
4968 close(i);
4969 continue;
4970 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004971 if (rp->renamed[i] != EMPTY) {
4972 if (!drop) {
4973 close(i);
4974 copyfd(rp->renamed[i], i);
4975 }
4976 close(rp->renamed[i]);
4977 }
4978 }
4979 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004980 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 free(rp);
4982 INT_ON;
4983}
4984
4985/*
4986 * Undo all redirections. Called on error or interrupt.
4987 */
4988
4989/*
4990 * Discard all saved file descriptors.
4991 */
4992static void
4993clearredir(int drop)
4994{
4995 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004996 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004997 if (!redirlist)
4998 break;
4999 popredir(drop);
5000 }
5001}
5002
5003static int
5004redirectsafe(union node *redir, int flags)
5005{
5006 int err;
5007 volatile int saveint;
5008 struct jmploc *volatile savehandler = exception_handler;
5009 struct jmploc jmploc;
5010
5011 SAVE_INT(saveint);
5012 err = setjmp(jmploc.loc) * 2;
5013 if (!err) {
5014 exception_handler = &jmploc;
5015 redirect(redir, flags);
5016 }
5017 exception_handler = savehandler;
5018 if (err && exception != EXERROR)
5019 longjmp(exception_handler->loc, 1);
5020 RESTORE_INT(saveint);
5021 return err;
5022}
5023
5024
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005025/* ============ Routines to expand arguments to commands
5026 *
5027 * We have to deal with backquotes, shell variables, and file metacharacters.
5028 */
5029
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005030#if ENABLE_ASH_MATH_SUPPORT_64
5031typedef int64_t arith_t;
5032#define arith_t_type long long
5033#else
5034typedef long arith_t;
5035#define arith_t_type long
5036#endif
5037
5038#if ENABLE_ASH_MATH_SUPPORT
5039static arith_t dash_arith(const char *);
5040static arith_t arith(const char *expr, int *perrcode);
5041#endif
5042
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005043/*
5044 * expandarg flags
5045 */
5046#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5047#define EXP_TILDE 0x2 /* do normal tilde expansion */
5048#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5049#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5050#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5051#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5052#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5053#define EXP_WORD 0x80 /* expand word in parameter expansion */
5054#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5055/*
5056 * _rmescape() flags
5057 */
5058#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5059#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5060#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5061#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5062#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5063
5064/*
5065 * Structure specifying which parts of the string should be searched
5066 * for IFS characters.
5067 */
5068struct ifsregion {
5069 struct ifsregion *next; /* next region in list */
5070 int begoff; /* offset of start of region */
5071 int endoff; /* offset of end of region */
5072 int nulonly; /* search for nul bytes only */
5073};
5074
5075struct arglist {
5076 struct strlist *list;
5077 struct strlist **lastp;
5078};
5079
5080/* output of current string */
5081static char *expdest;
5082/* list of back quote expressions */
5083static struct nodelist *argbackq;
5084/* first struct in list of ifs regions */
5085static struct ifsregion ifsfirst;
5086/* last struct in list */
5087static struct ifsregion *ifslastp;
5088/* holds expanded arg list */
5089static struct arglist exparg;
5090
5091/*
5092 * Our own itoa().
5093 */
5094static int
5095cvtnum(arith_t num)
5096{
5097 int len;
5098
5099 expdest = makestrspace(32, expdest);
5100#if ENABLE_ASH_MATH_SUPPORT_64
5101 len = fmtstr(expdest, 32, "%lld", (long long) num);
5102#else
5103 len = fmtstr(expdest, 32, "%ld", num);
5104#endif
5105 STADJUST(len, expdest);
5106 return len;
5107}
5108
5109static size_t
5110esclen(const char *start, const char *p)
5111{
5112 size_t esc = 0;
5113
5114 while (p > start && *--p == CTLESC) {
5115 esc++;
5116 }
5117 return esc;
5118}
5119
5120/*
5121 * Remove any CTLESC characters from a string.
5122 */
5123static char *
5124_rmescapes(char *str, int flag)
5125{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005126 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005127
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005128 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005129 unsigned inquotes;
5130 int notescaped;
5131 int globbing;
5132
5133 p = strpbrk(str, qchars);
5134 if (!p) {
5135 return str;
5136 }
5137 q = p;
5138 r = str;
5139 if (flag & RMESCAPE_ALLOC) {
5140 size_t len = p - str;
5141 size_t fulllen = len + strlen(p) + 1;
5142
5143 if (flag & RMESCAPE_GROW) {
5144 r = makestrspace(fulllen, expdest);
5145 } else if (flag & RMESCAPE_HEAP) {
5146 r = ckmalloc(fulllen);
5147 } else {
5148 r = stalloc(fulllen);
5149 }
5150 q = r;
5151 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005152 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005153 }
5154 }
5155 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5156 globbing = flag & RMESCAPE_GLOB;
5157 notescaped = globbing;
5158 while (*p) {
5159 if (*p == CTLQUOTEMARK) {
5160 inquotes = ~inquotes;
5161 p++;
5162 notescaped = globbing;
5163 continue;
5164 }
5165 if (*p == '\\') {
5166 /* naked back slash */
5167 notescaped = 0;
5168 goto copy;
5169 }
5170 if (*p == CTLESC) {
5171 p++;
5172 if (notescaped && inquotes && *p != '/') {
5173 *q++ = '\\';
5174 }
5175 }
5176 notescaped = globbing;
5177 copy:
5178 *q++ = *p++;
5179 }
5180 *q = '\0';
5181 if (flag & RMESCAPE_GROW) {
5182 expdest = r;
5183 STADJUST(q - r + 1, expdest);
5184 }
5185 return r;
5186}
5187#define rmescapes(p) _rmescapes((p), 0)
5188
5189#define pmatch(a, b) !fnmatch((a), (b), 0)
5190
5191/*
5192 * Prepare a pattern for a expmeta (internal glob(3)) call.
5193 *
5194 * Returns an stalloced string.
5195 */
5196static char *
5197preglob(const char *pattern, int quoted, int flag)
5198{
5199 flag |= RMESCAPE_GLOB;
5200 if (quoted) {
5201 flag |= RMESCAPE_QUOTED;
5202 }
5203 return _rmescapes((char *)pattern, flag);
5204}
5205
5206/*
5207 * Put a string on the stack.
5208 */
5209static void
5210memtodest(const char *p, size_t len, int syntax, int quotes)
5211{
5212 char *q = expdest;
5213
5214 q = makestrspace(len * 2, q);
5215
5216 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005217 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005218 if (!c)
5219 continue;
5220 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5221 USTPUTC(CTLESC, q);
5222 USTPUTC(c, q);
5223 }
5224
5225 expdest = q;
5226}
5227
5228static void
5229strtodest(const char *p, int syntax, int quotes)
5230{
5231 memtodest(p, strlen(p), syntax, quotes);
5232}
5233
5234/*
5235 * Record the fact that we have to scan this region of the
5236 * string for IFS characters.
5237 */
5238static void
5239recordregion(int start, int end, int nulonly)
5240{
5241 struct ifsregion *ifsp;
5242
5243 if (ifslastp == NULL) {
5244 ifsp = &ifsfirst;
5245 } else {
5246 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005247 ifsp = ckzalloc(sizeof(*ifsp));
5248 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005249 ifslastp->next = ifsp;
5250 INT_ON;
5251 }
5252 ifslastp = ifsp;
5253 ifslastp->begoff = start;
5254 ifslastp->endoff = end;
5255 ifslastp->nulonly = nulonly;
5256}
5257
5258static void
5259removerecordregions(int endoff)
5260{
5261 if (ifslastp == NULL)
5262 return;
5263
5264 if (ifsfirst.endoff > endoff) {
5265 while (ifsfirst.next != NULL) {
5266 struct ifsregion *ifsp;
5267 INT_OFF;
5268 ifsp = ifsfirst.next->next;
5269 free(ifsfirst.next);
5270 ifsfirst.next = ifsp;
5271 INT_ON;
5272 }
5273 if (ifsfirst.begoff > endoff)
5274 ifslastp = NULL;
5275 else {
5276 ifslastp = &ifsfirst;
5277 ifsfirst.endoff = endoff;
5278 }
5279 return;
5280 }
5281
5282 ifslastp = &ifsfirst;
5283 while (ifslastp->next && ifslastp->next->begoff < endoff)
5284 ifslastp=ifslastp->next;
5285 while (ifslastp->next != NULL) {
5286 struct ifsregion *ifsp;
5287 INT_OFF;
5288 ifsp = ifslastp->next->next;
5289 free(ifslastp->next);
5290 ifslastp->next = ifsp;
5291 INT_ON;
5292 }
5293 if (ifslastp->endoff > endoff)
5294 ifslastp->endoff = endoff;
5295}
5296
5297static char *
5298exptilde(char *startp, char *p, int flag)
5299{
5300 char c;
5301 char *name;
5302 struct passwd *pw;
5303 const char *home;
5304 int quotes = flag & (EXP_FULL | EXP_CASE);
5305 int startloc;
5306
5307 name = p + 1;
5308
5309 while ((c = *++p) != '\0') {
5310 switch (c) {
5311 case CTLESC:
5312 return startp;
5313 case CTLQUOTEMARK:
5314 return startp;
5315 case ':':
5316 if (flag & EXP_VARTILDE)
5317 goto done;
5318 break;
5319 case '/':
5320 case CTLENDVAR:
5321 goto done;
5322 }
5323 }
5324 done:
5325 *p = '\0';
5326 if (*name == '\0') {
5327 home = lookupvar(homestr);
5328 } else {
5329 pw = getpwnam(name);
5330 if (pw == NULL)
5331 goto lose;
5332 home = pw->pw_dir;
5333 }
5334 if (!home || !*home)
5335 goto lose;
5336 *p = c;
5337 startloc = expdest - (char *)stackblock();
5338 strtodest(home, SQSYNTAX, quotes);
5339 recordregion(startloc, expdest - (char *)stackblock(), 0);
5340 return p;
5341 lose:
5342 *p = c;
5343 return startp;
5344}
5345
5346/*
5347 * Execute a command inside back quotes. If it's a builtin command, we
5348 * want to save its output in a block obtained from malloc. Otherwise
5349 * we fork off a subprocess and get the output of the command via a pipe.
5350 * Should be called with interrupts off.
5351 */
5352struct backcmd { /* result of evalbackcmd */
5353 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005354 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005355 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005356 struct job *jp; /* job structure for command */
5357};
5358
5359/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005360static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005361#define EV_EXIT 01 /* exit after evaluating tree */
5362static void evaltree(union node *, int);
5363
5364static void
5365evalbackcmd(union node *n, struct backcmd *result)
5366{
5367 int saveherefd;
5368
5369 result->fd = -1;
5370 result->buf = NULL;
5371 result->nleft = 0;
5372 result->jp = NULL;
5373 if (n == NULL) {
5374 goto out;
5375 }
5376
5377 saveherefd = herefd;
5378 herefd = -1;
5379
5380 {
5381 int pip[2];
5382 struct job *jp;
5383
5384 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005385 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005386 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005387 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5388 FORCE_INT_ON;
5389 close(pip[0]);
5390 if (pip[1] != 1) {
5391 close(1);
5392 copyfd(pip[1], 1);
5393 close(pip[1]);
5394 }
5395 eflag = 0;
5396 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5397 /* NOTREACHED */
5398 }
5399 close(pip[1]);
5400 result->fd = pip[0];
5401 result->jp = jp;
5402 }
5403 herefd = saveherefd;
5404 out:
5405 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5406 result->fd, result->buf, result->nleft, result->jp));
5407}
5408
5409/*
5410 * Expand stuff in backwards quotes.
5411 */
5412static void
5413expbackq(union node *cmd, int quoted, int quotes)
5414{
5415 struct backcmd in;
5416 int i;
5417 char buf[128];
5418 char *p;
5419 char *dest;
5420 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005421 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005422 struct stackmark smark;
5423
5424 INT_OFF;
5425 setstackmark(&smark);
5426 dest = expdest;
5427 startloc = dest - (char *)stackblock();
5428 grabstackstr(dest);
5429 evalbackcmd(cmd, &in);
5430 popstackmark(&smark);
5431
5432 p = in.buf;
5433 i = in.nleft;
5434 if (i == 0)
5435 goto read;
5436 for (;;) {
5437 memtodest(p, i, syntax, quotes);
5438 read:
5439 if (in.fd < 0)
5440 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005441 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005442 TRACE(("expbackq: read returns %d\n", i));
5443 if (i <= 0)
5444 break;
5445 p = buf;
5446 }
5447
Denis Vlasenko60818682007-09-28 22:07:23 +00005448 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005449 if (in.fd >= 0) {
5450 close(in.fd);
5451 back_exitstatus = waitforjob(in.jp);
5452 }
5453 INT_ON;
5454
5455 /* Eat all trailing newlines */
5456 dest = expdest;
5457 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5458 STUNPUTC(dest);
5459 expdest = dest;
5460
5461 if (quoted == 0)
5462 recordregion(startloc, dest - (char *)stackblock(), 0);
5463 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5464 (dest - (char *)stackblock()) - startloc,
5465 (dest - (char *)stackblock()) - startloc,
5466 stackblock() + startloc));
5467}
5468
5469#if ENABLE_ASH_MATH_SUPPORT
5470/*
5471 * Expand arithmetic expression. Backup to start of expression,
5472 * evaluate, place result in (backed up) result, adjust string position.
5473 */
5474static void
5475expari(int quotes)
5476{
5477 char *p, *start;
5478 int begoff;
5479 int flag;
5480 int len;
5481
5482 /* ifsfree(); */
5483
5484 /*
5485 * This routine is slightly over-complicated for
5486 * efficiency. Next we scan backwards looking for the
5487 * start of arithmetic.
5488 */
5489 start = stackblock();
5490 p = expdest - 1;
5491 *p = '\0';
5492 p--;
5493 do {
5494 int esc;
5495
5496 while (*p != CTLARI) {
5497 p--;
5498#if DEBUG
5499 if (p < start) {
5500 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5501 }
5502#endif
5503 }
5504
5505 esc = esclen(start, p);
5506 if (!(esc % 2)) {
5507 break;
5508 }
5509
5510 p -= esc + 1;
5511 } while (1);
5512
5513 begoff = p - start;
5514
5515 removerecordregions(begoff);
5516
5517 flag = p[1];
5518
5519 expdest = p;
5520
5521 if (quotes)
5522 rmescapes(p + 2);
5523
5524 len = cvtnum(dash_arith(p + 2));
5525
5526 if (flag != '"')
5527 recordregion(begoff, begoff + len, 0);
5528}
5529#endif
5530
5531/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005532static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005533
5534/*
5535 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5536 * characters to allow for further processing. Otherwise treat
5537 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005538 *
5539 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5540 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5541 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542 */
5543static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005544argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005545{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005546 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005547 '=',
5548 ':',
5549 CTLQUOTEMARK,
5550 CTLENDVAR,
5551 CTLESC,
5552 CTLVAR,
5553 CTLBACKQ,
5554 CTLBACKQ | CTLQUOTE,
5555#if ENABLE_ASH_MATH_SUPPORT
5556 CTLENDARI,
5557#endif
5558 0
5559 };
5560 const char *reject = spclchars;
5561 int c;
5562 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5563 int breakall = flag & EXP_WORD;
5564 int inquotes;
5565 size_t length;
5566 int startloc;
5567
5568 if (!(flag & EXP_VARTILDE)) {
5569 reject += 2;
5570 } else if (flag & EXP_VARTILDE2) {
5571 reject++;
5572 }
5573 inquotes = 0;
5574 length = 0;
5575 if (flag & EXP_TILDE) {
5576 char *q;
5577
5578 flag &= ~EXP_TILDE;
5579 tilde:
5580 q = p;
5581 if (*q == CTLESC && (flag & EXP_QWORD))
5582 q++;
5583 if (*q == '~')
5584 p = exptilde(p, q, flag);
5585 }
5586 start:
5587 startloc = expdest - (char *)stackblock();
5588 for (;;) {
5589 length += strcspn(p + length, reject);
5590 c = p[length];
5591 if (c && (!(c & 0x80)
5592#if ENABLE_ASH_MATH_SUPPORT
5593 || c == CTLENDARI
5594#endif
5595 )) {
5596 /* c == '=' || c == ':' || c == CTLENDARI */
5597 length++;
5598 }
5599 if (length > 0) {
5600 int newloc;
5601 expdest = stack_nputstr(p, length, expdest);
5602 newloc = expdest - (char *)stackblock();
5603 if (breakall && !inquotes && newloc > startloc) {
5604 recordregion(startloc, newloc, 0);
5605 }
5606 startloc = newloc;
5607 }
5608 p += length + 1;
5609 length = 0;
5610
5611 switch (c) {
5612 case '\0':
5613 goto breakloop;
5614 case '=':
5615 if (flag & EXP_VARTILDE2) {
5616 p--;
5617 continue;
5618 }
5619 flag |= EXP_VARTILDE2;
5620 reject++;
5621 /* fall through */
5622 case ':':
5623 /*
5624 * sort of a hack - expand tildes in variable
5625 * assignments (after the first '=' and after ':'s).
5626 */
5627 if (*--p == '~') {
5628 goto tilde;
5629 }
5630 continue;
5631 }
5632
5633 switch (c) {
5634 case CTLENDVAR: /* ??? */
5635 goto breakloop;
5636 case CTLQUOTEMARK:
5637 /* "$@" syntax adherence hack */
5638 if (
5639 !inquotes &&
5640 !memcmp(p, dolatstr, 4) &&
5641 (p[4] == CTLQUOTEMARK || (
5642 p[4] == CTLENDVAR &&
5643 p[5] == CTLQUOTEMARK
5644 ))
5645 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005646 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005647 goto start;
5648 }
5649 inquotes = !inquotes;
5650 addquote:
5651 if (quotes) {
5652 p--;
5653 length++;
5654 startloc++;
5655 }
5656 break;
5657 case CTLESC:
5658 startloc++;
5659 length++;
5660 goto addquote;
5661 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005662 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005663 goto start;
5664 case CTLBACKQ:
5665 c = 0;
5666 case CTLBACKQ|CTLQUOTE:
5667 expbackq(argbackq->n, c, quotes);
5668 argbackq = argbackq->next;
5669 goto start;
5670#if ENABLE_ASH_MATH_SUPPORT
5671 case CTLENDARI:
5672 p--;
5673 expari(quotes);
5674 goto start;
5675#endif
5676 }
5677 }
5678 breakloop:
5679 ;
5680}
5681
5682static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005683scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005684 int zero)
5685{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005686// This commented out code was added by James Simmons <jsimmons@infradead.org>
5687// as part of a larger change when he added support for ${var/a/b}.
5688// However, it broke # and % operators:
5689//
5690//var=ababcdcd
5691// ok bad
5692//echo ${var#ab} abcdcd abcdcd
5693//echo ${var##ab} abcdcd abcdcd
5694//echo ${var#a*b} abcdcd ababcdcd (!)
5695//echo ${var##a*b} cdcd cdcd
5696//echo ${var#?} babcdcd ababcdcd (!)
5697//echo ${var##?} babcdcd babcdcd
5698//echo ${var#*} ababcdcd babcdcd (!)
5699//echo ${var##*}
5700//echo ${var%cd} ababcd ababcd
5701//echo ${var%%cd} ababcd abab (!)
5702//echo ${var%c*d} ababcd ababcd
5703//echo ${var%%c*d} abab ababcdcd (!)
5704//echo ${var%?} ababcdc ababcdc
5705//echo ${var%%?} ababcdc ababcdcd (!)
5706//echo ${var%*} ababcdcd ababcdcd
5707//echo ${var%%*}
5708//
5709// Commenting it back out helped. Remove it completely if it really
5710// is not needed.
5711
5712 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005713 char c;
5714
5715 loc = startp;
5716 loc2 = rmesc;
5717 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005718 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005719 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005720
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005721 c = *loc2;
5722 if (zero) {
5723 *loc2 = '\0';
5724 s = rmesc;
5725 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005726 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005727
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005728// // chop off end if its '*'
5729// full = strrchr(str, '*');
5730// if (full && full != str)
5731// match--;
5732//
5733// // If str starts with '*' replace with s.
5734// if ((*str == '*') && strlen(s) >= match) {
5735// full = xstrdup(s);
5736// strncpy(full+strlen(s)-match+1, str+1, match-1);
5737// } else
5738// full = xstrndup(str, match);
5739// match = strncmp(s, full, strlen(full));
5740// free(full);
5741//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005742 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005743 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005744 return loc;
5745 if (quotes && *loc == CTLESC)
5746 loc++;
5747 loc++;
5748 loc2++;
5749 } while (c);
5750 return 0;
5751}
5752
5753static char *
5754scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5755 int zero)
5756{
5757 int esc = 0;
5758 char *loc;
5759 char *loc2;
5760
5761 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5762 int match;
5763 char c = *loc2;
5764 const char *s = loc2;
5765 if (zero) {
5766 *loc2 = '\0';
5767 s = rmesc;
5768 }
5769 match = pmatch(str, s);
5770 *loc2 = c;
5771 if (match)
5772 return loc;
5773 loc--;
5774 if (quotes) {
5775 if (--esc < 0) {
5776 esc = esclen(startp, loc);
5777 }
5778 if (esc % 2) {
5779 esc--;
5780 loc--;
5781 }
5782 }
5783 }
5784 return 0;
5785}
5786
5787static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5788static void
5789varunset(const char *end, const char *var, const char *umsg, int varflags)
5790{
5791 const char *msg;
5792 const char *tail;
5793
5794 tail = nullstr;
5795 msg = "parameter not set";
5796 if (umsg) {
5797 if (*end == CTLENDVAR) {
5798 if (varflags & VSNUL)
5799 tail = " or null";
5800 } else
5801 msg = umsg;
5802 }
5803 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5804}
5805
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005806#if ENABLE_ASH_BASH_COMPAT
5807static char *
5808parse_sub_pattern(char *arg, int inquotes)
5809{
5810 char *idx, *repl = NULL;
5811 unsigned char c;
5812
Denis Vlasenko2659c632008-06-14 06:04:59 +00005813 idx = arg;
5814 while (1) {
5815 c = *arg;
5816 if (!c)
5817 break;
5818 if (c == '/') {
5819 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005820 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005821 repl = idx + 1;
5822 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005823 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005824 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005825 *idx++ = c;
5826 if (!inquotes && c == '\\' && arg[1] == '\\')
5827 arg++; /* skip both \\, not just first one */
5828 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005829 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005830 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005831
5832 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005833}
5834#endif /* ENABLE_ASH_BASH_COMPAT */
5835
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005836static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005837subevalvar(char *p, char *str, int strloc, int subtype,
5838 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005840 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005841 char *startp;
5842 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005843 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005844 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5845 USE_ASH_BASH_COMPAT(char null = '\0';)
5846 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5847 int saveherefd = herefd;
5848 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005849 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005850 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005851
5852 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005853 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5854 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005855 STPUTC('\0', expdest);
5856 herefd = saveherefd;
5857 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005858 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005859
5860 switch (subtype) {
5861 case VSASSIGN:
5862 setvar(str, startp, 0);
5863 amount = startp - expdest;
5864 STADJUST(amount, expdest);
5865 return startp;
5866
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005867#if ENABLE_ASH_BASH_COMPAT
5868 case VSSUBSTR:
5869 loc = str = stackblock() + strloc;
5870// TODO: number() instead? It does error checking...
5871 pos = atoi(loc);
5872 len = str - startp - 1;
5873
5874 /* *loc != '\0', guaranteed by parser */
5875 if (quotes) {
5876 char *ptr;
5877
5878 /* We must adjust the length by the number of escapes we find. */
5879 for (ptr = startp; ptr < (str - 1); ptr++) {
5880 if(*ptr == CTLESC) {
5881 len--;
5882 ptr++;
5883 }
5884 }
5885 }
5886 orig_len = len;
5887
5888 if (*loc++ == ':') {
5889// TODO: number() instead? It does error checking...
5890 len = atoi(loc);
5891 } else {
5892 len = orig_len;
5893 while (*loc && *loc != ':')
5894 loc++;
5895 if (*loc++ == ':')
5896// TODO: number() instead? It does error checking...
5897 len = atoi(loc);
5898 }
5899 if (pos >= orig_len) {
5900 pos = 0;
5901 len = 0;
5902 }
5903 if (len > (orig_len - pos))
5904 len = orig_len - pos;
5905
5906 for (str = startp; pos; str++, pos--) {
5907 if (quotes && *str == CTLESC)
5908 str++;
5909 }
5910 for (loc = startp; len; len--) {
5911 if (quotes && *str == CTLESC)
5912 *loc++ = *str++;
5913 *loc++ = *str++;
5914 }
5915 *loc = '\0';
5916 amount = loc - expdest;
5917 STADJUST(amount, expdest);
5918 return loc;
5919#endif
5920
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005921 case VSQUESTION:
5922 varunset(p, str, startp, varflags);
5923 /* NOTREACHED */
5924 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005925 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005926
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005927 /* We'll comeback here if we grow the stack while handling
5928 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5929 * stack will need rebasing, and we'll need to remove our work
5930 * areas each time
5931 */
5932 USE_ASH_BASH_COMPAT(restart:)
5933
5934 amount = expdest - ((char *)stackblock() + resetloc);
5935 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005936 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005937
5938 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005939 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005940 if (quotes) {
5941 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5942 if (rmesc != startp) {
5943 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005944 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005945 }
5946 }
5947 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005948 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005949 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005950 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005952#if ENABLE_ASH_BASH_COMPAT
5953 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5954 char *idx, *end, *restart_detect;
5955
5956 if(!repl) {
5957 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5958 if (!repl)
5959 repl = &null;
5960 }
5961
5962 /* If there's no pattern to match, return the expansion unmolested */
5963 if (*str == '\0')
5964 return 0;
5965
5966 len = 0;
5967 idx = startp;
5968 end = str - 1;
5969 while (idx < end) {
5970 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5971 if (!loc) {
5972 /* No match, advance */
5973 restart_detect = stackblock();
5974 STPUTC(*idx, expdest);
5975 if (quotes && *idx == CTLESC) {
5976 idx++;
5977 len++;
5978 STPUTC(*idx, expdest);
5979 }
5980 if (stackblock() != restart_detect)
5981 goto restart;
5982 idx++;
5983 len++;
5984 rmesc++;
5985 continue;
5986 }
5987
5988 if (subtype == VSREPLACEALL) {
5989 while (idx < loc) {
5990 if (quotes && *idx == CTLESC)
5991 idx++;
5992 idx++;
5993 rmesc++;
5994 }
5995 } else
5996 idx = loc;
5997
5998 for (loc = repl; *loc; loc++) {
5999 restart_detect = stackblock();
6000 STPUTC(*loc, expdest);
6001 if (stackblock() != restart_detect)
6002 goto restart;
6003 len++;
6004 }
6005
6006 if (subtype == VSREPLACE) {
6007 while (*idx) {
6008 restart_detect = stackblock();
6009 STPUTC(*idx, expdest);
6010 if (stackblock() != restart_detect)
6011 goto restart;
6012 len++;
6013 idx++;
6014 }
6015 break;
6016 }
6017 }
6018
6019 /* We've put the replaced text into a buffer at workloc, now
6020 * move it to the right place and adjust the stack.
6021 */
6022 startp = stackblock() + startloc;
6023 STPUTC('\0', expdest);
6024 memmove(startp, stackblock() + workloc, len);
6025 startp[len++] = '\0';
6026 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6027 STADJUST(-amount, expdest);
6028 return startp;
6029 }
6030#endif /* ENABLE_ASH_BASH_COMPAT */
6031
6032 subtype -= VSTRIMRIGHT;
6033#if DEBUG
6034 if (subtype < 0 || subtype > 7)
6035 abort();
6036#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6038 zero = subtype >> 1;
6039 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6040 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6041
6042 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6043 if (loc) {
6044 if (zero) {
6045 memmove(startp, loc, str - loc);
6046 loc = startp + (str - loc) - 1;
6047 }
6048 *loc = '\0';
6049 amount = loc - expdest;
6050 STADJUST(amount, expdest);
6051 }
6052 return loc;
6053}
6054
6055/*
6056 * Add the value of a specialized variable to the stack string.
6057 */
6058static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006059varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006060{
6061 int num;
6062 char *p;
6063 int i;
6064 int sep = 0;
6065 int sepq = 0;
6066 ssize_t len = 0;
6067 char **ap;
6068 int syntax;
6069 int quoted = varflags & VSQUOTE;
6070 int subtype = varflags & VSTYPE;
6071 int quotes = flags & (EXP_FULL | EXP_CASE);
6072
6073 if (quoted && (flags & EXP_FULL))
6074 sep = 1 << CHAR_BIT;
6075
6076 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6077 switch (*name) {
6078 case '$':
6079 num = rootpid;
6080 goto numvar;
6081 case '?':
6082 num = exitstatus;
6083 goto numvar;
6084 case '#':
6085 num = shellparam.nparam;
6086 goto numvar;
6087 case '!':
6088 num = backgndpid;
6089 if (num == 0)
6090 return -1;
6091 numvar:
6092 len = cvtnum(num);
6093 break;
6094 case '-':
6095 p = makestrspace(NOPTS, expdest);
6096 for (i = NOPTS - 1; i >= 0; i--) {
6097 if (optlist[i]) {
6098 USTPUTC(optletters(i), p);
6099 len++;
6100 }
6101 }
6102 expdest = p;
6103 break;
6104 case '@':
6105 if (sep)
6106 goto param;
6107 /* fall through */
6108 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006109 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006110 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6111 sepq = 1;
6112 param:
6113 ap = shellparam.p;
6114 if (!ap)
6115 return -1;
6116 while ((p = *ap++)) {
6117 size_t partlen;
6118
6119 partlen = strlen(p);
6120 len += partlen;
6121
6122 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6123 memtodest(p, partlen, syntax, quotes);
6124
6125 if (*ap && sep) {
6126 char *q;
6127
6128 len++;
6129 if (subtype == VSPLUS || subtype == VSLENGTH) {
6130 continue;
6131 }
6132 q = expdest;
6133 if (sepq)
6134 STPUTC(CTLESC, q);
6135 STPUTC(sep, q);
6136 expdest = q;
6137 }
6138 }
6139 return len;
6140 case '0':
6141 case '1':
6142 case '2':
6143 case '3':
6144 case '4':
6145 case '5':
6146 case '6':
6147 case '7':
6148 case '8':
6149 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006150// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006151 num = atoi(name);
6152 if (num < 0 || num > shellparam.nparam)
6153 return -1;
6154 p = num ? shellparam.p[num - 1] : arg0;
6155 goto value;
6156 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006157 /* NB: name has form "VAR=..." */
6158
6159 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6160 * which should be considered before we check variables. */
6161 if (var_str_list) {
6162 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6163 p = NULL;
6164 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006165 char *str, *eq;
6166 str = var_str_list->text;
6167 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006168 if (!eq) /* stop at first non-assignment */
6169 break;
6170 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006171 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006172 && strncmp(str, name, name_len) == 0) {
6173 p = eq;
6174 /* goto value; - WRONG! */
6175 /* think "A=1 A=2 B=$A" */
6176 }
6177 var_str_list = var_str_list->next;
6178 } while (var_str_list);
6179 if (p)
6180 goto value;
6181 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182 p = lookupvar(name);
6183 value:
6184 if (!p)
6185 return -1;
6186
6187 len = strlen(p);
6188 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6189 memtodest(p, len, syntax, quotes);
6190 return len;
6191 }
6192
6193 if (subtype == VSPLUS || subtype == VSLENGTH)
6194 STADJUST(-len, expdest);
6195 return len;
6196}
6197
6198/*
6199 * Expand a variable, and return a pointer to the next character in the
6200 * input string.
6201 */
6202static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006203evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006204{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006205 char varflags;
6206 char subtype;
6207 char quoted;
6208 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006209 char *var;
6210 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006211 int startloc;
6212 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006214 varflags = *p++;
6215 subtype = varflags & VSTYPE;
6216 quoted = varflags & VSQUOTE;
6217 var = p;
6218 easy = (!quoted || (*var == '@' && shellparam.nparam));
6219 startloc = expdest - (char *)stackblock();
6220 p = strchr(p, '=') + 1;
6221
6222 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006223 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006224 if (varflags & VSNUL)
6225 varlen--;
6226
6227 if (subtype == VSPLUS) {
6228 varlen = -1 - varlen;
6229 goto vsplus;
6230 }
6231
6232 if (subtype == VSMINUS) {
6233 vsplus:
6234 if (varlen < 0) {
6235 argstr(
6236 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006237 (quoted ? EXP_QWORD : EXP_WORD),
6238 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006239 );
6240 goto end;
6241 }
6242 if (easy)
6243 goto record;
6244 goto end;
6245 }
6246
6247 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6248 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006249 if (subevalvar(p, var, /* strloc: */ 0,
6250 subtype, startloc, varflags,
6251 /* quotes: */ 0,
6252 var_str_list)
6253 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006254 varflags &= ~VSNUL;
6255 /*
6256 * Remove any recorded regions beyond
6257 * start of variable
6258 */
6259 removerecordregions(startloc);
6260 goto again;
6261 }
6262 goto end;
6263 }
6264 if (easy)
6265 goto record;
6266 goto end;
6267 }
6268
6269 if (varlen < 0 && uflag)
6270 varunset(p, var, 0, 0);
6271
6272 if (subtype == VSLENGTH) {
6273 cvtnum(varlen > 0 ? varlen : 0);
6274 goto record;
6275 }
6276
6277 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006278 if (easy)
6279 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280 goto end;
6281 }
6282
6283#if DEBUG
6284 switch (subtype) {
6285 case VSTRIMLEFT:
6286 case VSTRIMLEFTMAX:
6287 case VSTRIMRIGHT:
6288 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006289#if ENABLE_ASH_BASH_COMPAT
6290 case VSSUBSTR:
6291 case VSREPLACE:
6292 case VSREPLACEALL:
6293#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294 break;
6295 default:
6296 abort();
6297 }
6298#endif
6299
6300 if (varlen >= 0) {
6301 /*
6302 * Terminate the string and start recording the pattern
6303 * right after it
6304 */
6305 STPUTC('\0', expdest);
6306 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006307 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6308 startloc, varflags,
6309 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6310 var_str_list)
6311 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006312 int amount = expdest - (
6313 (char *)stackblock() + patloc - 1
6314 );
6315 STADJUST(-amount, expdest);
6316 }
6317 /* Remove any recorded regions beyond start of variable */
6318 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006319 record:
6320 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006321 }
6322
6323 end:
6324 if (subtype != VSNORMAL) { /* skip to end of alternative */
6325 int nesting = 1;
6326 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006327 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006328 if (c == CTLESC)
6329 p++;
6330 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6331 if (varlen >= 0)
6332 argbackq = argbackq->next;
6333 } else if (c == CTLVAR) {
6334 if ((*p++ & VSTYPE) != VSNORMAL)
6335 nesting++;
6336 } else if (c == CTLENDVAR) {
6337 if (--nesting == 0)
6338 break;
6339 }
6340 }
6341 }
6342 return p;
6343}
6344
6345/*
6346 * Break the argument string into pieces based upon IFS and add the
6347 * strings to the argument list. The regions of the string to be
6348 * searched for IFS characters have been stored by recordregion.
6349 */
6350static void
6351ifsbreakup(char *string, struct arglist *arglist)
6352{
6353 struct ifsregion *ifsp;
6354 struct strlist *sp;
6355 char *start;
6356 char *p;
6357 char *q;
6358 const char *ifs, *realifs;
6359 int ifsspc;
6360 int nulonly;
6361
6362 start = string;
6363 if (ifslastp != NULL) {
6364 ifsspc = 0;
6365 nulonly = 0;
6366 realifs = ifsset() ? ifsval() : defifs;
6367 ifsp = &ifsfirst;
6368 do {
6369 p = string + ifsp->begoff;
6370 nulonly = ifsp->nulonly;
6371 ifs = nulonly ? nullstr : realifs;
6372 ifsspc = 0;
6373 while (p < string + ifsp->endoff) {
6374 q = p;
6375 if (*p == CTLESC)
6376 p++;
6377 if (!strchr(ifs, *p)) {
6378 p++;
6379 continue;
6380 }
6381 if (!nulonly)
6382 ifsspc = (strchr(defifs, *p) != NULL);
6383 /* Ignore IFS whitespace at start */
6384 if (q == start && ifsspc) {
6385 p++;
6386 start = p;
6387 continue;
6388 }
6389 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006390 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006391 sp->text = start;
6392 *arglist->lastp = sp;
6393 arglist->lastp = &sp->next;
6394 p++;
6395 if (!nulonly) {
6396 for (;;) {
6397 if (p >= string + ifsp->endoff) {
6398 break;
6399 }
6400 q = p;
6401 if (*p == CTLESC)
6402 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006403 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404 p = q;
6405 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006406 }
6407 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006408 if (ifsspc) {
6409 p++;
6410 ifsspc = 0;
6411 } else {
6412 p = q;
6413 break;
6414 }
6415 } else
6416 p++;
6417 }
6418 }
6419 start = p;
6420 } /* while */
6421 ifsp = ifsp->next;
6422 } while (ifsp != NULL);
6423 if (nulonly)
6424 goto add;
6425 }
6426
6427 if (!*start)
6428 return;
6429
6430 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006431 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006432 sp->text = start;
6433 *arglist->lastp = sp;
6434 arglist->lastp = &sp->next;
6435}
6436
6437static void
6438ifsfree(void)
6439{
6440 struct ifsregion *p;
6441
6442 INT_OFF;
6443 p = ifsfirst.next;
6444 do {
6445 struct ifsregion *ifsp;
6446 ifsp = p->next;
6447 free(p);
6448 p = ifsp;
6449 } while (p);
6450 ifslastp = NULL;
6451 ifsfirst.next = NULL;
6452 INT_ON;
6453}
6454
6455/*
6456 * Add a file name to the list.
6457 */
6458static void
6459addfname(const char *name)
6460{
6461 struct strlist *sp;
6462
Denis Vlasenko597906c2008-02-20 16:38:54 +00006463 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006464 sp->text = ststrdup(name);
6465 *exparg.lastp = sp;
6466 exparg.lastp = &sp->next;
6467}
6468
6469static char *expdir;
6470
6471/*
6472 * Do metacharacter (i.e. *, ?, [...]) expansion.
6473 */
6474static void
6475expmeta(char *enddir, char *name)
6476{
6477 char *p;
6478 const char *cp;
6479 char *start;
6480 char *endname;
6481 int metaflag;
6482 struct stat statb;
6483 DIR *dirp;
6484 struct dirent *dp;
6485 int atend;
6486 int matchdot;
6487
6488 metaflag = 0;
6489 start = name;
6490 for (p = name; *p; p++) {
6491 if (*p == '*' || *p == '?')
6492 metaflag = 1;
6493 else if (*p == '[') {
6494 char *q = p + 1;
6495 if (*q == '!')
6496 q++;
6497 for (;;) {
6498 if (*q == '\\')
6499 q++;
6500 if (*q == '/' || *q == '\0')
6501 break;
6502 if (*++q == ']') {
6503 metaflag = 1;
6504 break;
6505 }
6506 }
6507 } else if (*p == '\\')
6508 p++;
6509 else if (*p == '/') {
6510 if (metaflag)
6511 goto out;
6512 start = p + 1;
6513 }
6514 }
6515 out:
6516 if (metaflag == 0) { /* we've reached the end of the file name */
6517 if (enddir != expdir)
6518 metaflag++;
6519 p = name;
6520 do {
6521 if (*p == '\\')
6522 p++;
6523 *enddir++ = *p;
6524 } while (*p++);
6525 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6526 addfname(expdir);
6527 return;
6528 }
6529 endname = p;
6530 if (name < start) {
6531 p = name;
6532 do {
6533 if (*p == '\\')
6534 p++;
6535 *enddir++ = *p++;
6536 } while (p < start);
6537 }
6538 if (enddir == expdir) {
6539 cp = ".";
6540 } else if (enddir == expdir + 1 && *expdir == '/') {
6541 cp = "/";
6542 } else {
6543 cp = expdir;
6544 enddir[-1] = '\0';
6545 }
6546 dirp = opendir(cp);
6547 if (dirp == NULL)
6548 return;
6549 if (enddir != expdir)
6550 enddir[-1] = '/';
6551 if (*endname == 0) {
6552 atend = 1;
6553 } else {
6554 atend = 0;
6555 *endname++ = '\0';
6556 }
6557 matchdot = 0;
6558 p = start;
6559 if (*p == '\\')
6560 p++;
6561 if (*p == '.')
6562 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006563 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006564 if (dp->d_name[0] == '.' && ! matchdot)
6565 continue;
6566 if (pmatch(start, dp->d_name)) {
6567 if (atend) {
6568 strcpy(enddir, dp->d_name);
6569 addfname(expdir);
6570 } else {
6571 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6572 continue;
6573 p[-1] = '/';
6574 expmeta(p, endname);
6575 }
6576 }
6577 }
6578 closedir(dirp);
6579 if (! atend)
6580 endname[-1] = '/';
6581}
6582
6583static struct strlist *
6584msort(struct strlist *list, int len)
6585{
6586 struct strlist *p, *q = NULL;
6587 struct strlist **lpp;
6588 int half;
6589 int n;
6590
6591 if (len <= 1)
6592 return list;
6593 half = len >> 1;
6594 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006595 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006596 q = p;
6597 p = p->next;
6598 }
6599 q->next = NULL; /* terminate first half of list */
6600 q = msort(list, half); /* sort first half of list */
6601 p = msort(p, len - half); /* sort second half */
6602 lpp = &list;
6603 for (;;) {
6604#if ENABLE_LOCALE_SUPPORT
6605 if (strcoll(p->text, q->text) < 0)
6606#else
6607 if (strcmp(p->text, q->text) < 0)
6608#endif
6609 {
6610 *lpp = p;
6611 lpp = &p->next;
6612 p = *lpp;
6613 if (p == NULL) {
6614 *lpp = q;
6615 break;
6616 }
6617 } else {
6618 *lpp = q;
6619 lpp = &q->next;
6620 q = *lpp;
6621 if (q == NULL) {
6622 *lpp = p;
6623 break;
6624 }
6625 }
6626 }
6627 return list;
6628}
6629
6630/*
6631 * Sort the results of file name expansion. It calculates the number of
6632 * strings to sort and then calls msort (short for merge sort) to do the
6633 * work.
6634 */
6635static struct strlist *
6636expsort(struct strlist *str)
6637{
6638 int len;
6639 struct strlist *sp;
6640
6641 len = 0;
6642 for (sp = str; sp; sp = sp->next)
6643 len++;
6644 return msort(str, len);
6645}
6646
6647static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006648expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006649{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006650 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006651 '*', '?', '[', 0
6652 };
6653 /* TODO - EXP_REDIR */
6654
6655 while (str) {
6656 struct strlist **savelastp;
6657 struct strlist *sp;
6658 char *p;
6659
6660 if (fflag)
6661 goto nometa;
6662 if (!strpbrk(str->text, metachars))
6663 goto nometa;
6664 savelastp = exparg.lastp;
6665
6666 INT_OFF;
6667 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6668 {
6669 int i = strlen(str->text);
6670 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6671 }
6672
6673 expmeta(expdir, p);
6674 free(expdir);
6675 if (p != str->text)
6676 free(p);
6677 INT_ON;
6678 if (exparg.lastp == savelastp) {
6679 /*
6680 * no matches
6681 */
6682 nometa:
6683 *exparg.lastp = str;
6684 rmescapes(str->text);
6685 exparg.lastp = &str->next;
6686 } else {
6687 *exparg.lastp = NULL;
6688 *savelastp = sp = expsort(*savelastp);
6689 while (sp->next != NULL)
6690 sp = sp->next;
6691 exparg.lastp = &sp->next;
6692 }
6693 str = str->next;
6694 }
6695}
6696
6697/*
6698 * Perform variable substitution and command substitution on an argument,
6699 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6700 * perform splitting and file name expansion. When arglist is NULL, perform
6701 * here document expansion.
6702 */
6703static void
6704expandarg(union node *arg, struct arglist *arglist, int flag)
6705{
6706 struct strlist *sp;
6707 char *p;
6708
6709 argbackq = arg->narg.backquote;
6710 STARTSTACKSTR(expdest);
6711 ifsfirst.next = NULL;
6712 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006713 argstr(arg->narg.text, flag,
6714 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 p = _STPUTC('\0', expdest);
6716 expdest = p - 1;
6717 if (arglist == NULL) {
6718 return; /* here document expanded */
6719 }
6720 p = grabstackstr(p);
6721 exparg.lastp = &exparg.list;
6722 /*
6723 * TODO - EXP_REDIR
6724 */
6725 if (flag & EXP_FULL) {
6726 ifsbreakup(p, &exparg);
6727 *exparg.lastp = NULL;
6728 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006729 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006730 } else {
6731 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6732 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006733 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006734 sp->text = p;
6735 *exparg.lastp = sp;
6736 exparg.lastp = &sp->next;
6737 }
6738 if (ifsfirst.next)
6739 ifsfree();
6740 *exparg.lastp = NULL;
6741 if (exparg.list) {
6742 *arglist->lastp = exparg.list;
6743 arglist->lastp = exparg.lastp;
6744 }
6745}
6746
6747/*
6748 * Expand shell variables and backquotes inside a here document.
6749 */
6750static void
6751expandhere(union node *arg, int fd)
6752{
6753 herefd = fd;
6754 expandarg(arg, (struct arglist *)NULL, 0);
6755 full_write(fd, stackblock(), expdest - (char *)stackblock());
6756}
6757
6758/*
6759 * Returns true if the pattern matches the string.
6760 */
6761static int
6762patmatch(char *pattern, const char *string)
6763{
6764 return pmatch(preglob(pattern, 0, 0), string);
6765}
6766
6767/*
6768 * See if a pattern matches in a case statement.
6769 */
6770static int
6771casematch(union node *pattern, char *val)
6772{
6773 struct stackmark smark;
6774 int result;
6775
6776 setstackmark(&smark);
6777 argbackq = pattern->narg.backquote;
6778 STARTSTACKSTR(expdest);
6779 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006780 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6781 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006782 STACKSTRNUL(expdest);
6783 result = patmatch(stackblock(), val);
6784 popstackmark(&smark);
6785 return result;
6786}
6787
6788
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006789/* ============ find_command */
6790
6791struct builtincmd {
6792 const char *name;
6793 int (*builtin)(int, char **);
6794 /* unsigned flags; */
6795};
6796#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006797/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006798 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006799#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006800#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006801
6802struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006803 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006804 union param {
6805 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006806 /* index >= 0 for commands without path (slashes) */
6807 /* (TODO: what exactly does the value mean? PATH position?) */
6808 /* index == -1 for commands with slashes */
6809 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006810 const struct builtincmd *cmd;
6811 struct funcnode *func;
6812 } u;
6813};
6814/* values of cmdtype */
6815#define CMDUNKNOWN -1 /* no entry in table for command */
6816#define CMDNORMAL 0 /* command is an executable program */
6817#define CMDFUNCTION 1 /* command is a shell function */
6818#define CMDBUILTIN 2 /* command is a shell builtin */
6819
6820/* action to find_command() */
6821#define DO_ERR 0x01 /* prints errors */
6822#define DO_ABS 0x02 /* checks absolute paths */
6823#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6824#define DO_ALTPATH 0x08 /* using alternate path */
6825#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6826
6827static void find_command(char *, struct cmdentry *, int, const char *);
6828
6829
6830/* ============ Hashing commands */
6831
6832/*
6833 * When commands are first encountered, they are entered in a hash table.
6834 * This ensures that a full path search will not have to be done for them
6835 * on each invocation.
6836 *
6837 * We should investigate converting to a linear search, even though that
6838 * would make the command name "hash" a misnomer.
6839 */
6840
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006841struct tblentry {
6842 struct tblentry *next; /* next entry in hash chain */
6843 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006844 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006845 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006846 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006847};
6848
Denis Vlasenko01631112007-12-16 17:20:38 +00006849static struct tblentry **cmdtable;
6850#define INIT_G_cmdtable() do { \
6851 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6852} while (0)
6853
6854static int builtinloc = -1; /* index in path of %builtin, or -1 */
6855
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006856
6857static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006858tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006859{
6860 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006861
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006862#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006863 if (applet_no >= 0) {
6864 if (APPLET_IS_NOEXEC(applet_no))
6865 run_applet_no_and_exit(applet_no, argv);
6866 /* re-exec ourselves with the new arguments */
6867 execve(bb_busybox_exec_path, argv, envp);
6868 /* If they called chroot or otherwise made the binary no longer
6869 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006870 }
6871#endif
6872
6873 repeat:
6874#ifdef SYSV
6875 do {
6876 execve(cmd, argv, envp);
6877 } while (errno == EINTR);
6878#else
6879 execve(cmd, argv, envp);
6880#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006881 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006882 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006883 return;
6884 }
6885 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006886 char **ap;
6887 char **new;
6888
6889 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006890 continue;
6891 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006892 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006893 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006894 ap += 2;
6895 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006896 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006897 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006898 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006899 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900 goto repeat;
6901 }
6902}
6903
6904/*
6905 * Exec a program. Never returns. If you change this routine, you may
6906 * have to change the find_command routine as well.
6907 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006908static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6909static void
6910shellexec(char **argv, const char *path, int idx)
6911{
6912 char *cmdname;
6913 int e;
6914 char **envp;
6915 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006916#if ENABLE_FEATURE_SH_STANDALONE
6917 int applet_no = -1;
6918#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006919
6920 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006921 envp = listvars(VEXPORT, VUNSET, 0);
6922 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006923#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006924 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006925#endif
6926 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006927 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006928 e = errno;
6929 } else {
6930 e = ENOENT;
6931 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6932 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006933 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006934 if (errno != ENOENT && errno != ENOTDIR)
6935 e = errno;
6936 }
6937 stunalloc(cmdname);
6938 }
6939 }
6940
6941 /* Map to POSIX errors */
6942 switch (e) {
6943 case EACCES:
6944 exerrno = 126;
6945 break;
6946 case ENOENT:
6947 exerrno = 127;
6948 break;
6949 default:
6950 exerrno = 2;
6951 break;
6952 }
6953 exitstatus = exerrno;
6954 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006955 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006956 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6957 /* NOTREACHED */
6958}
6959
6960static void
6961printentry(struct tblentry *cmdp)
6962{
6963 int idx;
6964 const char *path;
6965 char *name;
6966
6967 idx = cmdp->param.index;
6968 path = pathval();
6969 do {
6970 name = padvance(&path, cmdp->cmdname);
6971 stunalloc(name);
6972 } while (--idx >= 0);
6973 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6974}
6975
6976/*
6977 * Clear out command entries. The argument specifies the first entry in
6978 * PATH which has changed.
6979 */
6980static void
6981clearcmdentry(int firstchange)
6982{
6983 struct tblentry **tblp;
6984 struct tblentry **pp;
6985 struct tblentry *cmdp;
6986
6987 INT_OFF;
6988 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6989 pp = tblp;
6990 while ((cmdp = *pp) != NULL) {
6991 if ((cmdp->cmdtype == CMDNORMAL &&
6992 cmdp->param.index >= firstchange)
6993 || (cmdp->cmdtype == CMDBUILTIN &&
6994 builtinloc >= firstchange)
6995 ) {
6996 *pp = cmdp->next;
6997 free(cmdp);
6998 } else {
6999 pp = &cmdp->next;
7000 }
7001 }
7002 }
7003 INT_ON;
7004}
7005
7006/*
7007 * Locate a command in the command hash table. If "add" is nonzero,
7008 * add the command to the table if it is not already present. The
7009 * variable "lastcmdentry" is set to point to the address of the link
7010 * pointing to the entry, so that delete_cmd_entry can delete the
7011 * entry.
7012 *
7013 * Interrupts must be off if called with add != 0.
7014 */
7015static struct tblentry **lastcmdentry;
7016
7017static struct tblentry *
7018cmdlookup(const char *name, int add)
7019{
7020 unsigned int hashval;
7021 const char *p;
7022 struct tblentry *cmdp;
7023 struct tblentry **pp;
7024
7025 p = name;
7026 hashval = (unsigned char)*p << 4;
7027 while (*p)
7028 hashval += (unsigned char)*p++;
7029 hashval &= 0x7FFF;
7030 pp = &cmdtable[hashval % CMDTABLESIZE];
7031 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7032 if (strcmp(cmdp->cmdname, name) == 0)
7033 break;
7034 pp = &cmdp->next;
7035 }
7036 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007037 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7038 + strlen(name)
7039 /* + 1 - already done because
7040 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007041 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007042 cmdp->cmdtype = CMDUNKNOWN;
7043 strcpy(cmdp->cmdname, name);
7044 }
7045 lastcmdentry = pp;
7046 return cmdp;
7047}
7048
7049/*
7050 * Delete the command entry returned on the last lookup.
7051 */
7052static void
7053delete_cmd_entry(void)
7054{
7055 struct tblentry *cmdp;
7056
7057 INT_OFF;
7058 cmdp = *lastcmdentry;
7059 *lastcmdentry = cmdp->next;
7060 if (cmdp->cmdtype == CMDFUNCTION)
7061 freefunc(cmdp->param.func);
7062 free(cmdp);
7063 INT_ON;
7064}
7065
7066/*
7067 * Add a new command entry, replacing any existing command entry for
7068 * the same name - except special builtins.
7069 */
7070static void
7071addcmdentry(char *name, struct cmdentry *entry)
7072{
7073 struct tblentry *cmdp;
7074
7075 cmdp = cmdlookup(name, 1);
7076 if (cmdp->cmdtype == CMDFUNCTION) {
7077 freefunc(cmdp->param.func);
7078 }
7079 cmdp->cmdtype = entry->cmdtype;
7080 cmdp->param = entry->u;
7081 cmdp->rehash = 0;
7082}
7083
7084static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007085hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007086{
7087 struct tblentry **pp;
7088 struct tblentry *cmdp;
7089 int c;
7090 struct cmdentry entry;
7091 char *name;
7092
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007093 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007094 clearcmdentry(0);
7095 return 0;
7096 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007097
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007098 if (*argptr == NULL) {
7099 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7100 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7101 if (cmdp->cmdtype == CMDNORMAL)
7102 printentry(cmdp);
7103 }
7104 }
7105 return 0;
7106 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007107
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007108 c = 0;
7109 while ((name = *argptr) != NULL) {
7110 cmdp = cmdlookup(name, 0);
7111 if (cmdp != NULL
7112 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007113 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7114 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007115 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007116 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007117 find_command(name, &entry, DO_ERR, pathval());
7118 if (entry.cmdtype == CMDUNKNOWN)
7119 c = 1;
7120 argptr++;
7121 }
7122 return c;
7123}
7124
7125/*
7126 * Called when a cd is done. Marks all commands so the next time they
7127 * are executed they will be rehashed.
7128 */
7129static void
7130hashcd(void)
7131{
7132 struct tblentry **pp;
7133 struct tblentry *cmdp;
7134
7135 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7136 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007137 if (cmdp->cmdtype == CMDNORMAL
7138 || (cmdp->cmdtype == CMDBUILTIN
7139 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7140 && builtinloc > 0)
7141 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007142 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007143 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007144 }
7145 }
7146}
7147
7148/*
7149 * Fix command hash table when PATH changed.
7150 * Called before PATH is changed. The argument is the new value of PATH;
7151 * pathval() still returns the old value at this point.
7152 * Called with interrupts off.
7153 */
7154static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007155changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007156{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007157 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007158 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007159 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007160 int idx_bltin;
7161
7162 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007163 firstchange = 9999; /* assume no change */
7164 idx = 0;
7165 idx_bltin = -1;
7166 for (;;) {
7167 if (*old != *new) {
7168 firstchange = idx;
7169 if ((*old == '\0' && *new == ':')
7170 || (*old == ':' && *new == '\0'))
7171 firstchange++;
7172 old = new; /* ignore subsequent differences */
7173 }
7174 if (*new == '\0')
7175 break;
7176 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7177 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007178 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007179 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007180 new++, old++;
7181 }
7182 if (builtinloc < 0 && idx_bltin >= 0)
7183 builtinloc = idx_bltin; /* zap builtins */
7184 if (builtinloc >= 0 && idx_bltin < 0)
7185 firstchange = 0;
7186 clearcmdentry(firstchange);
7187 builtinloc = idx_bltin;
7188}
7189
7190#define TEOF 0
7191#define TNL 1
7192#define TREDIR 2
7193#define TWORD 3
7194#define TSEMI 4
7195#define TBACKGND 5
7196#define TAND 6
7197#define TOR 7
7198#define TPIPE 8
7199#define TLP 9
7200#define TRP 10
7201#define TENDCASE 11
7202#define TENDBQUOTE 12
7203#define TNOT 13
7204#define TCASE 14
7205#define TDO 15
7206#define TDONE 16
7207#define TELIF 17
7208#define TELSE 18
7209#define TESAC 19
7210#define TFI 20
7211#define TFOR 21
7212#define TIF 22
7213#define TIN 23
7214#define TTHEN 24
7215#define TUNTIL 25
7216#define TWHILE 26
7217#define TBEGIN 27
7218#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007219typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007220
7221/* first char is indicating which tokens mark the end of a list */
7222static const char *const tokname_array[] = {
7223 "\1end of file",
7224 "\0newline",
7225 "\0redirection",
7226 "\0word",
7227 "\0;",
7228 "\0&",
7229 "\0&&",
7230 "\0||",
7231 "\0|",
7232 "\0(",
7233 "\1)",
7234 "\1;;",
7235 "\1`",
7236#define KWDOFFSET 13
7237 /* the following are keywords */
7238 "\0!",
7239 "\0case",
7240 "\1do",
7241 "\1done",
7242 "\1elif",
7243 "\1else",
7244 "\1esac",
7245 "\1fi",
7246 "\0for",
7247 "\0if",
7248 "\0in",
7249 "\1then",
7250 "\0until",
7251 "\0while",
7252 "\0{",
7253 "\1}",
7254};
7255
7256static const char *
7257tokname(int tok)
7258{
7259 static char buf[16];
7260
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007261//try this:
7262//if (tok < TSEMI) return tokname_array[tok] + 1;
7263//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7264//return buf;
7265
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007266 if (tok >= TSEMI)
7267 buf[0] = '"';
7268 sprintf(buf + (tok >= TSEMI), "%s%c",
7269 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7270 return buf;
7271}
7272
7273/* Wrapper around strcmp for qsort/bsearch/... */
7274static int
7275pstrcmp(const void *a, const void *b)
7276{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007277 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007278}
7279
7280static const char *const *
7281findkwd(const char *s)
7282{
7283 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007284 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7285 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007286}
7287
7288/*
7289 * Locate and print what a word is...
7290 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007291static int
7292describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007293{
7294 struct cmdentry entry;
7295 struct tblentry *cmdp;
7296#if ENABLE_ASH_ALIAS
7297 const struct alias *ap;
7298#endif
7299 const char *path = pathval();
7300
7301 if (describe_command_verbose) {
7302 out1str(command);
7303 }
7304
7305 /* First look at the keywords */
7306 if (findkwd(command)) {
7307 out1str(describe_command_verbose ? " is a shell keyword" : command);
7308 goto out;
7309 }
7310
7311#if ENABLE_ASH_ALIAS
7312 /* Then look at the aliases */
7313 ap = lookupalias(command, 0);
7314 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007315 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007316 out1str("alias ");
7317 printalias(ap);
7318 return 0;
7319 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007320 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007321 goto out;
7322 }
7323#endif
7324 /* Then check if it is a tracked alias */
7325 cmdp = cmdlookup(command, 0);
7326 if (cmdp != NULL) {
7327 entry.cmdtype = cmdp->cmdtype;
7328 entry.u = cmdp->param;
7329 } else {
7330 /* Finally use brute force */
7331 find_command(command, &entry, DO_ABS, path);
7332 }
7333
7334 switch (entry.cmdtype) {
7335 case CMDNORMAL: {
7336 int j = entry.u.index;
7337 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007338 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007339 p = command;
7340 } else {
7341 do {
7342 p = padvance(&path, command);
7343 stunalloc(p);
7344 } while (--j >= 0);
7345 }
7346 if (describe_command_verbose) {
7347 out1fmt(" is%s %s",
7348 (cmdp ? " a tracked alias for" : nullstr), p
7349 );
7350 } else {
7351 out1str(p);
7352 }
7353 break;
7354 }
7355
7356 case CMDFUNCTION:
7357 if (describe_command_verbose) {
7358 out1str(" is a shell function");
7359 } else {
7360 out1str(command);
7361 }
7362 break;
7363
7364 case CMDBUILTIN:
7365 if (describe_command_verbose) {
7366 out1fmt(" is a %sshell builtin",
7367 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7368 "special " : nullstr
7369 );
7370 } else {
7371 out1str(command);
7372 }
7373 break;
7374
7375 default:
7376 if (describe_command_verbose) {
7377 out1str(": not found\n");
7378 }
7379 return 127;
7380 }
7381 out:
7382 outstr("\n", stdout);
7383 return 0;
7384}
7385
7386static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007387typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007389 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007391 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007392
Denis Vlasenko46846e22007-05-20 13:08:31 +00007393 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007394 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007395 i++;
7396 verbose = 0;
7397 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007398 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007399 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007400 }
7401 return err;
7402}
7403
7404#if ENABLE_ASH_CMDCMD
7405static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007406commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407{
7408 int c;
7409 enum {
7410 VERIFY_BRIEF = 1,
7411 VERIFY_VERBOSE = 2,
7412 } verify = 0;
7413
7414 while ((c = nextopt("pvV")) != '\0')
7415 if (c == 'V')
7416 verify |= VERIFY_VERBOSE;
7417 else if (c == 'v')
7418 verify |= VERIFY_BRIEF;
7419#if DEBUG
7420 else if (c != 'p')
7421 abort();
7422#endif
7423 if (verify)
7424 return describe_command(*argptr, verify - VERIFY_BRIEF);
7425
7426 return 0;
7427}
7428#endif
7429
7430
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007431/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007432
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007433static int funcblocksize; /* size of structures in function */
7434static int funcstringsize; /* size of strings in node */
7435static void *funcblock; /* block to allocate function from */
7436static char *funcstring; /* block to allocate strings from */
7437
Eric Andersencb57d552001-06-28 07:25:16 +00007438/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007439#define EV_EXIT 01 /* exit after evaluating tree */
7440#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7441#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007442
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007443static const short nodesize[26] = {
7444 SHELL_ALIGN(sizeof(struct ncmd)),
7445 SHELL_ALIGN(sizeof(struct npipe)),
7446 SHELL_ALIGN(sizeof(struct nredir)),
7447 SHELL_ALIGN(sizeof(struct nredir)),
7448 SHELL_ALIGN(sizeof(struct nredir)),
7449 SHELL_ALIGN(sizeof(struct nbinary)),
7450 SHELL_ALIGN(sizeof(struct nbinary)),
7451 SHELL_ALIGN(sizeof(struct nbinary)),
7452 SHELL_ALIGN(sizeof(struct nif)),
7453 SHELL_ALIGN(sizeof(struct nbinary)),
7454 SHELL_ALIGN(sizeof(struct nbinary)),
7455 SHELL_ALIGN(sizeof(struct nfor)),
7456 SHELL_ALIGN(sizeof(struct ncase)),
7457 SHELL_ALIGN(sizeof(struct nclist)),
7458 SHELL_ALIGN(sizeof(struct narg)),
7459 SHELL_ALIGN(sizeof(struct narg)),
7460 SHELL_ALIGN(sizeof(struct nfile)),
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 ndup)),
7466 SHELL_ALIGN(sizeof(struct ndup)),
7467 SHELL_ALIGN(sizeof(struct nhere)),
7468 SHELL_ALIGN(sizeof(struct nhere)),
7469 SHELL_ALIGN(sizeof(struct nnot)),
7470};
7471
7472static void calcsize(union node *n);
7473
7474static void
7475sizenodelist(struct nodelist *lp)
7476{
7477 while (lp) {
7478 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7479 calcsize(lp->n);
7480 lp = lp->next;
7481 }
7482}
7483
7484static void
7485calcsize(union node *n)
7486{
7487 if (n == NULL)
7488 return;
7489 funcblocksize += nodesize[n->type];
7490 switch (n->type) {
7491 case NCMD:
7492 calcsize(n->ncmd.redirect);
7493 calcsize(n->ncmd.args);
7494 calcsize(n->ncmd.assign);
7495 break;
7496 case NPIPE:
7497 sizenodelist(n->npipe.cmdlist);
7498 break;
7499 case NREDIR:
7500 case NBACKGND:
7501 case NSUBSHELL:
7502 calcsize(n->nredir.redirect);
7503 calcsize(n->nredir.n);
7504 break;
7505 case NAND:
7506 case NOR:
7507 case NSEMI:
7508 case NWHILE:
7509 case NUNTIL:
7510 calcsize(n->nbinary.ch2);
7511 calcsize(n->nbinary.ch1);
7512 break;
7513 case NIF:
7514 calcsize(n->nif.elsepart);
7515 calcsize(n->nif.ifpart);
7516 calcsize(n->nif.test);
7517 break;
7518 case NFOR:
7519 funcstringsize += strlen(n->nfor.var) + 1;
7520 calcsize(n->nfor.body);
7521 calcsize(n->nfor.args);
7522 break;
7523 case NCASE:
7524 calcsize(n->ncase.cases);
7525 calcsize(n->ncase.expr);
7526 break;
7527 case NCLIST:
7528 calcsize(n->nclist.body);
7529 calcsize(n->nclist.pattern);
7530 calcsize(n->nclist.next);
7531 break;
7532 case NDEFUN:
7533 case NARG:
7534 sizenodelist(n->narg.backquote);
7535 funcstringsize += strlen(n->narg.text) + 1;
7536 calcsize(n->narg.next);
7537 break;
7538 case NTO:
7539 case NCLOBBER:
7540 case NFROM:
7541 case NFROMTO:
7542 case NAPPEND:
7543 calcsize(n->nfile.fname);
7544 calcsize(n->nfile.next);
7545 break;
7546 case NTOFD:
7547 case NFROMFD:
7548 calcsize(n->ndup.vname);
7549 calcsize(n->ndup.next);
7550 break;
7551 case NHERE:
7552 case NXHERE:
7553 calcsize(n->nhere.doc);
7554 calcsize(n->nhere.next);
7555 break;
7556 case NNOT:
7557 calcsize(n->nnot.com);
7558 break;
7559 };
7560}
7561
7562static char *
7563nodeckstrdup(char *s)
7564{
7565 char *rtn = funcstring;
7566
7567 strcpy(funcstring, s);
7568 funcstring += strlen(s) + 1;
7569 return rtn;
7570}
7571
7572static union node *copynode(union node *);
7573
7574static struct nodelist *
7575copynodelist(struct nodelist *lp)
7576{
7577 struct nodelist *start;
7578 struct nodelist **lpp;
7579
7580 lpp = &start;
7581 while (lp) {
7582 *lpp = funcblock;
7583 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7584 (*lpp)->n = copynode(lp->n);
7585 lp = lp->next;
7586 lpp = &(*lpp)->next;
7587 }
7588 *lpp = NULL;
7589 return start;
7590}
7591
7592static union node *
7593copynode(union node *n)
7594{
7595 union node *new;
7596
7597 if (n == NULL)
7598 return NULL;
7599 new = funcblock;
7600 funcblock = (char *) funcblock + nodesize[n->type];
7601
7602 switch (n->type) {
7603 case NCMD:
7604 new->ncmd.redirect = copynode(n->ncmd.redirect);
7605 new->ncmd.args = copynode(n->ncmd.args);
7606 new->ncmd.assign = copynode(n->ncmd.assign);
7607 break;
7608 case NPIPE:
7609 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7610 new->npipe.backgnd = n->npipe.backgnd;
7611 break;
7612 case NREDIR:
7613 case NBACKGND:
7614 case NSUBSHELL:
7615 new->nredir.redirect = copynode(n->nredir.redirect);
7616 new->nredir.n = copynode(n->nredir.n);
7617 break;
7618 case NAND:
7619 case NOR:
7620 case NSEMI:
7621 case NWHILE:
7622 case NUNTIL:
7623 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7624 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7625 break;
7626 case NIF:
7627 new->nif.elsepart = copynode(n->nif.elsepart);
7628 new->nif.ifpart = copynode(n->nif.ifpart);
7629 new->nif.test = copynode(n->nif.test);
7630 break;
7631 case NFOR:
7632 new->nfor.var = nodeckstrdup(n->nfor.var);
7633 new->nfor.body = copynode(n->nfor.body);
7634 new->nfor.args = copynode(n->nfor.args);
7635 break;
7636 case NCASE:
7637 new->ncase.cases = copynode(n->ncase.cases);
7638 new->ncase.expr = copynode(n->ncase.expr);
7639 break;
7640 case NCLIST:
7641 new->nclist.body = copynode(n->nclist.body);
7642 new->nclist.pattern = copynode(n->nclist.pattern);
7643 new->nclist.next = copynode(n->nclist.next);
7644 break;
7645 case NDEFUN:
7646 case NARG:
7647 new->narg.backquote = copynodelist(n->narg.backquote);
7648 new->narg.text = nodeckstrdup(n->narg.text);
7649 new->narg.next = copynode(n->narg.next);
7650 break;
7651 case NTO:
7652 case NCLOBBER:
7653 case NFROM:
7654 case NFROMTO:
7655 case NAPPEND:
7656 new->nfile.fname = copynode(n->nfile.fname);
7657 new->nfile.fd = n->nfile.fd;
7658 new->nfile.next = copynode(n->nfile.next);
7659 break;
7660 case NTOFD:
7661 case NFROMFD:
7662 new->ndup.vname = copynode(n->ndup.vname);
7663 new->ndup.dupfd = n->ndup.dupfd;
7664 new->ndup.fd = n->ndup.fd;
7665 new->ndup.next = copynode(n->ndup.next);
7666 break;
7667 case NHERE:
7668 case NXHERE:
7669 new->nhere.doc = copynode(n->nhere.doc);
7670 new->nhere.fd = n->nhere.fd;
7671 new->nhere.next = copynode(n->nhere.next);
7672 break;
7673 case NNOT:
7674 new->nnot.com = copynode(n->nnot.com);
7675 break;
7676 };
7677 new->type = n->type;
7678 return new;
7679}
7680
7681/*
7682 * Make a copy of a parse tree.
7683 */
7684static struct funcnode *
7685copyfunc(union node *n)
7686{
7687 struct funcnode *f;
7688 size_t blocksize;
7689
7690 funcblocksize = offsetof(struct funcnode, n);
7691 funcstringsize = 0;
7692 calcsize(n);
7693 blocksize = funcblocksize;
7694 f = ckmalloc(blocksize + funcstringsize);
7695 funcblock = (char *) f + offsetof(struct funcnode, n);
7696 funcstring = (char *) f + blocksize;
7697 copynode(n);
7698 f->count = 0;
7699 return f;
7700}
7701
7702/*
7703 * Define a shell function.
7704 */
7705static void
7706defun(char *name, union node *func)
7707{
7708 struct cmdentry entry;
7709
7710 INT_OFF;
7711 entry.cmdtype = CMDFUNCTION;
7712 entry.u.func = copyfunc(func);
7713 addcmdentry(name, &entry);
7714 INT_ON;
7715}
7716
7717static int evalskip; /* set if we are skipping commands */
7718/* reasons for skipping commands (see comment on breakcmd routine) */
7719#define SKIPBREAK (1 << 0)
7720#define SKIPCONT (1 << 1)
7721#define SKIPFUNC (1 << 2)
7722#define SKIPFILE (1 << 3)
7723#define SKIPEVAL (1 << 4)
7724static int skipcount; /* number of levels to skip */
7725static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007726static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007727
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007728/* forward decl way out to parsing code - dotrap needs it */
7729static int evalstring(char *s, int mask);
7730
7731/*
7732 * Called to execute a trap. Perhaps we should avoid entering new trap
7733 * handlers while we are executing a trap handler.
7734 */
7735static int
7736dotrap(void)
7737{
7738 char *p;
7739 char *q;
7740 int i;
7741 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007742 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007743
7744 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007745 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007746 xbarrier();
7747
7748 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7749 if (!*q)
7750 continue;
7751 *q = '\0';
7752
7753 p = trap[i + 1];
7754 if (!p)
7755 continue;
7756 skip = evalstring(p, SKIPEVAL);
7757 exitstatus = savestatus;
7758 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007759 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007760 }
7761
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007762 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007763}
7764
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007765/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007766static void evalloop(union node *, int);
7767static void evalfor(union node *, int);
7768static void evalcase(union node *, int);
7769static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007770static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007771static void evalpipe(union node *, int);
7772static void evalcommand(union node *, int);
7773static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007774static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007775
Eric Andersen62483552001-07-10 06:09:16 +00007776/*
Eric Andersenc470f442003-07-28 09:56:35 +00007777 * Evaluate a parse tree. The value is left in the global variable
7778 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007779 */
Eric Andersenc470f442003-07-28 09:56:35 +00007780static void
7781evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007782{
Eric Andersenc470f442003-07-28 09:56:35 +00007783 int checkexit = 0;
7784 void (*evalfn)(union node *, int);
7785 unsigned isor;
7786 int status;
7787 if (n == NULL) {
7788 TRACE(("evaltree(NULL) called\n"));
7789 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007790 }
Eric Andersenc470f442003-07-28 09:56:35 +00007791 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007792 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007793 switch (n->type) {
7794 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007795#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007796 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007797 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007798 break;
7799#endif
7800 case NNOT:
7801 evaltree(n->nnot.com, EV_TESTED);
7802 status = !exitstatus;
7803 goto setstatus;
7804 case NREDIR:
7805 expredir(n->nredir.redirect);
7806 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7807 if (!status) {
7808 evaltree(n->nredir.n, flags & EV_TESTED);
7809 status = exitstatus;
7810 }
7811 popredir(0);
7812 goto setstatus;
7813 case NCMD:
7814 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007815 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007816 if (eflag && !(flags & EV_TESTED))
7817 checkexit = ~0;
7818 goto calleval;
7819 case NFOR:
7820 evalfn = evalfor;
7821 goto calleval;
7822 case NWHILE:
7823 case NUNTIL:
7824 evalfn = evalloop;
7825 goto calleval;
7826 case NSUBSHELL:
7827 case NBACKGND:
7828 evalfn = evalsubshell;
7829 goto calleval;
7830 case NPIPE:
7831 evalfn = evalpipe;
7832 goto checkexit;
7833 case NCASE:
7834 evalfn = evalcase;
7835 goto calleval;
7836 case NAND:
7837 case NOR:
7838 case NSEMI:
7839#if NAND + 1 != NOR
7840#error NAND + 1 != NOR
7841#endif
7842#if NOR + 1 != NSEMI
7843#error NOR + 1 != NSEMI
7844#endif
7845 isor = n->type - NAND;
7846 evaltree(
7847 n->nbinary.ch1,
7848 (flags | ((isor >> 1) - 1)) & EV_TESTED
7849 );
7850 if (!exitstatus == isor)
7851 break;
7852 if (!evalskip) {
7853 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007854 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007855 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007856 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007857 evalfn(n, flags);
7858 break;
7859 }
7860 break;
7861 case NIF:
7862 evaltree(n->nif.test, EV_TESTED);
7863 if (evalskip)
7864 break;
7865 if (exitstatus == 0) {
7866 n = n->nif.ifpart;
7867 goto evaln;
7868 } else if (n->nif.elsepart) {
7869 n = n->nif.elsepart;
7870 goto evaln;
7871 }
7872 goto success;
7873 case NDEFUN:
7874 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007875 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007876 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007877 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007878 exitstatus = status;
7879 break;
7880 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007881 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007882 if ((checkexit & exitstatus))
7883 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007884 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007885 goto exexit;
7886
7887 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007888 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007889 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007890 }
Eric Andersen62483552001-07-10 06:09:16 +00007891}
7892
Eric Andersenc470f442003-07-28 09:56:35 +00007893#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7894static
7895#endif
7896void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7897
Eric Andersenc470f442003-07-28 09:56:35 +00007898static void
7899evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007900{
7901 int status;
7902
7903 loopnest++;
7904 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007905 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007906 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007907 int i;
7908
Eric Andersencb57d552001-06-28 07:25:16 +00007909 evaltree(n->nbinary.ch1, EV_TESTED);
7910 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007911 skipping:
7912 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007913 evalskip = 0;
7914 continue;
7915 }
7916 if (evalskip == SKIPBREAK && --skipcount <= 0)
7917 evalskip = 0;
7918 break;
7919 }
Eric Andersenc470f442003-07-28 09:56:35 +00007920 i = exitstatus;
7921 if (n->type != NWHILE)
7922 i = !i;
7923 if (i != 0)
7924 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007925 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007926 status = exitstatus;
7927 if (evalskip)
7928 goto skipping;
7929 }
7930 loopnest--;
7931 exitstatus = status;
7932}
7933
Eric Andersenc470f442003-07-28 09:56:35 +00007934static void
7935evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007936{
7937 struct arglist arglist;
7938 union node *argp;
7939 struct strlist *sp;
7940 struct stackmark smark;
7941
7942 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007943 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007944 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007945 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007946 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007947 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007948 if (evalskip)
7949 goto out;
7950 }
7951 *arglist.lastp = NULL;
7952
7953 exitstatus = 0;
7954 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007955 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007956 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007957 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007958 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007959 if (evalskip) {
7960 if (evalskip == SKIPCONT && --skipcount <= 0) {
7961 evalskip = 0;
7962 continue;
7963 }
7964 if (evalskip == SKIPBREAK && --skipcount <= 0)
7965 evalskip = 0;
7966 break;
7967 }
7968 }
7969 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007970 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007971 popstackmark(&smark);
7972}
7973
Eric Andersenc470f442003-07-28 09:56:35 +00007974static void
7975evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007976{
7977 union node *cp;
7978 union node *patp;
7979 struct arglist arglist;
7980 struct stackmark smark;
7981
7982 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007983 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007984 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007985 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007986 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007987 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7988 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007989 if (casematch(patp, arglist.list->text)) {
7990 if (evalskip == 0) {
7991 evaltree(cp->nclist.body, flags);
7992 }
7993 goto out;
7994 }
7995 }
7996 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007997 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007998 popstackmark(&smark);
7999}
8000
Eric Andersenc470f442003-07-28 09:56:35 +00008001/*
8002 * Kick off a subshell to evaluate a tree.
8003 */
Eric Andersenc470f442003-07-28 09:56:35 +00008004static void
8005evalsubshell(union node *n, int flags)
8006{
8007 struct job *jp;
8008 int backgnd = (n->type == NBACKGND);
8009 int status;
8010
8011 expredir(n->nredir.redirect);
8012 if (!backgnd && flags & EV_EXIT && !trap[0])
8013 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008014 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008015 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008016 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008017 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008018 flags |= EV_EXIT;
8019 if (backgnd)
8020 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008021 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008022 redirect(n->nredir.redirect, 0);
8023 evaltreenr(n->nredir.n, flags);
8024 /* never returns */
8025 }
8026 status = 0;
8027 if (! backgnd)
8028 status = waitforjob(jp);
8029 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008030 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008031}
8032
Eric Andersenc470f442003-07-28 09:56:35 +00008033/*
8034 * Compute the names of the files in a redirection list.
8035 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008036static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008037static void
8038expredir(union node *n)
8039{
8040 union node *redir;
8041
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008042 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008043 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008044
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008045 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008046 fn.lastp = &fn.list;
8047 switch (redir->type) {
8048 case NFROMTO:
8049 case NFROM:
8050 case NTO:
8051 case NCLOBBER:
8052 case NAPPEND:
8053 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8054 redir->nfile.expfname = fn.list->text;
8055 break;
8056 case NFROMFD:
8057 case NTOFD:
8058 if (redir->ndup.vname) {
8059 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008060 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008061 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008062 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008063 }
8064 break;
8065 }
8066 }
8067}
8068
Eric Andersencb57d552001-06-28 07:25:16 +00008069/*
Eric Andersencb57d552001-06-28 07:25:16 +00008070 * Evaluate a pipeline. All the processes in the pipeline are children
8071 * of the process creating the pipeline. (This differs from some versions
8072 * of the shell, which make the last process in a pipeline the parent
8073 * of all the rest.)
8074 */
Eric Andersenc470f442003-07-28 09:56:35 +00008075static void
8076evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008077{
8078 struct job *jp;
8079 struct nodelist *lp;
8080 int pipelen;
8081 int prevfd;
8082 int pip[2];
8083
Eric Andersenc470f442003-07-28 09:56:35 +00008084 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008085 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008086 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008087 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008088 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008089 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008090 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008091 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008092 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008093 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008094 pip[1] = -1;
8095 if (lp->next) {
8096 if (pipe(pip) < 0) {
8097 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008098 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008099 }
8100 }
8101 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008102 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008103 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008104 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008105 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008106 if (prevfd > 0) {
8107 dup2(prevfd, 0);
8108 close(prevfd);
8109 }
8110 if (pip[1] > 1) {
8111 dup2(pip[1], 1);
8112 close(pip[1]);
8113 }
Eric Andersenc470f442003-07-28 09:56:35 +00008114 evaltreenr(lp->n, flags);
8115 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008116 }
8117 if (prevfd >= 0)
8118 close(prevfd);
8119 prevfd = pip[0];
8120 close(pip[1]);
8121 }
Eric Andersencb57d552001-06-28 07:25:16 +00008122 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008123 exitstatus = waitforjob(jp);
8124 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008125 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008126 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008127}
8128
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008129/*
8130 * Controls whether the shell is interactive or not.
8131 */
8132static void
8133setinteractive(int on)
8134{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008135 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008136
8137 if (++on == is_interactive)
8138 return;
8139 is_interactive = on;
8140 setsignal(SIGINT);
8141 setsignal(SIGQUIT);
8142 setsignal(SIGTERM);
8143#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8144 if (is_interactive > 1) {
8145 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008146 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008147
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008148 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008149 out1fmt(
8150 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008151 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008152 "Enter 'help' for a list of built-in commands."
8153 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008154 bb_banner);
8155 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008156 }
8157 }
8158#endif
8159}
8160
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008161static void
8162optschanged(void)
8163{
8164#if DEBUG
8165 opentrace();
8166#endif
8167 setinteractive(iflag);
8168 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008169#if ENABLE_FEATURE_EDITING_VI
8170 if (viflag)
8171 line_input_state->flags |= VI_MODE;
8172 else
8173 line_input_state->flags &= ~VI_MODE;
8174#else
8175 viflag = 0; /* forcibly keep the option off */
8176#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008177}
8178
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008179static struct localvar *localvars;
8180
8181/*
8182 * Called after a function returns.
8183 * Interrupts must be off.
8184 */
8185static void
8186poplocalvars(void)
8187{
8188 struct localvar *lvp;
8189 struct var *vp;
8190
8191 while ((lvp = localvars) != NULL) {
8192 localvars = lvp->next;
8193 vp = lvp->vp;
8194 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8195 if (vp == NULL) { /* $- saved */
8196 memcpy(optlist, lvp->text, sizeof(optlist));
8197 free((char*)lvp->text);
8198 optschanged();
8199 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8200 unsetvar(vp->text);
8201 } else {
8202 if (vp->func)
8203 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8204 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8205 free((char*)vp->text);
8206 vp->flags = lvp->flags;
8207 vp->text = lvp->text;
8208 }
8209 free(lvp);
8210 }
8211}
8212
8213static int
8214evalfun(struct funcnode *func, int argc, char **argv, int flags)
8215{
8216 volatile struct shparam saveparam;
8217 struct localvar *volatile savelocalvars;
8218 struct jmploc *volatile savehandler;
8219 struct jmploc jmploc;
8220 int e;
8221
8222 saveparam = shellparam;
8223 savelocalvars = localvars;
8224 e = setjmp(jmploc.loc);
8225 if (e) {
8226 goto funcdone;
8227 }
8228 INT_OFF;
8229 savehandler = exception_handler;
8230 exception_handler = &jmploc;
8231 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008232 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008233 func->count++;
8234 funcnest++;
8235 INT_ON;
8236 shellparam.nparam = argc - 1;
8237 shellparam.p = argv + 1;
8238#if ENABLE_ASH_GETOPTS
8239 shellparam.optind = 1;
8240 shellparam.optoff = -1;
8241#endif
8242 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008243 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008244 INT_OFF;
8245 funcnest--;
8246 freefunc(func);
8247 poplocalvars();
8248 localvars = savelocalvars;
8249 freeparam(&shellparam);
8250 shellparam = saveparam;
8251 exception_handler = savehandler;
8252 INT_ON;
8253 evalskip &= ~SKIPFUNC;
8254 return e;
8255}
8256
Denis Vlasenko131ae172007-02-18 13:00:19 +00008257#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008258static char **
8259parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008260{
8261 char *cp, c;
8262
8263 for (;;) {
8264 cp = *++argv;
8265 if (!cp)
8266 return 0;
8267 if (*cp++ != '-')
8268 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008269 c = *cp++;
8270 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008271 break;
8272 if (c == '-' && !*cp) {
8273 argv++;
8274 break;
8275 }
8276 do {
8277 switch (c) {
8278 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008279 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008280 break;
8281 default:
8282 /* run 'typecmd' for other options */
8283 return 0;
8284 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008285 c = *cp++;
8286 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008287 }
8288 return argv;
8289}
8290#endif
8291
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008292/*
8293 * Make a variable a local variable. When a variable is made local, it's
8294 * value and flags are saved in a localvar structure. The saved values
8295 * will be restored when the shell function returns. We handle the name
8296 * "-" as a special case.
8297 */
8298static void
8299mklocal(char *name)
8300{
8301 struct localvar *lvp;
8302 struct var **vpp;
8303 struct var *vp;
8304
8305 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008306 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008307 if (LONE_DASH(name)) {
8308 char *p;
8309 p = ckmalloc(sizeof(optlist));
8310 lvp->text = memcpy(p, optlist, sizeof(optlist));
8311 vp = NULL;
8312 } else {
8313 char *eq;
8314
8315 vpp = hashvar(name);
8316 vp = *findvar(vpp, name);
8317 eq = strchr(name, '=');
8318 if (vp == NULL) {
8319 if (eq)
8320 setvareq(name, VSTRFIXED);
8321 else
8322 setvar(name, NULL, VSTRFIXED);
8323 vp = *vpp; /* the new variable */
8324 lvp->flags = VUNSET;
8325 } else {
8326 lvp->text = vp->text;
8327 lvp->flags = vp->flags;
8328 vp->flags |= VSTRFIXED|VTEXTFIXED;
8329 if (eq)
8330 setvareq(name, 0);
8331 }
8332 }
8333 lvp->vp = vp;
8334 lvp->next = localvars;
8335 localvars = lvp;
8336 INT_ON;
8337}
8338
8339/*
8340 * The "local" command.
8341 */
8342static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008343localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008344{
8345 char *name;
8346
8347 argv = argptr;
8348 while ((name = *argv++) != NULL) {
8349 mklocal(name);
8350 }
8351 return 0;
8352}
8353
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008354static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008355falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008356{
8357 return 1;
8358}
8359
8360static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008361truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008362{
8363 return 0;
8364}
8365
8366static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008367execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008368{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008369 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008370 iflag = 0; /* exit on error */
8371 mflag = 0;
8372 optschanged();
8373 shellexec(argv + 1, pathval(), 0);
8374 }
8375 return 0;
8376}
8377
8378/*
8379 * The return command.
8380 */
8381static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008382returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008383{
8384 /*
8385 * If called outside a function, do what ksh does;
8386 * skip the rest of the file.
8387 */
8388 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8389 return argv[1] ? number(argv[1]) : exitstatus;
8390}
8391
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008392/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008393static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008394static int dotcmd(int, char **);
8395static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008396static int exitcmd(int, char **);
8397static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008398#if ENABLE_ASH_GETOPTS
8399static int getoptscmd(int, char **);
8400#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008401#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008402static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008403#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008404#if ENABLE_ASH_MATH_SUPPORT
8405static int letcmd(int, char **);
8406#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008407static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008408static int setcmd(int, char **);
8409static int shiftcmd(int, char **);
8410static int timescmd(int, char **);
8411static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008412static int umaskcmd(int, char **);
8413static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008414static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008416#define BUILTIN_NOSPEC "0"
8417#define BUILTIN_SPECIAL "1"
8418#define BUILTIN_REGULAR "2"
8419#define BUILTIN_SPEC_REG "3"
8420#define BUILTIN_ASSIGN "4"
8421#define BUILTIN_SPEC_ASSG "5"
8422#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008423#define BUILTIN_SPEC_REG_ASSG "7"
8424
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008425/* We do not handle [[ expr ]] bashism bash-compatibly,
8426 * we make it a synonym of [ expr ].
8427 * Basically, word splitting and pathname expansion should NOT be performed
8428 * Examples:
8429 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8430 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8431 * Additional operators:
8432 * || and && should work as -o and -a
8433 * =~ regexp match
8434 * Apart from the above, [[ expr ]] should work as [ expr ]
8435 */
8436
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008437#define echocmd echo_main
8438#define printfcmd printf_main
8439#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008440
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008441/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008442static const struct builtincmd builtintab[] = {
8443 { BUILTIN_SPEC_REG ".", dotcmd },
8444 { BUILTIN_SPEC_REG ":", truecmd },
8445#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008446 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008447#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008448 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008449#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008450#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008451#if ENABLE_ASH_ALIAS
8452 { BUILTIN_REG_ASSG "alias", aliascmd },
8453#endif
8454#if JOBS
8455 { BUILTIN_REGULAR "bg", fg_bgcmd },
8456#endif
8457 { BUILTIN_SPEC_REG "break", breakcmd },
8458 { BUILTIN_REGULAR "cd", cdcmd },
8459 { BUILTIN_NOSPEC "chdir", cdcmd },
8460#if ENABLE_ASH_CMDCMD
8461 { BUILTIN_REGULAR "command", commandcmd },
8462#endif
8463 { BUILTIN_SPEC_REG "continue", breakcmd },
8464#if ENABLE_ASH_BUILTIN_ECHO
8465 { BUILTIN_REGULAR "echo", echocmd },
8466#endif
8467 { BUILTIN_SPEC_REG "eval", evalcmd },
8468 { BUILTIN_SPEC_REG "exec", execcmd },
8469 { BUILTIN_SPEC_REG "exit", exitcmd },
8470 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8471 { BUILTIN_REGULAR "false", falsecmd },
8472#if JOBS
8473 { BUILTIN_REGULAR "fg", fg_bgcmd },
8474#endif
8475#if ENABLE_ASH_GETOPTS
8476 { BUILTIN_REGULAR "getopts", getoptscmd },
8477#endif
8478 { BUILTIN_NOSPEC "hash", hashcmd },
8479#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8480 { BUILTIN_NOSPEC "help", helpcmd },
8481#endif
8482#if JOBS
8483 { BUILTIN_REGULAR "jobs", jobscmd },
8484 { BUILTIN_REGULAR "kill", killcmd },
8485#endif
8486#if ENABLE_ASH_MATH_SUPPORT
8487 { BUILTIN_NOSPEC "let", letcmd },
8488#endif
8489 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008490#if ENABLE_ASH_BUILTIN_PRINTF
8491 { BUILTIN_REGULAR "printf", printfcmd },
8492#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008493 { BUILTIN_NOSPEC "pwd", pwdcmd },
8494 { BUILTIN_REGULAR "read", readcmd },
8495 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8496 { BUILTIN_SPEC_REG "return", returncmd },
8497 { BUILTIN_SPEC_REG "set", setcmd },
8498 { BUILTIN_SPEC_REG "shift", shiftcmd },
8499 { BUILTIN_SPEC_REG "source", dotcmd },
8500#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008501 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008502#endif
8503 { BUILTIN_SPEC_REG "times", timescmd },
8504 { BUILTIN_SPEC_REG "trap", trapcmd },
8505 { BUILTIN_REGULAR "true", truecmd },
8506 { BUILTIN_NOSPEC "type", typecmd },
8507 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8508 { BUILTIN_REGULAR "umask", umaskcmd },
8509#if ENABLE_ASH_ALIAS
8510 { BUILTIN_REGULAR "unalias", unaliascmd },
8511#endif
8512 { BUILTIN_SPEC_REG "unset", unsetcmd },
8513 { BUILTIN_REGULAR "wait", waitcmd },
8514};
8515
Denis Vlasenko80591b02008-03-25 07:49:43 +00008516/* Should match the above table! */
8517#define COMMANDCMD (builtintab + \
8518 2 + \
8519 1 * ENABLE_ASH_BUILTIN_TEST + \
8520 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8521 1 * ENABLE_ASH_ALIAS + \
8522 1 * ENABLE_ASH_JOB_CONTROL + \
8523 3)
8524#define EXECCMD (builtintab + \
8525 2 + \
8526 1 * ENABLE_ASH_BUILTIN_TEST + \
8527 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8528 1 * ENABLE_ASH_ALIAS + \
8529 1 * ENABLE_ASH_JOB_CONTROL + \
8530 3 + \
8531 1 * ENABLE_ASH_CMDCMD + \
8532 1 + \
8533 ENABLE_ASH_BUILTIN_ECHO + \
8534 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008535
8536/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008537 * Search the table of builtin commands.
8538 */
8539static struct builtincmd *
8540find_builtin(const char *name)
8541{
8542 struct builtincmd *bp;
8543
8544 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008545 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008546 pstrcmp
8547 );
8548 return bp;
8549}
8550
8551/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008552 * Execute a simple command.
8553 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008554static int
8555isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008556{
8557 const char *q = endofname(p);
8558 if (p == q)
8559 return 0;
8560 return *q == '=';
8561}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008562static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008563bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008564{
8565 /* Preserve exitstatus of a previous possible redirection
8566 * as POSIX mandates */
8567 return back_exitstatus;
8568}
Eric Andersenc470f442003-07-28 09:56:35 +00008569static void
8570evalcommand(union node *cmd, int flags)
8571{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008572 static const struct builtincmd null_bltin = {
8573 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008574 };
Eric Andersenc470f442003-07-28 09:56:35 +00008575 struct stackmark smark;
8576 union node *argp;
8577 struct arglist arglist;
8578 struct arglist varlist;
8579 char **argv;
8580 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008581 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008582 struct cmdentry cmdentry;
8583 struct job *jp;
8584 char *lastarg;
8585 const char *path;
8586 int spclbltin;
8587 int cmd_is_exec;
8588 int status;
8589 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008590 struct builtincmd *bcmd;
8591 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008592
8593 /* First expand the arguments. */
8594 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8595 setstackmark(&smark);
8596 back_exitstatus = 0;
8597
8598 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008599 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008600 varlist.lastp = &varlist.list;
8601 *varlist.lastp = NULL;
8602 arglist.lastp = &arglist.list;
8603 *arglist.lastp = NULL;
8604
8605 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008606 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008607 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8608 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8609 }
8610
Eric Andersenc470f442003-07-28 09:56:35 +00008611 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8612 struct strlist **spp;
8613
8614 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008615 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008616 expandarg(argp, &arglist, EXP_VARTILDE);
8617 else
8618 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8619
Eric Andersenc470f442003-07-28 09:56:35 +00008620 for (sp = *spp; sp; sp = sp->next)
8621 argc++;
8622 }
8623
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008624 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008625 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008626 TRACE(("evalcommand arg: %s\n", sp->text));
8627 *nargv++ = sp->text;
8628 }
8629 *nargv = NULL;
8630
8631 lastarg = NULL;
8632 if (iflag && funcnest == 0 && argc > 0)
8633 lastarg = nargv[-1];
8634
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008635 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008636 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008637 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008638
8639 path = vpath.text;
8640 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8641 struct strlist **spp;
8642 char *p;
8643
8644 spp = varlist.lastp;
8645 expandarg(argp, &varlist, EXP_VARTILDE);
8646
8647 /*
8648 * Modify the command lookup path, if a PATH= assignment
8649 * is present
8650 */
8651 p = (*spp)->text;
8652 if (varequal(p, path))
8653 path = p;
8654 }
8655
8656 /* Print the command if xflag is set. */
8657 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008658 int n;
8659 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008660
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008661 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008662 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008663
8664 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008665 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008666 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008667 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008668 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008669 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008670 p--;
8671 }
8672 }
8673 sp = arglist.list;
8674 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008675 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008676 }
8677
8678 cmd_is_exec = 0;
8679 spclbltin = -1;
8680
8681 /* Now locate the command. */
8682 if (argc) {
8683 const char *oldpath;
8684 int cmd_flag = DO_ERR;
8685
8686 path += 5;
8687 oldpath = path;
8688 for (;;) {
8689 find_command(argv[0], &cmdentry, cmd_flag, path);
8690 if (cmdentry.cmdtype == CMDUNKNOWN) {
8691 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008692 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008693 goto bail;
8694 }
8695
8696 /* implement bltin and command here */
8697 if (cmdentry.cmdtype != CMDBUILTIN)
8698 break;
8699 if (spclbltin < 0)
8700 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8701 if (cmdentry.u.cmd == EXECCMD)
8702 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008703#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008704 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008705 path = oldpath;
8706 nargv = parse_command_args(argv, &path);
8707 if (!nargv)
8708 break;
8709 argc -= nargv - argv;
8710 argv = nargv;
8711 cmd_flag |= DO_NOFUNC;
8712 } else
8713#endif
8714 break;
8715 }
8716 }
8717
8718 if (status) {
8719 /* We have a redirection error. */
8720 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008721 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008722 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008723 exitstatus = status;
8724 goto out;
8725 }
8726
8727 /* Execute the command. */
8728 switch (cmdentry.cmdtype) {
8729 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008730#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008731 {
8732 /* find_command() encodes applet_no as (-2 - applet_no) */
8733 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008734 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008735 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008736 /* run <applet>_main() */
8737 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008738 break;
8739 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008740 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008741#endif
8742
Eric Andersenc470f442003-07-28 09:56:35 +00008743 /* Fork off a child process if necessary. */
8744 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008745 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008746 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008747 if (forkshell(jp, cmd, FORK_FG) != 0) {
8748 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008749 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008750 break;
8751 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008752 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008753 }
8754 listsetvar(varlist.list, VEXPORT|VSTACK);
8755 shellexec(argv, path, cmdentry.u.index);
8756 /* NOTREACHED */
8757
8758 case CMDBUILTIN:
8759 cmdenviron = varlist.list;
8760 if (cmdenviron) {
8761 struct strlist *list = cmdenviron;
8762 int i = VNOSET;
8763 if (spclbltin > 0 || argc == 0) {
8764 i = 0;
8765 if (cmd_is_exec && argc > 1)
8766 i = VEXPORT;
8767 }
8768 listsetvar(list, i);
8769 }
8770 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8771 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008772 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008773 if (i == EXEXIT)
8774 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008775 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008776 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008777 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008778 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008779 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008780 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008781 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008782 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008783 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008784 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008785 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008786 }
8787 break;
8788
8789 case CMDFUNCTION:
8790 listsetvar(varlist.list, 0);
8791 if (evalfun(cmdentry.u.func, argc, argv, flags))
8792 goto raise;
8793 break;
8794 }
8795
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008796 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008797 popredir(cmd_is_exec);
8798 if (lastarg)
8799 /* dsl: I think this is intended to be used to support
8800 * '_' in 'vi' command mode during line editing...
8801 * However I implemented that within libedit itself.
8802 */
8803 setvar("_", lastarg, 0);
8804 popstackmark(&smark);
8805}
8806
8807static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008808evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8809{
Eric Andersenc470f442003-07-28 09:56:35 +00008810 char *volatile savecmdname;
8811 struct jmploc *volatile savehandler;
8812 struct jmploc jmploc;
8813 int i;
8814
8815 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008816 i = setjmp(jmploc.loc);
8817 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008818 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008819 savehandler = exception_handler;
8820 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008821 commandname = argv[0];
8822 argptr = argv + 1;
8823 optptr = NULL; /* initialize nextopt */
8824 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008825 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008826 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008827 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008828 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008829 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008830// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008831 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008832
8833 return i;
8834}
8835
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008836static int
8837goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008838{
8839 return !*endofname(p);
8840}
8841
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008842
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008843/*
8844 * Search for a command. This is called before we fork so that the
8845 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008846 * the child. The check for "goodname" is an overly conservative
8847 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008848 */
Eric Andersenc470f442003-07-28 09:56:35 +00008849static void
8850prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008851{
8852 struct cmdentry entry;
8853
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008854 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8855 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008856}
8857
Eric Andersencb57d552001-06-28 07:25:16 +00008858
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008859/* ============ Builtin commands
8860 *
8861 * Builtin commands whose functions are closely tied to evaluation
8862 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008863 */
8864
8865/*
Eric Andersencb57d552001-06-28 07:25:16 +00008866 * Handle break and continue commands. Break, continue, and return are
8867 * all handled by setting the evalskip flag. The evaluation routines
8868 * above all check this flag, and if it is set they start skipping
8869 * commands rather than executing them. The variable skipcount is
8870 * the number of loops to break/continue, or the number of function
8871 * levels to return. (The latter is always 1.) It should probably
8872 * be an error to break out of more loops than exist, but it isn't
8873 * in the standard shell so we don't make it one here.
8874 */
Eric Andersenc470f442003-07-28 09:56:35 +00008875static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008876breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008877{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008878 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008879
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008880 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008881 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008882 if (n > loopnest)
8883 n = loopnest;
8884 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008885 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008886 skipcount = n;
8887 }
8888 return 0;
8889}
8890
Eric Andersenc470f442003-07-28 09:56:35 +00008891
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008892/* ============ input.c
8893 *
Eric Andersen90898442003-08-06 11:20:52 +00008894 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008895 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008896
Eric Andersenc470f442003-07-28 09:56:35 +00008897#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008898
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008899enum {
8900 INPUT_PUSH_FILE = 1,
8901 INPUT_NOFILE_OK = 2,
8902};
Eric Andersencb57d552001-06-28 07:25:16 +00008903
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008904static int plinno = 1; /* input line number */
8905/* number of characters left in input buffer */
8906static int parsenleft; /* copy of parsefile->nleft */
8907static int parselleft; /* copy of parsefile->lleft */
8908/* next character in input buffer */
8909static char *parsenextc; /* copy of parsefile->nextc */
8910
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008911static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008912/* values of checkkwd variable */
8913#define CHKALIAS 0x1
8914#define CHKKWD 0x2
8915#define CHKNL 0x4
8916
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008917static void
8918popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008919{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008920 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008921
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008922 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008923#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008924 if (sp->ap) {
8925 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8926 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008927 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008928 if (sp->string != sp->ap->val) {
8929 free(sp->string);
8930 }
8931 sp->ap->flag &= ~ALIASINUSE;
8932 if (sp->ap->flag & ALIASDEAD) {
8933 unalias(sp->ap->name);
8934 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008935 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008936#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008937 parsenextc = sp->prevstring;
8938 parsenleft = sp->prevnleft;
8939/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008940 g_parsefile->strpush = sp->prev;
8941 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008942 free(sp);
8943 INT_ON;
8944}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008945
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008946static int
8947preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008948{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008949 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008950 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008951 parsenextc = buf;
8952
Denis Vlasenko38f63192007-01-22 09:03:07 +00008953#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008954 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008955 if (!iflag || g_parsefile->fd)
8956 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008957 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008958#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008959 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008960#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008961 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8962 if (nr == 0) {
8963 /* Ctrl+C pressed */
8964 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008965 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008966 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008967 raise(SIGINT);
8968 return 1;
8969 }
Eric Andersenc470f442003-07-28 09:56:35 +00008970 goto retry;
8971 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008972 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008973 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008974 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008975 }
Eric Andersencb57d552001-06-28 07:25:16 +00008976 }
8977#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008978 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008979#endif
8980
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008981#if 0
8982/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008983 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008984 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008985 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008986 if (flags >= 0 && (flags & O_NONBLOCK)) {
8987 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008988 if (fcntl(0, F_SETFL, flags) >= 0) {
8989 out2str("sh: turning off NDELAY mode\n");
8990 goto retry;
8991 }
8992 }
8993 }
8994 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008995#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008996 return nr;
8997}
8998
8999/*
9000 * Refill the input buffer and return the next input character:
9001 *
9002 * 1) If a string was pushed back on the input, pop it;
9003 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9004 * from a string so we can't refill the buffer, return EOF.
9005 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9006 * 4) Process input up to the next newline, deleting nul characters.
9007 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009008static int
Eric Andersenc470f442003-07-28 09:56:35 +00009009preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009010{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009011 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009012 int more;
9013 char savec;
9014
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009015 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009016#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009017 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009018 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009019 return PEOA;
9020 }
Eric Andersen2870d962001-07-02 17:27:21 +00009021#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009022 popstring();
9023 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009024 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009025 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009026 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009027 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009028 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009029
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009030 more = parselleft;
9031 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009032 again:
9033 more = preadfd();
9034 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009035 parselleft = parsenleft = EOF_NLEFT;
9036 return PEOF;
9037 }
9038 }
9039
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009040 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009041
9042 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009043 for (;;) {
9044 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009045
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009046 more--;
9047 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009048
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009049 if (!c)
9050 memmove(q, q + 1, more);
9051 else {
9052 q++;
9053 if (c == '\n') {
9054 parsenleft = q - parsenextc - 1;
9055 break;
9056 }
Eric Andersencb57d552001-06-28 07:25:16 +00009057 }
9058
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009059 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009060 parsenleft = q - parsenextc - 1;
9061 if (parsenleft < 0)
9062 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009063 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009064 }
9065 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009066 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009067
9068 savec = *q;
9069 *q = '\0';
9070
9071 if (vflag) {
9072 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009073 }
9074
9075 *q = savec;
9076
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009077 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009078}
9079
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009080#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009081static int
9082pgetc(void)
9083{
9084 return pgetc_as_macro();
9085}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009086
9087#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9088#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009089#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009090#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009091#endif
9092
9093/*
9094 * Same as pgetc(), but ignores PEOA.
9095 */
9096#if ENABLE_ASH_ALIAS
9097static int
9098pgetc2(void)
9099{
9100 int c;
9101
9102 do {
9103 c = pgetc_macro();
9104 } while (c == PEOA);
9105 return c;
9106}
9107#else
9108static int
9109pgetc2(void)
9110{
9111 return pgetc_macro();
9112}
9113#endif
9114
9115/*
9116 * Read a line from the script.
9117 */
9118static char *
9119pfgets(char *line, int len)
9120{
9121 char *p = line;
9122 int nleft = len;
9123 int c;
9124
9125 while (--nleft > 0) {
9126 c = pgetc2();
9127 if (c == PEOF) {
9128 if (p == line)
9129 return NULL;
9130 break;
9131 }
9132 *p++ = c;
9133 if (c == '\n')
9134 break;
9135 }
9136 *p = '\0';
9137 return line;
9138}
9139
Eric Andersenc470f442003-07-28 09:56:35 +00009140/*
9141 * Undo the last call to pgetc. Only one character may be pushed back.
9142 * PEOF may be pushed back.
9143 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009144static void
Eric Andersenc470f442003-07-28 09:56:35 +00009145pungetc(void)
9146{
9147 parsenleft++;
9148 parsenextc--;
9149}
Eric Andersencb57d552001-06-28 07:25:16 +00009150
9151/*
9152 * Push a string back onto the input at this current parsefile level.
9153 * We handle aliases this way.
9154 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009155#if !ENABLE_ASH_ALIAS
9156#define pushstring(s, ap) pushstring(s)
9157#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009158static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009159pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009160{
Eric Andersencb57d552001-06-28 07:25:16 +00009161 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009162 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009163
Eric Andersenc470f442003-07-28 09:56:35 +00009164 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009165 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009166/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009167 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009168 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009169 sp->prev = g_parsefile->strpush;
9170 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009171 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009172 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009173 sp->prevstring = parsenextc;
9174 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009175#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009176 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009177 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009178 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009179 sp->string = s;
9180 }
Eric Andersen2870d962001-07-02 17:27:21 +00009181#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009182 parsenextc = s;
9183 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009184 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009185}
9186
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009187/*
9188 * To handle the "." command, a stack of input files is used. Pushfile
9189 * adds a new entry to the stack and popfile restores the previous level.
9190 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009191static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009192pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009193{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009194 struct parsefile *pf;
9195
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009196 g_parsefile->nleft = parsenleft;
9197 g_parsefile->lleft = parselleft;
9198 g_parsefile->nextc = parsenextc;
9199 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009200 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009201 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009202 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009203 /*pf->strpush = NULL; - ckzalloc did it */
9204 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009205 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009206}
9207
9208static void
9209popfile(void)
9210{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009211 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009212
Denis Vlasenkob012b102007-02-19 22:43:01 +00009213 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009214 if (pf->fd >= 0)
9215 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009216 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009217 while (pf->strpush)
9218 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009219 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009220 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009221 parsenleft = g_parsefile->nleft;
9222 parselleft = g_parsefile->lleft;
9223 parsenextc = g_parsefile->nextc;
9224 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009225 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009226}
9227
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009228/*
9229 * Return to top level.
9230 */
9231static void
9232popallfiles(void)
9233{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009234 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009235 popfile();
9236}
9237
9238/*
9239 * Close the file(s) that the shell is reading commands from. Called
9240 * after a fork is done.
9241 */
9242static void
9243closescript(void)
9244{
9245 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009246 if (g_parsefile->fd > 0) {
9247 close(g_parsefile->fd);
9248 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009249 }
9250}
9251
9252/*
9253 * Like setinputfile, but takes an open file descriptor. Call this with
9254 * interrupts off.
9255 */
9256static void
9257setinputfd(int fd, int push)
9258{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009259 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009260 if (push) {
9261 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009262 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009263 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009264 g_parsefile->fd = fd;
9265 if (g_parsefile->buf == NULL)
9266 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009267 parselleft = parsenleft = 0;
9268 plinno = 1;
9269}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009270
Eric Andersenc470f442003-07-28 09:56:35 +00009271/*
9272 * Set the input to take input from a file. If push is set, push the
9273 * old input onto the stack first.
9274 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009275static int
9276setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009277{
9278 int fd;
9279 int fd2;
9280
Denis Vlasenkob012b102007-02-19 22:43:01 +00009281 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009282 fd = open(fname, O_RDONLY);
9283 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009284 if (flags & INPUT_NOFILE_OK)
9285 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009286 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009287 }
Eric Andersenc470f442003-07-28 09:56:35 +00009288 if (fd < 10) {
9289 fd2 = copyfd(fd, 10);
9290 close(fd);
9291 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009292 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009293 fd = fd2;
9294 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009295 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009296 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009297 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009298 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009299}
9300
Eric Andersencb57d552001-06-28 07:25:16 +00009301/*
9302 * Like setinputfile, but takes input from a string.
9303 */
Eric Andersenc470f442003-07-28 09:56:35 +00009304static void
9305setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009306{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009307 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009308 pushfile();
9309 parsenextc = string;
9310 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009311 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009312 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009313 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009314}
9315
9316
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009317/* ============ mail.c
9318 *
9319 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009320 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009321
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009322#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009323
Eric Andersencb57d552001-06-28 07:25:16 +00009324#define MAXMBOXES 10
9325
Eric Andersenc470f442003-07-28 09:56:35 +00009326/* times of mailboxes */
9327static time_t mailtime[MAXMBOXES];
9328/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009329static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009330
Eric Andersencb57d552001-06-28 07:25:16 +00009331/*
Eric Andersenc470f442003-07-28 09:56:35 +00009332 * Print appropriate message(s) if mail has arrived.
9333 * If mail_var_path_changed is set,
9334 * then the value of MAIL has mail_var_path_changed,
9335 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009336 */
Eric Andersenc470f442003-07-28 09:56:35 +00009337static void
9338chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009339{
Eric Andersencb57d552001-06-28 07:25:16 +00009340 const char *mpath;
9341 char *p;
9342 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009343 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009344 struct stackmark smark;
9345 struct stat statb;
9346
Eric Andersencb57d552001-06-28 07:25:16 +00009347 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009348 mpath = mpathset() ? mpathval() : mailval();
9349 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009350 p = padvance(&mpath, nullstr);
9351 if (p == NULL)
9352 break;
9353 if (*p == '\0')
9354 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009355 for (q = p; *q; q++)
9356 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009357#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009358 if (q[-1] != '/')
9359 abort();
9360#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009361 q[-1] = '\0'; /* delete trailing '/' */
9362 if (stat(p, &statb) < 0) {
9363 *mtp = 0;
9364 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009365 }
Eric Andersenc470f442003-07-28 09:56:35 +00009366 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9367 fprintf(
9368 stderr, snlfmt,
9369 pathopt ? pathopt : "you have mail"
9370 );
9371 }
9372 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009373 }
Eric Andersenc470f442003-07-28 09:56:35 +00009374 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009375 popstackmark(&smark);
9376}
Eric Andersencb57d552001-06-28 07:25:16 +00009377
Eric Andersenc470f442003-07-28 09:56:35 +00009378static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009379changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009380{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009381 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009382}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009383
Denis Vlasenko131ae172007-02-18 13:00:19 +00009384#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009385
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009386
9387/* ============ ??? */
9388
Eric Andersencb57d552001-06-28 07:25:16 +00009389/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009390 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009391 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009392static void
9393setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009394{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009395 char **newparam;
9396 char **ap;
9397 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009398
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009399 for (nparam = 0; argv[nparam]; nparam++)
9400 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009401 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9402 while (*argv) {
9403 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009404 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009405 *ap = NULL;
9406 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009407 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009408 shellparam.nparam = nparam;
9409 shellparam.p = newparam;
9410#if ENABLE_ASH_GETOPTS
9411 shellparam.optind = 1;
9412 shellparam.optoff = -1;
9413#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009414}
9415
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009416/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009417 * Process shell options. The global variable argptr contains a pointer
9418 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009419 *
9420 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9421 * For a non-interactive shell, an error condition encountered
9422 * by a special built-in ... shall cause the shell to write a diagnostic message
9423 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009424 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009425 * ...
9426 * Utility syntax error (option or operand error) Shall exit
9427 * ...
9428 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9429 * we see that bash does not do that (set "finishes" with error code 1 instead,
9430 * and shell continues), and people rely on this behavior!
9431 * Testcase:
9432 * set -o barfoo 2>/dev/null
9433 * echo $?
9434 *
9435 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009436 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009437static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009438plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009439{
9440 int i;
9441
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009442 if (name) {
9443 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009444 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009445 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009446 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009447 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009448 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009449 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009450 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009451 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009452 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009453 if (val) {
9454 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9455 } else {
9456 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9457 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009458 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009459 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009460}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009461static void
9462setoption(int flag, int val)
9463{
9464 int i;
9465
9466 for (i = 0; i < NOPTS; i++) {
9467 if (optletters(i) == flag) {
9468 optlist[i] = val;
9469 return;
9470 }
9471 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009472 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009473 /* NOTREACHED */
9474}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009475static int
Eric Andersenc470f442003-07-28 09:56:35 +00009476options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009477{
9478 char *p;
9479 int val;
9480 int c;
9481
9482 if (cmdline)
9483 minusc = NULL;
9484 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009485 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009486 if (c != '-' && c != '+')
9487 break;
9488 argptr++;
9489 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009490 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009491 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009492 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009493 if (!cmdline) {
9494 /* "-" means turn off -x and -v */
9495 if (p[0] == '\0')
9496 xflag = vflag = 0;
9497 /* "--" means reset params */
9498 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009499 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009500 }
Eric Andersenc470f442003-07-28 09:56:35 +00009501 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009502 }
Eric Andersencb57d552001-06-28 07:25:16 +00009503 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009504 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009505 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009506 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009507 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009508 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009509 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009510 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009511 /* it already printed err message */
9512 return 1; /* error */
9513 }
Eric Andersencb57d552001-06-28 07:25:16 +00009514 if (*argptr)
9515 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009516 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9517 isloginsh = 1;
9518 /* bash does not accept +-login, we also won't */
9519 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009520 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009521 isloginsh = 1;
9522 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009523 } else {
9524 setoption(c, val);
9525 }
9526 }
9527 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009528 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009529}
9530
Eric Andersencb57d552001-06-28 07:25:16 +00009531/*
Eric Andersencb57d552001-06-28 07:25:16 +00009532 * The shift builtin command.
9533 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009534static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009535shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009536{
9537 int n;
9538 char **ap1, **ap2;
9539
9540 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009541 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009542 n = number(argv[1]);
9543 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009544 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009545 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009546 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009547 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009548 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009549 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009550 }
9551 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009552 while ((*ap2++ = *ap1++) != NULL)
9553 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009554#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009555 shellparam.optind = 1;
9556 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009557#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009558 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009559 return 0;
9560}
9561
Eric Andersencb57d552001-06-28 07:25:16 +00009562/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009563 * POSIX requires that 'set' (but not export or readonly) output the
9564 * variables in lexicographic order - by the locale's collating order (sigh).
9565 * Maybe we could keep them in an ordered balanced binary tree
9566 * instead of hashed lists.
9567 * For now just roll 'em through qsort for printing...
9568 */
9569static int
9570showvars(const char *sep_prefix, int on, int off)
9571{
9572 const char *sep;
9573 char **ep, **epend;
9574
9575 ep = listvars(on, off, &epend);
9576 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9577
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009578 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009579
9580 for (; ep < epend; ep++) {
9581 const char *p;
9582 const char *q;
9583
9584 p = strchrnul(*ep, '=');
9585 q = nullstr;
9586 if (*p)
9587 q = single_quote(++p);
9588 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9589 }
9590 return 0;
9591}
9592
9593/*
Eric Andersencb57d552001-06-28 07:25:16 +00009594 * The set command builtin.
9595 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009596static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009597setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009598{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009599 int retval;
9600
Denis Vlasenko68404f12008-03-17 09:00:54 +00009601 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009602 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009603 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009604 retval = 1;
9605 if (!options(0)) { /* if no parse error... */
9606 retval = 0;
9607 optschanged();
9608 if (*argptr != NULL) {
9609 setparam(argptr);
9610 }
Eric Andersencb57d552001-06-28 07:25:16 +00009611 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009612 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009613 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009614}
9615
Denis Vlasenko131ae172007-02-18 13:00:19 +00009616#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009617/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009618static void
9619change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009620{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009621 /* Galois LFSR parameter */
9622 /* Taps at 32 31 29 1: */
9623 enum { MASK = 0x8000000b };
9624 /* Another example - taps at 32 31 30 10: */
9625 /* MASK = 0x00400007 */
9626
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009627 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009628 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009629 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009630
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009631 /* LCG has period of 2^32 and alternating lowest bit */
9632 random_LCG = 1664525 * random_LCG + 1013904223;
9633 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9634 t = (random_galois_LFSR << 1);
9635 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9636 t ^= MASK;
9637 random_galois_LFSR = t;
9638 /* Both are weak, xoring them gives better randomness
9639 * and ~2^64 period. & 0x7fff is probably bash compat
9640 * for $RANDOM range. */
9641 t = (t ^ random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009642 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009643 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009644 vrandom.flags &= ~VNOFUNC;
9645 } else {
9646 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009647 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009648 }
Eric Andersenef02f822004-03-11 13:34:24 +00009649}
Eric Andersen16767e22004-03-16 05:14:10 +00009650#endif
9651
Denis Vlasenko131ae172007-02-18 13:00:19 +00009652#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009653static int
Eric Andersenc470f442003-07-28 09:56:35 +00009654getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009655{
9656 char *p, *q;
9657 char c = '?';
9658 int done = 0;
9659 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009660 char s[12];
9661 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009662
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009663 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009664 return 1;
9665 optnext = optfirst + *param_optind - 1;
9666
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009667 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009668 p = NULL;
9669 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009670 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009671 if (p == NULL || *p == '\0') {
9672 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009673 p = *optnext;
9674 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009675 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009676 p = NULL;
9677 done = 1;
9678 goto out;
9679 }
9680 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009681 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009682 goto atend;
9683 }
9684
9685 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009686 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009687 if (*q == '\0') {
9688 if (optstr[0] == ':') {
9689 s[0] = c;
9690 s[1] = '\0';
9691 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009692 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009693 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009694 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009695 }
9696 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009697 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009698 }
9699 if (*++q == ':')
9700 q++;
9701 }
9702
9703 if (*++q == ':') {
9704 if (*p == '\0' && (p = *optnext) == NULL) {
9705 if (optstr[0] == ':') {
9706 s[0] = c;
9707 s[1] = '\0';
9708 err |= setvarsafe("OPTARG", s, 0);
9709 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009710 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009711 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009712 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009713 c = '?';
9714 }
Eric Andersenc470f442003-07-28 09:56:35 +00009715 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009716 }
9717
9718 if (p == *optnext)
9719 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009720 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009721 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009722 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009723 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009724 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009725 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009726 *param_optind = optnext - optfirst + 1;
9727 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009728 err |= setvarsafe("OPTIND", s, VNOFUNC);
9729 s[0] = c;
9730 s[1] = '\0';
9731 err |= setvarsafe(optvar, s, 0);
9732 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009733 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009734 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009735 flush_stdout_stderr();
9736 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009737 }
9738 return done;
9739}
Eric Andersenc470f442003-07-28 09:56:35 +00009740
9741/*
9742 * The getopts builtin. Shellparam.optnext points to the next argument
9743 * to be processed. Shellparam.optptr points to the next character to
9744 * be processed in the current argument. If shellparam.optnext is NULL,
9745 * then it's the first time getopts has been called.
9746 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009747static int
Eric Andersenc470f442003-07-28 09:56:35 +00009748getoptscmd(int argc, char **argv)
9749{
9750 char **optbase;
9751
9752 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009753 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009754 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009755 optbase = shellparam.p;
9756 if (shellparam.optind > shellparam.nparam + 1) {
9757 shellparam.optind = 1;
9758 shellparam.optoff = -1;
9759 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009760 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009761 optbase = &argv[3];
9762 if (shellparam.optind > argc - 2) {
9763 shellparam.optind = 1;
9764 shellparam.optoff = -1;
9765 }
9766 }
9767
9768 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009769 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009770}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009771#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009772
Eric Andersencb57d552001-06-28 07:25:16 +00009773
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009774/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009775
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009776struct heredoc {
9777 struct heredoc *next; /* next here document in list */
9778 union node *here; /* redirection node */
9779 char *eofmark; /* string indicating end of input */
9780 smallint striptabs; /* if set, strip leading tabs */
9781};
9782
9783static smallint tokpushback; /* last token pushed back */
9784static smallint parsebackquote; /* nonzero if we are inside backquotes */
9785static smallint quoteflag; /* set if (part of) last token was quoted */
9786static token_id_t lasttoken; /* last token read (integer id Txxx) */
9787static struct heredoc *heredoclist; /* list of here documents to read */
9788static char *wordtext; /* text of last word returned by readtoken */
9789static struct nodelist *backquotelist;
9790static union node *redirnode;
9791static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009792/*
9793 * NEOF is returned by parsecmd when it encounters an end of file. It
9794 * must be distinct from NULL, so we use the address of a variable that
9795 * happens to be handy.
9796 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009797#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009798
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009799static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9800static void
9801raise_error_syntax(const char *msg)
9802{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009803 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009804 /* NOTREACHED */
9805}
9806
9807/*
9808 * Called when an unexpected token is read during the parse. The argument
9809 * is the token that is expected, or -1 if more than one type of token can
9810 * occur at this point.
9811 */
9812static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9813static void
9814raise_error_unexpected_syntax(int token)
9815{
9816 char msg[64];
9817 int l;
9818
9819 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9820 if (token >= 0)
9821 sprintf(msg + l, " (expecting %s)", tokname(token));
9822 raise_error_syntax(msg);
9823 /* NOTREACHED */
9824}
Eric Andersencb57d552001-06-28 07:25:16 +00009825
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009826#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009827
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009828/* parsing is heavily cross-recursive, need these forward decls */
9829static union node *andor(void);
9830static union node *pipeline(void);
9831static union node *parse_command(void);
9832static void parseheredoc(void);
9833static char peektoken(void);
9834static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009835
Eric Andersenc470f442003-07-28 09:56:35 +00009836static union node *
9837list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009838{
9839 union node *n1, *n2, *n3;
9840 int tok;
9841
Eric Andersenc470f442003-07-28 09:56:35 +00009842 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9843 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009844 return NULL;
9845 n1 = NULL;
9846 for (;;) {
9847 n2 = andor();
9848 tok = readtoken();
9849 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009850 if (n2->type == NPIPE) {
9851 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009852 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009853 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009854 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009855 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009856 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009857 n2 = n3;
9858 }
9859 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009860 }
9861 }
9862 if (n1 == NULL) {
9863 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009864 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009865 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009866 n3->type = NSEMI;
9867 n3->nbinary.ch1 = n1;
9868 n3->nbinary.ch2 = n2;
9869 n1 = n3;
9870 }
9871 switch (tok) {
9872 case TBACKGND:
9873 case TSEMI:
9874 tok = readtoken();
9875 /* fall through */
9876 case TNL:
9877 if (tok == TNL) {
9878 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009879 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009880 return n1;
9881 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009882 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009883 }
Eric Andersenc470f442003-07-28 09:56:35 +00009884 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009885 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009886 return n1;
9887 break;
9888 case TEOF:
9889 if (heredoclist)
9890 parseheredoc();
9891 else
Eric Andersenc470f442003-07-28 09:56:35 +00009892 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009893 return n1;
9894 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009895 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009896 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009897 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009898 return n1;
9899 }
9900 }
9901}
9902
Eric Andersenc470f442003-07-28 09:56:35 +00009903static union node *
9904andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009905{
Eric Andersencb57d552001-06-28 07:25:16 +00009906 union node *n1, *n2, *n3;
9907 int t;
9908
Eric Andersencb57d552001-06-28 07:25:16 +00009909 n1 = pipeline();
9910 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009911 t = readtoken();
9912 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009913 t = NAND;
9914 } else if (t == TOR) {
9915 t = NOR;
9916 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009917 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009918 return n1;
9919 }
Eric Andersenc470f442003-07-28 09:56:35 +00009920 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009921 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009922 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009923 n3->type = t;
9924 n3->nbinary.ch1 = n1;
9925 n3->nbinary.ch2 = n2;
9926 n1 = n3;
9927 }
9928}
9929
Eric Andersenc470f442003-07-28 09:56:35 +00009930static union node *
9931pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009932{
Eric Andersencb57d552001-06-28 07:25:16 +00009933 union node *n1, *n2, *pipenode;
9934 struct nodelist *lp, *prev;
9935 int negate;
9936
9937 negate = 0;
9938 TRACE(("pipeline: entered\n"));
9939 if (readtoken() == TNOT) {
9940 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009941 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009942 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009943 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009944 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009945 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009946 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009947 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009948 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009949 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009950 pipenode->npipe.cmdlist = lp;
9951 lp->n = n1;
9952 do {
9953 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009954 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009955 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009956 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009957 prev->next = lp;
9958 } while (readtoken() == TPIPE);
9959 lp->next = NULL;
9960 n1 = pipenode;
9961 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009962 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009963 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009964 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009965 n2->type = NNOT;
9966 n2->nnot.com = n1;
9967 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009968 }
9969 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009970}
9971
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009972static union node *
9973makename(void)
9974{
9975 union node *n;
9976
Denis Vlasenko597906c2008-02-20 16:38:54 +00009977 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009978 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009979 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009980 n->narg.text = wordtext;
9981 n->narg.backquote = backquotelist;
9982 return n;
9983}
9984
9985static void
9986fixredir(union node *n, const char *text, int err)
9987{
9988 TRACE(("Fix redir %s %d\n", text, err));
9989 if (!err)
9990 n->ndup.vname = NULL;
9991
9992 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009993 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009994 else if (LONE_DASH(text))
9995 n->ndup.dupfd = -1;
9996 else {
9997 if (err)
9998 raise_error_syntax("Bad fd number");
9999 n->ndup.vname = makename();
10000 }
10001}
10002
10003/*
10004 * Returns true if the text contains nothing to expand (no dollar signs
10005 * or backquotes).
10006 */
10007static int
10008noexpand(char *text)
10009{
10010 char *p;
10011 char c;
10012
10013 p = text;
10014 while ((c = *p++) != '\0') {
10015 if (c == CTLQUOTEMARK)
10016 continue;
10017 if (c == CTLESC)
10018 p++;
10019 else if (SIT(c, BASESYNTAX) == CCTL)
10020 return 0;
10021 }
10022 return 1;
10023}
10024
10025static void
10026parsefname(void)
10027{
10028 union node *n = redirnode;
10029
10030 if (readtoken() != TWORD)
10031 raise_error_unexpected_syntax(-1);
10032 if (n->type == NHERE) {
10033 struct heredoc *here = heredoc;
10034 struct heredoc *p;
10035 int i;
10036
10037 if (quoteflag == 0)
10038 n->type = NXHERE;
10039 TRACE(("Here document %d\n", n->type));
10040 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10041 raise_error_syntax("Illegal eof marker for << redirection");
10042 rmescapes(wordtext);
10043 here->eofmark = wordtext;
10044 here->next = NULL;
10045 if (heredoclist == NULL)
10046 heredoclist = here;
10047 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010048 for (p = heredoclist; p->next; p = p->next)
10049 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010050 p->next = here;
10051 }
10052 } else if (n->type == NTOFD || n->type == NFROMFD) {
10053 fixredir(n, wordtext, 0);
10054 } else {
10055 n->nfile.fname = makename();
10056 }
10057}
Eric Andersencb57d552001-06-28 07:25:16 +000010058
Eric Andersenc470f442003-07-28 09:56:35 +000010059static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010060simplecmd(void)
10061{
10062 union node *args, **app;
10063 union node *n = NULL;
10064 union node *vars, **vpp;
10065 union node **rpp, *redir;
10066 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010067#if ENABLE_ASH_BASH_COMPAT
10068 smallint double_brackets_flag = 0;
10069#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010070
10071 args = NULL;
10072 app = &args;
10073 vars = NULL;
10074 vpp = &vars;
10075 redir = NULL;
10076 rpp = &redir;
10077
10078 savecheckkwd = CHKALIAS;
10079 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010080 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010081 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010082 t = readtoken();
10083 switch (t) {
10084#if ENABLE_ASH_BASH_COMPAT
10085 case TAND: /* "&&" */
10086 case TOR: /* "||" */
10087 if (!double_brackets_flag) {
10088 tokpushback = 1;
10089 goto out;
10090 }
10091 wordtext = (char *) (t == TAND ? "-a" : "-o");
10092#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010093 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010094 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010095 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010096 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010097 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010098#if ENABLE_ASH_BASH_COMPAT
10099 if (strcmp("[[", wordtext) == 0)
10100 double_brackets_flag = 1;
10101 else if (strcmp("]]", wordtext) == 0)
10102 double_brackets_flag = 0;
10103#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010104 n->narg.backquote = backquotelist;
10105 if (savecheckkwd && isassignment(wordtext)) {
10106 *vpp = n;
10107 vpp = &n->narg.next;
10108 } else {
10109 *app = n;
10110 app = &n->narg.next;
10111 savecheckkwd = 0;
10112 }
10113 break;
10114 case TREDIR:
10115 *rpp = n = redirnode;
10116 rpp = &n->nfile.next;
10117 parsefname(); /* read name of redirection file */
10118 break;
10119 case TLP:
10120 if (args && app == &args->narg.next
10121 && !vars && !redir
10122 ) {
10123 struct builtincmd *bcmd;
10124 const char *name;
10125
10126 /* We have a function */
10127 if (readtoken() != TRP)
10128 raise_error_unexpected_syntax(TRP);
10129 name = n->narg.text;
10130 if (!goodname(name)
10131 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10132 ) {
10133 raise_error_syntax("Bad function name");
10134 }
10135 n->type = NDEFUN;
10136 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10137 n->narg.next = parse_command();
10138 return n;
10139 }
10140 /* fall through */
10141 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010142 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010143 goto out;
10144 }
10145 }
10146 out:
10147 *app = NULL;
10148 *vpp = NULL;
10149 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010150 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010151 n->type = NCMD;
10152 n->ncmd.args = args;
10153 n->ncmd.assign = vars;
10154 n->ncmd.redirect = redir;
10155 return n;
10156}
10157
10158static union node *
10159parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010160{
Eric Andersencb57d552001-06-28 07:25:16 +000010161 union node *n1, *n2;
10162 union node *ap, **app;
10163 union node *cp, **cpp;
10164 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010165 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010166 int t;
10167
10168 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010169 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010170
Eric Andersencb57d552001-06-28 07:25:16 +000010171 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010172 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010173 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010174 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010175 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010176 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010177 n1->type = NIF;
10178 n1->nif.test = list(0);
10179 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010180 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010181 n1->nif.ifpart = list(0);
10182 n2 = n1;
10183 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010184 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010185 n2 = n2->nif.elsepart;
10186 n2->type = NIF;
10187 n2->nif.test = list(0);
10188 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010189 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010190 n2->nif.ifpart = list(0);
10191 }
10192 if (lasttoken == TELSE)
10193 n2->nif.elsepart = list(0);
10194 else {
10195 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010196 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010197 }
Eric Andersenc470f442003-07-28 09:56:35 +000010198 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010199 break;
10200 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010201 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010202 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010203 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010204 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010205 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010206 got = readtoken();
10207 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010208 TRACE(("expecting DO got %s %s\n", tokname(got),
10209 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010210 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010211 }
10212 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010213 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010214 break;
10215 }
10216 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010217 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010218 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010219 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010220 n1->type = NFOR;
10221 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010222 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010223 if (readtoken() == TIN) {
10224 app = &ap;
10225 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010226 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010227 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010228 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010229 n2->narg.text = wordtext;
10230 n2->narg.backquote = backquotelist;
10231 *app = n2;
10232 app = &n2->narg.next;
10233 }
10234 *app = NULL;
10235 n1->nfor.args = ap;
10236 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010237 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010238 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010239 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010240 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010241 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010242 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010243 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010244 n1->nfor.args = n2;
10245 /*
10246 * Newline or semicolon here is optional (but note
10247 * that the original Bourne shell only allowed NL).
10248 */
10249 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010250 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010251 }
Eric Andersenc470f442003-07-28 09:56:35 +000010252 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010253 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010254 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010255 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010256 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010257 break;
10258 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010259 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010260 n1->type = NCASE;
10261 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010262 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010263 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010264 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010265 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010266 n2->narg.text = wordtext;
10267 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010268 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010269 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010270 } while (readtoken() == TNL);
10271 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010272 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010273 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010274 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010275 checkkwd = CHKNL | CHKKWD;
10276 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010277 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010278 if (lasttoken == TLP)
10279 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010280 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010281 cp->type = NCLIST;
10282 app = &cp->nclist.pattern;
10283 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010284 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010285 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010286 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010287 ap->narg.text = wordtext;
10288 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010289 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010290 break;
10291 app = &ap->narg.next;
10292 readtoken();
10293 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010294 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010295 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010296 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010297 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010298
Eric Andersenc470f442003-07-28 09:56:35 +000010299 cpp = &cp->nclist.next;
10300
10301 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010302 t = readtoken();
10303 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010304 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010305 raise_error_unexpected_syntax(TENDCASE);
10306 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010307 }
Eric Andersenc470f442003-07-28 09:56:35 +000010308 }
Eric Andersencb57d552001-06-28 07:25:16 +000010309 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010310 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010311 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010312 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010313 n1->type = NSUBSHELL;
10314 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010315 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010316 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010317 break;
10318 case TBEGIN:
10319 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010320 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010321 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010322 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010323 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010324 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010325 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010326 }
10327
Eric Andersenc470f442003-07-28 09:56:35 +000010328 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010329 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010330
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010331 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010332 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010333 checkkwd = CHKKWD | CHKALIAS;
10334 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010335 while (readtoken() == TREDIR) {
10336 *rpp = n2 = redirnode;
10337 rpp = &n2->nfile.next;
10338 parsefname();
10339 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010340 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010341 *rpp = NULL;
10342 if (redir) {
10343 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010344 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010345 n2->type = NREDIR;
10346 n2->nredir.n = n1;
10347 n1 = n2;
10348 }
10349 n1->nredir.redirect = redir;
10350 }
Eric Andersencb57d552001-06-28 07:25:16 +000010351 return n1;
10352}
10353
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010354#if ENABLE_ASH_BASH_COMPAT
10355static int decode_dollar_squote(void)
10356{
10357 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10358 int c, cnt;
10359 char *p;
10360 char buf[4];
10361
10362 c = pgetc();
10363 p = strchr(C_escapes, c);
10364 if (p) {
10365 buf[0] = c;
10366 p = buf;
10367 cnt = 3;
10368 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10369 do {
10370 c = pgetc();
10371 *++p = c;
10372 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10373 pungetc();
10374 } else if (c == 'x') { /* \xHH */
10375 do {
10376 c = pgetc();
10377 *++p = c;
10378 } while (isxdigit(c) && --cnt);
10379 pungetc();
10380 if (cnt == 3) { /* \x but next char is "bad" */
10381 c = 'x';
10382 goto unrecognized;
10383 }
10384 } else { /* simple seq like \\ or \t */
10385 p++;
10386 }
10387 *p = '\0';
10388 p = buf;
10389 c = bb_process_escape_sequence((void*)&p);
10390 } else { /* unrecognized "\z": print both chars unless ' or " */
10391 if (c != '\'' && c != '"') {
10392 unrecognized:
10393 c |= 0x100; /* "please encode \, then me" */
10394 }
10395 }
10396 return c;
10397}
10398#endif
10399
Eric Andersencb57d552001-06-28 07:25:16 +000010400/*
10401 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10402 * is not NULL, read a here document. In the latter case, eofmark is the
10403 * word which marks the end of the document and striptabs is true if
10404 * leading tabs should be stripped from the document. The argument firstc
10405 * is the first character of the input token or document.
10406 *
10407 * Because C does not have internal subroutines, I have simulated them
10408 * using goto's to implement the subroutine linkage. The following macros
10409 * will run code that appears at the end of readtoken1.
10410 */
Eric Andersen2870d962001-07-02 17:27:21 +000010411#define CHECKEND() {goto checkend; checkend_return:;}
10412#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10413#define PARSESUB() {goto parsesub; parsesub_return:;}
10414#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10415#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10416#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010417static int
Eric Andersenc470f442003-07-28 09:56:35 +000010418readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010419{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010420 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010421 int c = firstc;
10422 char *out;
10423 int len;
10424 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010425 struct nodelist *bqlist;
10426 smallint quotef;
10427 smallint dblquote;
10428 smallint oldstyle;
10429 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010430#if ENABLE_ASH_EXPAND_PRMT
10431 smallint pssyntax; /* we are expanding a prompt string */
10432#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010433 int varnest; /* levels of variables expansion */
10434 int arinest; /* levels of arithmetic expansion */
10435 int parenlevel; /* levels of parens in arithmetic */
10436 int dqvarnest; /* levels of variables expansion within double quotes */
10437
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010438 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10439
Eric Andersencb57d552001-06-28 07:25:16 +000010440#if __GNUC__
10441 /* Avoid longjmp clobbering */
10442 (void) &out;
10443 (void) &quotef;
10444 (void) &dblquote;
10445 (void) &varnest;
10446 (void) &arinest;
10447 (void) &parenlevel;
10448 (void) &dqvarnest;
10449 (void) &oldstyle;
10450 (void) &prevsyntax;
10451 (void) &syntax;
10452#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010453 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010454 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010455 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010456 oldstyle = 0;
10457 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010458#if ENABLE_ASH_EXPAND_PRMT
10459 pssyntax = (syntax == PSSYNTAX);
10460 if (pssyntax)
10461 syntax = DQSYNTAX;
10462#endif
10463 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010464 varnest = 0;
10465 arinest = 0;
10466 parenlevel = 0;
10467 dqvarnest = 0;
10468
10469 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010470 loop: { /* for each line, until end of word */
10471 CHECKEND(); /* set c to PEOF if at end of here document */
10472 for (;;) { /* until end of line or end of word */
10473 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010474 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010475 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010476 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010477 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010478 USTPUTC(c, out);
10479 plinno++;
10480 if (doprompt)
10481 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010482 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010483 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010484 case CWORD:
10485 USTPUTC(c, out);
10486 break;
10487 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010488 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010489 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010490#if ENABLE_ASH_BASH_COMPAT
10491 if (c == '\\' && bash_dollar_squote) {
10492 c = decode_dollar_squote();
10493 if (c & 0x100) {
10494 USTPUTC('\\', out);
10495 c = (unsigned char)c;
10496 }
10497 }
10498#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010499 USTPUTC(c, out);
10500 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010501 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010502 c = pgetc2();
10503 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010504 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010505 USTPUTC('\\', out);
10506 pungetc();
10507 } else if (c == '\n') {
10508 if (doprompt)
10509 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010510 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010511#if ENABLE_ASH_EXPAND_PRMT
10512 if (c == '$' && pssyntax) {
10513 USTPUTC(CTLESC, out);
10514 USTPUTC('\\', out);
10515 }
10516#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010517 if (dblquote && c != '\\'
10518 && c != '`' && c != '$'
10519 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010520 ) {
10521 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010522 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010523 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010524 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010525 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010526 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010527 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010528 }
10529 break;
10530 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010531 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010532 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010533 if (eofmark == NULL) {
10534 USTPUTC(CTLQUOTEMARK, out);
10535 }
Eric Andersencb57d552001-06-28 07:25:16 +000010536 break;
10537 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010538 syntax = DQSYNTAX;
10539 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010540 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010542 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010543 if (eofmark != NULL && arinest == 0
10544 && varnest == 0
10545 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010546 USTPUTC(c, out);
10547 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010548 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010549 syntax = BASESYNTAX;
10550 dblquote = 0;
10551 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010552 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010553 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010554 }
10555 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010556 case CVAR: /* '$' */
10557 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010558 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010559 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010560 if (varnest > 0) {
10561 varnest--;
10562 if (dqvarnest > 0) {
10563 dqvarnest--;
10564 }
10565 USTPUTC(CTLENDVAR, out);
10566 } else {
10567 USTPUTC(c, out);
10568 }
10569 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010570#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010571 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010572 parenlevel++;
10573 USTPUTC(c, out);
10574 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010575 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010576 if (parenlevel > 0) {
10577 USTPUTC(c, out);
10578 --parenlevel;
10579 } else {
10580 if (pgetc() == ')') {
10581 if (--arinest == 0) {
10582 USTPUTC(CTLENDARI, out);
10583 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010584 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010585 } else
10586 USTPUTC(')', out);
10587 } else {
10588 /*
10589 * unbalanced parens
10590 * (don't 2nd guess - no error)
10591 */
10592 pungetc();
10593 USTPUTC(')', out);
10594 }
10595 }
10596 break;
10597#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010598 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010599 PARSEBACKQOLD();
10600 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010601 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010602 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010603 case CIGN:
10604 break;
10605 default:
10606 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010607 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010608#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010609 if (c != PEOA)
10610#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010611 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010612
Eric Andersencb57d552001-06-28 07:25:16 +000010613 }
10614 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010615 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010616 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010617 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010618#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010619 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010620 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010621#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010622 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010623 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010624 if (varnest != 0) {
10625 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010626 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010627 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010628 }
10629 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010630 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010631 out = stackblock();
10632 if (eofmark == NULL) {
10633 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010634 && quotef == 0
10635 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010636 && (*out == '\0' || isdigit(*out))
10637 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010638 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010639 lasttoken = TREDIR;
10640 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010641 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010642 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010643 }
10644 quoteflag = quotef;
10645 backquotelist = bqlist;
10646 grabstackblock(len);
10647 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010648 lasttoken = TWORD;
10649 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010650/* end of readtoken routine */
10651
Eric Andersencb57d552001-06-28 07:25:16 +000010652/*
10653 * Check to see whether we are at the end of the here document. When this
10654 * is called, c is set to the first character of the next input line. If
10655 * we are at the end of the here document, this routine sets the c to PEOF.
10656 */
Eric Andersenc470f442003-07-28 09:56:35 +000010657checkend: {
10658 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010659#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010660 if (c == PEOA) {
10661 c = pgetc2();
10662 }
10663#endif
10664 if (striptabs) {
10665 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010666 c = pgetc2();
10667 }
Eric Andersenc470f442003-07-28 09:56:35 +000010668 }
10669 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010670 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010671 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010672
Eric Andersenc470f442003-07-28 09:56:35 +000010673 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010674 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10675 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010676 if (*p == '\n' && *q == '\0') {
10677 c = PEOF;
10678 plinno++;
10679 needprompt = doprompt;
10680 } else {
10681 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010682 }
10683 }
10684 }
10685 }
Eric Andersenc470f442003-07-28 09:56:35 +000010686 goto checkend_return;
10687}
Eric Andersencb57d552001-06-28 07:25:16 +000010688
Eric Andersencb57d552001-06-28 07:25:16 +000010689/*
10690 * Parse a redirection operator. The variable "out" points to a string
10691 * specifying the fd to be redirected. The variable "c" contains the
10692 * first character of the redirection operator.
10693 */
Eric Andersenc470f442003-07-28 09:56:35 +000010694parseredir: {
10695 char fd = *out;
10696 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010697
Denis Vlasenko597906c2008-02-20 16:38:54 +000010698 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010699 if (c == '>') {
10700 np->nfile.fd = 1;
10701 c = pgetc();
10702 if (c == '>')
10703 np->type = NAPPEND;
10704 else if (c == '|')
10705 np->type = NCLOBBER;
10706 else if (c == '&')
10707 np->type = NTOFD;
10708 else {
10709 np->type = NTO;
10710 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010711 }
Eric Andersenc470f442003-07-28 09:56:35 +000010712 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010713 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010714 c = pgetc();
10715 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010716 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010717 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010718 np = stzalloc(sizeof(struct nhere));
10719 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010720 }
10721 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010722 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010723 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010724 c = pgetc();
10725 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010726 heredoc->striptabs = 1;
10727 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010728 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010729 pungetc();
10730 }
10731 break;
10732
10733 case '&':
10734 np->type = NFROMFD;
10735 break;
10736
10737 case '>':
10738 np->type = NFROMTO;
10739 break;
10740
10741 default:
10742 np->type = NFROM;
10743 pungetc();
10744 break;
10745 }
Eric Andersencb57d552001-06-28 07:25:16 +000010746 }
Eric Andersenc470f442003-07-28 09:56:35 +000010747 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010748 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010749 redirnode = np;
10750 goto parseredir_return;
10751}
Eric Andersencb57d552001-06-28 07:25:16 +000010752
Eric Andersencb57d552001-06-28 07:25:16 +000010753/*
10754 * Parse a substitution. At this point, we have read the dollar sign
10755 * and nothing else.
10756 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010757
10758/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10759 * (assuming ascii char codes, as the original implementation did) */
10760#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010761 (((unsigned)(c) - 33 < 32) \
10762 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010763parsesub: {
10764 int subtype;
10765 int typeloc;
10766 int flags;
10767 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010768 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010769
Eric Andersenc470f442003-07-28 09:56:35 +000010770 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010771 if (c <= PEOA_OR_PEOF
10772 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010773 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010774#if ENABLE_ASH_BASH_COMPAT
10775 if (c == '\'')
10776 bash_dollar_squote = 1;
10777 else
10778#endif
10779 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010780 pungetc();
10781 } else if (c == '(') { /* $(command) or $((arith)) */
10782 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010783#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010784 PARSEARITH();
10785#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010786 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010787#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010788 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010789 pungetc();
10790 PARSEBACKQNEW();
10791 }
10792 } else {
10793 USTPUTC(CTLVAR, out);
10794 typeloc = out - (char *)stackblock();
10795 USTPUTC(VSNORMAL, out);
10796 subtype = VSNORMAL;
10797 if (c == '{') {
10798 c = pgetc();
10799 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010800 c = pgetc();
10801 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010802 c = '#';
10803 else
10804 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010805 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010806 subtype = 0;
10807 }
10808 if (c > PEOA_OR_PEOF && is_name(c)) {
10809 do {
10810 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010811 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010812 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010813 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010814 do {
10815 STPUTC(c, out);
10816 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010817 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010818 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010819 USTPUTC(c, out);
10820 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010821 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010822 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010823
Eric Andersenc470f442003-07-28 09:56:35 +000010824 STPUTC('=', out);
10825 flags = 0;
10826 if (subtype == 0) {
10827 switch (c) {
10828 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010829 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010830#if ENABLE_ASH_BASH_COMPAT
10831 if (c == ':' || c == '$' || isdigit(c)) {
10832 pungetc();
10833 subtype = VSSUBSTR;
10834 break;
10835 }
10836#endif
10837 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010838 /*FALLTHROUGH*/
10839 default:
10840 p = strchr(types, c);
10841 if (p == NULL)
10842 goto badsub;
10843 subtype = p - types + VSNORMAL;
10844 break;
10845 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010846 case '#': {
10847 int cc = c;
10848 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10849 c = pgetc();
10850 if (c == cc)
10851 subtype++;
10852 else
10853 pungetc();
10854 break;
10855 }
10856#if ENABLE_ASH_BASH_COMPAT
10857 case '/':
10858 subtype = VSREPLACE;
10859 c = pgetc();
10860 if (c == '/')
10861 subtype++; /* VSREPLACEALL */
10862 else
10863 pungetc();
10864 break;
10865#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010866 }
Eric Andersenc470f442003-07-28 09:56:35 +000010867 } else {
10868 pungetc();
10869 }
10870 if (dblquote || arinest)
10871 flags |= VSQUOTE;
10872 *((char *)stackblock() + typeloc) = subtype | flags;
10873 if (subtype != VSNORMAL) {
10874 varnest++;
10875 if (dblquote || arinest) {
10876 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 }
10878 }
10879 }
Eric Andersenc470f442003-07-28 09:56:35 +000010880 goto parsesub_return;
10881}
Eric Andersencb57d552001-06-28 07:25:16 +000010882
Eric Andersencb57d552001-06-28 07:25:16 +000010883/*
10884 * Called to parse command substitutions. Newstyle is set if the command
10885 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10886 * list of commands (passed by reference), and savelen is the number of
10887 * characters on the top of the stack which must be preserved.
10888 */
Eric Andersenc470f442003-07-28 09:56:35 +000010889parsebackq: {
10890 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010891 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010892 union node *n;
10893 char *volatile str;
10894 struct jmploc jmploc;
10895 struct jmploc *volatile savehandler;
10896 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010897 smallint saveprompt = 0;
10898
Eric Andersencb57d552001-06-28 07:25:16 +000010899#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010900 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010901#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010902 savepbq = parsebackquote;
10903 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010904 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010905 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010906 exception_handler = savehandler;
10907 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010908 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010909 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010910 str = NULL;
10911 savelen = out - (char *)stackblock();
10912 if (savelen > 0) {
10913 str = ckmalloc(savelen);
10914 memcpy(str, stackblock(), savelen);
10915 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010916 savehandler = exception_handler;
10917 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010918 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010919 if (oldstyle) {
10920 /* We must read until the closing backquote, giving special
10921 treatment to some slashes, and then push the string and
10922 reread it as input, interpreting it normally. */
10923 char *pout;
10924 int pc;
10925 size_t psavelen;
10926 char *pstr;
10927
10928
10929 STARTSTACKSTR(pout);
10930 for (;;) {
10931 if (needprompt) {
10932 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010933 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010934 pc = pgetc();
10935 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010936 case '`':
10937 goto done;
10938
10939 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010940 pc = pgetc();
10941 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010942 plinno++;
10943 if (doprompt)
10944 setprompt(2);
10945 /*
10946 * If eating a newline, avoid putting
10947 * the newline into the new character
10948 * stream (via the STPUTC after the
10949 * switch).
10950 */
10951 continue;
10952 }
10953 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010954 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010955 STPUTC('\\', pout);
10956 if (pc > PEOA_OR_PEOF) {
10957 break;
10958 }
10959 /* fall through */
10960
10961 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010962#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010963 case PEOA:
10964#endif
10965 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010966 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010967
10968 case '\n':
10969 plinno++;
10970 needprompt = doprompt;
10971 break;
10972
10973 default:
10974 break;
10975 }
10976 STPUTC(pc, pout);
10977 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010978 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010979 STPUTC('\0', pout);
10980 psavelen = pout - (char *)stackblock();
10981 if (psavelen > 0) {
10982 pstr = grabstackstr(pout);
10983 setinputstring(pstr);
10984 }
10985 }
10986 nlpp = &bqlist;
10987 while (*nlpp)
10988 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010989 *nlpp = stzalloc(sizeof(**nlpp));
10990 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010991 parsebackquote = oldstyle;
10992
10993 if (oldstyle) {
10994 saveprompt = doprompt;
10995 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010996 }
10997
Eric Andersenc470f442003-07-28 09:56:35 +000010998 n = list(2);
10999
11000 if (oldstyle)
11001 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011002 else if (readtoken() != TRP)
11003 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011004
11005 (*nlpp)->n = n;
11006 if (oldstyle) {
11007 /*
11008 * Start reading from old file again, ignoring any pushed back
11009 * tokens left from the backquote parsing
11010 */
11011 popfile();
11012 tokpushback = 0;
11013 }
11014 while (stackblocksize() <= savelen)
11015 growstackblock();
11016 STARTSTACKSTR(out);
11017 if (str) {
11018 memcpy(out, str, savelen);
11019 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011020 INT_OFF;
11021 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011022 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011023 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011024 }
11025 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011026 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011027 if (arinest || dblquote)
11028 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11029 else
11030 USTPUTC(CTLBACKQ, out);
11031 if (oldstyle)
11032 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011033 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011034}
11035
Denis Vlasenko131ae172007-02-18 13:00:19 +000011036#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011037/*
11038 * Parse an arithmetic expansion (indicate start of one and set state)
11039 */
Eric Andersenc470f442003-07-28 09:56:35 +000011040parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011041 if (++arinest == 1) {
11042 prevsyntax = syntax;
11043 syntax = ARISYNTAX;
11044 USTPUTC(CTLARI, out);
11045 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011046 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011047 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011048 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011049 } else {
11050 /*
11051 * we collapse embedded arithmetic expansion to
11052 * parenthesis, which should be equivalent
11053 */
11054 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011055 }
Eric Andersenc470f442003-07-28 09:56:35 +000011056 goto parsearith_return;
11057}
11058#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011059
Eric Andersenc470f442003-07-28 09:56:35 +000011060} /* end of readtoken */
11061
Eric Andersencb57d552001-06-28 07:25:16 +000011062/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011063 * Read the next input token.
11064 * If the token is a word, we set backquotelist to the list of cmds in
11065 * backquotes. We set quoteflag to true if any part of the word was
11066 * quoted.
11067 * If the token is TREDIR, then we set redirnode to a structure containing
11068 * the redirection.
11069 * In all cases, the variable startlinno is set to the number of the line
11070 * on which the token starts.
11071 *
11072 * [Change comment: here documents and internal procedures]
11073 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11074 * word parsing code into a separate routine. In this case, readtoken
11075 * doesn't need to have any internal procedures, but parseword does.
11076 * We could also make parseoperator in essence the main routine, and
11077 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011078 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011079#define NEW_xxreadtoken
11080#ifdef NEW_xxreadtoken
11081/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011082static const char xxreadtoken_chars[7] ALIGN1 = {
11083 '\n', '(', ')', '&', '|', ';', 0
11084};
Eric Andersencb57d552001-06-28 07:25:16 +000011085
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011086static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011087 TNL, TLP, TRP, /* only single occurrence allowed */
11088 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11089 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011090 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011091};
11092
11093#define xxreadtoken_doubles \
11094 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11095#define xxreadtoken_singles \
11096 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11097
11098static int
11099xxreadtoken(void)
11100{
11101 int c;
11102
11103 if (tokpushback) {
11104 tokpushback = 0;
11105 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011106 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011107 if (needprompt) {
11108 setprompt(2);
11109 }
11110 startlinno = plinno;
11111 for (;;) { /* until token or start of word found */
11112 c = pgetc_macro();
11113
11114 if ((c != ' ') && (c != '\t')
11115#if ENABLE_ASH_ALIAS
11116 && (c != PEOA)
11117#endif
11118 ) {
11119 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011120 while ((c = pgetc()) != '\n' && c != PEOF)
11121 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011122 pungetc();
11123 } else if (c == '\\') {
11124 if (pgetc() != '\n') {
11125 pungetc();
11126 goto READTOKEN1;
11127 }
11128 startlinno = ++plinno;
11129 if (doprompt)
11130 setprompt(2);
11131 } else {
11132 const char *p
11133 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11134
11135 if (c != PEOF) {
11136 if (c == '\n') {
11137 plinno++;
11138 needprompt = doprompt;
11139 }
11140
11141 p = strchr(xxreadtoken_chars, c);
11142 if (p == NULL) {
11143 READTOKEN1:
11144 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11145 }
11146
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011147 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011148 if (pgetc() == *p) { /* double occurrence? */
11149 p += xxreadtoken_doubles + 1;
11150 } else {
11151 pungetc();
11152 }
11153 }
11154 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011155 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11156 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011157 }
11158 }
11159 } /* for */
11160}
11161#else
11162#define RETURN(token) return lasttoken = token
11163static int
11164xxreadtoken(void)
11165{
11166 int c;
11167
11168 if (tokpushback) {
11169 tokpushback = 0;
11170 return lasttoken;
11171 }
11172 if (needprompt) {
11173 setprompt(2);
11174 }
11175 startlinno = plinno;
11176 for (;;) { /* until token or start of word found */
11177 c = pgetc_macro();
11178 switch (c) {
11179 case ' ': case '\t':
11180#if ENABLE_ASH_ALIAS
11181 case PEOA:
11182#endif
11183 continue;
11184 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011185 while ((c = pgetc()) != '\n' && c != PEOF)
11186 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011187 pungetc();
11188 continue;
11189 case '\\':
11190 if (pgetc() == '\n') {
11191 startlinno = ++plinno;
11192 if (doprompt)
11193 setprompt(2);
11194 continue;
11195 }
11196 pungetc();
11197 goto breakloop;
11198 case '\n':
11199 plinno++;
11200 needprompt = doprompt;
11201 RETURN(TNL);
11202 case PEOF:
11203 RETURN(TEOF);
11204 case '&':
11205 if (pgetc() == '&')
11206 RETURN(TAND);
11207 pungetc();
11208 RETURN(TBACKGND);
11209 case '|':
11210 if (pgetc() == '|')
11211 RETURN(TOR);
11212 pungetc();
11213 RETURN(TPIPE);
11214 case ';':
11215 if (pgetc() == ';')
11216 RETURN(TENDCASE);
11217 pungetc();
11218 RETURN(TSEMI);
11219 case '(':
11220 RETURN(TLP);
11221 case ')':
11222 RETURN(TRP);
11223 default:
11224 goto breakloop;
11225 }
11226 }
11227 breakloop:
11228 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11229#undef RETURN
11230}
11231#endif /* NEW_xxreadtoken */
11232
11233static int
11234readtoken(void)
11235{
11236 int t;
11237#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011238 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011239#endif
11240
11241#if ENABLE_ASH_ALIAS
11242 top:
11243#endif
11244
11245 t = xxreadtoken();
11246
11247 /*
11248 * eat newlines
11249 */
11250 if (checkkwd & CHKNL) {
11251 while (t == TNL) {
11252 parseheredoc();
11253 t = xxreadtoken();
11254 }
11255 }
11256
11257 if (t != TWORD || quoteflag) {
11258 goto out;
11259 }
11260
11261 /*
11262 * check for keywords
11263 */
11264 if (checkkwd & CHKKWD) {
11265 const char *const *pp;
11266
11267 pp = findkwd(wordtext);
11268 if (pp) {
11269 lasttoken = t = pp - tokname_array;
11270 TRACE(("keyword %s recognized\n", tokname(t)));
11271 goto out;
11272 }
11273 }
11274
11275 if (checkkwd & CHKALIAS) {
11276#if ENABLE_ASH_ALIAS
11277 struct alias *ap;
11278 ap = lookupalias(wordtext, 1);
11279 if (ap != NULL) {
11280 if (*ap->val) {
11281 pushstring(ap->val, ap);
11282 }
11283 goto top;
11284 }
11285#endif
11286 }
11287 out:
11288 checkkwd = 0;
11289#if DEBUG
11290 if (!alreadyseen)
11291 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11292 else
11293 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11294#endif
11295 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011296}
11297
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011298static char
11299peektoken(void)
11300{
11301 int t;
11302
11303 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011304 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011305 return tokname_array[t][0];
11306}
Eric Andersencb57d552001-06-28 07:25:16 +000011307
11308/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011309 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11310 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011311 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011312static union node *
11313parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011314{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011315 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011316
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011317 tokpushback = 0;
11318 doprompt = interact;
11319 if (doprompt)
11320 setprompt(doprompt);
11321 needprompt = 0;
11322 t = readtoken();
11323 if (t == TEOF)
11324 return NEOF;
11325 if (t == TNL)
11326 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011327 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011328 return list(1);
11329}
11330
11331/*
11332 * Input any here documents.
11333 */
11334static void
11335parseheredoc(void)
11336{
11337 struct heredoc *here;
11338 union node *n;
11339
11340 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011341 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011342
11343 while (here) {
11344 if (needprompt) {
11345 setprompt(2);
11346 }
11347 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11348 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011349 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011350 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011351 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011352 n->narg.text = wordtext;
11353 n->narg.backquote = backquotelist;
11354 here->here->nhere.doc = n;
11355 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011356 }
Eric Andersencb57d552001-06-28 07:25:16 +000011357}
11358
11359
11360/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011361 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011362 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011363#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011364static const char *
11365expandstr(const char *ps)
11366{
11367 union node n;
11368
11369 /* XXX Fix (char *) cast. */
11370 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011371 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011372 popfile();
11373
11374 n.narg.type = NARG;
11375 n.narg.next = NULL;
11376 n.narg.text = wordtext;
11377 n.narg.backquote = backquotelist;
11378
11379 expandarg(&n, NULL, 0);
11380 return stackblock();
11381}
11382#endif
11383
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011384/*
11385 * Execute a command or commands contained in a string.
11386 */
11387static int
11388evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011389{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011390 union node *n;
11391 struct stackmark smark;
11392 int skip;
11393
11394 setinputstring(s);
11395 setstackmark(&smark);
11396
11397 skip = 0;
11398 while ((n = parsecmd(0)) != NEOF) {
11399 evaltree(n, 0);
11400 popstackmark(&smark);
11401 skip = evalskip;
11402 if (skip)
11403 break;
11404 }
11405 popfile();
11406
11407 skip &= mask;
11408 evalskip = skip;
11409 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011410}
11411
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011412/*
11413 * The eval command.
11414 */
11415static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011416evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011417{
11418 char *p;
11419 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011420
Denis Vlasenko68404f12008-03-17 09:00:54 +000011421 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011422 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011423 argv += 2;
11424 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011425 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011426 for (;;) {
11427 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011428 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011429 if (p == NULL)
11430 break;
11431 STPUTC(' ', concat);
11432 }
11433 STPUTC('\0', concat);
11434 p = grabstackstr(concat);
11435 }
11436 evalstring(p, ~SKIPEVAL);
11437
11438 }
11439 return exitstatus;
11440}
11441
11442/*
11443 * Read and execute commands. "Top" is nonzero for the top level command
11444 * loop; it turns on prompting if the shell is interactive.
11445 */
11446static int
11447cmdloop(int top)
11448{
11449 union node *n;
11450 struct stackmark smark;
11451 int inter;
11452 int numeof = 0;
11453
11454 TRACE(("cmdloop(%d) called\n", top));
11455 for (;;) {
11456 int skip;
11457
11458 setstackmark(&smark);
11459#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011460 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011461 showjobs(stderr, SHOW_CHANGED);
11462#endif
11463 inter = 0;
11464 if (iflag && top) {
11465 inter++;
11466#if ENABLE_ASH_MAIL
11467 chkmail();
11468#endif
11469 }
11470 n = parsecmd(inter);
11471 /* showtree(n); DEBUG */
11472 if (n == NEOF) {
11473 if (!top || numeof >= 50)
11474 break;
11475 if (!stoppedjobs()) {
11476 if (!Iflag)
11477 break;
11478 out2str("\nUse \"exit\" to leave shell.\n");
11479 }
11480 numeof++;
11481 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011482 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11483 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011484 numeof = 0;
11485 evaltree(n, 0);
11486 }
11487 popstackmark(&smark);
11488 skip = evalskip;
11489
11490 if (skip) {
11491 evalskip = 0;
11492 return skip & SKIPEVAL;
11493 }
11494 }
11495 return 0;
11496}
11497
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011498/*
11499 * Take commands from a file. To be compatible we should do a path
11500 * search for the file, which is necessary to find sub-commands.
11501 */
11502static char *
11503find_dot_file(char *name)
11504{
11505 char *fullname;
11506 const char *path = pathval();
11507 struct stat statb;
11508
11509 /* don't try this for absolute or relative paths */
11510 if (strchr(name, '/'))
11511 return name;
11512
11513 while ((fullname = padvance(&path, name)) != NULL) {
11514 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11515 /*
11516 * Don't bother freeing here, since it will
11517 * be freed by the caller.
11518 */
11519 return fullname;
11520 }
11521 stunalloc(fullname);
11522 }
11523
11524 /* not found in the PATH */
11525 ash_msg_and_raise_error("%s: not found", name);
11526 /* NOTREACHED */
11527}
11528
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011529static int
11530dotcmd(int argc, char **argv)
11531{
11532 struct strlist *sp;
11533 volatile struct shparam saveparam;
11534 int status = 0;
11535
11536 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011537 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011538
Denis Vlasenko68404f12008-03-17 09:00:54 +000011539 if (argv[1]) { /* That's what SVR2 does */
11540 char *fullname = find_dot_file(argv[1]);
11541 argv += 2;
11542 argc -= 2;
11543 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011544 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011545 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011546 shellparam.nparam = argc;
11547 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011548 };
11549
11550 setinputfile(fullname, INPUT_PUSH_FILE);
11551 commandname = fullname;
11552 cmdloop(0);
11553 popfile();
11554
Denis Vlasenko68404f12008-03-17 09:00:54 +000011555 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011556 freeparam(&shellparam);
11557 shellparam = saveparam;
11558 };
11559 status = exitstatus;
11560 }
11561 return status;
11562}
11563
11564static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011565exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011566{
11567 if (stoppedjobs())
11568 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011569 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011570 exitstatus = number(argv[1]);
11571 raise_exception(EXEXIT);
11572 /* NOTREACHED */
11573}
11574
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011575/*
11576 * Read a file containing shell functions.
11577 */
11578static void
11579readcmdfile(char *name)
11580{
11581 setinputfile(name, INPUT_PUSH_FILE);
11582 cmdloop(0);
11583 popfile();
11584}
11585
11586
Denis Vlasenkocc571512007-02-23 21:10:35 +000011587/* ============ find_command inplementation */
11588
11589/*
11590 * Resolve a command name. If you change this routine, you may have to
11591 * change the shellexec routine as well.
11592 */
11593static void
11594find_command(char *name, struct cmdentry *entry, int act, const char *path)
11595{
11596 struct tblentry *cmdp;
11597 int idx;
11598 int prev;
11599 char *fullname;
11600 struct stat statb;
11601 int e;
11602 int updatetbl;
11603 struct builtincmd *bcmd;
11604
11605 /* If name contains a slash, don't use PATH or hash table */
11606 if (strchr(name, '/') != NULL) {
11607 entry->u.index = -1;
11608 if (act & DO_ABS) {
11609 while (stat(name, &statb) < 0) {
11610#ifdef SYSV
11611 if (errno == EINTR)
11612 continue;
11613#endif
11614 entry->cmdtype = CMDUNKNOWN;
11615 return;
11616 }
11617 }
11618 entry->cmdtype = CMDNORMAL;
11619 return;
11620 }
11621
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011622/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011623
11624 updatetbl = (path == pathval());
11625 if (!updatetbl) {
11626 act |= DO_ALTPATH;
11627 if (strstr(path, "%builtin") != NULL)
11628 act |= DO_ALTBLTIN;
11629 }
11630
11631 /* If name is in the table, check answer will be ok */
11632 cmdp = cmdlookup(name, 0);
11633 if (cmdp != NULL) {
11634 int bit;
11635
11636 switch (cmdp->cmdtype) {
11637 default:
11638#if DEBUG
11639 abort();
11640#endif
11641 case CMDNORMAL:
11642 bit = DO_ALTPATH;
11643 break;
11644 case CMDFUNCTION:
11645 bit = DO_NOFUNC;
11646 break;
11647 case CMDBUILTIN:
11648 bit = DO_ALTBLTIN;
11649 break;
11650 }
11651 if (act & bit) {
11652 updatetbl = 0;
11653 cmdp = NULL;
11654 } else if (cmdp->rehash == 0)
11655 /* if not invalidated by cd, we're done */
11656 goto success;
11657 }
11658
11659 /* If %builtin not in path, check for builtin next */
11660 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011661 if (bcmd) {
11662 if (IS_BUILTIN_REGULAR(bcmd))
11663 goto builtin_success;
11664 if (act & DO_ALTPATH) {
11665 if (!(act & DO_ALTBLTIN))
11666 goto builtin_success;
11667 } else if (builtinloc <= 0) {
11668 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011669 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011670 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011671
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011672#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011673 {
11674 int applet_no = find_applet_by_name(name);
11675 if (applet_no >= 0) {
11676 entry->cmdtype = CMDNORMAL;
11677 entry->u.index = -2 - applet_no;
11678 return;
11679 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011680 }
11681#endif
11682
Denis Vlasenkocc571512007-02-23 21:10:35 +000011683 /* We have to search path. */
11684 prev = -1; /* where to start */
11685 if (cmdp && cmdp->rehash) { /* doing a rehash */
11686 if (cmdp->cmdtype == CMDBUILTIN)
11687 prev = builtinloc;
11688 else
11689 prev = cmdp->param.index;
11690 }
11691
11692 e = ENOENT;
11693 idx = -1;
11694 loop:
11695 while ((fullname = padvance(&path, name)) != NULL) {
11696 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011697 /* NB: code below will still use fullname
11698 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011699 idx++;
11700 if (pathopt) {
11701 if (prefix(pathopt, "builtin")) {
11702 if (bcmd)
11703 goto builtin_success;
11704 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011705 }
11706 if ((act & DO_NOFUNC)
11707 || !prefix(pathopt, "func")
11708 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011709 continue;
11710 }
11711 }
11712 /* if rehash, don't redo absolute path names */
11713 if (fullname[0] == '/' && idx <= prev) {
11714 if (idx < prev)
11715 continue;
11716 TRACE(("searchexec \"%s\": no change\n", name));
11717 goto success;
11718 }
11719 while (stat(fullname, &statb) < 0) {
11720#ifdef SYSV
11721 if (errno == EINTR)
11722 continue;
11723#endif
11724 if (errno != ENOENT && errno != ENOTDIR)
11725 e = errno;
11726 goto loop;
11727 }
11728 e = EACCES; /* if we fail, this will be the error */
11729 if (!S_ISREG(statb.st_mode))
11730 continue;
11731 if (pathopt) { /* this is a %func directory */
11732 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011733 /* NB: stalloc will return space pointed by fullname
11734 * (because we don't have any intervening allocations
11735 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011736 readcmdfile(fullname);
11737 cmdp = cmdlookup(name, 0);
11738 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11739 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11740 stunalloc(fullname);
11741 goto success;
11742 }
11743 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11744 if (!updatetbl) {
11745 entry->cmdtype = CMDNORMAL;
11746 entry->u.index = idx;
11747 return;
11748 }
11749 INT_OFF;
11750 cmdp = cmdlookup(name, 1);
11751 cmdp->cmdtype = CMDNORMAL;
11752 cmdp->param.index = idx;
11753 INT_ON;
11754 goto success;
11755 }
11756
11757 /* We failed. If there was an entry for this command, delete it */
11758 if (cmdp && updatetbl)
11759 delete_cmd_entry();
11760 if (act & DO_ERR)
11761 ash_msg("%s: %s", name, errmsg(e, "not found"));
11762 entry->cmdtype = CMDUNKNOWN;
11763 return;
11764
11765 builtin_success:
11766 if (!updatetbl) {
11767 entry->cmdtype = CMDBUILTIN;
11768 entry->u.cmd = bcmd;
11769 return;
11770 }
11771 INT_OFF;
11772 cmdp = cmdlookup(name, 1);
11773 cmdp->cmdtype = CMDBUILTIN;
11774 cmdp->param.cmd = bcmd;
11775 INT_ON;
11776 success:
11777 cmdp->rehash = 0;
11778 entry->cmdtype = cmdp->cmdtype;
11779 entry->u = cmdp->param;
11780}
11781
11782
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011783/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011784
Eric Andersencb57d552001-06-28 07:25:16 +000011785/*
Eric Andersencb57d552001-06-28 07:25:16 +000011786 * The trap builtin.
11787 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011788static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011789trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011790{
11791 char *action;
11792 char **ap;
11793 int signo;
11794
Eric Andersenc470f442003-07-28 09:56:35 +000011795 nextopt(nullstr);
11796 ap = argptr;
11797 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011798 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011799 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011800 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011801
Rob Landleyc9c1a412006-07-12 19:17:55 +000011802 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011803 out1fmt("trap -- %s %s\n",
11804 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011805 }
11806 }
11807 return 0;
11808 }
Eric Andersenc470f442003-07-28 09:56:35 +000011809 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011810 action = NULL;
11811 else
11812 action = *ap++;
11813 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011814 signo = get_signum(*ap);
11815 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011816 ash_msg_and_raise_error("%s: bad trap", *ap);
11817 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011818 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011819 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011820 action = NULL;
11821 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011822 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011823 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011824 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011825 trap[signo] = action;
11826 if (signo != 0)
11827 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011828 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011829 ap++;
11830 }
11831 return 0;
11832}
11833
Eric Andersenc470f442003-07-28 09:56:35 +000011834
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011835/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011836
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011837#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011838/*
11839 * Lists available builtins
11840 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011841static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011842helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011843{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011844 unsigned col;
11845 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011846
11847 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011848 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011849 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011850 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011851 if (col > 60) {
11852 out1fmt("\n");
11853 col = 0;
11854 }
11855 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011856#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011857 {
11858 const char *a = applet_names;
11859 while (*a) {
11860 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11861 if (col > 60) {
11862 out1fmt("\n");
11863 col = 0;
11864 }
11865 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011866 }
11867 }
11868#endif
11869 out1fmt("\n\n");
11870 return EXIT_SUCCESS;
11871}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011872#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011873
Eric Andersencb57d552001-06-28 07:25:16 +000011874/*
Eric Andersencb57d552001-06-28 07:25:16 +000011875 * The export and readonly commands.
11876 */
Eric Andersenc470f442003-07-28 09:56:35 +000011877static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011878exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011879{
11880 struct var *vp;
11881 char *name;
11882 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011883 char **aptr;
11884 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011885
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011886 if (nextopt("p") != 'p') {
11887 aptr = argptr;
11888 name = *aptr;
11889 if (name) {
11890 do {
11891 p = strchr(name, '=');
11892 if (p != NULL) {
11893 p++;
11894 } else {
11895 vp = *findvar(hashvar(name), name);
11896 if (vp) {
11897 vp->flags |= flag;
11898 continue;
11899 }
Eric Andersencb57d552001-06-28 07:25:16 +000011900 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011901 setvar(name, p, flag);
11902 } while ((name = *++aptr) != NULL);
11903 return 0;
11904 }
Eric Andersencb57d552001-06-28 07:25:16 +000011905 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011906 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011907 return 0;
11908}
11909
Eric Andersencb57d552001-06-28 07:25:16 +000011910/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011911 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011912 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011913static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011914unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011915{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011916 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011917
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011918 cmdp = cmdlookup(name, 0);
11919 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11920 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011921}
11922
Eric Andersencb57d552001-06-28 07:25:16 +000011923/*
Eric Andersencb57d552001-06-28 07:25:16 +000011924 * The unset builtin command. We unset the function before we unset the
11925 * variable to allow a function to be unset when there is a readonly variable
11926 * with the same name.
11927 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011928static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011929unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011930{
11931 char **ap;
11932 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011933 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011934 int ret = 0;
11935
11936 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011937 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011938 }
Eric Andersencb57d552001-06-28 07:25:16 +000011939
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011940 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011941 if (flag != 'f') {
11942 i = unsetvar(*ap);
11943 ret |= i;
11944 if (!(i & 2))
11945 continue;
11946 }
11947 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011948 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011949 }
Eric Andersenc470f442003-07-28 09:56:35 +000011950 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011951}
11952
11953
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011954/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011955
Eric Andersenc470f442003-07-28 09:56:35 +000011956#include <sys/times.h>
11957
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011958static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011959 ' ', offsetof(struct tms, tms_utime),
11960 '\n', offsetof(struct tms, tms_stime),
11961 ' ', offsetof(struct tms, tms_cutime),
11962 '\n', offsetof(struct tms, tms_cstime),
11963 0
11964};
Eric Andersencb57d552001-06-28 07:25:16 +000011965
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011966static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011967timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011968{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011969 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011970 const unsigned char *p;
11971 struct tms buf;
11972
11973 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011974 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011975
11976 p = timescmd_str;
11977 do {
11978 t = *(clock_t *)(((char *) &buf) + p[1]);
11979 s = t / clk_tck;
11980 out1fmt("%ldm%ld.%.3lds%c",
11981 s/60, s%60,
11982 ((t - s * clk_tck) * 1000) / clk_tck,
11983 p[0]);
11984 } while (*(p += 2));
11985
Eric Andersencb57d552001-06-28 07:25:16 +000011986 return 0;
11987}
11988
Denis Vlasenko131ae172007-02-18 13:00:19 +000011989#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011990static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011991dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011992{
Eric Andersened9ecf72004-06-22 08:29:45 +000011993 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011994 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011995
Denis Vlasenkob012b102007-02-19 22:43:01 +000011996 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011997 result = arith(s, &errcode);
11998 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011999 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012000 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012001 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012002 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012003 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012004 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012005 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012006 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012007 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012008
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012009 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012010}
Eric Andersenc470f442003-07-28 09:56:35 +000012011
Eric Andersenc470f442003-07-28 09:56:35 +000012012/*
Eric Andersen90898442003-08-06 11:20:52 +000012013 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12014 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12015 *
12016 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012017 */
12018static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012019letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012020{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012021 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012022
Denis Vlasenko68404f12008-03-17 09:00:54 +000012023 argv++;
12024 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012025 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012026 do {
12027 i = dash_arith(*argv);
12028 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012029
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012030 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012031}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012032#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012033
Eric Andersenc470f442003-07-28 09:56:35 +000012034
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012035/* ============ miscbltin.c
12036 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012037 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012038 */
12039
12040#undef rflag
12041
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012042#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012043typedef enum __rlimit_resource rlim_t;
12044#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012045
Eric Andersenc470f442003-07-28 09:56:35 +000012046/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012047 * The read builtin. Options:
12048 * -r Do not interpret '\' specially
12049 * -s Turn off echo (tty only)
12050 * -n NCHARS Read NCHARS max
12051 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12052 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12053 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012054 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012055 * TODO: bash also has:
12056 * -a ARRAY Read into array[0],[1],etc
12057 * -d DELIM End on DELIM char, not newline
12058 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012059 */
Eric Andersenc470f442003-07-28 09:56:35 +000012060static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012061readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012062{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012063 static const char *const arg_REPLY[] = { "REPLY", NULL };
12064
Eric Andersenc470f442003-07-28 09:56:35 +000012065 char **ap;
12066 int backslash;
12067 char c;
12068 int rflag;
12069 char *prompt;
12070 const char *ifs;
12071 char *p;
12072 int startword;
12073 int status;
12074 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012075 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012076#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012077 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012078 int silent = 0;
12079 struct termios tty, old_tty;
12080#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012081#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012082 unsigned end_ms = 0;
12083 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012084#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012085
12086 rflag = 0;
12087 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012088 while ((i = nextopt("p:u:r"
12089 USE_ASH_READ_TIMEOUT("t:")
12090 USE_ASH_READ_NCHARS("n:s")
12091 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012092 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012093 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012094 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012095 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012096#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012097 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012098 nchars = bb_strtou(optionarg, NULL, 10);
12099 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012100 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012101 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012102 break;
12103 case 's':
12104 silent = 1;
12105 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012106#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012107#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012108 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012109 timeout = bb_strtou(optionarg, NULL, 10);
12110 if (errno || timeout > UINT_MAX / 2048)
12111 ash_msg_and_raise_error("invalid timeout");
12112 timeout *= 1000;
12113#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012114 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012115 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012116 /* EINVAL means number is ok, but not terminated by NUL */
12117 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012118 char *p2;
12119 if (*++p) {
12120 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012121 ts.tv_usec = bb_strtou(p, &p2, 10);
12122 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012123 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012124 scale = p2 - p;
12125 /* normalize to usec */
12126 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012127 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012128 while (scale++ < 6)
12129 ts.tv_usec *= 10;
12130 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012131 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012132 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012133 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012134 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012135 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012136 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012137#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012138 break;
12139#endif
12140 case 'r':
12141 rflag = 1;
12142 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012143 case 'u':
12144 fd = bb_strtou(optionarg, NULL, 10);
12145 if (fd < 0 || errno)
12146 ash_msg_and_raise_error("invalid file descriptor");
12147 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012148 default:
12149 break;
12150 }
Eric Andersenc470f442003-07-28 09:56:35 +000012151 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012152 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012153 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012154 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012155 ap = argptr;
12156 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012157 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012158 ifs = bltinlookup("IFS");
12159 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012160 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012161#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012162 tcgetattr(fd, &tty);
12163 old_tty = tty;
12164 if (nchars || silent) {
12165 if (nchars) {
12166 tty.c_lflag &= ~ICANON;
12167 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012168 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012169 if (silent) {
12170 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12171 }
12172 /* if tcgetattr failed, tcsetattr will fail too.
12173 * Ignoring, it's harmless. */
12174 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012175 }
12176#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012177
Eric Andersenc470f442003-07-28 09:56:35 +000012178 status = 0;
12179 startword = 1;
12180 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012181#if ENABLE_ASH_READ_TIMEOUT
12182 if (timeout) /* NB: ensuring end_ms is nonzero */
12183 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12184#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012185 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012186 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012187#if ENABLE_ASH_READ_TIMEOUT
12188 if (end_ms) {
12189 struct pollfd pfd[1];
12190 pfd[0].fd = fd;
12191 pfd[0].events = POLLIN;
12192 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12193 if ((int)timeout <= 0 /* already late? */
12194 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12195 ) { /* timed out! */
12196#if ENABLE_ASH_READ_NCHARS
12197 tcsetattr(fd, TCSANOW, &old_tty);
12198#endif
12199 return 1;
12200 }
12201 }
12202#endif
12203 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012204 status = 1;
12205 break;
12206 }
12207 if (c == '\0')
12208 continue;
12209 if (backslash) {
12210 backslash = 0;
12211 if (c != '\n')
12212 goto put;
12213 continue;
12214 }
12215 if (!rflag && c == '\\') {
12216 backslash++;
12217 continue;
12218 }
12219 if (c == '\n')
12220 break;
12221 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12222 continue;
12223 }
12224 startword = 0;
12225 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12226 STACKSTRNUL(p);
12227 setvar(*ap, stackblock(), 0);
12228 ap++;
12229 startword = 1;
12230 STARTSTACKSTR(p);
12231 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012232 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012233 STPUTC(c, p);
12234 }
12235 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012236/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012237#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012238 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012239#else
12240 while (1);
12241#endif
12242
12243#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012244 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012245#endif
12246
Eric Andersenc470f442003-07-28 09:56:35 +000012247 STACKSTRNUL(p);
12248 /* Remove trailing blanks */
12249 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12250 *p = '\0';
12251 setvar(*ap, stackblock(), 0);
12252 while (*++ap != NULL)
12253 setvar(*ap, nullstr, 0);
12254 return status;
12255}
12256
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012257static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012258umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012259{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012260 static const char permuser[3] ALIGN1 = "ugo";
12261 static const char permmode[3] ALIGN1 = "rwx";
12262 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012263 S_IRUSR, S_IWUSR, S_IXUSR,
12264 S_IRGRP, S_IWGRP, S_IXGRP,
12265 S_IROTH, S_IWOTH, S_IXOTH
12266 };
12267
12268 char *ap;
12269 mode_t mask;
12270 int i;
12271 int symbolic_mode = 0;
12272
12273 while (nextopt("S") != '\0') {
12274 symbolic_mode = 1;
12275 }
12276
Denis Vlasenkob012b102007-02-19 22:43:01 +000012277 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012278 mask = umask(0);
12279 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012280 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012281
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012282 ap = *argptr;
12283 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012284 if (symbolic_mode) {
12285 char buf[18];
12286 char *p = buf;
12287
12288 for (i = 0; i < 3; i++) {
12289 int j;
12290
12291 *p++ = permuser[i];
12292 *p++ = '=';
12293 for (j = 0; j < 3; j++) {
12294 if ((mask & permmask[3 * i + j]) == 0) {
12295 *p++ = permmode[j];
12296 }
12297 }
12298 *p++ = ',';
12299 }
12300 *--p = 0;
12301 puts(buf);
12302 } else {
12303 out1fmt("%.4o\n", mask);
12304 }
12305 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012306 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012307 mask = 0;
12308 do {
12309 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012310 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012311 mask = (mask << 3) + (*ap - '0');
12312 } while (*++ap != '\0');
12313 umask(mask);
12314 } else {
12315 mask = ~mask & 0777;
12316 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012317 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012318 }
12319 umask(~mask & 0777);
12320 }
12321 }
12322 return 0;
12323}
12324
12325/*
12326 * ulimit builtin
12327 *
12328 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12329 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12330 * ash by J.T. Conklin.
12331 *
12332 * Public domain.
12333 */
12334
12335struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012336 uint8_t cmd; /* RLIMIT_xxx fit into it */
12337 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012338 char option;
12339};
12340
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012341static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012342#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012343 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012344#endif
12345#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012346 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012347#endif
12348#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012349 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012350#endif
12351#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012352 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012353#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012354#ifdef RLIMIT_CORE
12355 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012356#endif
12357#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012358 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012359#endif
12360#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012361 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012362#endif
12363#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012364 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012365#endif
12366#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012367 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012368#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012369#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012370 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012371#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012372#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012373 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012374#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012375};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012376static const char limits_name[] =
12377#ifdef RLIMIT_CPU
12378 "time(seconds)" "\0"
12379#endif
12380#ifdef RLIMIT_FSIZE
12381 "file(blocks)" "\0"
12382#endif
12383#ifdef RLIMIT_DATA
12384 "data(kb)" "\0"
12385#endif
12386#ifdef RLIMIT_STACK
12387 "stack(kb)" "\0"
12388#endif
12389#ifdef RLIMIT_CORE
12390 "coredump(blocks)" "\0"
12391#endif
12392#ifdef RLIMIT_RSS
12393 "memory(kb)" "\0"
12394#endif
12395#ifdef RLIMIT_MEMLOCK
12396 "locked memory(kb)" "\0"
12397#endif
12398#ifdef RLIMIT_NPROC
12399 "process" "\0"
12400#endif
12401#ifdef RLIMIT_NOFILE
12402 "nofiles" "\0"
12403#endif
12404#ifdef RLIMIT_AS
12405 "vmemory(kb)" "\0"
12406#endif
12407#ifdef RLIMIT_LOCKS
12408 "locks" "\0"
12409#endif
12410;
Eric Andersenc470f442003-07-28 09:56:35 +000012411
Glenn L McGrath76620622004-01-13 10:19:37 +000012412enum limtype { SOFT = 0x1, HARD = 0x2 };
12413
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012414static void
12415printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012416 const struct limits *l)
12417{
12418 rlim_t val;
12419
12420 val = limit->rlim_max;
12421 if (how & SOFT)
12422 val = limit->rlim_cur;
12423
12424 if (val == RLIM_INFINITY)
12425 out1fmt("unlimited\n");
12426 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012427 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012428 out1fmt("%lld\n", (long long) val);
12429 }
12430}
12431
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012432static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012433ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012434{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012435 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012436 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012437 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012438 const struct limits *l;
12439 int set, all = 0;
12440 int optc, what;
12441 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012442
12443 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012444 while ((optc = nextopt("HSa"
12445#ifdef RLIMIT_CPU
12446 "t"
12447#endif
12448#ifdef RLIMIT_FSIZE
12449 "f"
12450#endif
12451#ifdef RLIMIT_DATA
12452 "d"
12453#endif
12454#ifdef RLIMIT_STACK
12455 "s"
12456#endif
12457#ifdef RLIMIT_CORE
12458 "c"
12459#endif
12460#ifdef RLIMIT_RSS
12461 "m"
12462#endif
12463#ifdef RLIMIT_MEMLOCK
12464 "l"
12465#endif
12466#ifdef RLIMIT_NPROC
12467 "p"
12468#endif
12469#ifdef RLIMIT_NOFILE
12470 "n"
12471#endif
12472#ifdef RLIMIT_AS
12473 "v"
12474#endif
12475#ifdef RLIMIT_LOCKS
12476 "w"
12477#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012478 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012479 switch (optc) {
12480 case 'H':
12481 how = HARD;
12482 break;
12483 case 'S':
12484 how = SOFT;
12485 break;
12486 case 'a':
12487 all = 1;
12488 break;
12489 default:
12490 what = optc;
12491 }
12492
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012493 for (l = limits_tbl; l->option != what; l++)
12494 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012495
12496 set = *argptr ? 1 : 0;
12497 if (set) {
12498 char *p = *argptr;
12499
12500 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012501 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012502 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012503 val = RLIM_INFINITY;
12504 else {
12505 val = (rlim_t) 0;
12506
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012507 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012508 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012509 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012510 if (val < (rlim_t) 0)
12511 break;
12512 }
12513 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012514 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012515 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012516 }
12517 }
12518 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012519 const char *lname = limits_name;
12520 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012521 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012522 out1fmt("%-20s ", lname);
12523 lname += strlen(lname) + 1;
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
12529 getrlimit(l->cmd, &limit);
12530 if (set) {
12531 if (how & HARD)
12532 limit.rlim_max = val;
12533 if (how & SOFT)
12534 limit.rlim_cur = val;
12535 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012536 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012537 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012538 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012539 }
12540 return 0;
12541}
12542
Eric Andersen90898442003-08-06 11:20:52 +000012543
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012544/* ============ Math support */
12545
Denis Vlasenko131ae172007-02-18 13:00:19 +000012546#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012547
12548/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12549
12550 Permission is hereby granted, free of charge, to any person obtaining
12551 a copy of this software and associated documentation files (the
12552 "Software"), to deal in the Software without restriction, including
12553 without limitation the rights to use, copy, modify, merge, publish,
12554 distribute, sublicense, and/or sell copies of the Software, and to
12555 permit persons to whom the Software is furnished to do so, subject to
12556 the following conditions:
12557
12558 The above copyright notice and this permission notice shall be
12559 included in all copies or substantial portions of the Software.
12560
12561 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12562 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12563 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12564 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12565 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12566 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12567 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12568*/
12569
12570/* This is my infix parser/evaluator. It is optimized for size, intended
12571 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012572 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012573 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012574 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012575 * be that which POSIX specifies for shells. */
12576
12577/* The code uses a simple two-stack algorithm. See
12578 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012579 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012580 * this is based (this code differs in that it applies operators immediately
12581 * to the stack instead of adding them to a queue to end up with an
12582 * expression). */
12583
12584/* To use the routine, call it with an expression string and error return
12585 * pointer */
12586
12587/*
12588 * Aug 24, 2001 Manuel Novoa III
12589 *
12590 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12591 *
12592 * 1) In arith_apply():
12593 * a) Cached values of *numptr and &(numptr[-1]).
12594 * b) Removed redundant test for zero denominator.
12595 *
12596 * 2) In arith():
12597 * a) Eliminated redundant code for processing operator tokens by moving
12598 * to a table-based implementation. Also folded handling of parens
12599 * into the table.
12600 * b) Combined all 3 loops which called arith_apply to reduce generated
12601 * code size at the cost of speed.
12602 *
12603 * 3) The following expressions were treated as valid by the original code:
12604 * 1() , 0! , 1 ( *3 ) .
12605 * These bugs have been fixed by internally enclosing the expression in
12606 * parens and then checking that all binary ops and right parens are
12607 * preceded by a valid expression (NUM_TOKEN).
12608 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012609 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012610 * ctype's isspace() if it is used by another busybox applet or if additional
12611 * whitespace chars should be considered. Look below the "#include"s for a
12612 * precompiler test.
12613 */
12614
12615/*
12616 * Aug 26, 2001 Manuel Novoa III
12617 *
12618 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12619 *
12620 * Merge in Aaron's comments previously posted to the busybox list,
12621 * modified slightly to take account of my changes to the code.
12622 *
12623 */
12624
12625/*
12626 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12627 *
12628 * - allow access to variable,
12629 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12630 * - realize assign syntax (VAR=expr, +=, *= etc)
12631 * - realize exponentiation (** operator)
12632 * - realize comma separated - expr, expr
12633 * - realise ++expr --expr expr++ expr--
12634 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012635 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012636 * - was restored loses XOR operator
12637 * - remove one goto label, added three ;-)
12638 * - protect $((num num)) as true zero expr (Manuel`s error)
12639 * - always use special isspace(), see comment from bash ;-)
12640 */
12641
Eric Andersen90898442003-08-06 11:20:52 +000012642#define arith_isspace(arithval) \
12643 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12644
Eric Andersen90898442003-08-06 11:20:52 +000012645typedef unsigned char operator;
12646
12647/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012648 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012649 * precedence. The ID portion is so that multiple operators can have the
12650 * same precedence, ensuring that the leftmost one is evaluated first.
12651 * Consider * and /. */
12652
12653#define tok_decl(prec,id) (((id)<<5)|(prec))
12654#define PREC(op) ((op) & 0x1F)
12655
12656#define TOK_LPAREN tok_decl(0,0)
12657
12658#define TOK_COMMA tok_decl(1,0)
12659
12660#define TOK_ASSIGN tok_decl(2,0)
12661#define TOK_AND_ASSIGN tok_decl(2,1)
12662#define TOK_OR_ASSIGN tok_decl(2,2)
12663#define TOK_XOR_ASSIGN tok_decl(2,3)
12664#define TOK_PLUS_ASSIGN tok_decl(2,4)
12665#define TOK_MINUS_ASSIGN tok_decl(2,5)
12666#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12667#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12668
12669#define TOK_MUL_ASSIGN tok_decl(3,0)
12670#define TOK_DIV_ASSIGN tok_decl(3,1)
12671#define TOK_REM_ASSIGN tok_decl(3,2)
12672
12673/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012674#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012675
12676/* conditional is right associativity too */
12677#define TOK_CONDITIONAL tok_decl(4,0)
12678#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12679
12680#define TOK_OR tok_decl(5,0)
12681
12682#define TOK_AND tok_decl(6,0)
12683
12684#define TOK_BOR tok_decl(7,0)
12685
12686#define TOK_BXOR tok_decl(8,0)
12687
12688#define TOK_BAND tok_decl(9,0)
12689
12690#define TOK_EQ tok_decl(10,0)
12691#define TOK_NE tok_decl(10,1)
12692
12693#define TOK_LT tok_decl(11,0)
12694#define TOK_GT tok_decl(11,1)
12695#define TOK_GE tok_decl(11,2)
12696#define TOK_LE tok_decl(11,3)
12697
12698#define TOK_LSHIFT tok_decl(12,0)
12699#define TOK_RSHIFT tok_decl(12,1)
12700
12701#define TOK_ADD tok_decl(13,0)
12702#define TOK_SUB tok_decl(13,1)
12703
12704#define TOK_MUL tok_decl(14,0)
12705#define TOK_DIV tok_decl(14,1)
12706#define TOK_REM tok_decl(14,2)
12707
12708/* exponent is right associativity */
12709#define TOK_EXPONENT tok_decl(15,1)
12710
12711/* For now unary operators. */
12712#define UNARYPREC 16
12713#define TOK_BNOT tok_decl(UNARYPREC,0)
12714#define TOK_NOT tok_decl(UNARYPREC,1)
12715
12716#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12717#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12718
12719#define PREC_PRE (UNARYPREC+2)
12720
12721#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12722#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12723
12724#define PREC_POST (UNARYPREC+3)
12725
12726#define TOK_POST_INC tok_decl(PREC_POST, 0)
12727#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12728
12729#define SPEC_PREC (UNARYPREC+4)
12730
12731#define TOK_NUM tok_decl(SPEC_PREC, 0)
12732#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12733
12734#define NUMPTR (*numstackptr)
12735
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012736static int
12737tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012738{
12739 operator prec = PREC(op);
12740
12741 convert_prec_is_assing(prec);
12742 return (prec == PREC(TOK_ASSIGN) ||
12743 prec == PREC_PRE || prec == PREC_POST);
12744}
12745
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012746static int
12747is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012748{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012749 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12750 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012751}
12752
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012753typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012754 arith_t val;
12755 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012756 char contidional_second_val_initialized;
12757 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012758 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012759} v_n_t;
12760
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012761typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012762 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012763 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012764} chk_var_recursive_looped_t;
12765
12766static chk_var_recursive_looped_t *prev_chk_var_recursive;
12767
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012768static int
12769arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012770{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012771 if (t->var) {
12772 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012773
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012774 if (p) {
12775 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012776
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012777 /* recursive try as expression */
12778 chk_var_recursive_looped_t *cur;
12779 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012780
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012781 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12782 if (strcmp(cur->var, t->var) == 0) {
12783 /* expression recursion loop detected */
12784 return -5;
12785 }
12786 }
12787 /* save current lookuped var name */
12788 cur = prev_chk_var_recursive;
12789 cur_save.var = t->var;
12790 cur_save.next = cur;
12791 prev_chk_var_recursive = &cur_save;
12792
12793 t->val = arith (p, &errcode);
12794 /* restore previous ptr after recursiving */
12795 prev_chk_var_recursive = cur;
12796 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012797 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012798 /* allow undefined var as 0 */
12799 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012800 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012801 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012802}
12803
12804/* "applying" a token means performing it on the top elements on the integer
12805 * stack. For a unary operator it will only change the top element, but a
12806 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012807static int
12808arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012809{
Eric Andersen90898442003-08-06 11:20:52 +000012810 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012811 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012812 int ret_arith_lookup_val;
12813
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012814 /* There is no operator that can work without arguments */
12815 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012816 numptr_m1 = NUMPTR - 1;
12817
12818 /* check operand is var with noninteger value */
12819 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012820 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012821 return ret_arith_lookup_val;
12822
12823 rez = numptr_m1->val;
12824 if (op == TOK_UMINUS)
12825 rez *= -1;
12826 else if (op == TOK_NOT)
12827 rez = !rez;
12828 else if (op == TOK_BNOT)
12829 rez = ~rez;
12830 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12831 rez++;
12832 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12833 rez--;
12834 else if (op != TOK_UPLUS) {
12835 /* Binary operators */
12836
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012837 /* check and binary operators need two arguments */
12838 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012839
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012840 /* ... and they pop one */
12841 --NUMPTR;
12842 numptr_val = rez;
12843 if (op == TOK_CONDITIONAL) {
12844 if (! numptr_m1->contidional_second_val_initialized) {
12845 /* protect $((expr1 ? expr2)) without ": expr" */
12846 goto err;
12847 }
12848 rez = numptr_m1->contidional_second_val;
12849 } else if (numptr_m1->contidional_second_val_initialized) {
12850 /* protect $((expr1 : expr2)) without "expr ? " */
12851 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012852 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012853 numptr_m1 = NUMPTR - 1;
12854 if (op != TOK_ASSIGN) {
12855 /* check operand is var with noninteger value for not '=' */
12856 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12857 if (ret_arith_lookup_val)
12858 return ret_arith_lookup_val;
12859 }
12860 if (op == TOK_CONDITIONAL) {
12861 numptr_m1->contidional_second_val = rez;
12862 }
12863 rez = numptr_m1->val;
12864 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012865 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012866 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012867 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012868 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012869 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012870 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012871 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012872 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012873 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012874 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012875 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012876 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012877 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012878 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012879 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012880 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012881 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012882 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012883 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012884 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012885 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012886 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012887 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012888 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012889 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012890 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012891 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012892 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012893 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012894 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012895 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012896 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012897 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012898 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012899 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012900 /* protect $((expr : expr)) without "expr ? " */
12901 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012902 }
12903 numptr_m1->contidional_second_val_initialized = op;
12904 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012905 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012906 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012908 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012909 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012910 return -3; /* exponent less than 0 */
12911 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012912 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012913
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012914 if (numptr_val)
12915 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012916 c *= rez;
12917 rez = c;
12918 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012919 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012920 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012921 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012922 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012923 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012924 rez %= numptr_val;
12925 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012926 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012927 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012928
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012929 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012930 /* Hmm, 1=2 ? */
12931 goto err;
12932 }
12933 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012934#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012935 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012936#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012937 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012938#endif
Eric Andersen90898442003-08-06 11:20:52 +000012939 setvar(numptr_m1->var, buf, 0);
12940 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012941 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012942 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012943 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012944 rez++;
12945 }
12946 numptr_m1->val = rez;
12947 /* protect geting var value, is number now */
12948 numptr_m1->var = NULL;
12949 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012950 err:
12951 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012952}
12953
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012954/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012955static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012956 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12957 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12958 '<','<', 0, TOK_LSHIFT,
12959 '>','>', 0, TOK_RSHIFT,
12960 '|','|', 0, TOK_OR,
12961 '&','&', 0, TOK_AND,
12962 '!','=', 0, TOK_NE,
12963 '<','=', 0, TOK_LE,
12964 '>','=', 0, TOK_GE,
12965 '=','=', 0, TOK_EQ,
12966 '|','=', 0, TOK_OR_ASSIGN,
12967 '&','=', 0, TOK_AND_ASSIGN,
12968 '*','=', 0, TOK_MUL_ASSIGN,
12969 '/','=', 0, TOK_DIV_ASSIGN,
12970 '%','=', 0, TOK_REM_ASSIGN,
12971 '+','=', 0, TOK_PLUS_ASSIGN,
12972 '-','=', 0, TOK_MINUS_ASSIGN,
12973 '-','-', 0, TOK_POST_DEC,
12974 '^','=', 0, TOK_XOR_ASSIGN,
12975 '+','+', 0, TOK_POST_INC,
12976 '*','*', 0, TOK_EXPONENT,
12977 '!', 0, TOK_NOT,
12978 '<', 0, TOK_LT,
12979 '>', 0, TOK_GT,
12980 '=', 0, TOK_ASSIGN,
12981 '|', 0, TOK_BOR,
12982 '&', 0, TOK_BAND,
12983 '*', 0, TOK_MUL,
12984 '/', 0, TOK_DIV,
12985 '%', 0, TOK_REM,
12986 '+', 0, TOK_ADD,
12987 '-', 0, TOK_SUB,
12988 '^', 0, TOK_BXOR,
12989 /* uniq */
12990 '~', 0, TOK_BNOT,
12991 ',', 0, TOK_COMMA,
12992 '?', 0, TOK_CONDITIONAL,
12993 ':', 0, TOK_CONDITIONAL_SEP,
12994 ')', 0, TOK_RPAREN,
12995 '(', 0, TOK_LPAREN,
12996 0
12997};
12998/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012999#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013000
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013001static arith_t
13002arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013003{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013004 char arithval; /* Current character under analysis */
13005 operator lasttok, op;
13006 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013007 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013008 const char *p = endexpression;
13009 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013010 v_n_t *numstack, *numstackptr;
13011 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013012
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013013 /* Stack of integers */
13014 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13015 * in any given correct or incorrect expression is left as an exercise to
13016 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013017 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013018 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013019 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013020
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013021 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13022 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013023
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013024 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013025 arithval = *expr;
13026 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 if (p == endexpression) {
13028 /* Null expression. */
13029 return 0;
13030 }
13031
13032 /* This is only reached after all tokens have been extracted from the
13033 * input stream. If there are still tokens on the operator stack, they
13034 * are to be applied in order. At the end, there should be a final
13035 * result on the integer stack */
13036
13037 if (expr != endexpression + 1) {
13038 /* If we haven't done so already, */
13039 /* append a closing right paren */
13040 expr = endexpression;
13041 /* and let the loop process it. */
13042 continue;
13043 }
13044 /* At this point, we're done with the expression. */
13045 if (numstackptr != numstack+1) {
13046 /* ... but if there isn't, it's bad */
13047 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013048 *perrcode = -1;
13049 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013050 }
13051 if (numstack->var) {
13052 /* expression is $((var)) only, lookup now */
13053 errcode = arith_lookup_val(numstack);
13054 }
13055 ret:
13056 *perrcode = errcode;
13057 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013058 }
13059
Eric Andersen90898442003-08-06 11:20:52 +000013060 /* Continue processing the expression. */
13061 if (arith_isspace(arithval)) {
13062 /* Skip whitespace */
13063 goto prologue;
13064 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013065 p = endofname(expr);
13066 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013067 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013068
13069 numstackptr->var = alloca(var_name_size);
13070 safe_strncpy(numstackptr->var, expr, var_name_size);
13071 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013072 num:
Eric Andersen90898442003-08-06 11:20:52 +000013073 numstackptr->contidional_second_val_initialized = 0;
13074 numstackptr++;
13075 lasttok = TOK_NUM;
13076 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013077 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013078 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013079 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013080#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013081 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013082#else
13083 numstackptr->val = strtol(expr, (char **) &expr, 0);
13084#endif
Eric Andersen90898442003-08-06 11:20:52 +000013085 goto num;
13086 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013087 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013088 const char *o;
13089
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013090 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013091 /* strange operator not found */
13092 goto err;
13093 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013095 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013097 /* found */
13098 expr = o - 1;
13099 break;
13100 }
13101 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013102 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013103 p++;
13104 /* skip zero delim */
13105 p++;
13106 }
13107 op = p[1];
13108
13109 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013110 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13111 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013112
13113 /* Plus and minus are binary (not unary) _only_ if the last
13114 * token was as number, or a right paren (which pretends to be
13115 * a number, since it evaluates to one). Think about it.
13116 * It makes sense. */
13117 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013118 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013119 case TOK_ADD:
13120 op = TOK_UPLUS;
13121 break;
13122 case TOK_SUB:
13123 op = TOK_UMINUS;
13124 break;
13125 case TOK_POST_INC:
13126 op = TOK_PRE_INC;
13127 break;
13128 case TOK_POST_DEC:
13129 op = TOK_PRE_DEC;
13130 break;
Eric Andersen90898442003-08-06 11:20:52 +000013131 }
13132 }
13133 /* We don't want a unary operator to cause recursive descent on the
13134 * stack, because there can be many in a row and it could cause an
13135 * operator to be evaluated before its argument is pushed onto the
13136 * integer stack. */
13137 /* But for binary operators, "apply" everything on the operator
13138 * stack until we find an operator with a lesser priority than the
13139 * one we have just extracted. */
13140 /* Left paren is given the lowest priority so it will never be
13141 * "applied" in this way.
13142 * if associativity is right and priority eq, applied also skip
13143 */
13144 prec = PREC(op);
13145 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13146 /* not left paren or unary */
13147 if (lasttok != TOK_NUM) {
13148 /* binary op must be preceded by a num */
13149 goto err;
13150 }
13151 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013152 if (op == TOK_RPAREN) {
13153 /* The algorithm employed here is simple: while we don't
13154 * hit an open paren nor the bottom of the stack, pop
13155 * tokens and apply them */
13156 if (stackptr[-1] == TOK_LPAREN) {
13157 --stackptr;
13158 /* Any operator directly after a */
13159 lasttok = TOK_NUM;
13160 /* close paren should consider itself binary */
13161 goto prologue;
13162 }
13163 } else {
13164 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013165
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013166 convert_prec_is_assing(prec);
13167 convert_prec_is_assing(prev_prec);
13168 if (prev_prec < prec)
13169 break;
13170 /* check right assoc */
13171 if (prev_prec == prec && is_right_associativity(prec))
13172 break;
13173 }
13174 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13175 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013176 }
13177 if (op == TOK_RPAREN) {
13178 goto err;
13179 }
13180 }
13181
13182 /* Push this operator to the stack and remember it. */
13183 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013184 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013185 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013186 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013187}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013188#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013189
13190
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013191/* ============ main() and helpers */
13192
13193/*
13194 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013195 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013196static void exitshell(void) ATTRIBUTE_NORETURN;
13197static void
13198exitshell(void)
13199{
13200 struct jmploc loc;
13201 char *p;
13202 int status;
13203
13204 status = exitstatus;
13205 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13206 if (setjmp(loc.loc)) {
13207 if (exception == EXEXIT)
13208/* dash bug: it just does _exit(exitstatus) here
13209 * but we have to do setjobctl(0) first!
13210 * (bug is still not fixed in dash-0.5.3 - if you run dash
13211 * under Midnight Commander, on exit from dash MC is backgrounded) */
13212 status = exitstatus;
13213 goto out;
13214 }
13215 exception_handler = &loc;
13216 p = trap[0];
13217 if (p) {
13218 trap[0] = NULL;
13219 evalstring(p, 0);
13220 }
13221 flush_stdout_stderr();
13222 out:
13223 setjobctl(0);
13224 _exit(status);
13225 /* NOTREACHED */
13226}
13227
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013228static void
13229init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013230{
13231 /* from input.c: */
13232 basepf.nextc = basepf.buf = basebuf;
13233
13234 /* from trap.c: */
13235 signal(SIGCHLD, SIG_DFL);
13236
13237 /* from var.c: */
13238 {
13239 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013240 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013241 const char *p;
13242 struct stat st1, st2;
13243
13244 initvar();
13245 for (envp = environ; envp && *envp; envp++) {
13246 if (strchr(*envp, '=')) {
13247 setvareq(*envp, VEXPORT|VTEXTFIXED);
13248 }
13249 }
13250
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013251 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013252 setvar("PPID", ppid, 0);
13253
13254 p = lookupvar("PWD");
13255 if (p)
13256 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13257 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13258 p = '\0';
13259 setpwd(p, 0);
13260 }
13261}
13262
13263/*
13264 * Process the shell command line arguments.
13265 */
13266static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013267procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013268{
13269 int i;
13270 const char *xminusc;
13271 char **xargv;
13272
13273 xargv = argv;
13274 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013275 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013276 xargv++;
13277 for (i = 0; i < NOPTS; i++)
13278 optlist[i] = 2;
13279 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013280 if (options(1)) {
13281 /* it already printed err message */
13282 raise_exception(EXERROR);
13283 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013284 xargv = argptr;
13285 xminusc = minusc;
13286 if (*xargv == NULL) {
13287 if (xminusc)
13288 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13289 sflag = 1;
13290 }
13291 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13292 iflag = 1;
13293 if (mflag == 2)
13294 mflag = iflag;
13295 for (i = 0; i < NOPTS; i++)
13296 if (optlist[i] == 2)
13297 optlist[i] = 0;
13298#if DEBUG == 2
13299 debug = 1;
13300#endif
13301 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13302 if (xminusc) {
13303 minusc = *xargv++;
13304 if (*xargv)
13305 goto setarg0;
13306 } else if (!sflag) {
13307 setinputfile(*xargv, 0);
13308 setarg0:
13309 arg0 = *xargv++;
13310 commandname = arg0;
13311 }
13312
13313 shellparam.p = xargv;
13314#if ENABLE_ASH_GETOPTS
13315 shellparam.optind = 1;
13316 shellparam.optoff = -1;
13317#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013318 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013319 while (*xargv) {
13320 shellparam.nparam++;
13321 xargv++;
13322 }
13323 optschanged();
13324}
13325
13326/*
13327 * Read /etc/profile or .profile.
13328 */
13329static void
13330read_profile(const char *name)
13331{
13332 int skip;
13333
13334 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13335 return;
13336 skip = cmdloop(0);
13337 popfile();
13338 if (skip)
13339 exitshell();
13340}
13341
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013342/*
13343 * This routine is called when an error or an interrupt occurs in an
13344 * interactive shell and control is returned to the main command loop.
13345 */
13346static void
13347reset(void)
13348{
13349 /* from eval.c: */
13350 evalskip = 0;
13351 loopnest = 0;
13352 /* from input.c: */
13353 parselleft = parsenleft = 0; /* clear input buffer */
13354 popallfiles();
13355 /* from parser.c: */
13356 tokpushback = 0;
13357 checkkwd = 0;
13358 /* from redir.c: */
13359 clearredir(0);
13360}
13361
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013362#if PROFILE
13363static short profile_buf[16384];
13364extern int etext();
13365#endif
13366
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013367/*
13368 * Main routine. We initialize things, parse the arguments, execute
13369 * profiles if we're a login shell, and then call cmdloop to execute
13370 * commands. The setjmp call sets up the location to jump to when an
13371 * exception occurs. When an exception occurs the variable "state"
13372 * is used to figure out how far we had gotten.
13373 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013374int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013375int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013376{
13377 char *shinit;
13378 volatile int state;
13379 struct jmploc jmploc;
13380 struct stackmark smark;
13381
Denis Vlasenko01631112007-12-16 17:20:38 +000013382 /* Initialize global data */
13383 INIT_G_misc();
13384 INIT_G_memstack();
13385 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013386#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013387 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013388#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013389 INIT_G_cmdtable();
13390
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013391#if PROFILE
13392 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13393#endif
13394
13395#if ENABLE_FEATURE_EDITING
13396 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13397#endif
13398 state = 0;
13399 if (setjmp(jmploc.loc)) {
13400 int e;
13401 int s;
13402
13403 reset();
13404
13405 e = exception;
13406 if (e == EXERROR)
13407 exitstatus = 2;
13408 s = state;
13409 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13410 exitshell();
13411
13412 if (e == EXINT) {
13413 outcslow('\n', stderr);
13414 }
13415 popstackmark(&smark);
13416 FORCE_INT_ON; /* enable interrupts */
13417 if (s == 1)
13418 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013419 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013420 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013421 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013422 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013423 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013424 }
13425 exception_handler = &jmploc;
13426#if DEBUG
13427 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013428 trace_puts("Shell args: ");
13429 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013430#endif
13431 rootpid = getpid();
13432
13433#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko448d30e2008-06-27 00:24:11 +000013434 random_galois_LFSR = random_LCG = rootpid + time(NULL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013435#endif
13436 init();
13437 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013438 procargs(argv);
13439
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013440#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13441 if (iflag) {
13442 const char *hp = lookupvar("HISTFILE");
13443
13444 if (hp == NULL) {
13445 hp = lookupvar("HOME");
13446 if (hp != NULL) {
13447 char *defhp = concat_path_file(hp, ".ash_history");
13448 setvar("HISTFILE", defhp, 0);
13449 free(defhp);
13450 }
13451 }
13452 }
13453#endif
13454 if (argv[0] && argv[0][0] == '-')
13455 isloginsh = 1;
13456 if (isloginsh) {
13457 state = 1;
13458 read_profile("/etc/profile");
13459 state1:
13460 state = 2;
13461 read_profile(".profile");
13462 }
13463 state2:
13464 state = 3;
13465 if (
13466#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013467 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013468#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013469 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013470 ) {
13471 shinit = lookupvar("ENV");
13472 if (shinit != NULL && *shinit != '\0') {
13473 read_profile(shinit);
13474 }
13475 }
13476 state3:
13477 state = 4;
13478 if (minusc)
13479 evalstring(minusc, 0);
13480
13481 if (sflag || minusc == NULL) {
13482#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013483 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013484 const char *hp = lookupvar("HISTFILE");
13485
13486 if (hp != NULL)
13487 line_input_state->hist_file = hp;
13488 }
13489#endif
13490 state4: /* XXX ??? - why isn't this before the "if" statement */
13491 cmdloop(1);
13492 }
13493#if PROFILE
13494 monitor(0);
13495#endif
13496#ifdef GPROF
13497 {
13498 extern void _mcleanup(void);
13499 _mcleanup();
13500 }
13501#endif
13502 exitshell();
13503 /* NOTREACHED */
13504}
13505
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013506#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013507const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013508int main(int argc, char **argv)
13509{
13510 return ash_main(argc, argv);
13511}
13512#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013513
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013514
Eric Andersendf82f612001-06-28 07:46:40 +000013515/*-
13516 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013517 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013518 *
13519 * This code is derived from software contributed to Berkeley by
13520 * Kenneth Almquist.
13521 *
13522 * Redistribution and use in source and binary forms, with or without
13523 * modification, are permitted provided that the following conditions
13524 * are met:
13525 * 1. Redistributions of source code must retain the above copyright
13526 * notice, this list of conditions and the following disclaimer.
13527 * 2. Redistributions in binary form must reproduce the above copyright
13528 * notice, this list of conditions and the following disclaimer in the
13529 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013530 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013531 * may be used to endorse or promote products derived from this software
13532 * without specific prior written permission.
13533 *
13534 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13535 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13536 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13537 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13538 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13539 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13540 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13541 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13542 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13543 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13544 * SUCH DAMAGE.
13545 */