blob: dd20fe33817f162f231581069a69461e12c1690c [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
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114
115#define eflag optlist[0]
116#define fflag optlist[1]
117#define Iflag optlist[2]
118#define iflag optlist[3]
119#define mflag optlist[4]
120#define nflag optlist[5]
121#define sflag optlist[6]
122#define xflag optlist[7]
123#define vflag optlist[8]
124#define Cflag optlist[9]
125#define aflag optlist[10]
126#define bflag optlist[11]
127#define uflag optlist[12]
128#define viflag optlist[13]
129#if DEBUG
130#define nolog optlist[14]
131#define debug optlist[15]
132#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000133
134
Denis Vlasenkob012b102007-02-19 22:43:01 +0000135/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000136
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000137static const char homestr[] ALIGN1 = "HOME";
138static const char snlfmt[] ALIGN1 = "%s\n";
139static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000140
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000141/*
Eric Andersenc470f442003-07-28 09:56:35 +0000142 * We enclose jmp_buf in a structure so that we can declare pointers to
143 * jump locations. The global variable handler contains the location to
144 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000145 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000146 * exception handlers, the user should save the value of handler on entry
147 * to an inner scope, set handler to point to a jmploc structure for the
148 * inner scope, and restore handler on exit from the scope.
149 */
Eric Andersenc470f442003-07-28 09:56:35 +0000150struct jmploc {
151 jmp_buf loc;
152};
Denis Vlasenko01631112007-12-16 17:20:38 +0000153
154struct globals_misc {
155 /* pid of main shell */
156 int rootpid;
157 /* shell level: 0 for the main shell, 1 for its children, and so on */
158 int shlvl;
159#define rootshell (!shlvl)
160 char *minusc; /* argument to -c option */
161
162 char *curdir; // = nullstr; /* current working directory */
163 char *physdir; // = nullstr; /* physical working directory */
164
165 char *arg0; /* value of $0 */
166
167 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000168
169// disabled by vda: cannot understand how it was supposed to work -
170// cannot fix bugs. That's why you have to explain your non-trivial designs!
171// /* do we generate EXSIG events */
172// int exsig; /* counter */
173 volatile int suppressint; /* counter */
174 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
175 /* last pending signal */
176 volatile /*sig_atomic_t*/ smallint pendingsig;
177 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000178 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000179#define EXINT 0 /* SIGINT received */
180#define EXERROR 1 /* a generic error */
181#define EXSHELLPROC 2 /* execute a shell procedure */
182#define EXEXEC 3 /* command execution failed */
183#define EXEXIT 4 /* exit the shell */
184#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000185
Denis Vlasenko01631112007-12-16 17:20:38 +0000186 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000187 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000188 char *trap[NSIG];
189 char nullstr[1]; /* zero length string */
Denis Vlasenko01631112007-12-16 17:20:38 +0000190 /*
191 * Sigmode records the current value of the signal handlers for the various
192 * modes. A value of zero means that the current handler is not known.
193 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
194 */
195 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000196#define S_DFL 1 /* default signal handling (SIG_DFL) */
197#define S_CATCH 2 /* signal is caught */
198#define S_IGN 3 /* signal is ignored (SIG_IGN) */
199#define S_HARD_IGN 4 /* signal is ignored permenantly */
200#define S_RESET 5 /* temporary - to reset a hard ignored sig */
201
Denis Vlasenko01631112007-12-16 17:20:38 +0000202 /* indicates specified signal received */
203 char gotsig[NSIG - 1];
204};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000205extern struct globals_misc *const ash_ptr_to_globals_misc;
206#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko01631112007-12-16 17:20:38 +0000207#define rootpid (G_misc.rootpid )
208#define shlvl (G_misc.shlvl )
209#define minusc (G_misc.minusc )
210#define curdir (G_misc.curdir )
211#define physdir (G_misc.physdir )
212#define arg0 (G_misc.arg0 )
213#define exception_handler (G_misc.exception_handler)
214#define exception (G_misc.exception )
215#define suppressint (G_misc.suppressint )
216#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000217//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000218#define pendingsig (G_misc.pendingsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000219#define isloginsh (G_misc.isloginsh)
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000220#define trap (G_misc.trap )
Denis Vlasenko01631112007-12-16 17:20:38 +0000221#define nullstr (G_misc.nullstr )
222#define sigmode (G_misc.sigmode )
223#define gotsig (G_misc.gotsig )
224#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000225 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
226 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000227 curdir = nullstr; \
228 physdir = nullstr; \
229} while (0)
230
231
232/* ============ Interrupts / exceptions */
233
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000234/*
Eric Andersen2870d962001-07-02 17:27:21 +0000235 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000236 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000237 * much more efficient and portable. (But hacking the kernel is so much
238 * more fun than worrying about efficiency and portability. :-))
239 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000240#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000241 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000242 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000243 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000244 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000245
246/*
247 * Called to raise an exception. Since C doesn't include exceptions, we
248 * just do a longjmp to the exception handler. The type of exception is
249 * stored in the global variable "exception".
250 */
251static void raise_exception(int) ATTRIBUTE_NORETURN;
252static void
253raise_exception(int e)
254{
255#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000256 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000257 abort();
258#endif
259 INT_OFF;
260 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000261 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000262}
263
264/*
265 * Called from trap.c when a SIGINT is received. (If the user specifies
266 * that SIGINT is to be trapped or ignored using the trap builtin, then
267 * this routine is not called.) Suppressint is nonzero when interrupts
268 * are held using the INT_OFF macro. (The test for iflag is just
269 * defensive programming.)
270 */
271static void raise_interrupt(void) ATTRIBUTE_NORETURN;
272static void
273raise_interrupt(void)
274{
275 int i;
276
277 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000278 /* Signal is not automatically unmasked after it is raised,
279 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000280 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000281 /* pendingsig = 0; - now done in onsig() */
282
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 i = EXSIG;
284 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
285 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000286 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287 signal(SIGINT, SIG_DFL);
288 raise(SIGINT);
289 }
290 i = EXINT;
291 }
292 raise_exception(i);
293 /* NOTREACHED */
294}
295
296#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000297static void
298int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299{
300 if (--suppressint == 0 && intpending) {
301 raise_interrupt();
302 }
303}
304#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000305static void
306force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307{
308 suppressint = 0;
309 if (intpending)
310 raise_interrupt();
311}
312#define FORCE_INT_ON force_int_on()
313#else
314#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000315 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000316 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000317 if (--suppressint == 0 && intpending) \
318 raise_interrupt(); \
319 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000320#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000321 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000322 xbarrier(); \
323 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000324 if (intpending) \
325 raise_interrupt(); \
326 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000327#endif /* ASH_OPTIMIZE_FOR_SIZE */
328
329#define SAVE_INT(v) ((v) = suppressint)
330
331#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000332 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000333 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000334 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000335 if (suppressint == 0 && intpending) \
336 raise_interrupt(); \
337 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000338
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000339/*
340 * Ignore a signal. Only one usage site - in forkchild()
341 */
342static void
343ignoresig(int signo)
344{
345 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
346 signal(signo, SIG_IGN);
347 }
348 sigmode[signo - 1] = S_HARD_IGN;
349}
350
351/*
352 * Signal handler. Only one usage site - in setsignal()
353 */
354static void
355onsig(int signo)
356{
357 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000358 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000359
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000360 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000361 if (!suppressint) {
362 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000363 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000364 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000365 intpending = 1;
366 }
367}
368
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000369
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000370/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000371
Eric Andersenc470f442003-07-28 09:56:35 +0000372static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000373outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000374{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375 INT_OFF;
376 fputs(p, file);
377 INT_ON;
378}
379
380static void
381flush_stdout_stderr(void)
382{
383 INT_OFF;
384 fflush(stdout);
385 fflush(stderr);
386 INT_ON;
387}
388
389static void
390flush_stderr(void)
391{
392 INT_OFF;
393 fflush(stderr);
394 INT_ON;
395}
396
397static void
398outcslow(int c, FILE *dest)
399{
400 INT_OFF;
401 putc(c, dest);
402 fflush(dest);
403 INT_ON;
404}
405
406static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
407static int
408out1fmt(const char *fmt, ...)
409{
410 va_list ap;
411 int r;
412
413 INT_OFF;
414 va_start(ap, fmt);
415 r = vprintf(fmt, ap);
416 va_end(ap);
417 INT_ON;
418 return r;
419}
420
421static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
422static int
423fmtstr(char *outbuf, size_t length, const char *fmt, ...)
424{
425 va_list ap;
426 int ret;
427
428 va_start(ap, fmt);
429 INT_OFF;
430 ret = vsnprintf(outbuf, length, fmt, ap);
431 va_end(ap);
432 INT_ON;
433 return ret;
434}
435
436static void
437out1str(const char *p)
438{
439 outstr(p, stdout);
440}
441
442static void
443out2str(const char *p)
444{
445 outstr(p, stderr);
446 flush_stderr();
447}
448
449
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000450/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000451
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000452/* control characters in argument strings */
453#define CTLESC '\201' /* escape next character */
454#define CTLVAR '\202' /* variable defn */
455#define CTLENDVAR '\203'
456#define CTLBACKQ '\204'
457#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
458/* CTLBACKQ | CTLQUOTE == '\205' */
459#define CTLARI '\206' /* arithmetic expression */
460#define CTLENDARI '\207'
461#define CTLQUOTEMARK '\210'
462
463/* variable substitution byte (follows CTLVAR) */
464#define VSTYPE 0x0f /* type of variable substitution */
465#define VSNUL 0x10 /* colon--treat the empty string as unset */
466#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
467
468/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000469#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
470#define VSMINUS 0x2 /* ${var-text} */
471#define VSPLUS 0x3 /* ${var+text} */
472#define VSQUESTION 0x4 /* ${var?message} */
473#define VSASSIGN 0x5 /* ${var=text} */
474#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
475#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
476#define VSTRIMLEFT 0x8 /* ${var#pattern} */
477#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
478#define VSLENGTH 0xa /* ${#var} */
479#if ENABLE_ASH_BASH_COMPAT
480#define VSSUBSTR 0xc /* ${var:position:length} */
481#define VSREPLACE 0xd /* ${var/pattern/replacement} */
482#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
483#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000484
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000485static const char dolatstr[] ALIGN1 = {
486 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
487};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000488
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000489#define NCMD 0
490#define NPIPE 1
491#define NREDIR 2
492#define NBACKGND 3
493#define NSUBSHELL 4
494#define NAND 5
495#define NOR 6
496#define NSEMI 7
497#define NIF 8
498#define NWHILE 9
499#define NUNTIL 10
500#define NFOR 11
501#define NCASE 12
502#define NCLIST 13
503#define NDEFUN 14
504#define NARG 15
505#define NTO 16
506#define NCLOBBER 17
507#define NFROM 18
508#define NFROMTO 19
509#define NAPPEND 20
510#define NTOFD 21
511#define NFROMFD 22
512#define NHERE 23
513#define NXHERE 24
514#define NNOT 25
515
516union node;
517
518struct ncmd {
519 int type;
520 union node *assign;
521 union node *args;
522 union node *redirect;
523};
524
525struct npipe {
526 int type;
527 int backgnd;
528 struct nodelist *cmdlist;
529};
530
531struct nredir {
532 int type;
533 union node *n;
534 union node *redirect;
535};
536
537struct nbinary {
538 int type;
539 union node *ch1;
540 union node *ch2;
541};
542
543struct nif {
544 int type;
545 union node *test;
546 union node *ifpart;
547 union node *elsepart;
548};
549
550struct nfor {
551 int type;
552 union node *args;
553 union node *body;
554 char *var;
555};
556
557struct ncase {
558 int type;
559 union node *expr;
560 union node *cases;
561};
562
563struct nclist {
564 int type;
565 union node *next;
566 union node *pattern;
567 union node *body;
568};
569
570struct narg {
571 int type;
572 union node *next;
573 char *text;
574 struct nodelist *backquote;
575};
576
577struct nfile {
578 int type;
579 union node *next;
580 int fd;
581 union node *fname;
582 char *expfname;
583};
584
585struct ndup {
586 int type;
587 union node *next;
588 int fd;
589 int dupfd;
590 union node *vname;
591};
592
593struct nhere {
594 int type;
595 union node *next;
596 int fd;
597 union node *doc;
598};
599
600struct nnot {
601 int type;
602 union node *com;
603};
604
605union node {
606 int type;
607 struct ncmd ncmd;
608 struct npipe npipe;
609 struct nredir nredir;
610 struct nbinary nbinary;
611 struct nif nif;
612 struct nfor nfor;
613 struct ncase ncase;
614 struct nclist nclist;
615 struct narg narg;
616 struct nfile nfile;
617 struct ndup ndup;
618 struct nhere nhere;
619 struct nnot nnot;
620};
621
622struct nodelist {
623 struct nodelist *next;
624 union node *n;
625};
626
627struct funcnode {
628 int count;
629 union node n;
630};
631
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000632/*
633 * Free a parse tree.
634 */
635static void
636freefunc(struct funcnode *f)
637{
638 if (f && --f->count < 0)
639 free(f);
640}
641
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000642
643/* ============ Debugging output */
644
645#if DEBUG
646
647static FILE *tracefile;
648
649static void
650trace_printf(const char *fmt, ...)
651{
652 va_list va;
653
654 if (debug != 1)
655 return;
656 va_start(va, fmt);
657 vfprintf(tracefile, fmt, va);
658 va_end(va);
659}
660
661static void
662trace_vprintf(const char *fmt, va_list va)
663{
664 if (debug != 1)
665 return;
666 vfprintf(tracefile, fmt, va);
667}
668
669static void
670trace_puts(const char *s)
671{
672 if (debug != 1)
673 return;
674 fputs(s, tracefile);
675}
676
677static void
678trace_puts_quoted(char *s)
679{
680 char *p;
681 char c;
682
683 if (debug != 1)
684 return;
685 putc('"', tracefile);
686 for (p = s; *p; p++) {
687 switch (*p) {
688 case '\n': c = 'n'; goto backslash;
689 case '\t': c = 't'; goto backslash;
690 case '\r': c = 'r'; goto backslash;
691 case '"': c = '"'; goto backslash;
692 case '\\': c = '\\'; goto backslash;
693 case CTLESC: c = 'e'; goto backslash;
694 case CTLVAR: c = 'v'; goto backslash;
695 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
696 case CTLBACKQ: c = 'q'; goto backslash;
697 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
698 backslash:
699 putc('\\', tracefile);
700 putc(c, tracefile);
701 break;
702 default:
703 if (*p >= ' ' && *p <= '~')
704 putc(*p, tracefile);
705 else {
706 putc('\\', tracefile);
707 putc(*p >> 6 & 03, tracefile);
708 putc(*p >> 3 & 07, tracefile);
709 putc(*p & 07, tracefile);
710 }
711 break;
712 }
713 }
714 putc('"', tracefile);
715}
716
717static void
718trace_puts_args(char **ap)
719{
720 if (debug != 1)
721 return;
722 if (!*ap)
723 return;
724 while (1) {
725 trace_puts_quoted(*ap);
726 if (!*++ap) {
727 putc('\n', tracefile);
728 break;
729 }
730 putc(' ', tracefile);
731 }
732}
733
734static void
735opentrace(void)
736{
737 char s[100];
738#ifdef O_APPEND
739 int flags;
740#endif
741
742 if (debug != 1) {
743 if (tracefile)
744 fflush(tracefile);
745 /* leave open because libedit might be using it */
746 return;
747 }
748 strcpy(s, "./trace");
749 if (tracefile) {
750 if (!freopen(s, "a", tracefile)) {
751 fprintf(stderr, "Can't re-open %s\n", s);
752 debug = 0;
753 return;
754 }
755 } else {
756 tracefile = fopen(s, "a");
757 if (tracefile == NULL) {
758 fprintf(stderr, "Can't open %s\n", s);
759 debug = 0;
760 return;
761 }
762 }
763#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000764 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000765 if (flags >= 0)
766 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
767#endif
768 setlinebuf(tracefile);
769 fputs("\nTracing started.\n", tracefile);
770}
771
772static void
773indent(int amount, char *pfx, FILE *fp)
774{
775 int i;
776
777 for (i = 0; i < amount; i++) {
778 if (pfx && i == amount - 1)
779 fputs(pfx, fp);
780 putc('\t', fp);
781 }
782}
783
784/* little circular references here... */
785static void shtree(union node *n, int ind, char *pfx, FILE *fp);
786
787static void
788sharg(union node *arg, FILE *fp)
789{
790 char *p;
791 struct nodelist *bqlist;
792 int subtype;
793
794 if (arg->type != NARG) {
795 out1fmt("<node type %d>\n", arg->type);
796 abort();
797 }
798 bqlist = arg->narg.backquote;
799 for (p = arg->narg.text; *p; p++) {
800 switch (*p) {
801 case CTLESC:
802 putc(*++p, fp);
803 break;
804 case CTLVAR:
805 putc('$', fp);
806 putc('{', fp);
807 subtype = *++p;
808 if (subtype == VSLENGTH)
809 putc('#', fp);
810
811 while (*p != '=')
812 putc(*p++, fp);
813
814 if (subtype & VSNUL)
815 putc(':', fp);
816
817 switch (subtype & VSTYPE) {
818 case VSNORMAL:
819 putc('}', fp);
820 break;
821 case VSMINUS:
822 putc('-', fp);
823 break;
824 case VSPLUS:
825 putc('+', fp);
826 break;
827 case VSQUESTION:
828 putc('?', fp);
829 break;
830 case VSASSIGN:
831 putc('=', fp);
832 break;
833 case VSTRIMLEFT:
834 putc('#', fp);
835 break;
836 case VSTRIMLEFTMAX:
837 putc('#', fp);
838 putc('#', fp);
839 break;
840 case VSTRIMRIGHT:
841 putc('%', fp);
842 break;
843 case VSTRIMRIGHTMAX:
844 putc('%', fp);
845 putc('%', fp);
846 break;
847 case VSLENGTH:
848 break;
849 default:
850 out1fmt("<subtype %d>", subtype);
851 }
852 break;
853 case CTLENDVAR:
854 putc('}', fp);
855 break;
856 case CTLBACKQ:
857 case CTLBACKQ|CTLQUOTE:
858 putc('$', fp);
859 putc('(', fp);
860 shtree(bqlist->n, -1, NULL, fp);
861 putc(')', fp);
862 break;
863 default:
864 putc(*p, fp);
865 break;
866 }
867 }
868}
869
870static void
871shcmd(union node *cmd, FILE *fp)
872{
873 union node *np;
874 int first;
875 const char *s;
876 int dftfd;
877
878 first = 1;
879 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000880 if (!first)
881 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000882 sharg(np, fp);
883 first = 0;
884 }
885 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000886 if (!first)
887 putc(' ', fp);
888 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000889 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000890 case NTO: s = ">>"+1; dftfd = 1; break;
891 case NCLOBBER: s = ">|"; dftfd = 1; break;
892 case NAPPEND: s = ">>"; dftfd = 1; break;
893 case NTOFD: s = ">&"; dftfd = 1; break;
894 case NFROM: s = "<"; break;
895 case NFROMFD: s = "<&"; break;
896 case NFROMTO: s = "<>"; break;
897 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000898 }
899 if (np->nfile.fd != dftfd)
900 fprintf(fp, "%d", np->nfile.fd);
901 fputs(s, fp);
902 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
903 fprintf(fp, "%d", np->ndup.dupfd);
904 } else {
905 sharg(np->nfile.fname, fp);
906 }
907 first = 0;
908 }
909}
910
911static void
912shtree(union node *n, int ind, char *pfx, FILE *fp)
913{
914 struct nodelist *lp;
915 const char *s;
916
917 if (n == NULL)
918 return;
919
920 indent(ind, pfx, fp);
921 switch (n->type) {
922 case NSEMI:
923 s = "; ";
924 goto binop;
925 case NAND:
926 s = " && ";
927 goto binop;
928 case NOR:
929 s = " || ";
930 binop:
931 shtree(n->nbinary.ch1, ind, NULL, fp);
932 /* if (ind < 0) */
933 fputs(s, fp);
934 shtree(n->nbinary.ch2, ind, NULL, fp);
935 break;
936 case NCMD:
937 shcmd(n, fp);
938 if (ind >= 0)
939 putc('\n', fp);
940 break;
941 case NPIPE:
942 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
943 shcmd(lp->n, fp);
944 if (lp->next)
945 fputs(" | ", fp);
946 }
947 if (n->npipe.backgnd)
948 fputs(" &", fp);
949 if (ind >= 0)
950 putc('\n', fp);
951 break;
952 default:
953 fprintf(fp, "<node type %d>", n->type);
954 if (ind >= 0)
955 putc('\n', fp);
956 break;
957 }
958}
959
960static void
961showtree(union node *n)
962{
963 trace_puts("showtree called\n");
964 shtree(n, 1, NULL, stdout);
965}
966
967#define TRACE(param) trace_printf param
968#define TRACEV(param) trace_vprintf param
969
970#else
971
972#define TRACE(param)
973#define TRACEV(param)
974
975#endif /* DEBUG */
976
977
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000978/* ============ Parser data */
979
980/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000981 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
982 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000983struct strlist {
984 struct strlist *next;
985 char *text;
986};
987
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000988#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000989struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000990#endif
991
Denis Vlasenkob012b102007-02-19 22:43:01 +0000992struct strpush {
993 struct strpush *prev; /* preceding string on stack */
994 char *prevstring;
995 int prevnleft;
996#if ENABLE_ASH_ALIAS
997 struct alias *ap; /* if push was associated with an alias */
998#endif
999 char *string; /* remember the string since it may change */
1000};
1001
1002struct parsefile {
1003 struct parsefile *prev; /* preceding file on stack */
1004 int linno; /* current line */
1005 int fd; /* file descriptor (or -1 if string) */
1006 int nleft; /* number of chars left in this line */
1007 int lleft; /* number of chars left in this buffer */
1008 char *nextc; /* next char in buffer */
1009 char *buf; /* input buffer */
1010 struct strpush *strpush; /* for pushing strings at this level */
1011 struct strpush basestrpush; /* so pushing one is fast */
1012};
1013
1014static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001015static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001016static int startlinno; /* line # where last token started */
1017static char *commandname; /* currently executing command */
1018static struct strlist *cmdenviron; /* environment for builtin command */
1019static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001020
1021
1022/* ============ Message printing */
1023
1024static void
1025ash_vmsg(const char *msg, va_list ap)
1026{
1027 fprintf(stderr, "%s: ", arg0);
1028 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001029 if (strcmp(arg0, commandname))
1030 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001031 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001032 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001033 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034 vfprintf(stderr, msg, ap);
1035 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001036}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001037
1038/*
1039 * Exverror is called to raise the error exception. If the second argument
1040 * is not NULL then error prints an error message using printf style
1041 * formatting. It then raises the error exception.
1042 */
1043static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1044static void
1045ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001046{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001047#if DEBUG
1048 if (msg) {
1049 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1050 TRACEV((msg, ap));
1051 TRACE(("\") pid=%d\n", getpid()));
1052 } else
1053 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1054 if (msg)
1055#endif
1056 ash_vmsg(msg, ap);
1057
1058 flush_stdout_stderr();
1059 raise_exception(cond);
1060 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001061}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001062
1063static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1064static void
1065ash_msg_and_raise_error(const char *msg, ...)
1066{
1067 va_list ap;
1068
1069 va_start(ap, msg);
1070 ash_vmsg_and_raise(EXERROR, msg, ap);
1071 /* NOTREACHED */
1072 va_end(ap);
1073}
1074
1075static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1076static void
1077ash_msg_and_raise(int cond, const char *msg, ...)
1078{
1079 va_list ap;
1080
1081 va_start(ap, msg);
1082 ash_vmsg_and_raise(cond, msg, ap);
1083 /* NOTREACHED */
1084 va_end(ap);
1085}
1086
1087/*
1088 * error/warning routines for external builtins
1089 */
1090static void
1091ash_msg(const char *fmt, ...)
1092{
1093 va_list ap;
1094
1095 va_start(ap, fmt);
1096 ash_vmsg(fmt, ap);
1097 va_end(ap);
1098}
1099
1100/*
1101 * Return a string describing an error. The returned string may be a
1102 * pointer to a static buffer that will be overwritten on the next call.
1103 * Action describes the operation that got the error.
1104 */
1105static const char *
1106errmsg(int e, const char *em)
1107{
1108 if (e == ENOENT || e == ENOTDIR) {
1109 return em;
1110 }
1111 return strerror(e);
1112}
1113
1114
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001115/* ============ Memory allocation */
1116
1117/*
1118 * It appears that grabstackstr() will barf with such alignments
1119 * because stalloc() will return a string allocated in a new stackblock.
1120 */
1121#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1122enum {
1123 /* Most machines require the value returned from malloc to be aligned
1124 * in some way. The following macro will get this right
1125 * on many machines. */
1126 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1127 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001128 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001129};
1130
1131struct stack_block {
1132 struct stack_block *prev;
1133 char space[MINSIZE];
1134};
1135
1136struct stackmark {
1137 struct stack_block *stackp;
1138 char *stacknxt;
1139 size_t stacknleft;
1140 struct stackmark *marknext;
1141};
1142
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001143
Denis Vlasenko01631112007-12-16 17:20:38 +00001144struct globals_memstack {
1145 struct stack_block *g_stackp; // = &stackbase;
1146 struct stackmark *markp;
1147 char *g_stacknxt; // = stackbase.space;
1148 char *sstrend; // = stackbase.space + MINSIZE;
1149 size_t g_stacknleft; // = MINSIZE;
1150 int herefd; // = -1;
1151 struct stack_block stackbase;
1152};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001153extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1154#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001155#define g_stackp (G_memstack.g_stackp )
1156#define markp (G_memstack.markp )
1157#define g_stacknxt (G_memstack.g_stacknxt )
1158#define sstrend (G_memstack.sstrend )
1159#define g_stacknleft (G_memstack.g_stacknleft)
1160#define herefd (G_memstack.herefd )
1161#define stackbase (G_memstack.stackbase )
1162#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001163 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1164 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001165 g_stackp = &stackbase; \
1166 g_stacknxt = stackbase.space; \
1167 g_stacknleft = MINSIZE; \
1168 sstrend = stackbase.space + MINSIZE; \
1169 herefd = -1; \
1170} while (0)
1171
1172#define stackblock() ((void *)g_stacknxt)
1173#define stackblocksize() g_stacknleft
1174
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001175
1176static void *
1177ckrealloc(void * p, size_t nbytes)
1178{
1179 p = realloc(p, nbytes);
1180 if (!p)
1181 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1182 return p;
1183}
1184
1185static void *
1186ckmalloc(size_t nbytes)
1187{
1188 return ckrealloc(NULL, nbytes);
1189}
1190
Denis Vlasenko597906c2008-02-20 16:38:54 +00001191static void *
1192ckzalloc(size_t nbytes)
1193{
1194 return memset(ckmalloc(nbytes), 0, nbytes);
1195}
1196
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001197/*
1198 * Make a copy of a string in safe storage.
1199 */
1200static char *
1201ckstrdup(const char *s)
1202{
1203 char *p = strdup(s);
1204 if (!p)
1205 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1206 return p;
1207}
1208
1209/*
1210 * Parse trees for commands are allocated in lifo order, so we use a stack
1211 * to make this more efficient, and also to avoid all sorts of exception
1212 * handling code to handle interrupts in the middle of a parse.
1213 *
1214 * The size 504 was chosen because the Ultrix malloc handles that size
1215 * well.
1216 */
1217static void *
1218stalloc(size_t nbytes)
1219{
1220 char *p;
1221 size_t aligned;
1222
1223 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001224 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001225 size_t len;
1226 size_t blocksize;
1227 struct stack_block *sp;
1228
1229 blocksize = aligned;
1230 if (blocksize < MINSIZE)
1231 blocksize = MINSIZE;
1232 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1233 if (len < blocksize)
1234 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1235 INT_OFF;
1236 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001237 sp->prev = g_stackp;
1238 g_stacknxt = sp->space;
1239 g_stacknleft = blocksize;
1240 sstrend = g_stacknxt + blocksize;
1241 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001242 INT_ON;
1243 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001244 p = g_stacknxt;
1245 g_stacknxt += aligned;
1246 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001247 return p;
1248}
1249
Denis Vlasenko597906c2008-02-20 16:38:54 +00001250static void *
1251stzalloc(size_t nbytes)
1252{
1253 return memset(stalloc(nbytes), 0, nbytes);
1254}
1255
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256static void
1257stunalloc(void *p)
1258{
1259#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001260 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001261 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001262 abort();
1263 }
1264#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001265 g_stacknleft += g_stacknxt - (char *)p;
1266 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001267}
1268
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001269/*
1270 * Like strdup but works with the ash stack.
1271 */
1272static char *
1273ststrdup(const char *p)
1274{
1275 size_t len = strlen(p) + 1;
1276 return memcpy(stalloc(len), p, len);
1277}
1278
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001279static void
1280setstackmark(struct stackmark *mark)
1281{
Denis Vlasenko01631112007-12-16 17:20:38 +00001282 mark->stackp = g_stackp;
1283 mark->stacknxt = g_stacknxt;
1284 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001285 mark->marknext = markp;
1286 markp = mark;
1287}
1288
1289static void
1290popstackmark(struct stackmark *mark)
1291{
1292 struct stack_block *sp;
1293
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001294 if (!mark->stackp)
1295 return;
1296
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297 INT_OFF;
1298 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001299 while (g_stackp != mark->stackp) {
1300 sp = g_stackp;
1301 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001302 free(sp);
1303 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001304 g_stacknxt = mark->stacknxt;
1305 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001306 sstrend = mark->stacknxt + mark->stacknleft;
1307 INT_ON;
1308}
1309
1310/*
1311 * When the parser reads in a string, it wants to stick the string on the
1312 * stack and only adjust the stack pointer when it knows how big the
1313 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1314 * of space on top of the stack and stackblocklen returns the length of
1315 * this block. Growstackblock will grow this space by at least one byte,
1316 * possibly moving it (like realloc). Grabstackblock actually allocates the
1317 * part of the block that has been used.
1318 */
1319static void
1320growstackblock(void)
1321{
1322 size_t newlen;
1323
Denis Vlasenko01631112007-12-16 17:20:38 +00001324 newlen = g_stacknleft * 2;
1325 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1327 if (newlen < 128)
1328 newlen += 128;
1329
Denis Vlasenko01631112007-12-16 17:20:38 +00001330 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 struct stack_block *oldstackp;
1332 struct stackmark *xmark;
1333 struct stack_block *sp;
1334 struct stack_block *prevstackp;
1335 size_t grosslen;
1336
1337 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001338 oldstackp = g_stackp;
1339 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001340 prevstackp = sp->prev;
1341 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1342 sp = ckrealloc(sp, grosslen);
1343 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001344 g_stackp = sp;
1345 g_stacknxt = sp->space;
1346 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347 sstrend = sp->space + newlen;
1348
1349 /*
1350 * Stack marks pointing to the start of the old block
1351 * must be relocated to point to the new block
1352 */
1353 xmark = markp;
1354 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001355 xmark->stackp = g_stackp;
1356 xmark->stacknxt = g_stacknxt;
1357 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358 xmark = xmark->marknext;
1359 }
1360 INT_ON;
1361 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001363 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001364 char *p = stalloc(newlen);
1365
1366 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001367 g_stacknxt = memcpy(p, oldspace, oldlen);
1368 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001369 }
1370}
1371
1372static void
1373grabstackblock(size_t len)
1374{
1375 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 g_stacknxt += len;
1377 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378}
1379
1380/*
1381 * The following routines are somewhat easier to use than the above.
1382 * The user declares a variable of type STACKSTR, which may be declared
1383 * to be a register. The macro STARTSTACKSTR initializes things. Then
1384 * the user uses the macro STPUTC to add characters to the string. In
1385 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1386 * grown as necessary. When the user is done, she can just leave the
1387 * string there and refer to it using stackblock(). Or she can allocate
1388 * the space for it using grabstackstr(). If it is necessary to allow
1389 * someone else to use the stack temporarily and then continue to grow
1390 * the string, the user should use grabstack to allocate the space, and
1391 * then call ungrabstr(p) to return to the previous mode of operation.
1392 *
1393 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1394 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1395 * is space for at least one character.
1396 */
1397static void *
1398growstackstr(void)
1399{
1400 size_t len = stackblocksize();
1401 if (herefd >= 0 && len >= 1024) {
1402 full_write(herefd, stackblock(), len);
1403 return stackblock();
1404 }
1405 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001406 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001407}
1408
1409/*
1410 * Called from CHECKSTRSPACE.
1411 */
1412static char *
1413makestrspace(size_t newlen, char *p)
1414{
Denis Vlasenko01631112007-12-16 17:20:38 +00001415 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001416 size_t size = stackblocksize();
1417
1418 for (;;) {
1419 size_t nleft;
1420
1421 size = stackblocksize();
1422 nleft = size - len;
1423 if (nleft >= newlen)
1424 break;
1425 growstackblock();
1426 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001427 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001428}
1429
1430static char *
1431stack_nputstr(const char *s, size_t n, char *p)
1432{
1433 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001434 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001435 return p;
1436}
1437
1438static char *
1439stack_putstr(const char *s, char *p)
1440{
1441 return stack_nputstr(s, strlen(s), p);
1442}
1443
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001444static char *
1445_STPUTC(int c, char *p)
1446{
1447 if (p == sstrend)
1448 p = growstackstr();
1449 *p++ = c;
1450 return p;
1451}
1452
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001453#define STARTSTACKSTR(p) ((p) = stackblock())
1454#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001455#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001456 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001457 char *q = (p); \
1458 size_t l = (n); \
1459 size_t m = sstrend - q; \
1460 if (l > m) \
1461 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001462 } while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001463#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001464#define STACKSTRNUL(p) \
1465 do { \
1466 if ((p) == sstrend) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001467 (p) = growstackstr(); \
1468 *(p) = '\0'; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001469 } while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001470#define STUNPUTC(p) (--(p))
1471#define STTOPC(p) ((p)[-1])
1472#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001473
1474#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001475#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001476#define stackstrend() ((void *)sstrend)
1477
1478
1479/* ============ String helpers */
1480
1481/*
1482 * prefix -- see if pfx is a prefix of string.
1483 */
1484static char *
1485prefix(const char *string, const char *pfx)
1486{
1487 while (*pfx) {
1488 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001489 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001490 }
1491 return (char *) string;
1492}
1493
1494/*
1495 * Check for a valid number. This should be elsewhere.
1496 */
1497static int
1498is_number(const char *p)
1499{
1500 do {
1501 if (!isdigit(*p))
1502 return 0;
1503 } while (*++p != '\0');
1504 return 1;
1505}
1506
1507/*
1508 * Convert a string of digits to an integer, printing an error message on
1509 * failure.
1510 */
1511static int
1512number(const char *s)
1513{
1514 if (!is_number(s))
1515 ash_msg_and_raise_error(illnum, s);
1516 return atoi(s);
1517}
1518
1519/*
1520 * Produce a possibly single quoted string suitable as input to the shell.
1521 * The return string is allocated on the stack.
1522 */
1523static char *
1524single_quote(const char *s)
1525{
1526 char *p;
1527
1528 STARTSTACKSTR(p);
1529
1530 do {
1531 char *q;
1532 size_t len;
1533
1534 len = strchrnul(s, '\'') - s;
1535
1536 q = p = makestrspace(len + 3, p);
1537
1538 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001539 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001540 *q++ = '\'';
1541 s += len;
1542
1543 STADJUST(q - p, p);
1544
1545 len = strspn(s, "'");
1546 if (!len)
1547 break;
1548
1549 q = p = makestrspace(len + 3, p);
1550
1551 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001552 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001553 *q++ = '"';
1554 s += len;
1555
1556 STADJUST(q - p, p);
1557 } while (*s);
1558
1559 USTPUTC(0, p);
1560
1561 return stackblock();
1562}
1563
1564
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001565/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001566
1567static char **argptr; /* argument list for builtin commands */
1568static char *optionarg; /* set by nextopt (like getopt) */
1569static char *optptr; /* used by nextopt */
1570
1571/*
1572 * XXX - should get rid of. have all builtins use getopt(3). the
1573 * library getopt must have the BSD extension static variable "optreset"
1574 * otherwise it can't be used within the shell safely.
1575 *
1576 * Standard option processing (a la getopt) for builtin routines. The
1577 * only argument that is passed to nextopt is the option string; the
1578 * other arguments are unnecessary. It return the character, or '\0' on
1579 * end of input.
1580 */
1581static int
1582nextopt(const char *optstring)
1583{
1584 char *p;
1585 const char *q;
1586 char c;
1587
1588 p = optptr;
1589 if (p == NULL || *p == '\0') {
1590 p = *argptr;
1591 if (p == NULL || *p != '-' || *++p == '\0')
1592 return '\0';
1593 argptr++;
1594 if (LONE_DASH(p)) /* check for "--" */
1595 return '\0';
1596 }
1597 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001598 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001599 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001600 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001601 if (*++q == ':')
1602 q++;
1603 }
1604 if (*++q == ':') {
1605 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001606 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001607 optionarg = p;
1608 p = NULL;
1609 }
1610 optptr = p;
1611 return c;
1612}
1613
1614
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001615/* ============ Math support definitions */
1616
1617#if ENABLE_ASH_MATH_SUPPORT_64
1618typedef int64_t arith_t;
1619#define arith_t_type long long
1620#else
1621typedef long arith_t;
1622#define arith_t_type long
1623#endif
1624
1625#if ENABLE_ASH_MATH_SUPPORT
1626static arith_t dash_arith(const char *);
1627static arith_t arith(const char *expr, int *perrcode);
1628#endif
1629
1630#if ENABLE_ASH_RANDOM_SUPPORT
1631static unsigned long rseed;
1632#ifndef DYNAMIC_VAR
1633#define DYNAMIC_VAR
1634#endif
1635#endif
1636
1637
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001638/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001639
Denis Vlasenko01631112007-12-16 17:20:38 +00001640/*
1641 * The parsefile structure pointed to by the global variable parsefile
1642 * contains information about the current file being read.
1643 */
1644struct redirtab {
1645 struct redirtab *next;
1646 int renamed[10];
1647 int nullredirs;
1648};
1649
1650struct shparam {
1651 int nparam; /* # of positional parameters (without $0) */
1652#if ENABLE_ASH_GETOPTS
1653 int optind; /* next parameter to be processed by getopts */
1654 int optoff; /* used by getopts */
1655#endif
1656 unsigned char malloced; /* if parameter list dynamically allocated */
1657 char **p; /* parameter list */
1658};
1659
1660/*
1661 * Free the list of positional parameters.
1662 */
1663static void
1664freeparam(volatile struct shparam *param)
1665{
1666 char **ap;
1667
1668 if (param->malloced) {
1669 for (ap = param->p; *ap; ap++)
1670 free(*ap);
1671 free(param->p);
1672 }
1673}
1674
1675#if ENABLE_ASH_GETOPTS
1676static void getoptsreset(const char *value);
1677#endif
1678
1679struct var {
1680 struct var *next; /* next entry in hash list */
1681 int flags; /* flags are defined above */
1682 const char *text; /* name=value */
1683 void (*func)(const char *); /* function to be called when */
1684 /* the variable gets set/unset */
1685};
1686
1687struct localvar {
1688 struct localvar *next; /* next local variable in list */
1689 struct var *vp; /* the variable that was made local */
1690 int flags; /* saved flags */
1691 const char *text; /* saved text */
1692};
1693
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001694/* flags */
1695#define VEXPORT 0x01 /* variable is exported */
1696#define VREADONLY 0x02 /* variable cannot be modified */
1697#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1698#define VTEXTFIXED 0x08 /* text is statically allocated */
1699#define VSTACK 0x10 /* text is allocated on the stack */
1700#define VUNSET 0x20 /* the variable is not set */
1701#define VNOFUNC 0x40 /* don't call the callback function */
1702#define VNOSET 0x80 /* do not set variable - just readonly test */
1703#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1704#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001705# define VDYNAMIC 0x200 /* dynamic variable */
1706#else
1707# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708#endif
1709
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001710#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001711static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001712#define defifs (defifsvar + 4)
1713#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001714static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715#endif
1716
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001717
Denis Vlasenko01631112007-12-16 17:20:38 +00001718/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001719#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001720static void
1721change_lc_all(const char *value)
1722{
1723 if (value && *value != '\0')
1724 setlocale(LC_ALL, value);
1725}
1726static void
1727change_lc_ctype(const char *value)
1728{
1729 if (value && *value != '\0')
1730 setlocale(LC_CTYPE, value);
1731}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001732#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#if ENABLE_ASH_MAIL
1734static void chkmail(void);
1735static void changemail(const char *);
1736#endif
1737static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#if ENABLE_ASH_RANDOM_SUPPORT
1739static void change_random(const char *);
1740#endif
1741
Denis Vlasenko01631112007-12-16 17:20:38 +00001742static const struct {
1743 int flags;
1744 const char *text;
1745 void (*func)(const char *);
1746} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001748 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1754 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001756 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1757 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1758 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1759 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001761 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
1766#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001767 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1768 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769#endif
1770#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
1773};
1774
Denis Vlasenko01631112007-12-16 17:20:38 +00001775
1776struct globals_var {
1777 struct shparam shellparam; /* $@ current positional parameters */
1778 struct redirtab *redirlist;
1779 int g_nullredirs;
1780 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1781 struct var *vartab[VTABSIZE];
1782 struct var varinit[ARRAY_SIZE(varinit_data)];
1783};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001784extern struct globals_var *const ash_ptr_to_globals_var;
1785#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001786#define shellparam (G_var.shellparam )
1787#define redirlist (G_var.redirlist )
1788#define g_nullredirs (G_var.g_nullredirs )
1789#define preverrout_fd (G_var.preverrout_fd)
1790#define vartab (G_var.vartab )
1791#define varinit (G_var.varinit )
1792#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001793 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001794 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1795 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001796 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1797 varinit[i].flags = varinit_data[i].flags; \
1798 varinit[i].text = varinit_data[i].text; \
1799 varinit[i].func = varinit_data[i].func; \
1800 } \
1801} while (0)
1802
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001803#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001804#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001805# define vmail (&vifs)[1]
1806# define vmpath (&vmail)[1]
1807# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001808#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001809# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001810#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001811#define vps1 (&vpath)[1]
1812#define vps2 (&vps1)[1]
1813#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001814#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001815# define voptind (&vps4)[1]
1816# if ENABLE_ASH_RANDOM_SUPPORT
1817# define vrandom (&voptind)[1]
1818# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001820# if ENABLE_ASH_RANDOM_SUPPORT
1821# define vrandom (&vps4)[1]
1822# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001823#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001824
1825/*
1826 * The following macros access the values of the above variables.
1827 * They have to skip over the name. They return the null string
1828 * for unset variables.
1829 */
1830#define ifsval() (vifs.text + 4)
1831#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001832#if ENABLE_ASH_MAIL
1833# define mailval() (vmail.text + 5)
1834# define mpathval() (vmpath.text + 9)
1835# define mpathset() ((vmpath.flags & VUNSET) == 0)
1836#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837#define pathval() (vpath.text + 5)
1838#define ps1val() (vps1.text + 4)
1839#define ps2val() (vps2.text + 4)
1840#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001841#if ENABLE_ASH_GETOPTS
1842# define optindval() (voptind.text + 7)
1843#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001846#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1847#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1848
Denis Vlasenko01631112007-12-16 17:20:38 +00001849#if ENABLE_ASH_GETOPTS
1850static void
1851getoptsreset(const char *value)
1852{
1853 shellparam.optind = number(value);
1854 shellparam.optoff = -1;
1855}
1856#endif
1857
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858/*
1859 * Return of a legal variable name (a letter or underscore followed by zero or
1860 * more letters, underscores, and digits).
1861 */
1862static char *
1863endofname(const char *name)
1864{
1865 char *p;
1866
1867 p = (char *) name;
1868 if (!is_name(*p))
1869 return p;
1870 while (*++p) {
1871 if (!is_in_name(*p))
1872 break;
1873 }
1874 return p;
1875}
1876
1877/*
1878 * Compares two strings up to the first = or '\0'. The first
1879 * string must be terminated by '='; the second may be terminated by
1880 * either '=' or '\0'.
1881 */
1882static int
1883varcmp(const char *p, const char *q)
1884{
1885 int c, d;
1886
1887 while ((c = *p) == (d = *q)) {
1888 if (!c || c == '=')
1889 goto out;
1890 p++;
1891 q++;
1892 }
1893 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001894 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001895 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001896 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001897 out:
1898 return c - d;
1899}
1900
1901static int
1902varequal(const char *a, const char *b)
1903{
1904 return !varcmp(a, b);
1905}
1906
1907/*
1908 * Find the appropriate entry in the hash table from the name.
1909 */
1910static struct var **
1911hashvar(const char *p)
1912{
1913 unsigned hashval;
1914
1915 hashval = ((unsigned char) *p) << 4;
1916 while (*p && *p != '=')
1917 hashval += (unsigned char) *p++;
1918 return &vartab[hashval % VTABSIZE];
1919}
1920
1921static int
1922vpcmp(const void *a, const void *b)
1923{
1924 return varcmp(*(const char **)a, *(const char **)b);
1925}
1926
1927/*
1928 * This routine initializes the builtin variables.
1929 */
1930static void
1931initvar(void)
1932{
1933 struct var *vp;
1934 struct var *end;
1935 struct var **vpp;
1936
1937 /*
1938 * PS1 depends on uid
1939 */
1940#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1941 vps1.text = "PS1=\\w \\$ ";
1942#else
1943 if (!geteuid())
1944 vps1.text = "PS1=# ";
1945#endif
1946 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001947 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001948 do {
1949 vpp = hashvar(vp->text);
1950 vp->next = *vpp;
1951 *vpp = vp;
1952 } while (++vp < end);
1953}
1954
1955static struct var **
1956findvar(struct var **vpp, const char *name)
1957{
1958 for (; *vpp; vpp = &(*vpp)->next) {
1959 if (varequal((*vpp)->text, name)) {
1960 break;
1961 }
1962 }
1963 return vpp;
1964}
1965
1966/*
1967 * Find the value of a variable. Returns NULL if not set.
1968 */
1969static char *
1970lookupvar(const char *name)
1971{
1972 struct var *v;
1973
1974 v = *findvar(hashvar(name), name);
1975 if (v) {
1976#ifdef DYNAMIC_VAR
1977 /*
1978 * Dynamic variables are implemented roughly the same way they are
1979 * in bash. Namely, they're "special" so long as they aren't unset.
1980 * As soon as they're unset, they're no longer dynamic, and dynamic
1981 * lookup will no longer happen at that point. -- PFM.
1982 */
1983 if ((v->flags & VDYNAMIC))
1984 (*v->func)(NULL);
1985#endif
1986 if (!(v->flags & VUNSET))
1987 return strchrnul(v->text, '=') + 1;
1988 }
1989 return NULL;
1990}
1991
1992/*
1993 * Search the environment of a builtin command.
1994 */
1995static char *
1996bltinlookup(const char *name)
1997{
1998 struct strlist *sp;
1999
2000 for (sp = cmdenviron; sp; sp = sp->next) {
2001 if (varequal(sp->text, name))
2002 return strchrnul(sp->text, '=') + 1;
2003 }
2004 return lookupvar(name);
2005}
2006
2007/*
2008 * Same as setvar except that the variable and value are passed in
2009 * the first argument as name=value. Since the first argument will
2010 * be actually stored in the table, it should not be a string that
2011 * will go away.
2012 * Called with interrupts off.
2013 */
2014static void
2015setvareq(char *s, int flags)
2016{
2017 struct var *vp, **vpp;
2018
2019 vpp = hashvar(s);
2020 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2021 vp = *findvar(vpp, s);
2022 if (vp) {
2023 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2024 const char *n;
2025
2026 if (flags & VNOSAVE)
2027 free(s);
2028 n = vp->text;
2029 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2030 }
2031
2032 if (flags & VNOSET)
2033 return;
2034
2035 if (vp->func && (flags & VNOFUNC) == 0)
2036 (*vp->func)(strchrnul(s, '=') + 1);
2037
2038 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2039 free((char*)vp->text);
2040
2041 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2042 } else {
2043 if (flags & VNOSET)
2044 return;
2045 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002046 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002047 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002048 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002049 *vpp = vp;
2050 }
2051 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2052 s = ckstrdup(s);
2053 vp->text = s;
2054 vp->flags = flags;
2055}
2056
2057/*
2058 * Set the value of a variable. The flags argument is ored with the
2059 * flags of the variable. If val is NULL, the variable is unset.
2060 */
2061static void
2062setvar(const char *name, const char *val, int flags)
2063{
2064 char *p, *q;
2065 size_t namelen;
2066 char *nameeq;
2067 size_t vallen;
2068
2069 q = endofname(name);
2070 p = strchrnul(q, '=');
2071 namelen = p - name;
2072 if (!namelen || p != q)
2073 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2074 vallen = 0;
2075 if (val == NULL) {
2076 flags |= VUNSET;
2077 } else {
2078 vallen = strlen(val);
2079 }
2080 INT_OFF;
2081 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002082 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002083 if (val) {
2084 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002085 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002086 }
2087 *p = '\0';
2088 setvareq(nameeq, flags | VNOSAVE);
2089 INT_ON;
2090}
2091
2092#if ENABLE_ASH_GETOPTS
2093/*
2094 * Safe version of setvar, returns 1 on success 0 on failure.
2095 */
2096static int
2097setvarsafe(const char *name, const char *val, int flags)
2098{
2099 int err;
2100 volatile int saveint;
2101 struct jmploc *volatile savehandler = exception_handler;
2102 struct jmploc jmploc;
2103
2104 SAVE_INT(saveint);
2105 if (setjmp(jmploc.loc))
2106 err = 1;
2107 else {
2108 exception_handler = &jmploc;
2109 setvar(name, val, flags);
2110 err = 0;
2111 }
2112 exception_handler = savehandler;
2113 RESTORE_INT(saveint);
2114 return err;
2115}
2116#endif
2117
2118/*
2119 * Unset the specified variable.
2120 */
2121static int
2122unsetvar(const char *s)
2123{
2124 struct var **vpp;
2125 struct var *vp;
2126 int retval;
2127
2128 vpp = findvar(hashvar(s), s);
2129 vp = *vpp;
2130 retval = 2;
2131 if (vp) {
2132 int flags = vp->flags;
2133
2134 retval = 1;
2135 if (flags & VREADONLY)
2136 goto out;
2137#ifdef DYNAMIC_VAR
2138 vp->flags &= ~VDYNAMIC;
2139#endif
2140 if (flags & VUNSET)
2141 goto ok;
2142 if ((flags & VSTRFIXED) == 0) {
2143 INT_OFF;
2144 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2145 free((char*)vp->text);
2146 *vpp = vp->next;
2147 free(vp);
2148 INT_ON;
2149 } else {
2150 setvar(s, 0, 0);
2151 vp->flags &= ~VEXPORT;
2152 }
2153 ok:
2154 retval = 0;
2155 }
2156 out:
2157 return retval;
2158}
2159
2160/*
2161 * Process a linked list of variable assignments.
2162 */
2163static void
2164listsetvar(struct strlist *list_set_var, int flags)
2165{
2166 struct strlist *lp = list_set_var;
2167
2168 if (!lp)
2169 return;
2170 INT_OFF;
2171 do {
2172 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002173 lp = lp->next;
2174 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002175 INT_ON;
2176}
2177
2178/*
2179 * Generate a list of variables satisfying the given conditions.
2180 */
2181static char **
2182listvars(int on, int off, char ***end)
2183{
2184 struct var **vpp;
2185 struct var *vp;
2186 char **ep;
2187 int mask;
2188
2189 STARTSTACKSTR(ep);
2190 vpp = vartab;
2191 mask = on | off;
2192 do {
2193 for (vp = *vpp; vp; vp = vp->next) {
2194 if ((vp->flags & mask) == on) {
2195 if (ep == stackstrend())
2196 ep = growstackstr();
2197 *ep++ = (char *) vp->text;
2198 }
2199 }
2200 } while (++vpp < vartab + VTABSIZE);
2201 if (ep == stackstrend())
2202 ep = growstackstr();
2203 if (end)
2204 *end = ep;
2205 *ep++ = NULL;
2206 return grabstackstr(ep);
2207}
2208
2209
2210/* ============ Path search helper
2211 *
2212 * The variable path (passed by reference) should be set to the start
2213 * of the path before the first call; padvance will update
2214 * this value as it proceeds. Successive calls to padvance will return
2215 * the possible path expansions in sequence. If an option (indicated by
2216 * a percent sign) appears in the path entry then the global variable
2217 * pathopt will be set to point to it; otherwise pathopt will be set to
2218 * NULL.
2219 */
2220static const char *pathopt; /* set by padvance */
2221
2222static char *
2223padvance(const char **path, const char *name)
2224{
2225 const char *p;
2226 char *q;
2227 const char *start;
2228 size_t len;
2229
2230 if (*path == NULL)
2231 return NULL;
2232 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002233 for (p = start; *p && *p != ':' && *p != '%'; p++)
2234 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002235 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2236 while (stackblocksize() < len)
2237 growstackblock();
2238 q = stackblock();
2239 if (p != start) {
2240 memcpy(q, start, p - start);
2241 q += p - start;
2242 *q++ = '/';
2243 }
2244 strcpy(q, name);
2245 pathopt = NULL;
2246 if (*p == '%') {
2247 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002248 while (*p && *p != ':')
2249 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002250 }
2251 if (*p == ':')
2252 *path = p + 1;
2253 else
2254 *path = NULL;
2255 return stalloc(len);
2256}
2257
2258
2259/* ============ Prompt */
2260
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002261static smallint doprompt; /* if set, prompt the user */
2262static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002263
2264#if ENABLE_FEATURE_EDITING
2265static line_input_t *line_input_state;
2266static const char *cmdedit_prompt;
2267static void
2268putprompt(const char *s)
2269{
2270 if (ENABLE_ASH_EXPAND_PRMT) {
2271 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002272 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002273 return;
2274 }
2275 cmdedit_prompt = s;
2276}
2277#else
2278static void
2279putprompt(const char *s)
2280{
2281 out2str(s);
2282}
2283#endif
2284
2285#if ENABLE_ASH_EXPAND_PRMT
2286/* expandstr() needs parsing machinery, so it is far away ahead... */
2287static const char *expandstr(const char *ps);
2288#else
2289#define expandstr(s) s
2290#endif
2291
2292static void
2293setprompt(int whichprompt)
2294{
2295 const char *prompt;
2296#if ENABLE_ASH_EXPAND_PRMT
2297 struct stackmark smark;
2298#endif
2299
2300 needprompt = 0;
2301
2302 switch (whichprompt) {
2303 case 1:
2304 prompt = ps1val();
2305 break;
2306 case 2:
2307 prompt = ps2val();
2308 break;
2309 default: /* 0 */
2310 prompt = nullstr;
2311 }
2312#if ENABLE_ASH_EXPAND_PRMT
2313 setstackmark(&smark);
2314 stalloc(stackblocksize());
2315#endif
2316 putprompt(expandstr(prompt));
2317#if ENABLE_ASH_EXPAND_PRMT
2318 popstackmark(&smark);
2319#endif
2320}
2321
2322
2323/* ============ The cd and pwd commands */
2324
2325#define CD_PHYSICAL 1
2326#define CD_PRINT 2
2327
2328static int docd(const char *, int);
2329
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002330static int
2331cdopt(void)
2332{
2333 int flags = 0;
2334 int i, j;
2335
2336 j = 'L';
2337 while ((i = nextopt("LP"))) {
2338 if (i != j) {
2339 flags ^= CD_PHYSICAL;
2340 j = i;
2341 }
2342 }
2343
2344 return flags;
2345}
2346
2347/*
2348 * Update curdir (the name of the current directory) in response to a
2349 * cd command.
2350 */
2351static const char *
2352updatepwd(const char *dir)
2353{
2354 char *new;
2355 char *p;
2356 char *cdcomppath;
2357 const char *lim;
2358
2359 cdcomppath = ststrdup(dir);
2360 STARTSTACKSTR(new);
2361 if (*dir != '/') {
2362 if (curdir == nullstr)
2363 return 0;
2364 new = stack_putstr(curdir, new);
2365 }
2366 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002367 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002368 if (*dir != '/') {
2369 if (new[-1] != '/')
2370 USTPUTC('/', new);
2371 if (new > lim && *lim == '/')
2372 lim++;
2373 } else {
2374 USTPUTC('/', new);
2375 cdcomppath++;
2376 if (dir[1] == '/' && dir[2] != '/') {
2377 USTPUTC('/', new);
2378 cdcomppath++;
2379 lim++;
2380 }
2381 }
2382 p = strtok(cdcomppath, "/");
2383 while (p) {
2384 switch (*p) {
2385 case '.':
2386 if (p[1] == '.' && p[2] == '\0') {
2387 while (new > lim) {
2388 STUNPUTC(new);
2389 if (new[-1] == '/')
2390 break;
2391 }
2392 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002393 }
2394 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002395 break;
2396 /* fall through */
2397 default:
2398 new = stack_putstr(p, new);
2399 USTPUTC('/', new);
2400 }
2401 p = strtok(0, "/");
2402 }
2403 if (new > lim)
2404 STUNPUTC(new);
2405 *new = 0;
2406 return stackblock();
2407}
2408
2409/*
2410 * Find out what the current directory is. If we already know the current
2411 * directory, this routine returns immediately.
2412 */
2413static char *
2414getpwd(void)
2415{
Denis Vlasenko01631112007-12-16 17:20:38 +00002416 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002417 return dir ? dir : nullstr;
2418}
2419
2420static void
2421setpwd(const char *val, int setold)
2422{
2423 char *oldcur, *dir;
2424
2425 oldcur = dir = curdir;
2426
2427 if (setold) {
2428 setvar("OLDPWD", oldcur, VEXPORT);
2429 }
2430 INT_OFF;
2431 if (physdir != nullstr) {
2432 if (physdir != oldcur)
2433 free(physdir);
2434 physdir = nullstr;
2435 }
2436 if (oldcur == val || !val) {
2437 char *s = getpwd();
2438 physdir = s;
2439 if (!val)
2440 dir = s;
2441 } else
2442 dir = ckstrdup(val);
2443 if (oldcur != dir && oldcur != nullstr) {
2444 free(oldcur);
2445 }
2446 curdir = dir;
2447 INT_ON;
2448 setvar("PWD", dir, VEXPORT);
2449}
2450
2451static void hashcd(void);
2452
2453/*
2454 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2455 * know that the current directory has changed.
2456 */
2457static int
2458docd(const char *dest, int flags)
2459{
2460 const char *dir = 0;
2461 int err;
2462
2463 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2464
2465 INT_OFF;
2466 if (!(flags & CD_PHYSICAL)) {
2467 dir = updatepwd(dest);
2468 if (dir)
2469 dest = dir;
2470 }
2471 err = chdir(dest);
2472 if (err)
2473 goto out;
2474 setpwd(dir, 1);
2475 hashcd();
2476 out:
2477 INT_ON;
2478 return err;
2479}
2480
2481static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002482cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002483{
2484 const char *dest;
2485 const char *path;
2486 const char *p;
2487 char c;
2488 struct stat statb;
2489 int flags;
2490
2491 flags = cdopt();
2492 dest = *argptr;
2493 if (!dest)
2494 dest = bltinlookup(homestr);
2495 else if (LONE_DASH(dest)) {
2496 dest = bltinlookup("OLDPWD");
2497 flags |= CD_PRINT;
2498 }
2499 if (!dest)
2500 dest = nullstr;
2501 if (*dest == '/')
2502 goto step7;
2503 if (*dest == '.') {
2504 c = dest[1];
2505 dotdot:
2506 switch (c) {
2507 case '\0':
2508 case '/':
2509 goto step6;
2510 case '.':
2511 c = dest[2];
2512 if (c != '.')
2513 goto dotdot;
2514 }
2515 }
2516 if (!*dest)
2517 dest = ".";
2518 path = bltinlookup("CDPATH");
2519 if (!path) {
2520 step6:
2521 step7:
2522 p = dest;
2523 goto docd;
2524 }
2525 do {
2526 c = *path;
2527 p = padvance(&path, dest);
2528 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2529 if (c && c != ':')
2530 flags |= CD_PRINT;
2531 docd:
2532 if (!docd(p, flags))
2533 goto out;
2534 break;
2535 }
2536 } while (path);
2537 ash_msg_and_raise_error("can't cd to %s", dest);
2538 /* NOTREACHED */
2539 out:
2540 if (flags & CD_PRINT)
2541 out1fmt(snlfmt, curdir);
2542 return 0;
2543}
2544
2545static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002546pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002547{
2548 int flags;
2549 const char *dir = curdir;
2550
2551 flags = cdopt();
2552 if (flags) {
2553 if (physdir == nullstr)
2554 setpwd(dir, 0);
2555 dir = physdir;
2556 }
2557 out1fmt(snlfmt, dir);
2558 return 0;
2559}
2560
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002561
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002562/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002563
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002564#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002565#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002566
Eric Andersenc470f442003-07-28 09:56:35 +00002567/* Syntax classes */
2568#define CWORD 0 /* character is nothing special */
2569#define CNL 1 /* newline character */
2570#define CBACK 2 /* a backslash character */
2571#define CSQUOTE 3 /* single quote */
2572#define CDQUOTE 4 /* double quote */
2573#define CENDQUOTE 5 /* a terminating quote */
2574#define CBQUOTE 6 /* backwards single quote */
2575#define CVAR 7 /* a dollar sign */
2576#define CENDVAR 8 /* a '}' character */
2577#define CLP 9 /* a left paren in arithmetic */
2578#define CRP 10 /* a right paren in arithmetic */
2579#define CENDFILE 11 /* end of file */
2580#define CCTL 12 /* like CWORD, except it must be escaped */
2581#define CSPCL 13 /* these terminate a word */
2582#define CIGN 14 /* character should be ignored */
2583
Denis Vlasenko131ae172007-02-18 13:00:19 +00002584#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002585#define SYNBASE 130
2586#define PEOF -130
2587#define PEOA -129
2588#define PEOA_OR_PEOF PEOA
2589#else
2590#define SYNBASE 129
2591#define PEOF -129
2592#define PEOA_OR_PEOF PEOF
2593#endif
2594
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002595/* number syntax index */
2596#define BASESYNTAX 0 /* not in quotes */
2597#define DQSYNTAX 1 /* in double quotes */
2598#define SQSYNTAX 2 /* in single quotes */
2599#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002600#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002601
Denis Vlasenko131ae172007-02-18 13:00:19 +00002602#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002603#define USE_SIT_FUNCTION
2604#endif
2605
Denis Vlasenko131ae172007-02-18 13:00:19 +00002606#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002607static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002608#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002610#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002611 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2612 { CNL, CNL, CNL, CNL }, /* 2, \n */
2613 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2614 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2615 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2616 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2617 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2618 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2619 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2620 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2621 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002622#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002623 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2624 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2625 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002626#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002627};
Eric Andersenc470f442003-07-28 09:56:35 +00002628#else
2629static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002631 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002632#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002633 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2634 { CNL, CNL, CNL }, /* 2, \n */
2635 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2636 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2637 { CVAR, CVAR, CWORD }, /* 5, $ */
2638 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2639 { CSPCL, CWORD, CWORD }, /* 7, ( */
2640 { CSPCL, CWORD, CWORD }, /* 8, ) */
2641 { CBACK, CBACK, CCTL }, /* 9, \ */
2642 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2643 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002644#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002645 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2646 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2647 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002648#endif
2649};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002650#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002651
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002652#ifdef USE_SIT_FUNCTION
2653
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002654static int
2655SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002657 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002658#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002659 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002660 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2661 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2662 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2663 11, 3 /* "}~" */
2664 };
2665#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002666 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002667 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2668 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2669 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2670 10, 2 /* "}~" */
2671 };
2672#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 const char *s;
2674 int indx;
2675
Eric Andersenc470f442003-07-28 09:56:35 +00002676 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002677 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002678#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002679 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002680 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002681 else
2682#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002683#define U_C(c) ((unsigned char)(c))
2684
2685 if ((unsigned char)c >= (unsigned char)(CTLESC)
2686 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2687 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002688 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002689 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002690 s = strchrnul(spec_symbls, c);
2691 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002693 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002694 }
2695 return S_I_T[indx][syntax];
2696}
2697
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002698#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002699
Denis Vlasenko131ae172007-02-18 13:00:19 +00002700#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002701#define CSPCL_CIGN_CIGN_CIGN 0
2702#define CSPCL_CWORD_CWORD_CWORD 1
2703#define CNL_CNL_CNL_CNL 2
2704#define CWORD_CCTL_CCTL_CWORD 3
2705#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2706#define CVAR_CVAR_CWORD_CVAR 5
2707#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2708#define CSPCL_CWORD_CWORD_CLP 7
2709#define CSPCL_CWORD_CWORD_CRP 8
2710#define CBACK_CBACK_CCTL_CBACK 9
2711#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2712#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2713#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2714#define CWORD_CWORD_CWORD_CWORD 13
2715#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002716#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002717#define CSPCL_CWORD_CWORD_CWORD 0
2718#define CNL_CNL_CNL_CNL 1
2719#define CWORD_CCTL_CCTL_CWORD 2
2720#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2721#define CVAR_CVAR_CWORD_CVAR 4
2722#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2723#define CSPCL_CWORD_CWORD_CLP 6
2724#define CSPCL_CWORD_CWORD_CRP 7
2725#define CBACK_CBACK_CCTL_CBACK 8
2726#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2727#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2728#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2729#define CWORD_CWORD_CWORD_CWORD 12
2730#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002731#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002732
2733static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002734 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002735 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002736#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002737 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2738#endif
2739 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2741 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2742 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2743 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2744 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2745 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2746 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2747 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002748 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2878 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2900 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002901 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002902 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2904 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002906 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002907 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2908 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2909 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2912 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2914 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2927 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2928 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2929 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2930 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2931 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2959 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2960 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2961 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2964 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2992 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2993 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2994 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002995};
2996
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002997#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2998
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002999#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003000
Eric Andersen2870d962001-07-02 17:27:21 +00003001
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003002/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003003
Denis Vlasenko131ae172007-02-18 13:00:19 +00003004#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003005
3006#define ALIASINUSE 1
3007#define ALIASDEAD 2
3008
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003009struct alias {
3010 struct alias *next;
3011 char *name;
3012 char *val;
3013 int flag;
3014};
3015
Denis Vlasenko01631112007-12-16 17:20:38 +00003016
3017static struct alias **atab; // [ATABSIZE];
3018#define INIT_G_alias() do { \
3019 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3020} while (0)
3021
Eric Andersen2870d962001-07-02 17:27:21 +00003022
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003023static struct alias **
3024__lookupalias(const char *name) {
3025 unsigned int hashval;
3026 struct alias **app;
3027 const char *p;
3028 unsigned int ch;
3029
3030 p = name;
3031
3032 ch = (unsigned char)*p;
3033 hashval = ch << 4;
3034 while (ch) {
3035 hashval += ch;
3036 ch = (unsigned char)*++p;
3037 }
3038 app = &atab[hashval % ATABSIZE];
3039
3040 for (; *app; app = &(*app)->next) {
3041 if (strcmp(name, (*app)->name) == 0) {
3042 break;
3043 }
3044 }
3045
3046 return app;
3047}
3048
3049static struct alias *
3050lookupalias(const char *name, int check)
3051{
3052 struct alias *ap = *__lookupalias(name);
3053
3054 if (check && ap && (ap->flag & ALIASINUSE))
3055 return NULL;
3056 return ap;
3057}
3058
3059static struct alias *
3060freealias(struct alias *ap)
3061{
3062 struct alias *next;
3063
3064 if (ap->flag & ALIASINUSE) {
3065 ap->flag |= ALIASDEAD;
3066 return ap;
3067 }
3068
3069 next = ap->next;
3070 free(ap->name);
3071 free(ap->val);
3072 free(ap);
3073 return next;
3074}
Eric Andersencb57d552001-06-28 07:25:16 +00003075
Eric Andersenc470f442003-07-28 09:56:35 +00003076static void
3077setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003078{
3079 struct alias *ap, **app;
3080
3081 app = __lookupalias(name);
3082 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003083 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003084 if (ap) {
3085 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003086 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003087 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003088 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003089 ap->flag &= ~ALIASDEAD;
3090 } else {
3091 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003092 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003093 ap->name = ckstrdup(name);
3094 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003095 /*ap->flag = 0; - ckzalloc did it */
3096 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003097 *app = ap;
3098 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003099 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003100}
3101
Eric Andersenc470f442003-07-28 09:56:35 +00003102static int
3103unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003104{
Eric Andersencb57d552001-06-28 07:25:16 +00003105 struct alias **app;
3106
3107 app = __lookupalias(name);
3108
3109 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003110 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003111 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003112 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003113 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003114 }
3115
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003116 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003117}
3118
Eric Andersenc470f442003-07-28 09:56:35 +00003119static void
3120rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003121{
Eric Andersencb57d552001-06-28 07:25:16 +00003122 struct alias *ap, **app;
3123 int i;
3124
Denis Vlasenkob012b102007-02-19 22:43:01 +00003125 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003126 for (i = 0; i < ATABSIZE; i++) {
3127 app = &atab[i];
3128 for (ap = *app; ap; ap = *app) {
3129 *app = freealias(*app);
3130 if (ap == *app) {
3131 app = &ap->next;
3132 }
3133 }
3134 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003135 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003136}
3137
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003138static void
3139printalias(const struct alias *ap)
3140{
3141 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3142}
3143
Eric Andersencb57d552001-06-28 07:25:16 +00003144/*
3145 * TODO - sort output
3146 */
Eric Andersenc470f442003-07-28 09:56:35 +00003147static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003148aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003149{
3150 char *n, *v;
3151 int ret = 0;
3152 struct alias *ap;
3153
Denis Vlasenko68404f12008-03-17 09:00:54 +00003154 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003155 int i;
3156
Denis Vlasenko68404f12008-03-17 09:00:54 +00003157 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003158 for (ap = atab[i]; ap; ap = ap->next) {
3159 printalias(ap);
3160 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003161 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003162 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003163 }
3164 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003165 v = strchr(n+1, '=');
3166 if (v == NULL) { /* n+1: funny ksh stuff */
3167 ap = *__lookupalias(n);
3168 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003169 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003170 ret = 1;
3171 } else
3172 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003173 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003174 *v++ = '\0';
3175 setalias(n, v);
3176 }
3177 }
3178
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003179 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003180}
3181
Eric Andersenc470f442003-07-28 09:56:35 +00003182static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003183unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003184{
3185 int i;
3186
3187 while ((i = nextopt("a")) != '\0') {
3188 if (i == 'a') {
3189 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003190 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003191 }
3192 }
3193 for (i = 0; *argptr; argptr++) {
3194 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003195 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003196 i = 1;
3197 }
3198 }
3199
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003200 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003201}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003202
Denis Vlasenko131ae172007-02-18 13:00:19 +00003203#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003204
Eric Andersenc470f442003-07-28 09:56:35 +00003205
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003206/* ============ jobs.c */
3207
3208/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3209#define FORK_FG 0
3210#define FORK_BG 1
3211#define FORK_NOJOB 2
3212
3213/* mode flags for showjob(s) */
3214#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3215#define SHOW_PID 0x04 /* include process pid */
3216#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3217
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003218/*
3219 * A job structure contains information about a job. A job is either a
3220 * single process or a set of processes contained in a pipeline. In the
3221 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3222 * array of pids.
3223 */
3224
3225struct procstat {
3226 pid_t pid; /* process id */
3227 int status; /* last process status from wait() */
3228 char *cmd; /* text of command being run */
3229};
3230
3231struct job {
3232 struct procstat ps0; /* status of process */
3233 struct procstat *ps; /* status or processes when more than one */
3234#if JOBS
3235 int stopstatus; /* status of a stopped job */
3236#endif
3237 uint32_t
3238 nprocs: 16, /* number of processes */
3239 state: 8,
3240#define JOBRUNNING 0 /* at least one proc running */
3241#define JOBSTOPPED 1 /* all procs are stopped */
3242#define JOBDONE 2 /* all procs are completed */
3243#if JOBS
3244 sigint: 1, /* job was killed by SIGINT */
3245 jobctl: 1, /* job running under job control */
3246#endif
3247 waited: 1, /* true if this entry has been waited for */
3248 used: 1, /* true if this entry is in used */
3249 changed: 1; /* true if status has changed */
3250 struct job *prev_job; /* previous job */
3251};
3252
3253static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003254static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003255
Denis Vlasenko68404f12008-03-17 09:00:54 +00003256static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003257#if !JOBS
3258#define forkshell(job, node, mode) forkshell(job, mode)
3259#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003260static int forkshell(struct job *, union node *, int);
3261static int waitforjob(struct job *);
3262
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003263#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003264enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003265#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003266#else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003267static smallint doing_jobctl;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003268static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003269#endif
3270
3271/*
3272 * Set the signal handler for the specified signal. The routine figures
3273 * out what it should be set to.
3274 */
3275static void
3276setsignal(int signo)
3277{
3278 int action;
3279 char *t, tsig;
3280 struct sigaction act;
3281
3282 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003283 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003284 if (t == NULL)
3285 action = S_DFL;
3286 else if (*t != '\0')
3287 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003288 if (rootshell && action == S_DFL) {
3289 switch (signo) {
3290 case SIGINT:
3291 if (iflag || minusc || sflag == 0)
3292 action = S_CATCH;
3293 break;
3294 case SIGQUIT:
3295#if DEBUG
3296 if (debug)
3297 break;
3298#endif
3299 /* FALLTHROUGH */
3300 case SIGTERM:
3301 if (iflag)
3302 action = S_IGN;
3303 break;
3304#if JOBS
3305 case SIGTSTP:
3306 case SIGTTOU:
3307 if (mflag)
3308 action = S_IGN;
3309 break;
3310#endif
3311 }
3312 }
3313
3314 t = &sigmode[signo - 1];
3315 tsig = *t;
3316 if (tsig == 0) {
3317 /*
3318 * current setting unknown
3319 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003320 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321 /*
3322 * Pretend it worked; maybe we should give a warning
3323 * here, but other shells don't. We don't alter
3324 * sigmode, so that we retry every time.
3325 */
3326 return;
3327 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003330 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331 if (mflag
3332 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3333 ) {
3334 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003335 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336 }
3337 }
3338 if (tsig == S_HARD_IGN || tsig == action)
3339 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003340 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 switch (action) {
3342 case S_CATCH:
3343 act.sa_handler = onsig;
3344 break;
3345 case S_IGN:
3346 act.sa_handler = SIG_IGN;
3347 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348 }
3349 *t = action;
3350 act.sa_flags = 0;
3351 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003352 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353}
3354
3355/* mode flags for set_curjob */
3356#define CUR_DELETE 2
3357#define CUR_RUNNING 1
3358#define CUR_STOPPED 0
3359
3360/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003361#define DOWAIT_NONBLOCK WNOHANG
3362#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003363
3364#if JOBS
3365/* pgrp of shell on invocation */
3366static int initialpgrp;
3367static int ttyfd = -1;
3368#endif
3369/* array of jobs */
3370static struct job *jobtab;
3371/* size of array */
3372static unsigned njobs;
3373/* current job */
3374static struct job *curjob;
3375/* number of presumed living untracked jobs */
3376static int jobless;
3377
3378static void
3379set_curjob(struct job *jp, unsigned mode)
3380{
3381 struct job *jp1;
3382 struct job **jpp, **curp;
3383
3384 /* first remove from list */
3385 jpp = curp = &curjob;
3386 do {
3387 jp1 = *jpp;
3388 if (jp1 == jp)
3389 break;
3390 jpp = &jp1->prev_job;
3391 } while (1);
3392 *jpp = jp1->prev_job;
3393
3394 /* Then re-insert in correct position */
3395 jpp = curp;
3396 switch (mode) {
3397 default:
3398#if DEBUG
3399 abort();
3400#endif
3401 case CUR_DELETE:
3402 /* job being deleted */
3403 break;
3404 case CUR_RUNNING:
3405 /* newly created job or backgrounded job,
3406 put after all stopped jobs. */
3407 do {
3408 jp1 = *jpp;
3409#if JOBS
3410 if (!jp1 || jp1->state != JOBSTOPPED)
3411#endif
3412 break;
3413 jpp = &jp1->prev_job;
3414 } while (1);
3415 /* FALLTHROUGH */
3416#if JOBS
3417 case CUR_STOPPED:
3418#endif
3419 /* newly stopped job - becomes curjob */
3420 jp->prev_job = *jpp;
3421 *jpp = jp;
3422 break;
3423 }
3424}
3425
3426#if JOBS || DEBUG
3427static int
3428jobno(const struct job *jp)
3429{
3430 return jp - jobtab + 1;
3431}
3432#endif
3433
3434/*
3435 * Convert a job name to a job structure.
3436 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003437#if !JOBS
3438#define getjob(name, getctl) getjob(name)
3439#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440static struct job *
3441getjob(const char *name, int getctl)
3442{
3443 struct job *jp;
3444 struct job *found;
3445 const char *err_msg = "No such job: %s";
3446 unsigned num;
3447 int c;
3448 const char *p;
3449 char *(*match)(const char *, const char *);
3450
3451 jp = curjob;
3452 p = name;
3453 if (!p)
3454 goto currentjob;
3455
3456 if (*p != '%')
3457 goto err;
3458
3459 c = *++p;
3460 if (!c)
3461 goto currentjob;
3462
3463 if (!p[1]) {
3464 if (c == '+' || c == '%') {
3465 currentjob:
3466 err_msg = "No current job";
3467 goto check;
3468 }
3469 if (c == '-') {
3470 if (jp)
3471 jp = jp->prev_job;
3472 err_msg = "No previous job";
3473 check:
3474 if (!jp)
3475 goto err;
3476 goto gotit;
3477 }
3478 }
3479
3480 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003481// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003482 num = atoi(p);
3483 if (num < njobs) {
3484 jp = jobtab + num - 1;
3485 if (jp->used)
3486 goto gotit;
3487 goto err;
3488 }
3489 }
3490
3491 match = prefix;
3492 if (*p == '?') {
3493 match = strstr;
3494 p++;
3495 }
3496
3497 found = 0;
3498 while (1) {
3499 if (!jp)
3500 goto err;
3501 if (match(jp->ps[0].cmd, p)) {
3502 if (found)
3503 goto err;
3504 found = jp;
3505 err_msg = "%s: ambiguous";
3506 }
3507 jp = jp->prev_job;
3508 }
3509
3510 gotit:
3511#if JOBS
3512 err_msg = "job %s not created under job control";
3513 if (getctl && jp->jobctl == 0)
3514 goto err;
3515#endif
3516 return jp;
3517 err:
3518 ash_msg_and_raise_error(err_msg, name);
3519}
3520
3521/*
3522 * Mark a job structure as unused.
3523 */
3524static void
3525freejob(struct job *jp)
3526{
3527 struct procstat *ps;
3528 int i;
3529
3530 INT_OFF;
3531 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3532 if (ps->cmd != nullstr)
3533 free(ps->cmd);
3534 }
3535 if (jp->ps != &jp->ps0)
3536 free(jp->ps);
3537 jp->used = 0;
3538 set_curjob(jp, CUR_DELETE);
3539 INT_ON;
3540}
3541
3542#if JOBS
3543static void
3544xtcsetpgrp(int fd, pid_t pgrp)
3545{
3546 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003547 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548}
3549
3550/*
3551 * Turn job control on and off.
3552 *
3553 * Note: This code assumes that the third arg to ioctl is a character
3554 * pointer, which is true on Berkeley systems but not System V. Since
3555 * System V doesn't have job control yet, this isn't a problem now.
3556 *
3557 * Called with interrupts off.
3558 */
3559static void
3560setjobctl(int on)
3561{
3562 int fd;
3563 int pgrp;
3564
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003565 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003566 return;
3567 if (on) {
3568 int ofd;
3569 ofd = fd = open(_PATH_TTY, O_RDWR);
3570 if (fd < 0) {
3571 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3572 * That sometimes helps to acquire controlling tty.
3573 * Obviously, a workaround for bugs when someone
3574 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003575 fd = 2;
3576 while (!isatty(fd))
3577 if (--fd < 0)
3578 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003579 }
3580 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003581 if (ofd >= 0)
3582 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003583 if (fd < 0)
3584 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003585 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003586 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003587 do { /* while we are in the background */
3588 pgrp = tcgetpgrp(fd);
3589 if (pgrp < 0) {
3590 out:
3591 ash_msg("can't access tty; job control turned off");
3592 mflag = on = 0;
3593 goto close;
3594 }
3595 if (pgrp == getpgrp())
3596 break;
3597 killpg(0, SIGTTIN);
3598 } while (1);
3599 initialpgrp = pgrp;
3600
3601 setsignal(SIGTSTP);
3602 setsignal(SIGTTOU);
3603 setsignal(SIGTTIN);
3604 pgrp = rootpid;
3605 setpgid(0, pgrp);
3606 xtcsetpgrp(fd, pgrp);
3607 } else {
3608 /* turning job control off */
3609 fd = ttyfd;
3610 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003611 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003612 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003613 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614 setpgid(0, pgrp);
3615 setsignal(SIGTSTP);
3616 setsignal(SIGTTOU);
3617 setsignal(SIGTTIN);
3618 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003619 if (fd >= 0)
3620 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621 fd = -1;
3622 }
3623 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003624 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003625}
3626
3627static int
3628killcmd(int argc, char **argv)
3629{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003630 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003631 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003632 do {
3633 if (argv[i][0] == '%') {
3634 struct job *jp = getjob(argv[i], 0);
3635 unsigned pid = jp->ps[0].pid;
3636 /* Enough space for ' -NNN<nul>' */
3637 argv[i] = alloca(sizeof(int)*3 + 3);
3638 /* kill_main has matching code to expect
3639 * leading space. Needed to not confuse
3640 * negative pids with "kill -SIGNAL_NO" syntax */
3641 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003642 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003643 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003644 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003645 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003646}
3647
3648static void
3649showpipe(struct job *jp, FILE *out)
3650{
3651 struct procstat *sp;
3652 struct procstat *spend;
3653
3654 spend = jp->ps + jp->nprocs;
3655 for (sp = jp->ps + 1; sp < spend; sp++)
3656 fprintf(out, " | %s", sp->cmd);
3657 outcslow('\n', out);
3658 flush_stdout_stderr();
3659}
3660
3661
3662static int
3663restartjob(struct job *jp, int mode)
3664{
3665 struct procstat *ps;
3666 int i;
3667 int status;
3668 pid_t pgid;
3669
3670 INT_OFF;
3671 if (jp->state == JOBDONE)
3672 goto out;
3673 jp->state = JOBRUNNING;
3674 pgid = jp->ps->pid;
3675 if (mode == FORK_FG)
3676 xtcsetpgrp(ttyfd, pgid);
3677 killpg(pgid, SIGCONT);
3678 ps = jp->ps;
3679 i = jp->nprocs;
3680 do {
3681 if (WIFSTOPPED(ps->status)) {
3682 ps->status = -1;
3683 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003684 ps++;
3685 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686 out:
3687 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3688 INT_ON;
3689 return status;
3690}
3691
3692static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003693fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003694{
3695 struct job *jp;
3696 FILE *out;
3697 int mode;
3698 int retval;
3699
3700 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3701 nextopt(nullstr);
3702 argv = argptr;
3703 out = stdout;
3704 do {
3705 jp = getjob(*argv, 1);
3706 if (mode == FORK_BG) {
3707 set_curjob(jp, CUR_RUNNING);
3708 fprintf(out, "[%d] ", jobno(jp));
3709 }
3710 outstr(jp->ps->cmd, out);
3711 showpipe(jp, out);
3712 retval = restartjob(jp, mode);
3713 } while (*argv && *++argv);
3714 return retval;
3715}
3716#endif
3717
3718static int
3719sprint_status(char *s, int status, int sigonly)
3720{
3721 int col;
3722 int st;
3723
3724 col = 0;
3725 if (!WIFEXITED(status)) {
3726#if JOBS
3727 if (WIFSTOPPED(status))
3728 st = WSTOPSIG(status);
3729 else
3730#endif
3731 st = WTERMSIG(status);
3732 if (sigonly) {
3733 if (st == SIGINT || st == SIGPIPE)
3734 goto out;
3735#if JOBS
3736 if (WIFSTOPPED(status))
3737 goto out;
3738#endif
3739 }
3740 st &= 0x7f;
3741 col = fmtstr(s, 32, strsignal(st));
3742 if (WCOREDUMP(status)) {
3743 col += fmtstr(s + col, 16, " (core dumped)");
3744 }
3745 } else if (!sigonly) {
3746 st = WEXITSTATUS(status);
3747 if (st)
3748 col = fmtstr(s, 16, "Done(%d)", st);
3749 else
3750 col = fmtstr(s, 16, "Done");
3751 }
3752 out:
3753 return col;
3754}
3755
3756/*
3757 * Do a wait system call. If job control is compiled in, we accept
3758 * stopped processes. If block is zero, we return a value of zero
3759 * rather than blocking.
3760 *
3761 * System V doesn't have a non-blocking wait system call. It does
3762 * have a SIGCLD signal that is sent to a process when one of it's
3763 * children dies. The obvious way to use SIGCLD would be to install
3764 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3765 * was received, and have waitproc bump another counter when it got
3766 * the status of a process. Waitproc would then know that a wait
3767 * system call would not block if the two counters were different.
3768 * This approach doesn't work because if a process has children that
3769 * have not been waited for, System V will send it a SIGCLD when it
3770 * installs a signal handler for SIGCLD. What this means is that when
3771 * a child exits, the shell will be sent SIGCLD signals continuously
3772 * until is runs out of stack space, unless it does a wait call before
3773 * restoring the signal handler. The code below takes advantage of
3774 * this (mis)feature by installing a signal handler for SIGCLD and
3775 * then checking to see whether it was called. If there are any
3776 * children to be waited for, it will be.
3777 *
3778 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3779 * waits at all. In this case, the user will not be informed when
3780 * a background process until the next time she runs a real program
3781 * (as opposed to running a builtin command or just typing return),
3782 * and the jobs command may give out of date information.
3783 */
3784static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003785waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003788 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003789 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003791 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3792 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003793}
3794
3795/*
3796 * Wait for a process to terminate.
3797 */
3798static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003799dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800{
3801 int pid;
3802 int status;
3803 struct job *jp;
3804 struct job *thisjob;
3805 int state;
3806
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003807 TRACE(("dowait(%d) called\n", wait_flags));
3808 pid = waitproc(wait_flags, &status);
3809 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003810 if (pid <= 0) {
3811 /* If we were doing blocking wait and (probably) got EINTR,
3812 * check for pending sigs received while waiting.
3813 * (NB: can be moved into callers if needed) */
3814 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3815 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003816 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003817 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003818 INT_OFF;
3819 thisjob = NULL;
3820 for (jp = curjob; jp; jp = jp->prev_job) {
3821 struct procstat *sp;
3822 struct procstat *spend;
3823 if (jp->state == JOBDONE)
3824 continue;
3825 state = JOBDONE;
3826 spend = jp->ps + jp->nprocs;
3827 sp = jp->ps;
3828 do {
3829 if (sp->pid == pid) {
3830 TRACE(("Job %d: changing status of proc %d "
3831 "from 0x%x to 0x%x\n",
3832 jobno(jp), pid, sp->status, status));
3833 sp->status = status;
3834 thisjob = jp;
3835 }
3836 if (sp->status == -1)
3837 state = JOBRUNNING;
3838#if JOBS
3839 if (state == JOBRUNNING)
3840 continue;
3841 if (WIFSTOPPED(sp->status)) {
3842 jp->stopstatus = sp->status;
3843 state = JOBSTOPPED;
3844 }
3845#endif
3846 } while (++sp < spend);
3847 if (thisjob)
3848 goto gotjob;
3849 }
3850#if JOBS
3851 if (!WIFSTOPPED(status))
3852#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003853 jobless--;
3854 goto out;
3855
3856 gotjob:
3857 if (state != JOBRUNNING) {
3858 thisjob->changed = 1;
3859
3860 if (thisjob->state != state) {
3861 TRACE(("Job %d: changing state from %d to %d\n",
3862 jobno(thisjob), thisjob->state, state));
3863 thisjob->state = state;
3864#if JOBS
3865 if (state == JOBSTOPPED) {
3866 set_curjob(thisjob, CUR_STOPPED);
3867 }
3868#endif
3869 }
3870 }
3871
3872 out:
3873 INT_ON;
3874
3875 if (thisjob && thisjob == job) {
3876 char s[48 + 1];
3877 int len;
3878
3879 len = sprint_status(s, status, 1);
3880 if (len) {
3881 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003882 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883 out2str(s);
3884 }
3885 }
3886 return pid;
3887}
3888
3889#if JOBS
3890static void
3891showjob(FILE *out, struct job *jp, int mode)
3892{
3893 struct procstat *ps;
3894 struct procstat *psend;
3895 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003896 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003897 char s[80];
3898
3899 ps = jp->ps;
3900
3901 if (mode & SHOW_PGID) {
3902 /* just output process (group) id of pipeline */
3903 fprintf(out, "%d\n", ps->pid);
3904 return;
3905 }
3906
3907 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003908 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003909
3910 if (jp == curjob)
3911 s[col - 2] = '+';
3912 else if (curjob && jp == curjob->prev_job)
3913 s[col - 2] = '-';
3914
3915 if (mode & SHOW_PID)
3916 col += fmtstr(s + col, 16, "%d ", ps->pid);
3917
3918 psend = ps + jp->nprocs;
3919
3920 if (jp->state == JOBRUNNING) {
3921 strcpy(s + col, "Running");
3922 col += sizeof("Running") - 1;
3923 } else {
3924 int status = psend[-1].status;
3925 if (jp->state == JOBSTOPPED)
3926 status = jp->stopstatus;
3927 col += sprint_status(s + col, status, 0);
3928 }
3929
3930 goto start;
3931
3932 do {
3933 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003934 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003935 start:
3936 fprintf(out, "%s%*c%s",
3937 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3938 );
3939 if (!(mode & SHOW_PID)) {
3940 showpipe(jp, out);
3941 break;
3942 }
3943 if (++ps == psend) {
3944 outcslow('\n', out);
3945 break;
3946 }
3947 } while (1);
3948
3949 jp->changed = 0;
3950
3951 if (jp->state == JOBDONE) {
3952 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3953 freejob(jp);
3954 }
3955}
3956
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957/*
3958 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3959 * statuses have changed since the last call to showjobs.
3960 */
3961static void
3962showjobs(FILE *out, int mode)
3963{
3964 struct job *jp;
3965
3966 TRACE(("showjobs(%x) called\n", mode));
3967
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003968 /* If not even one job changed, there is nothing to do */
3969 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 continue;
3971
3972 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003975 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 }
3977}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003978
3979static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003980jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003981{
3982 int mode, m;
3983
3984 mode = 0;
3985 while ((m = nextopt("lp"))) {
3986 if (m == 'l')
3987 mode = SHOW_PID;
3988 else
3989 mode = SHOW_PGID;
3990 }
3991
3992 argv = argptr;
3993 if (*argv) {
3994 do
3995 showjob(stdout, getjob(*argv,0), mode);
3996 while (*++argv);
3997 } else
3998 showjobs(stdout, mode);
3999
4000 return 0;
4001}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002#endif /* JOBS */
4003
4004static int
4005getstatus(struct job *job)
4006{
4007 int status;
4008 int retval;
4009
4010 status = job->ps[job->nprocs - 1].status;
4011 retval = WEXITSTATUS(status);
4012 if (!WIFEXITED(status)) {
4013#if JOBS
4014 retval = WSTOPSIG(status);
4015 if (!WIFSTOPPED(status))
4016#endif
4017 {
4018 /* XXX: limits number of signals */
4019 retval = WTERMSIG(status);
4020#if JOBS
4021 if (retval == SIGINT)
4022 job->sigint = 1;
4023#endif
4024 }
4025 retval += 128;
4026 }
4027 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4028 jobno(job), job->nprocs, status, retval));
4029 return retval;
4030}
4031
4032static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004033waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034{
4035 struct job *job;
4036 int retval;
4037 struct job *jp;
4038
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004039// exsig++;
4040// xbarrier();
4041 if (pendingsig)
4042 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043
4044 nextopt(nullstr);
4045 retval = 0;
4046
4047 argv = argptr;
4048 if (!*argv) {
4049 /* wait for all jobs */
4050 for (;;) {
4051 jp = curjob;
4052 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004053 if (!jp) /* no running procs */
4054 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 if (jp->state == JOBRUNNING)
4056 break;
4057 jp->waited = 1;
4058 jp = jp->prev_job;
4059 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004060 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 }
4062 }
4063
4064 retval = 127;
4065 do {
4066 if (**argv != '%') {
4067 pid_t pid = number(*argv);
4068 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004069 while (1) {
4070 if (!job)
4071 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004072 if (job->ps[job->nprocs - 1].pid == pid)
4073 break;
4074 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004075 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076 } else
4077 job = getjob(*argv, 0);
4078 /* loop until process terminated or stopped */
4079 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004080 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 job->waited = 1;
4082 retval = getstatus(job);
4083 repeat:
4084 ;
4085 } while (*++argv);
4086
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004087 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 return retval;
4089}
4090
4091static struct job *
4092growjobtab(void)
4093{
4094 size_t len;
4095 ptrdiff_t offset;
4096 struct job *jp, *jq;
4097
4098 len = njobs * sizeof(*jp);
4099 jq = jobtab;
4100 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4101
4102 offset = (char *)jp - (char *)jq;
4103 if (offset) {
4104 /* Relocate pointers */
4105 size_t l = len;
4106
4107 jq = (struct job *)((char *)jq + l);
4108 while (l) {
4109 l -= sizeof(*jp);
4110 jq--;
4111#define joff(p) ((struct job *)((char *)(p) + l))
4112#define jmove(p) (p) = (void *)((char *)(p) + offset)
4113 if (joff(jp)->ps == &jq->ps0)
4114 jmove(joff(jp)->ps);
4115 if (joff(jp)->prev_job)
4116 jmove(joff(jp)->prev_job);
4117 }
4118 if (curjob)
4119 jmove(curjob);
4120#undef joff
4121#undef jmove
4122 }
4123
4124 njobs += 4;
4125 jobtab = jp;
4126 jp = (struct job *)((char *)jp + len);
4127 jq = jp + 3;
4128 do {
4129 jq->used = 0;
4130 } while (--jq >= jp);
4131 return jp;
4132}
4133
4134/*
4135 * Return a new job structure.
4136 * Called with interrupts off.
4137 */
4138static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004139makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140{
4141 int i;
4142 struct job *jp;
4143
4144 for (i = njobs, jp = jobtab; ; jp++) {
4145 if (--i < 0) {
4146 jp = growjobtab();
4147 break;
4148 }
4149 if (jp->used == 0)
4150 break;
4151 if (jp->state != JOBDONE || !jp->waited)
4152 continue;
4153#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004154 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155 continue;
4156#endif
4157 freejob(jp);
4158 break;
4159 }
4160 memset(jp, 0, sizeof(*jp));
4161#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004162 /* jp->jobctl is a bitfield.
4163 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004164 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004165 jp->jobctl = 1;
4166#endif
4167 jp->prev_job = curjob;
4168 curjob = jp;
4169 jp->used = 1;
4170 jp->ps = &jp->ps0;
4171 if (nprocs > 1) {
4172 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4173 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004174 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 jobno(jp)));
4176 return jp;
4177}
4178
4179#if JOBS
4180/*
4181 * Return a string identifying a command (to be printed by the
4182 * jobs command).
4183 */
4184static char *cmdnextc;
4185
4186static void
4187cmdputs(const char *s)
4188{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004189 static const char vstype[VSTYPE + 1][3] = {
4190 "", "}", "-", "+", "?", "=",
4191 "%", "%%", "#", "##"
4192 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4193 };
4194
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 const char *p, *str;
4196 char c, cc[2] = " ";
4197 char *nextc;
4198 int subtype = 0;
4199 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004200
4201 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4202 p = s;
4203 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004204 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004205 switch (c) {
4206 case CTLESC:
4207 c = *p++;
4208 break;
4209 case CTLVAR:
4210 subtype = *p++;
4211 if ((subtype & VSTYPE) == VSLENGTH)
4212 str = "${#";
4213 else
4214 str = "${";
4215 if (!(subtype & VSQUOTE) == !(quoted & 1))
4216 goto dostr;
4217 quoted ^= 1;
4218 c = '"';
4219 break;
4220 case CTLENDVAR:
4221 str = "\"}" + !(quoted & 1);
4222 quoted >>= 1;
4223 subtype = 0;
4224 goto dostr;
4225 case CTLBACKQ:
4226 str = "$(...)";
4227 goto dostr;
4228 case CTLBACKQ+CTLQUOTE:
4229 str = "\"$(...)\"";
4230 goto dostr;
4231#if ENABLE_ASH_MATH_SUPPORT
4232 case CTLARI:
4233 str = "$((";
4234 goto dostr;
4235 case CTLENDARI:
4236 str = "))";
4237 goto dostr;
4238#endif
4239 case CTLQUOTEMARK:
4240 quoted ^= 1;
4241 c = '"';
4242 break;
4243 case '=':
4244 if (subtype == 0)
4245 break;
4246 if ((subtype & VSTYPE) != VSNORMAL)
4247 quoted <<= 1;
4248 str = vstype[subtype & VSTYPE];
4249 if (subtype & VSNUL)
4250 c = ':';
4251 else
4252 goto checkstr;
4253 break;
4254 case '\'':
4255 case '\\':
4256 case '"':
4257 case '$':
4258 /* These can only happen inside quotes */
4259 cc[0] = c;
4260 str = cc;
4261 c = '\\';
4262 break;
4263 default:
4264 break;
4265 }
4266 USTPUTC(c, nextc);
4267 checkstr:
4268 if (!str)
4269 continue;
4270 dostr:
4271 while ((c = *str++)) {
4272 USTPUTC(c, nextc);
4273 }
4274 }
4275 if (quoted & 1) {
4276 USTPUTC('"', nextc);
4277 }
4278 *nextc = 0;
4279 cmdnextc = nextc;
4280}
4281
4282/* cmdtxt() and cmdlist() call each other */
4283static void cmdtxt(union node *n);
4284
4285static void
4286cmdlist(union node *np, int sep)
4287{
4288 for (; np; np = np->narg.next) {
4289 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004290 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004291 cmdtxt(np);
4292 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004293 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004294 }
4295}
4296
4297static void
4298cmdtxt(union node *n)
4299{
4300 union node *np;
4301 struct nodelist *lp;
4302 const char *p;
4303 char s[2];
4304
4305 if (!n)
4306 return;
4307 switch (n->type) {
4308 default:
4309#if DEBUG
4310 abort();
4311#endif
4312 case NPIPE:
4313 lp = n->npipe.cmdlist;
4314 for (;;) {
4315 cmdtxt(lp->n);
4316 lp = lp->next;
4317 if (!lp)
4318 break;
4319 cmdputs(" | ");
4320 }
4321 break;
4322 case NSEMI:
4323 p = "; ";
4324 goto binop;
4325 case NAND:
4326 p = " && ";
4327 goto binop;
4328 case NOR:
4329 p = " || ";
4330 binop:
4331 cmdtxt(n->nbinary.ch1);
4332 cmdputs(p);
4333 n = n->nbinary.ch2;
4334 goto donode;
4335 case NREDIR:
4336 case NBACKGND:
4337 n = n->nredir.n;
4338 goto donode;
4339 case NNOT:
4340 cmdputs("!");
4341 n = n->nnot.com;
4342 donode:
4343 cmdtxt(n);
4344 break;
4345 case NIF:
4346 cmdputs("if ");
4347 cmdtxt(n->nif.test);
4348 cmdputs("; then ");
4349 n = n->nif.ifpart;
4350 if (n->nif.elsepart) {
4351 cmdtxt(n);
4352 cmdputs("; else ");
4353 n = n->nif.elsepart;
4354 }
4355 p = "; fi";
4356 goto dotail;
4357 case NSUBSHELL:
4358 cmdputs("(");
4359 n = n->nredir.n;
4360 p = ")";
4361 goto dotail;
4362 case NWHILE:
4363 p = "while ";
4364 goto until;
4365 case NUNTIL:
4366 p = "until ";
4367 until:
4368 cmdputs(p);
4369 cmdtxt(n->nbinary.ch1);
4370 n = n->nbinary.ch2;
4371 p = "; done";
4372 dodo:
4373 cmdputs("; do ");
4374 dotail:
4375 cmdtxt(n);
4376 goto dotail2;
4377 case NFOR:
4378 cmdputs("for ");
4379 cmdputs(n->nfor.var);
4380 cmdputs(" in ");
4381 cmdlist(n->nfor.args, 1);
4382 n = n->nfor.body;
4383 p = "; done";
4384 goto dodo;
4385 case NDEFUN:
4386 cmdputs(n->narg.text);
4387 p = "() { ... }";
4388 goto dotail2;
4389 case NCMD:
4390 cmdlist(n->ncmd.args, 1);
4391 cmdlist(n->ncmd.redirect, 0);
4392 break;
4393 case NARG:
4394 p = n->narg.text;
4395 dotail2:
4396 cmdputs(p);
4397 break;
4398 case NHERE:
4399 case NXHERE:
4400 p = "<<...";
4401 goto dotail2;
4402 case NCASE:
4403 cmdputs("case ");
4404 cmdputs(n->ncase.expr->narg.text);
4405 cmdputs(" in ");
4406 for (np = n->ncase.cases; np; np = np->nclist.next) {
4407 cmdtxt(np->nclist.pattern);
4408 cmdputs(") ");
4409 cmdtxt(np->nclist.body);
4410 cmdputs(";; ");
4411 }
4412 p = "esac";
4413 goto dotail2;
4414 case NTO:
4415 p = ">";
4416 goto redir;
4417 case NCLOBBER:
4418 p = ">|";
4419 goto redir;
4420 case NAPPEND:
4421 p = ">>";
4422 goto redir;
4423 case NTOFD:
4424 p = ">&";
4425 goto redir;
4426 case NFROM:
4427 p = "<";
4428 goto redir;
4429 case NFROMFD:
4430 p = "<&";
4431 goto redir;
4432 case NFROMTO:
4433 p = "<>";
4434 redir:
4435 s[0] = n->nfile.fd + '0';
4436 s[1] = '\0';
4437 cmdputs(s);
4438 cmdputs(p);
4439 if (n->type == NTOFD || n->type == NFROMFD) {
4440 s[0] = n->ndup.dupfd + '0';
4441 p = s;
4442 goto dotail2;
4443 }
4444 n = n->nfile.fname;
4445 goto donode;
4446 }
4447}
4448
4449static char *
4450commandtext(union node *n)
4451{
4452 char *name;
4453
4454 STARTSTACKSTR(cmdnextc);
4455 cmdtxt(n);
4456 name = stackblock();
4457 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4458 name, cmdnextc, cmdnextc));
4459 return ckstrdup(name);
4460}
4461#endif /* JOBS */
4462
4463/*
4464 * Fork off a subshell. If we are doing job control, give the subshell its
4465 * own process group. Jp is a job structure that the job is to be added to.
4466 * N is the command that will be evaluated by the child. Both jp and n may
4467 * be NULL. The mode parameter can be one of the following:
4468 * FORK_FG - Fork off a foreground process.
4469 * FORK_BG - Fork off a background process.
4470 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4471 * process group even if job control is on.
4472 *
4473 * When job control is turned off, background processes have their standard
4474 * input redirected to /dev/null (except for the second and later processes
4475 * in a pipeline).
4476 *
4477 * Called with interrupts off.
4478 */
4479/*
4480 * Clear traps on a fork.
4481 */
4482static void
4483clear_traps(void)
4484{
4485 char **tp;
4486
4487 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004488 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 INT_OFF;
4490 free(*tp);
4491 *tp = NULL;
4492 if (tp != &trap[0])
4493 setsignal(tp - trap);
4494 INT_ON;
4495 }
4496 }
4497}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004498
4499/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004501
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004502/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004504forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505{
4506 int oldlvl;
4507
4508 TRACE(("Child shell %d\n", getpid()));
4509 oldlvl = shlvl;
4510 shlvl++;
4511
4512 closescript();
4513 clear_traps();
4514#if JOBS
4515 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004516 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004517 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4518 pid_t pgrp;
4519
4520 if (jp->nprocs == 0)
4521 pgrp = getpid();
4522 else
4523 pgrp = jp->ps[0].pid;
4524 /* This can fail because we are doing it in the parent also */
4525 (void)setpgid(0, pgrp);
4526 if (mode == FORK_FG)
4527 xtcsetpgrp(ttyfd, pgrp);
4528 setsignal(SIGTSTP);
4529 setsignal(SIGTTOU);
4530 } else
4531#endif
4532 if (mode == FORK_BG) {
4533 ignoresig(SIGINT);
4534 ignoresig(SIGQUIT);
4535 if (jp->nprocs == 0) {
4536 close(0);
4537 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004538 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004539 }
4540 }
4541 if (!oldlvl && iflag) {
4542 setsignal(SIGINT);
4543 setsignal(SIGQUIT);
4544 setsignal(SIGTERM);
4545 }
4546 for (jp = curjob; jp; jp = jp->prev_job)
4547 freejob(jp);
4548 jobless = 0;
4549}
4550
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004551/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004552#if !JOBS
4553#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4554#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004555static void
4556forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4557{
4558 TRACE(("In parent shell: child = %d\n", pid));
4559 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004560 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4561 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004562 jobless++;
4563 return;
4564 }
4565#if JOBS
4566 if (mode != FORK_NOJOB && jp->jobctl) {
4567 int pgrp;
4568
4569 if (jp->nprocs == 0)
4570 pgrp = pid;
4571 else
4572 pgrp = jp->ps[0].pid;
4573 /* This can fail because we are doing it in the child also */
4574 setpgid(pid, pgrp);
4575 }
4576#endif
4577 if (mode == FORK_BG) {
4578 backgndpid = pid; /* set $! */
4579 set_curjob(jp, CUR_RUNNING);
4580 }
4581 if (jp) {
4582 struct procstat *ps = &jp->ps[jp->nprocs++];
4583 ps->pid = pid;
4584 ps->status = -1;
4585 ps->cmd = nullstr;
4586#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004587 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004588 ps->cmd = commandtext(n);
4589#endif
4590 }
4591}
4592
4593static int
4594forkshell(struct job *jp, union node *n, int mode)
4595{
4596 int pid;
4597
4598 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4599 pid = fork();
4600 if (pid < 0) {
4601 TRACE(("Fork failed, errno=%d", errno));
4602 if (jp)
4603 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004604 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004605 }
4606 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004607 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004608 else
4609 forkparent(jp, n, mode, pid);
4610 return pid;
4611}
4612
4613/*
4614 * Wait for job to finish.
4615 *
4616 * Under job control we have the problem that while a child process is
4617 * running interrupts generated by the user are sent to the child but not
4618 * to the shell. This means that an infinite loop started by an inter-
4619 * active user may be hard to kill. With job control turned off, an
4620 * interactive user may place an interactive program inside a loop. If
4621 * the interactive program catches interrupts, the user doesn't want
4622 * these interrupts to also abort the loop. The approach we take here
4623 * is to have the shell ignore interrupt signals while waiting for a
4624 * foreground process to terminate, and then send itself an interrupt
4625 * signal if the child process was terminated by an interrupt signal.
4626 * Unfortunately, some programs want to do a bit of cleanup and then
4627 * exit on interrupt; unless these processes terminate themselves by
4628 * sending a signal to themselves (instead of calling exit) they will
4629 * confuse this approach.
4630 *
4631 * Called with interrupts off.
4632 */
4633static int
4634waitforjob(struct job *jp)
4635{
4636 int st;
4637
4638 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4639 while (jp->state == JOBRUNNING) {
4640 dowait(DOWAIT_BLOCK, jp);
4641 }
4642 st = getstatus(jp);
4643#if JOBS
4644 if (jp->jobctl) {
4645 xtcsetpgrp(ttyfd, rootpid);
4646 /*
4647 * This is truly gross.
4648 * If we're doing job control, then we did a TIOCSPGRP which
4649 * caused us (the shell) to no longer be in the controlling
4650 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4651 * intuit from the subprocess exit status whether a SIGINT
4652 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4653 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004654 if (jp->sigint) /* TODO: do the same with all signals */
4655 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656 }
4657 if (jp->state == JOBDONE)
4658#endif
4659 freejob(jp);
4660 return st;
4661}
4662
4663/*
4664 * return 1 if there are stopped jobs, otherwise 0
4665 */
4666static int
4667stoppedjobs(void)
4668{
4669 struct job *jp;
4670 int retval;
4671
4672 retval = 0;
4673 if (job_warning)
4674 goto out;
4675 jp = curjob;
4676 if (jp && jp->state == JOBSTOPPED) {
4677 out2str("You have stopped jobs.\n");
4678 job_warning = 2;
4679 retval++;
4680 }
4681 out:
4682 return retval;
4683}
4684
4685
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004686/* ============ redir.c
4687 *
4688 * Code for dealing with input/output redirection.
4689 */
4690
4691#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004692#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004693#ifndef PIPE_BUF
4694# define PIPESIZE 4096 /* amount of buffering in a pipe */
4695#else
4696# define PIPESIZE PIPE_BUF
4697#endif
4698
4699/*
4700 * Open a file in noclobber mode.
4701 * The code was copied from bash.
4702 */
4703static int
4704noclobberopen(const char *fname)
4705{
4706 int r, fd;
4707 struct stat finfo, finfo2;
4708
4709 /*
4710 * If the file exists and is a regular file, return an error
4711 * immediately.
4712 */
4713 r = stat(fname, &finfo);
4714 if (r == 0 && S_ISREG(finfo.st_mode)) {
4715 errno = EEXIST;
4716 return -1;
4717 }
4718
4719 /*
4720 * If the file was not present (r != 0), make sure we open it
4721 * exclusively so that if it is created before we open it, our open
4722 * will fail. Make sure that we do not truncate an existing file.
4723 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4724 * file was not a regular file, we leave O_EXCL off.
4725 */
4726 if (r != 0)
4727 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4728 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4729
4730 /* If the open failed, return the file descriptor right away. */
4731 if (fd < 0)
4732 return fd;
4733
4734 /*
4735 * OK, the open succeeded, but the file may have been changed from a
4736 * non-regular file to a regular file between the stat and the open.
4737 * We are assuming that the O_EXCL open handles the case where FILENAME
4738 * did not exist and is symlinked to an existing file between the stat
4739 * and open.
4740 */
4741
4742 /*
4743 * If we can open it and fstat the file descriptor, and neither check
4744 * revealed that it was a regular file, and the file has not been
4745 * replaced, return the file descriptor.
4746 */
4747 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4748 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4749 return fd;
4750
4751 /* The file has been replaced. badness. */
4752 close(fd);
4753 errno = EEXIST;
4754 return -1;
4755}
4756
4757/*
4758 * Handle here documents. Normally we fork off a process to write the
4759 * data to a pipe. If the document is short, we can stuff the data in
4760 * the pipe without forking.
4761 */
4762/* openhere needs this forward reference */
4763static void expandhere(union node *arg, int fd);
4764static int
4765openhere(union node *redir)
4766{
4767 int pip[2];
4768 size_t len = 0;
4769
4770 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004771 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772 if (redir->type == NHERE) {
4773 len = strlen(redir->nhere.doc->narg.text);
4774 if (len <= PIPESIZE) {
4775 full_write(pip[1], redir->nhere.doc->narg.text, len);
4776 goto out;
4777 }
4778 }
4779 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4780 close(pip[0]);
4781 signal(SIGINT, SIG_IGN);
4782 signal(SIGQUIT, SIG_IGN);
4783 signal(SIGHUP, SIG_IGN);
4784#ifdef SIGTSTP
4785 signal(SIGTSTP, SIG_IGN);
4786#endif
4787 signal(SIGPIPE, SIG_DFL);
4788 if (redir->type == NHERE)
4789 full_write(pip[1], redir->nhere.doc->narg.text, len);
4790 else
4791 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004792 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004793 }
4794 out:
4795 close(pip[1]);
4796 return pip[0];
4797}
4798
4799static int
4800openredirect(union node *redir)
4801{
4802 char *fname;
4803 int f;
4804
4805 switch (redir->nfile.type) {
4806 case NFROM:
4807 fname = redir->nfile.expfname;
4808 f = open(fname, O_RDONLY);
4809 if (f < 0)
4810 goto eopen;
4811 break;
4812 case NFROMTO:
4813 fname = redir->nfile.expfname;
4814 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4815 if (f < 0)
4816 goto ecreate;
4817 break;
4818 case NTO:
4819 /* Take care of noclobber mode. */
4820 if (Cflag) {
4821 fname = redir->nfile.expfname;
4822 f = noclobberopen(fname);
4823 if (f < 0)
4824 goto ecreate;
4825 break;
4826 }
4827 /* FALLTHROUGH */
4828 case NCLOBBER:
4829 fname = redir->nfile.expfname;
4830 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4831 if (f < 0)
4832 goto ecreate;
4833 break;
4834 case NAPPEND:
4835 fname = redir->nfile.expfname;
4836 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4837 if (f < 0)
4838 goto ecreate;
4839 break;
4840 default:
4841#if DEBUG
4842 abort();
4843#endif
4844 /* Fall through to eliminate warning. */
4845 case NTOFD:
4846 case NFROMFD:
4847 f = -1;
4848 break;
4849 case NHERE:
4850 case NXHERE:
4851 f = openhere(redir);
4852 break;
4853 }
4854
4855 return f;
4856 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004857 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004858 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004859 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004860}
4861
4862/*
4863 * Copy a file descriptor to be >= to. Returns -1
4864 * if the source file descriptor is closed, EMPTY if there are no unused
4865 * file descriptors left.
4866 */
4867static int
4868copyfd(int from, int to)
4869{
4870 int newfd;
4871
4872 newfd = fcntl(from, F_DUPFD, to);
4873 if (newfd < 0) {
4874 if (errno == EMFILE)
4875 return EMPTY;
4876 ash_msg_and_raise_error("%d: %m", from);
4877 }
4878 return newfd;
4879}
4880
4881static void
4882dupredirect(union node *redir, int f)
4883{
4884 int fd = redir->nfile.fd;
4885
4886 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4887 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4888 copyfd(redir->ndup.dupfd, fd);
4889 }
4890 return;
4891 }
4892
4893 if (f != fd) {
4894 copyfd(f, fd);
4895 close(f);
4896 }
4897}
4898
4899/*
4900 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4901 * old file descriptors are stashed away so that the redirection can be
4902 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4903 * standard output, and the standard error if it becomes a duplicate of
4904 * stdout, is saved in memory.
4905 */
4906/* flags passed to redirect */
4907#define REDIR_PUSH 01 /* save previous values of file descriptors */
4908#define REDIR_SAVEFD2 03 /* set preverrout */
4909static void
4910redirect(union node *redir, int flags)
4911{
4912 union node *n;
4913 struct redirtab *sv;
4914 int i;
4915 int fd;
4916 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004917
Denis Vlasenko01631112007-12-16 17:20:38 +00004918 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919 if (!redir) {
4920 return;
4921 }
4922 sv = NULL;
4923 INT_OFF;
4924 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004925 sv = ckmalloc(sizeof(*sv));
4926 sv->next = redirlist;
4927 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004928 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004929 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004930 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004931 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004932 }
4933 n = redir;
4934 do {
4935 fd = n->nfile.fd;
4936 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4937 && n->ndup.dupfd == fd)
4938 continue; /* redirect from/to same file descriptor */
4939
4940 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004941 if (fd == newfd) {
4942 /* Descriptor wasn't open before redirect.
4943 * Mark it for close in the future */
4944 if (sv && sv->renamed[fd] == EMPTY)
4945 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004946 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004947 }
4948 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004949 i = fcntl(fd, F_DUPFD, 10);
4950
4951 if (i == -1) {
4952 i = errno;
4953 if (i != EBADF) {
4954 close(newfd);
4955 errno = i;
4956 ash_msg_and_raise_error("%d: %m", fd);
4957 /* NOTREACHED */
4958 }
4959 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004960 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004961 close(fd);
4962 }
4963 } else {
4964 close(fd);
4965 }
4966 dupredirect(n, newfd);
4967 } while ((n = n->nfile.next));
4968 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004969 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004970 preverrout_fd = sv->renamed[2];
4971}
4972
4973/*
4974 * Undo the effects of the last redirection.
4975 */
4976static void
4977popredir(int drop)
4978{
4979 struct redirtab *rp;
4980 int i;
4981
Denis Vlasenko01631112007-12-16 17:20:38 +00004982 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004983 return;
4984 INT_OFF;
4985 rp = redirlist;
4986 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004987 if (rp->renamed[i] == CLOSED) {
4988 if (!drop)
4989 close(i);
4990 continue;
4991 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004992 if (rp->renamed[i] != EMPTY) {
4993 if (!drop) {
4994 close(i);
4995 copyfd(rp->renamed[i], i);
4996 }
4997 close(rp->renamed[i]);
4998 }
4999 }
5000 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005001 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005002 free(rp);
5003 INT_ON;
5004}
5005
5006/*
5007 * Undo all redirections. Called on error or interrupt.
5008 */
5009
5010/*
5011 * Discard all saved file descriptors.
5012 */
5013static void
5014clearredir(int drop)
5015{
5016 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005017 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005018 if (!redirlist)
5019 break;
5020 popredir(drop);
5021 }
5022}
5023
5024static int
5025redirectsafe(union node *redir, int flags)
5026{
5027 int err;
5028 volatile int saveint;
5029 struct jmploc *volatile savehandler = exception_handler;
5030 struct jmploc jmploc;
5031
5032 SAVE_INT(saveint);
5033 err = setjmp(jmploc.loc) * 2;
5034 if (!err) {
5035 exception_handler = &jmploc;
5036 redirect(redir, flags);
5037 }
5038 exception_handler = savehandler;
5039 if (err && exception != EXERROR)
5040 longjmp(exception_handler->loc, 1);
5041 RESTORE_INT(saveint);
5042 return err;
5043}
5044
5045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005046/* ============ Routines to expand arguments to commands
5047 *
5048 * We have to deal with backquotes, shell variables, and file metacharacters.
5049 */
5050
5051/*
5052 * expandarg flags
5053 */
5054#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5055#define EXP_TILDE 0x2 /* do normal tilde expansion */
5056#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5057#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5058#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5059#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5060#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5061#define EXP_WORD 0x80 /* expand word in parameter expansion */
5062#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5063/*
5064 * _rmescape() flags
5065 */
5066#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5067#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5068#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5069#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5070#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5071
5072/*
5073 * Structure specifying which parts of the string should be searched
5074 * for IFS characters.
5075 */
5076struct ifsregion {
5077 struct ifsregion *next; /* next region in list */
5078 int begoff; /* offset of start of region */
5079 int endoff; /* offset of end of region */
5080 int nulonly; /* search for nul bytes only */
5081};
5082
5083struct arglist {
5084 struct strlist *list;
5085 struct strlist **lastp;
5086};
5087
5088/* output of current string */
5089static char *expdest;
5090/* list of back quote expressions */
5091static struct nodelist *argbackq;
5092/* first struct in list of ifs regions */
5093static struct ifsregion ifsfirst;
5094/* last struct in list */
5095static struct ifsregion *ifslastp;
5096/* holds expanded arg list */
5097static struct arglist exparg;
5098
5099/*
5100 * Our own itoa().
5101 */
5102static int
5103cvtnum(arith_t num)
5104{
5105 int len;
5106
5107 expdest = makestrspace(32, expdest);
5108#if ENABLE_ASH_MATH_SUPPORT_64
5109 len = fmtstr(expdest, 32, "%lld", (long long) num);
5110#else
5111 len = fmtstr(expdest, 32, "%ld", num);
5112#endif
5113 STADJUST(len, expdest);
5114 return len;
5115}
5116
5117static size_t
5118esclen(const char *start, const char *p)
5119{
5120 size_t esc = 0;
5121
5122 while (p > start && *--p == CTLESC) {
5123 esc++;
5124 }
5125 return esc;
5126}
5127
5128/*
5129 * Remove any CTLESC characters from a string.
5130 */
5131static char *
5132_rmescapes(char *str, int flag)
5133{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005134 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005135
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005136 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005137 unsigned inquotes;
5138 int notescaped;
5139 int globbing;
5140
5141 p = strpbrk(str, qchars);
5142 if (!p) {
5143 return str;
5144 }
5145 q = p;
5146 r = str;
5147 if (flag & RMESCAPE_ALLOC) {
5148 size_t len = p - str;
5149 size_t fulllen = len + strlen(p) + 1;
5150
5151 if (flag & RMESCAPE_GROW) {
5152 r = makestrspace(fulllen, expdest);
5153 } else if (flag & RMESCAPE_HEAP) {
5154 r = ckmalloc(fulllen);
5155 } else {
5156 r = stalloc(fulllen);
5157 }
5158 q = r;
5159 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005160 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005161 }
5162 }
5163 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5164 globbing = flag & RMESCAPE_GLOB;
5165 notescaped = globbing;
5166 while (*p) {
5167 if (*p == CTLQUOTEMARK) {
5168 inquotes = ~inquotes;
5169 p++;
5170 notescaped = globbing;
5171 continue;
5172 }
5173 if (*p == '\\') {
5174 /* naked back slash */
5175 notescaped = 0;
5176 goto copy;
5177 }
5178 if (*p == CTLESC) {
5179 p++;
5180 if (notescaped && inquotes && *p != '/') {
5181 *q++ = '\\';
5182 }
5183 }
5184 notescaped = globbing;
5185 copy:
5186 *q++ = *p++;
5187 }
5188 *q = '\0';
5189 if (flag & RMESCAPE_GROW) {
5190 expdest = r;
5191 STADJUST(q - r + 1, expdest);
5192 }
5193 return r;
5194}
5195#define rmescapes(p) _rmescapes((p), 0)
5196
5197#define pmatch(a, b) !fnmatch((a), (b), 0)
5198
5199/*
5200 * Prepare a pattern for a expmeta (internal glob(3)) call.
5201 *
5202 * Returns an stalloced string.
5203 */
5204static char *
5205preglob(const char *pattern, int quoted, int flag)
5206{
5207 flag |= RMESCAPE_GLOB;
5208 if (quoted) {
5209 flag |= RMESCAPE_QUOTED;
5210 }
5211 return _rmescapes((char *)pattern, flag);
5212}
5213
5214/*
5215 * Put a string on the stack.
5216 */
5217static void
5218memtodest(const char *p, size_t len, int syntax, int quotes)
5219{
5220 char *q = expdest;
5221
5222 q = makestrspace(len * 2, q);
5223
5224 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005225 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005226 if (!c)
5227 continue;
5228 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5229 USTPUTC(CTLESC, q);
5230 USTPUTC(c, q);
5231 }
5232
5233 expdest = q;
5234}
5235
5236static void
5237strtodest(const char *p, int syntax, int quotes)
5238{
5239 memtodest(p, strlen(p), syntax, quotes);
5240}
5241
5242/*
5243 * Record the fact that we have to scan this region of the
5244 * string for IFS characters.
5245 */
5246static void
5247recordregion(int start, int end, int nulonly)
5248{
5249 struct ifsregion *ifsp;
5250
5251 if (ifslastp == NULL) {
5252 ifsp = &ifsfirst;
5253 } else {
5254 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005255 ifsp = ckzalloc(sizeof(*ifsp));
5256 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005257 ifslastp->next = ifsp;
5258 INT_ON;
5259 }
5260 ifslastp = ifsp;
5261 ifslastp->begoff = start;
5262 ifslastp->endoff = end;
5263 ifslastp->nulonly = nulonly;
5264}
5265
5266static void
5267removerecordregions(int endoff)
5268{
5269 if (ifslastp == NULL)
5270 return;
5271
5272 if (ifsfirst.endoff > endoff) {
5273 while (ifsfirst.next != NULL) {
5274 struct ifsregion *ifsp;
5275 INT_OFF;
5276 ifsp = ifsfirst.next->next;
5277 free(ifsfirst.next);
5278 ifsfirst.next = ifsp;
5279 INT_ON;
5280 }
5281 if (ifsfirst.begoff > endoff)
5282 ifslastp = NULL;
5283 else {
5284 ifslastp = &ifsfirst;
5285 ifsfirst.endoff = endoff;
5286 }
5287 return;
5288 }
5289
5290 ifslastp = &ifsfirst;
5291 while (ifslastp->next && ifslastp->next->begoff < endoff)
5292 ifslastp=ifslastp->next;
5293 while (ifslastp->next != NULL) {
5294 struct ifsregion *ifsp;
5295 INT_OFF;
5296 ifsp = ifslastp->next->next;
5297 free(ifslastp->next);
5298 ifslastp->next = ifsp;
5299 INT_ON;
5300 }
5301 if (ifslastp->endoff > endoff)
5302 ifslastp->endoff = endoff;
5303}
5304
5305static char *
5306exptilde(char *startp, char *p, int flag)
5307{
5308 char c;
5309 char *name;
5310 struct passwd *pw;
5311 const char *home;
5312 int quotes = flag & (EXP_FULL | EXP_CASE);
5313 int startloc;
5314
5315 name = p + 1;
5316
5317 while ((c = *++p) != '\0') {
5318 switch (c) {
5319 case CTLESC:
5320 return startp;
5321 case CTLQUOTEMARK:
5322 return startp;
5323 case ':':
5324 if (flag & EXP_VARTILDE)
5325 goto done;
5326 break;
5327 case '/':
5328 case CTLENDVAR:
5329 goto done;
5330 }
5331 }
5332 done:
5333 *p = '\0';
5334 if (*name == '\0') {
5335 home = lookupvar(homestr);
5336 } else {
5337 pw = getpwnam(name);
5338 if (pw == NULL)
5339 goto lose;
5340 home = pw->pw_dir;
5341 }
5342 if (!home || !*home)
5343 goto lose;
5344 *p = c;
5345 startloc = expdest - (char *)stackblock();
5346 strtodest(home, SQSYNTAX, quotes);
5347 recordregion(startloc, expdest - (char *)stackblock(), 0);
5348 return p;
5349 lose:
5350 *p = c;
5351 return startp;
5352}
5353
5354/*
5355 * Execute a command inside back quotes. If it's a builtin command, we
5356 * want to save its output in a block obtained from malloc. Otherwise
5357 * we fork off a subprocess and get the output of the command via a pipe.
5358 * Should be called with interrupts off.
5359 */
5360struct backcmd { /* result of evalbackcmd */
5361 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005362 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005363 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005364 struct job *jp; /* job structure for command */
5365};
5366
5367/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005368static smalluint back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005369#define EV_EXIT 01 /* exit after evaluating tree */
5370static void evaltree(union node *, int);
5371
5372static void
5373evalbackcmd(union node *n, struct backcmd *result)
5374{
5375 int saveherefd;
5376
5377 result->fd = -1;
5378 result->buf = NULL;
5379 result->nleft = 0;
5380 result->jp = NULL;
5381 if (n == NULL) {
5382 goto out;
5383 }
5384
5385 saveherefd = herefd;
5386 herefd = -1;
5387
5388 {
5389 int pip[2];
5390 struct job *jp;
5391
5392 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005393 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005394 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005395 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5396 FORCE_INT_ON;
5397 close(pip[0]);
5398 if (pip[1] != 1) {
5399 close(1);
5400 copyfd(pip[1], 1);
5401 close(pip[1]);
5402 }
5403 eflag = 0;
5404 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5405 /* NOTREACHED */
5406 }
5407 close(pip[1]);
5408 result->fd = pip[0];
5409 result->jp = jp;
5410 }
5411 herefd = saveherefd;
5412 out:
5413 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5414 result->fd, result->buf, result->nleft, result->jp));
5415}
5416
5417/*
5418 * Expand stuff in backwards quotes.
5419 */
5420static void
5421expbackq(union node *cmd, int quoted, int quotes)
5422{
5423 struct backcmd in;
5424 int i;
5425 char buf[128];
5426 char *p;
5427 char *dest;
5428 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005429 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005430 struct stackmark smark;
5431
5432 INT_OFF;
5433 setstackmark(&smark);
5434 dest = expdest;
5435 startloc = dest - (char *)stackblock();
5436 grabstackstr(dest);
5437 evalbackcmd(cmd, &in);
5438 popstackmark(&smark);
5439
5440 p = in.buf;
5441 i = in.nleft;
5442 if (i == 0)
5443 goto read;
5444 for (;;) {
5445 memtodest(p, i, syntax, quotes);
5446 read:
5447 if (in.fd < 0)
5448 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005449 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005450 TRACE(("expbackq: read returns %d\n", i));
5451 if (i <= 0)
5452 break;
5453 p = buf;
5454 }
5455
Denis Vlasenko60818682007-09-28 22:07:23 +00005456 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005457 if (in.fd >= 0) {
5458 close(in.fd);
5459 back_exitstatus = waitforjob(in.jp);
5460 }
5461 INT_ON;
5462
5463 /* Eat all trailing newlines */
5464 dest = expdest;
5465 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5466 STUNPUTC(dest);
5467 expdest = dest;
5468
5469 if (quoted == 0)
5470 recordregion(startloc, dest - (char *)stackblock(), 0);
5471 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5472 (dest - (char *)stackblock()) - startloc,
5473 (dest - (char *)stackblock()) - startloc,
5474 stackblock() + startloc));
5475}
5476
5477#if ENABLE_ASH_MATH_SUPPORT
5478/*
5479 * Expand arithmetic expression. Backup to start of expression,
5480 * evaluate, place result in (backed up) result, adjust string position.
5481 */
5482static void
5483expari(int quotes)
5484{
5485 char *p, *start;
5486 int begoff;
5487 int flag;
5488 int len;
5489
5490 /* ifsfree(); */
5491
5492 /*
5493 * This routine is slightly over-complicated for
5494 * efficiency. Next we scan backwards looking for the
5495 * start of arithmetic.
5496 */
5497 start = stackblock();
5498 p = expdest - 1;
5499 *p = '\0';
5500 p--;
5501 do {
5502 int esc;
5503
5504 while (*p != CTLARI) {
5505 p--;
5506#if DEBUG
5507 if (p < start) {
5508 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5509 }
5510#endif
5511 }
5512
5513 esc = esclen(start, p);
5514 if (!(esc % 2)) {
5515 break;
5516 }
5517
5518 p -= esc + 1;
5519 } while (1);
5520
5521 begoff = p - start;
5522
5523 removerecordregions(begoff);
5524
5525 flag = p[1];
5526
5527 expdest = p;
5528
5529 if (quotes)
5530 rmescapes(p + 2);
5531
5532 len = cvtnum(dash_arith(p + 2));
5533
5534 if (flag != '"')
5535 recordregion(begoff, begoff + len, 0);
5536}
5537#endif
5538
5539/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005540static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541
5542/*
5543 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5544 * characters to allow for further processing. Otherwise treat
5545 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005546 *
5547 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5548 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5549 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005550 */
5551static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005552argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005554 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005555 '=',
5556 ':',
5557 CTLQUOTEMARK,
5558 CTLENDVAR,
5559 CTLESC,
5560 CTLVAR,
5561 CTLBACKQ,
5562 CTLBACKQ | CTLQUOTE,
5563#if ENABLE_ASH_MATH_SUPPORT
5564 CTLENDARI,
5565#endif
5566 0
5567 };
5568 const char *reject = spclchars;
5569 int c;
5570 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5571 int breakall = flag & EXP_WORD;
5572 int inquotes;
5573 size_t length;
5574 int startloc;
5575
5576 if (!(flag & EXP_VARTILDE)) {
5577 reject += 2;
5578 } else if (flag & EXP_VARTILDE2) {
5579 reject++;
5580 }
5581 inquotes = 0;
5582 length = 0;
5583 if (flag & EXP_TILDE) {
5584 char *q;
5585
5586 flag &= ~EXP_TILDE;
5587 tilde:
5588 q = p;
5589 if (*q == CTLESC && (flag & EXP_QWORD))
5590 q++;
5591 if (*q == '~')
5592 p = exptilde(p, q, flag);
5593 }
5594 start:
5595 startloc = expdest - (char *)stackblock();
5596 for (;;) {
5597 length += strcspn(p + length, reject);
5598 c = p[length];
5599 if (c && (!(c & 0x80)
5600#if ENABLE_ASH_MATH_SUPPORT
5601 || c == CTLENDARI
5602#endif
5603 )) {
5604 /* c == '=' || c == ':' || c == CTLENDARI */
5605 length++;
5606 }
5607 if (length > 0) {
5608 int newloc;
5609 expdest = stack_nputstr(p, length, expdest);
5610 newloc = expdest - (char *)stackblock();
5611 if (breakall && !inquotes && newloc > startloc) {
5612 recordregion(startloc, newloc, 0);
5613 }
5614 startloc = newloc;
5615 }
5616 p += length + 1;
5617 length = 0;
5618
5619 switch (c) {
5620 case '\0':
5621 goto breakloop;
5622 case '=':
5623 if (flag & EXP_VARTILDE2) {
5624 p--;
5625 continue;
5626 }
5627 flag |= EXP_VARTILDE2;
5628 reject++;
5629 /* fall through */
5630 case ':':
5631 /*
5632 * sort of a hack - expand tildes in variable
5633 * assignments (after the first '=' and after ':'s).
5634 */
5635 if (*--p == '~') {
5636 goto tilde;
5637 }
5638 continue;
5639 }
5640
5641 switch (c) {
5642 case CTLENDVAR: /* ??? */
5643 goto breakloop;
5644 case CTLQUOTEMARK:
5645 /* "$@" syntax adherence hack */
5646 if (
5647 !inquotes &&
5648 !memcmp(p, dolatstr, 4) &&
5649 (p[4] == CTLQUOTEMARK || (
5650 p[4] == CTLENDVAR &&
5651 p[5] == CTLQUOTEMARK
5652 ))
5653 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005654 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005655 goto start;
5656 }
5657 inquotes = !inquotes;
5658 addquote:
5659 if (quotes) {
5660 p--;
5661 length++;
5662 startloc++;
5663 }
5664 break;
5665 case CTLESC:
5666 startloc++;
5667 length++;
5668 goto addquote;
5669 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005670 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005671 goto start;
5672 case CTLBACKQ:
5673 c = 0;
5674 case CTLBACKQ|CTLQUOTE:
5675 expbackq(argbackq->n, c, quotes);
5676 argbackq = argbackq->next;
5677 goto start;
5678#if ENABLE_ASH_MATH_SUPPORT
5679 case CTLENDARI:
5680 p--;
5681 expari(quotes);
5682 goto start;
5683#endif
5684 }
5685 }
5686 breakloop:
5687 ;
5688}
5689
5690static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005691scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692 int zero)
5693{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005694// This commented out code was added by James Simmons <jsimmons@infradead.org>
5695// as part of a larger change when he added support for ${var/a/b}.
5696// However, it broke # and % operators:
5697//
5698//var=ababcdcd
5699// ok bad
5700//echo ${var#ab} abcdcd abcdcd
5701//echo ${var##ab} abcdcd abcdcd
5702//echo ${var#a*b} abcdcd ababcdcd (!)
5703//echo ${var##a*b} cdcd cdcd
5704//echo ${var#?} babcdcd ababcdcd (!)
5705//echo ${var##?} babcdcd babcdcd
5706//echo ${var#*} ababcdcd babcdcd (!)
5707//echo ${var##*}
5708//echo ${var%cd} ababcd ababcd
5709//echo ${var%%cd} ababcd abab (!)
5710//echo ${var%c*d} ababcd ababcd
5711//echo ${var%%c*d} abab ababcdcd (!)
5712//echo ${var%?} ababcdc ababcdc
5713//echo ${var%%?} ababcdc ababcdcd (!)
5714//echo ${var%*} ababcdcd ababcdcd
5715//echo ${var%%*}
5716//
5717// Commenting it back out helped. Remove it completely if it really
5718// is not needed.
5719
5720 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005721 char c;
5722
5723 loc = startp;
5724 loc2 = rmesc;
5725 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005726 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005727 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005728
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005729 c = *loc2;
5730 if (zero) {
5731 *loc2 = '\0';
5732 s = rmesc;
5733 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005734 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005735
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005736// // chop off end if its '*'
5737// full = strrchr(str, '*');
5738// if (full && full != str)
5739// match--;
5740//
5741// // If str starts with '*' replace with s.
5742// if ((*str == '*') && strlen(s) >= match) {
5743// full = xstrdup(s);
5744// strncpy(full+strlen(s)-match+1, str+1, match-1);
5745// } else
5746// full = xstrndup(str, match);
5747// match = strncmp(s, full, strlen(full));
5748// free(full);
5749//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005750 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005751 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005752 return loc;
5753 if (quotes && *loc == CTLESC)
5754 loc++;
5755 loc++;
5756 loc2++;
5757 } while (c);
5758 return 0;
5759}
5760
5761static char *
5762scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5763 int zero)
5764{
5765 int esc = 0;
5766 char *loc;
5767 char *loc2;
5768
5769 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5770 int match;
5771 char c = *loc2;
5772 const char *s = loc2;
5773 if (zero) {
5774 *loc2 = '\0';
5775 s = rmesc;
5776 }
5777 match = pmatch(str, s);
5778 *loc2 = c;
5779 if (match)
5780 return loc;
5781 loc--;
5782 if (quotes) {
5783 if (--esc < 0) {
5784 esc = esclen(startp, loc);
5785 }
5786 if (esc % 2) {
5787 esc--;
5788 loc--;
5789 }
5790 }
5791 }
5792 return 0;
5793}
5794
5795static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5796static void
5797varunset(const char *end, const char *var, const char *umsg, int varflags)
5798{
5799 const char *msg;
5800 const char *tail;
5801
5802 tail = nullstr;
5803 msg = "parameter not set";
5804 if (umsg) {
5805 if (*end == CTLENDVAR) {
5806 if (varflags & VSNUL)
5807 tail = " or null";
5808 } else
5809 msg = umsg;
5810 }
5811 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5812}
5813
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005814#if ENABLE_ASH_BASH_COMPAT
5815static char *
5816parse_sub_pattern(char *arg, int inquotes)
5817{
5818 char *idx, *repl = NULL;
5819 unsigned char c;
5820
Denis Vlasenko2659c632008-06-14 06:04:59 +00005821 idx = arg;
5822 while (1) {
5823 c = *arg;
5824 if (!c)
5825 break;
5826 if (c == '/') {
5827 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005828 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005829 repl = idx + 1;
5830 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005831 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005832 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005833 *idx++ = c;
5834 if (!inquotes && c == '\\' && arg[1] == '\\')
5835 arg++; /* skip both \\, not just first one */
5836 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005837 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005838 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005839
5840 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005841}
5842#endif /* ENABLE_ASH_BASH_COMPAT */
5843
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005845subevalvar(char *p, char *str, int strloc, int subtype,
5846 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005847{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005848 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005849 char *startp;
5850 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005851 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005852 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5853 USE_ASH_BASH_COMPAT(char null = '\0';)
5854 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5855 int saveherefd = herefd;
5856 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005857 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005858 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005859
5860 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005861 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5862 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005863 STPUTC('\0', expdest);
5864 herefd = saveherefd;
5865 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005866 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867
5868 switch (subtype) {
5869 case VSASSIGN:
5870 setvar(str, startp, 0);
5871 amount = startp - expdest;
5872 STADJUST(amount, expdest);
5873 return startp;
5874
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005875#if ENABLE_ASH_BASH_COMPAT
5876 case VSSUBSTR:
5877 loc = str = stackblock() + strloc;
5878// TODO: number() instead? It does error checking...
5879 pos = atoi(loc);
5880 len = str - startp - 1;
5881
5882 /* *loc != '\0', guaranteed by parser */
5883 if (quotes) {
5884 char *ptr;
5885
5886 /* We must adjust the length by the number of escapes we find. */
5887 for (ptr = startp; ptr < (str - 1); ptr++) {
5888 if(*ptr == CTLESC) {
5889 len--;
5890 ptr++;
5891 }
5892 }
5893 }
5894 orig_len = len;
5895
5896 if (*loc++ == ':') {
5897// TODO: number() instead? It does error checking...
5898 len = atoi(loc);
5899 } else {
5900 len = orig_len;
5901 while (*loc && *loc != ':')
5902 loc++;
5903 if (*loc++ == ':')
5904// TODO: number() instead? It does error checking...
5905 len = atoi(loc);
5906 }
5907 if (pos >= orig_len) {
5908 pos = 0;
5909 len = 0;
5910 }
5911 if (len > (orig_len - pos))
5912 len = orig_len - pos;
5913
5914 for (str = startp; pos; str++, pos--) {
5915 if (quotes && *str == CTLESC)
5916 str++;
5917 }
5918 for (loc = startp; len; len--) {
5919 if (quotes && *str == CTLESC)
5920 *loc++ = *str++;
5921 *loc++ = *str++;
5922 }
5923 *loc = '\0';
5924 amount = loc - expdest;
5925 STADJUST(amount, expdest);
5926 return loc;
5927#endif
5928
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005929 case VSQUESTION:
5930 varunset(p, str, startp, varflags);
5931 /* NOTREACHED */
5932 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005933 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005934
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005935 /* We'll comeback here if we grow the stack while handling
5936 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5937 * stack will need rebasing, and we'll need to remove our work
5938 * areas each time
5939 */
5940 USE_ASH_BASH_COMPAT(restart:)
5941
5942 amount = expdest - ((char *)stackblock() + resetloc);
5943 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005944 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005945
5946 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005947 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005948 if (quotes) {
5949 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5950 if (rmesc != startp) {
5951 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005952 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005953 }
5954 }
5955 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005956 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005957 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005958 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005959
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005960#if ENABLE_ASH_BASH_COMPAT
5961 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5962 char *idx, *end, *restart_detect;
5963
5964 if(!repl) {
5965 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5966 if (!repl)
5967 repl = &null;
5968 }
5969
5970 /* If there's no pattern to match, return the expansion unmolested */
5971 if (*str == '\0')
5972 return 0;
5973
5974 len = 0;
5975 idx = startp;
5976 end = str - 1;
5977 while (idx < end) {
5978 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5979 if (!loc) {
5980 /* No match, advance */
5981 restart_detect = stackblock();
5982 STPUTC(*idx, expdest);
5983 if (quotes && *idx == CTLESC) {
5984 idx++;
5985 len++;
5986 STPUTC(*idx, expdest);
5987 }
5988 if (stackblock() != restart_detect)
5989 goto restart;
5990 idx++;
5991 len++;
5992 rmesc++;
5993 continue;
5994 }
5995
5996 if (subtype == VSREPLACEALL) {
5997 while (idx < loc) {
5998 if (quotes && *idx == CTLESC)
5999 idx++;
6000 idx++;
6001 rmesc++;
6002 }
6003 } else
6004 idx = loc;
6005
6006 for (loc = repl; *loc; loc++) {
6007 restart_detect = stackblock();
6008 STPUTC(*loc, expdest);
6009 if (stackblock() != restart_detect)
6010 goto restart;
6011 len++;
6012 }
6013
6014 if (subtype == VSREPLACE) {
6015 while (*idx) {
6016 restart_detect = stackblock();
6017 STPUTC(*idx, expdest);
6018 if (stackblock() != restart_detect)
6019 goto restart;
6020 len++;
6021 idx++;
6022 }
6023 break;
6024 }
6025 }
6026
6027 /* We've put the replaced text into a buffer at workloc, now
6028 * move it to the right place and adjust the stack.
6029 */
6030 startp = stackblock() + startloc;
6031 STPUTC('\0', expdest);
6032 memmove(startp, stackblock() + workloc, len);
6033 startp[len++] = '\0';
6034 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6035 STADJUST(-amount, expdest);
6036 return startp;
6037 }
6038#endif /* ENABLE_ASH_BASH_COMPAT */
6039
6040 subtype -= VSTRIMRIGHT;
6041#if DEBUG
6042 if (subtype < 0 || subtype > 7)
6043 abort();
6044#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6046 zero = subtype >> 1;
6047 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6048 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6049
6050 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6051 if (loc) {
6052 if (zero) {
6053 memmove(startp, loc, str - loc);
6054 loc = startp + (str - loc) - 1;
6055 }
6056 *loc = '\0';
6057 amount = loc - expdest;
6058 STADJUST(amount, expdest);
6059 }
6060 return loc;
6061}
6062
6063/*
6064 * Add the value of a specialized variable to the stack string.
6065 */
6066static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006067varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068{
6069 int num;
6070 char *p;
6071 int i;
6072 int sep = 0;
6073 int sepq = 0;
6074 ssize_t len = 0;
6075 char **ap;
6076 int syntax;
6077 int quoted = varflags & VSQUOTE;
6078 int subtype = varflags & VSTYPE;
6079 int quotes = flags & (EXP_FULL | EXP_CASE);
6080
6081 if (quoted && (flags & EXP_FULL))
6082 sep = 1 << CHAR_BIT;
6083
6084 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6085 switch (*name) {
6086 case '$':
6087 num = rootpid;
6088 goto numvar;
6089 case '?':
6090 num = exitstatus;
6091 goto numvar;
6092 case '#':
6093 num = shellparam.nparam;
6094 goto numvar;
6095 case '!':
6096 num = backgndpid;
6097 if (num == 0)
6098 return -1;
6099 numvar:
6100 len = cvtnum(num);
6101 break;
6102 case '-':
6103 p = makestrspace(NOPTS, expdest);
6104 for (i = NOPTS - 1; i >= 0; i--) {
6105 if (optlist[i]) {
6106 USTPUTC(optletters(i), p);
6107 len++;
6108 }
6109 }
6110 expdest = p;
6111 break;
6112 case '@':
6113 if (sep)
6114 goto param;
6115 /* fall through */
6116 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006117 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006118 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6119 sepq = 1;
6120 param:
6121 ap = shellparam.p;
6122 if (!ap)
6123 return -1;
6124 while ((p = *ap++)) {
6125 size_t partlen;
6126
6127 partlen = strlen(p);
6128 len += partlen;
6129
6130 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6131 memtodest(p, partlen, syntax, quotes);
6132
6133 if (*ap && sep) {
6134 char *q;
6135
6136 len++;
6137 if (subtype == VSPLUS || subtype == VSLENGTH) {
6138 continue;
6139 }
6140 q = expdest;
6141 if (sepq)
6142 STPUTC(CTLESC, q);
6143 STPUTC(sep, q);
6144 expdest = q;
6145 }
6146 }
6147 return len;
6148 case '0':
6149 case '1':
6150 case '2':
6151 case '3':
6152 case '4':
6153 case '5':
6154 case '6':
6155 case '7':
6156 case '8':
6157 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006158// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006159 num = atoi(name);
6160 if (num < 0 || num > shellparam.nparam)
6161 return -1;
6162 p = num ? shellparam.p[num - 1] : arg0;
6163 goto value;
6164 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006165 /* NB: name has form "VAR=..." */
6166
6167 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6168 * which should be considered before we check variables. */
6169 if (var_str_list) {
6170 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6171 p = NULL;
6172 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006173 char *str, *eq;
6174 str = var_str_list->text;
6175 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006176 if (!eq) /* stop at first non-assignment */
6177 break;
6178 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006179 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006180 && strncmp(str, name, name_len) == 0) {
6181 p = eq;
6182 /* goto value; - WRONG! */
6183 /* think "A=1 A=2 B=$A" */
6184 }
6185 var_str_list = var_str_list->next;
6186 } while (var_str_list);
6187 if (p)
6188 goto value;
6189 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006190 p = lookupvar(name);
6191 value:
6192 if (!p)
6193 return -1;
6194
6195 len = strlen(p);
6196 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6197 memtodest(p, len, syntax, quotes);
6198 return len;
6199 }
6200
6201 if (subtype == VSPLUS || subtype == VSLENGTH)
6202 STADJUST(-len, expdest);
6203 return len;
6204}
6205
6206/*
6207 * Expand a variable, and return a pointer to the next character in the
6208 * input string.
6209 */
6210static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006211evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006212{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006213 char varflags;
6214 char subtype;
6215 char quoted;
6216 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 char *var;
6218 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006219 int startloc;
6220 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006221
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 varflags = *p++;
6223 subtype = varflags & VSTYPE;
6224 quoted = varflags & VSQUOTE;
6225 var = p;
6226 easy = (!quoted || (*var == '@' && shellparam.nparam));
6227 startloc = expdest - (char *)stackblock();
6228 p = strchr(p, '=') + 1;
6229
6230 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006231 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006232 if (varflags & VSNUL)
6233 varlen--;
6234
6235 if (subtype == VSPLUS) {
6236 varlen = -1 - varlen;
6237 goto vsplus;
6238 }
6239
6240 if (subtype == VSMINUS) {
6241 vsplus:
6242 if (varlen < 0) {
6243 argstr(
6244 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006245 (quoted ? EXP_QWORD : EXP_WORD),
6246 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006247 );
6248 goto end;
6249 }
6250 if (easy)
6251 goto record;
6252 goto end;
6253 }
6254
6255 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6256 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006257 if (subevalvar(p, var, /* strloc: */ 0,
6258 subtype, startloc, varflags,
6259 /* quotes: */ 0,
6260 var_str_list)
6261 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006262 varflags &= ~VSNUL;
6263 /*
6264 * Remove any recorded regions beyond
6265 * start of variable
6266 */
6267 removerecordregions(startloc);
6268 goto again;
6269 }
6270 goto end;
6271 }
6272 if (easy)
6273 goto record;
6274 goto end;
6275 }
6276
6277 if (varlen < 0 && uflag)
6278 varunset(p, var, 0, 0);
6279
6280 if (subtype == VSLENGTH) {
6281 cvtnum(varlen > 0 ? varlen : 0);
6282 goto record;
6283 }
6284
6285 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006286 if (easy)
6287 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006288 goto end;
6289 }
6290
6291#if DEBUG
6292 switch (subtype) {
6293 case VSTRIMLEFT:
6294 case VSTRIMLEFTMAX:
6295 case VSTRIMRIGHT:
6296 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006297#if ENABLE_ASH_BASH_COMPAT
6298 case VSSUBSTR:
6299 case VSREPLACE:
6300 case VSREPLACEALL:
6301#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006302 break;
6303 default:
6304 abort();
6305 }
6306#endif
6307
6308 if (varlen >= 0) {
6309 /*
6310 * Terminate the string and start recording the pattern
6311 * right after it
6312 */
6313 STPUTC('\0', expdest);
6314 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006315 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6316 startloc, varflags,
6317 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6318 var_str_list)
6319 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006320 int amount = expdest - (
6321 (char *)stackblock() + patloc - 1
6322 );
6323 STADJUST(-amount, expdest);
6324 }
6325 /* Remove any recorded regions beyond start of variable */
6326 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006327 record:
6328 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329 }
6330
6331 end:
6332 if (subtype != VSNORMAL) { /* skip to end of alternative */
6333 int nesting = 1;
6334 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006335 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 if (c == CTLESC)
6337 p++;
6338 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6339 if (varlen >= 0)
6340 argbackq = argbackq->next;
6341 } else if (c == CTLVAR) {
6342 if ((*p++ & VSTYPE) != VSNORMAL)
6343 nesting++;
6344 } else if (c == CTLENDVAR) {
6345 if (--nesting == 0)
6346 break;
6347 }
6348 }
6349 }
6350 return p;
6351}
6352
6353/*
6354 * Break the argument string into pieces based upon IFS and add the
6355 * strings to the argument list. The regions of the string to be
6356 * searched for IFS characters have been stored by recordregion.
6357 */
6358static void
6359ifsbreakup(char *string, struct arglist *arglist)
6360{
6361 struct ifsregion *ifsp;
6362 struct strlist *sp;
6363 char *start;
6364 char *p;
6365 char *q;
6366 const char *ifs, *realifs;
6367 int ifsspc;
6368 int nulonly;
6369
6370 start = string;
6371 if (ifslastp != NULL) {
6372 ifsspc = 0;
6373 nulonly = 0;
6374 realifs = ifsset() ? ifsval() : defifs;
6375 ifsp = &ifsfirst;
6376 do {
6377 p = string + ifsp->begoff;
6378 nulonly = ifsp->nulonly;
6379 ifs = nulonly ? nullstr : realifs;
6380 ifsspc = 0;
6381 while (p < string + ifsp->endoff) {
6382 q = p;
6383 if (*p == CTLESC)
6384 p++;
6385 if (!strchr(ifs, *p)) {
6386 p++;
6387 continue;
6388 }
6389 if (!nulonly)
6390 ifsspc = (strchr(defifs, *p) != NULL);
6391 /* Ignore IFS whitespace at start */
6392 if (q == start && ifsspc) {
6393 p++;
6394 start = p;
6395 continue;
6396 }
6397 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006398 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399 sp->text = start;
6400 *arglist->lastp = sp;
6401 arglist->lastp = &sp->next;
6402 p++;
6403 if (!nulonly) {
6404 for (;;) {
6405 if (p >= string + ifsp->endoff) {
6406 break;
6407 }
6408 q = p;
6409 if (*p == CTLESC)
6410 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006411 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006412 p = q;
6413 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006414 }
6415 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006416 if (ifsspc) {
6417 p++;
6418 ifsspc = 0;
6419 } else {
6420 p = q;
6421 break;
6422 }
6423 } else
6424 p++;
6425 }
6426 }
6427 start = p;
6428 } /* while */
6429 ifsp = ifsp->next;
6430 } while (ifsp != NULL);
6431 if (nulonly)
6432 goto add;
6433 }
6434
6435 if (!*start)
6436 return;
6437
6438 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006439 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006440 sp->text = start;
6441 *arglist->lastp = sp;
6442 arglist->lastp = &sp->next;
6443}
6444
6445static void
6446ifsfree(void)
6447{
6448 struct ifsregion *p;
6449
6450 INT_OFF;
6451 p = ifsfirst.next;
6452 do {
6453 struct ifsregion *ifsp;
6454 ifsp = p->next;
6455 free(p);
6456 p = ifsp;
6457 } while (p);
6458 ifslastp = NULL;
6459 ifsfirst.next = NULL;
6460 INT_ON;
6461}
6462
6463/*
6464 * Add a file name to the list.
6465 */
6466static void
6467addfname(const char *name)
6468{
6469 struct strlist *sp;
6470
Denis Vlasenko597906c2008-02-20 16:38:54 +00006471 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006472 sp->text = ststrdup(name);
6473 *exparg.lastp = sp;
6474 exparg.lastp = &sp->next;
6475}
6476
6477static char *expdir;
6478
6479/*
6480 * Do metacharacter (i.e. *, ?, [...]) expansion.
6481 */
6482static void
6483expmeta(char *enddir, char *name)
6484{
6485 char *p;
6486 const char *cp;
6487 char *start;
6488 char *endname;
6489 int metaflag;
6490 struct stat statb;
6491 DIR *dirp;
6492 struct dirent *dp;
6493 int atend;
6494 int matchdot;
6495
6496 metaflag = 0;
6497 start = name;
6498 for (p = name; *p; p++) {
6499 if (*p == '*' || *p == '?')
6500 metaflag = 1;
6501 else if (*p == '[') {
6502 char *q = p + 1;
6503 if (*q == '!')
6504 q++;
6505 for (;;) {
6506 if (*q == '\\')
6507 q++;
6508 if (*q == '/' || *q == '\0')
6509 break;
6510 if (*++q == ']') {
6511 metaflag = 1;
6512 break;
6513 }
6514 }
6515 } else if (*p == '\\')
6516 p++;
6517 else if (*p == '/') {
6518 if (metaflag)
6519 goto out;
6520 start = p + 1;
6521 }
6522 }
6523 out:
6524 if (metaflag == 0) { /* we've reached the end of the file name */
6525 if (enddir != expdir)
6526 metaflag++;
6527 p = name;
6528 do {
6529 if (*p == '\\')
6530 p++;
6531 *enddir++ = *p;
6532 } while (*p++);
6533 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6534 addfname(expdir);
6535 return;
6536 }
6537 endname = p;
6538 if (name < start) {
6539 p = name;
6540 do {
6541 if (*p == '\\')
6542 p++;
6543 *enddir++ = *p++;
6544 } while (p < start);
6545 }
6546 if (enddir == expdir) {
6547 cp = ".";
6548 } else if (enddir == expdir + 1 && *expdir == '/') {
6549 cp = "/";
6550 } else {
6551 cp = expdir;
6552 enddir[-1] = '\0';
6553 }
6554 dirp = opendir(cp);
6555 if (dirp == NULL)
6556 return;
6557 if (enddir != expdir)
6558 enddir[-1] = '/';
6559 if (*endname == 0) {
6560 atend = 1;
6561 } else {
6562 atend = 0;
6563 *endname++ = '\0';
6564 }
6565 matchdot = 0;
6566 p = start;
6567 if (*p == '\\')
6568 p++;
6569 if (*p == '.')
6570 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006571 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006572 if (dp->d_name[0] == '.' && ! matchdot)
6573 continue;
6574 if (pmatch(start, dp->d_name)) {
6575 if (atend) {
6576 strcpy(enddir, dp->d_name);
6577 addfname(expdir);
6578 } else {
6579 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6580 continue;
6581 p[-1] = '/';
6582 expmeta(p, endname);
6583 }
6584 }
6585 }
6586 closedir(dirp);
6587 if (! atend)
6588 endname[-1] = '/';
6589}
6590
6591static struct strlist *
6592msort(struct strlist *list, int len)
6593{
6594 struct strlist *p, *q = NULL;
6595 struct strlist **lpp;
6596 int half;
6597 int n;
6598
6599 if (len <= 1)
6600 return list;
6601 half = len >> 1;
6602 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006603 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006604 q = p;
6605 p = p->next;
6606 }
6607 q->next = NULL; /* terminate first half of list */
6608 q = msort(list, half); /* sort first half of list */
6609 p = msort(p, len - half); /* sort second half */
6610 lpp = &list;
6611 for (;;) {
6612#if ENABLE_LOCALE_SUPPORT
6613 if (strcoll(p->text, q->text) < 0)
6614#else
6615 if (strcmp(p->text, q->text) < 0)
6616#endif
6617 {
6618 *lpp = p;
6619 lpp = &p->next;
6620 p = *lpp;
6621 if (p == NULL) {
6622 *lpp = q;
6623 break;
6624 }
6625 } else {
6626 *lpp = q;
6627 lpp = &q->next;
6628 q = *lpp;
6629 if (q == NULL) {
6630 *lpp = p;
6631 break;
6632 }
6633 }
6634 }
6635 return list;
6636}
6637
6638/*
6639 * Sort the results of file name expansion. It calculates the number of
6640 * strings to sort and then calls msort (short for merge sort) to do the
6641 * work.
6642 */
6643static struct strlist *
6644expsort(struct strlist *str)
6645{
6646 int len;
6647 struct strlist *sp;
6648
6649 len = 0;
6650 for (sp = str; sp; sp = sp->next)
6651 len++;
6652 return msort(str, len);
6653}
6654
6655static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006656expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006657{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006658 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006659 '*', '?', '[', 0
6660 };
6661 /* TODO - EXP_REDIR */
6662
6663 while (str) {
6664 struct strlist **savelastp;
6665 struct strlist *sp;
6666 char *p;
6667
6668 if (fflag)
6669 goto nometa;
6670 if (!strpbrk(str->text, metachars))
6671 goto nometa;
6672 savelastp = exparg.lastp;
6673
6674 INT_OFF;
6675 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6676 {
6677 int i = strlen(str->text);
6678 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6679 }
6680
6681 expmeta(expdir, p);
6682 free(expdir);
6683 if (p != str->text)
6684 free(p);
6685 INT_ON;
6686 if (exparg.lastp == savelastp) {
6687 /*
6688 * no matches
6689 */
6690 nometa:
6691 *exparg.lastp = str;
6692 rmescapes(str->text);
6693 exparg.lastp = &str->next;
6694 } else {
6695 *exparg.lastp = NULL;
6696 *savelastp = sp = expsort(*savelastp);
6697 while (sp->next != NULL)
6698 sp = sp->next;
6699 exparg.lastp = &sp->next;
6700 }
6701 str = str->next;
6702 }
6703}
6704
6705/*
6706 * Perform variable substitution and command substitution on an argument,
6707 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6708 * perform splitting and file name expansion. When arglist is NULL, perform
6709 * here document expansion.
6710 */
6711static void
6712expandarg(union node *arg, struct arglist *arglist, int flag)
6713{
6714 struct strlist *sp;
6715 char *p;
6716
6717 argbackq = arg->narg.backquote;
6718 STARTSTACKSTR(expdest);
6719 ifsfirst.next = NULL;
6720 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006721 argstr(arg->narg.text, flag,
6722 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006723 p = _STPUTC('\0', expdest);
6724 expdest = p - 1;
6725 if (arglist == NULL) {
6726 return; /* here document expanded */
6727 }
6728 p = grabstackstr(p);
6729 exparg.lastp = &exparg.list;
6730 /*
6731 * TODO - EXP_REDIR
6732 */
6733 if (flag & EXP_FULL) {
6734 ifsbreakup(p, &exparg);
6735 *exparg.lastp = NULL;
6736 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006737 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006738 } else {
6739 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6740 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006741 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 sp->text = p;
6743 *exparg.lastp = sp;
6744 exparg.lastp = &sp->next;
6745 }
6746 if (ifsfirst.next)
6747 ifsfree();
6748 *exparg.lastp = NULL;
6749 if (exparg.list) {
6750 *arglist->lastp = exparg.list;
6751 arglist->lastp = exparg.lastp;
6752 }
6753}
6754
6755/*
6756 * Expand shell variables and backquotes inside a here document.
6757 */
6758static void
6759expandhere(union node *arg, int fd)
6760{
6761 herefd = fd;
6762 expandarg(arg, (struct arglist *)NULL, 0);
6763 full_write(fd, stackblock(), expdest - (char *)stackblock());
6764}
6765
6766/*
6767 * Returns true if the pattern matches the string.
6768 */
6769static int
6770patmatch(char *pattern, const char *string)
6771{
6772 return pmatch(preglob(pattern, 0, 0), string);
6773}
6774
6775/*
6776 * See if a pattern matches in a case statement.
6777 */
6778static int
6779casematch(union node *pattern, char *val)
6780{
6781 struct stackmark smark;
6782 int result;
6783
6784 setstackmark(&smark);
6785 argbackq = pattern->narg.backquote;
6786 STARTSTACKSTR(expdest);
6787 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006788 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6789 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006790 STACKSTRNUL(expdest);
6791 result = patmatch(stackblock(), val);
6792 popstackmark(&smark);
6793 return result;
6794}
6795
6796
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006797/* ============ find_command */
6798
6799struct builtincmd {
6800 const char *name;
6801 int (*builtin)(int, char **);
6802 /* unsigned flags; */
6803};
6804#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006805/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006806 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006807#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006808#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006809
6810struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006811 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006812 union param {
6813 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006814 /* index >= 0 for commands without path (slashes) */
6815 /* (TODO: what exactly does the value mean? PATH position?) */
6816 /* index == -1 for commands with slashes */
6817 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006818 const struct builtincmd *cmd;
6819 struct funcnode *func;
6820 } u;
6821};
6822/* values of cmdtype */
6823#define CMDUNKNOWN -1 /* no entry in table for command */
6824#define CMDNORMAL 0 /* command is an executable program */
6825#define CMDFUNCTION 1 /* command is a shell function */
6826#define CMDBUILTIN 2 /* command is a shell builtin */
6827
6828/* action to find_command() */
6829#define DO_ERR 0x01 /* prints errors */
6830#define DO_ABS 0x02 /* checks absolute paths */
6831#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6832#define DO_ALTPATH 0x08 /* using alternate path */
6833#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6834
6835static void find_command(char *, struct cmdentry *, int, const char *);
6836
6837
6838/* ============ Hashing commands */
6839
6840/*
6841 * When commands are first encountered, they are entered in a hash table.
6842 * This ensures that a full path search will not have to be done for them
6843 * on each invocation.
6844 *
6845 * We should investigate converting to a linear search, even though that
6846 * would make the command name "hash" a misnomer.
6847 */
6848
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006849struct tblentry {
6850 struct tblentry *next; /* next entry in hash chain */
6851 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006852 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006853 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006854 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006855};
6856
Denis Vlasenko01631112007-12-16 17:20:38 +00006857static struct tblentry **cmdtable;
6858#define INIT_G_cmdtable() do { \
6859 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6860} while (0)
6861
6862static int builtinloc = -1; /* index in path of %builtin, or -1 */
6863
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006864
6865static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006866tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006867{
6868 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006869
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006870#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006871 if (applet_no >= 0) {
6872 if (APPLET_IS_NOEXEC(applet_no))
6873 run_applet_no_and_exit(applet_no, argv);
6874 /* re-exec ourselves with the new arguments */
6875 execve(bb_busybox_exec_path, argv, envp);
6876 /* If they called chroot or otherwise made the binary no longer
6877 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006878 }
6879#endif
6880
6881 repeat:
6882#ifdef SYSV
6883 do {
6884 execve(cmd, argv, envp);
6885 } while (errno == EINTR);
6886#else
6887 execve(cmd, argv, envp);
6888#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006889 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006890 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006891 return;
6892 }
6893 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006894 char **ap;
6895 char **new;
6896
6897 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006898 continue;
6899 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006901 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006902 ap += 2;
6903 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006904 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006905 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006906 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006907 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006908 goto repeat;
6909 }
6910}
6911
6912/*
6913 * Exec a program. Never returns. If you change this routine, you may
6914 * have to change the find_command routine as well.
6915 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006916static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6917static void
6918shellexec(char **argv, const char *path, int idx)
6919{
6920 char *cmdname;
6921 int e;
6922 char **envp;
6923 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006924#if ENABLE_FEATURE_SH_STANDALONE
6925 int applet_no = -1;
6926#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006927
6928 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006929 envp = listvars(VEXPORT, VUNSET, 0);
6930 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006931#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006932 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006933#endif
6934 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006935 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006936 e = errno;
6937 } else {
6938 e = ENOENT;
6939 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6940 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006941 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006942 if (errno != ENOENT && errno != ENOTDIR)
6943 e = errno;
6944 }
6945 stunalloc(cmdname);
6946 }
6947 }
6948
6949 /* Map to POSIX errors */
6950 switch (e) {
6951 case EACCES:
6952 exerrno = 126;
6953 break;
6954 case ENOENT:
6955 exerrno = 127;
6956 break;
6957 default:
6958 exerrno = 2;
6959 break;
6960 }
6961 exitstatus = exerrno;
6962 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006963 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006964 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6965 /* NOTREACHED */
6966}
6967
6968static void
6969printentry(struct tblentry *cmdp)
6970{
6971 int idx;
6972 const char *path;
6973 char *name;
6974
6975 idx = cmdp->param.index;
6976 path = pathval();
6977 do {
6978 name = padvance(&path, cmdp->cmdname);
6979 stunalloc(name);
6980 } while (--idx >= 0);
6981 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6982}
6983
6984/*
6985 * Clear out command entries. The argument specifies the first entry in
6986 * PATH which has changed.
6987 */
6988static void
6989clearcmdentry(int firstchange)
6990{
6991 struct tblentry **tblp;
6992 struct tblentry **pp;
6993 struct tblentry *cmdp;
6994
6995 INT_OFF;
6996 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6997 pp = tblp;
6998 while ((cmdp = *pp) != NULL) {
6999 if ((cmdp->cmdtype == CMDNORMAL &&
7000 cmdp->param.index >= firstchange)
7001 || (cmdp->cmdtype == CMDBUILTIN &&
7002 builtinloc >= firstchange)
7003 ) {
7004 *pp = cmdp->next;
7005 free(cmdp);
7006 } else {
7007 pp = &cmdp->next;
7008 }
7009 }
7010 }
7011 INT_ON;
7012}
7013
7014/*
7015 * Locate a command in the command hash table. If "add" is nonzero,
7016 * add the command to the table if it is not already present. The
7017 * variable "lastcmdentry" is set to point to the address of the link
7018 * pointing to the entry, so that delete_cmd_entry can delete the
7019 * entry.
7020 *
7021 * Interrupts must be off if called with add != 0.
7022 */
7023static struct tblentry **lastcmdentry;
7024
7025static struct tblentry *
7026cmdlookup(const char *name, int add)
7027{
7028 unsigned int hashval;
7029 const char *p;
7030 struct tblentry *cmdp;
7031 struct tblentry **pp;
7032
7033 p = name;
7034 hashval = (unsigned char)*p << 4;
7035 while (*p)
7036 hashval += (unsigned char)*p++;
7037 hashval &= 0x7FFF;
7038 pp = &cmdtable[hashval % CMDTABLESIZE];
7039 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7040 if (strcmp(cmdp->cmdname, name) == 0)
7041 break;
7042 pp = &cmdp->next;
7043 }
7044 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007045 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7046 + strlen(name)
7047 /* + 1 - already done because
7048 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007049 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050 cmdp->cmdtype = CMDUNKNOWN;
7051 strcpy(cmdp->cmdname, name);
7052 }
7053 lastcmdentry = pp;
7054 return cmdp;
7055}
7056
7057/*
7058 * Delete the command entry returned on the last lookup.
7059 */
7060static void
7061delete_cmd_entry(void)
7062{
7063 struct tblentry *cmdp;
7064
7065 INT_OFF;
7066 cmdp = *lastcmdentry;
7067 *lastcmdentry = cmdp->next;
7068 if (cmdp->cmdtype == CMDFUNCTION)
7069 freefunc(cmdp->param.func);
7070 free(cmdp);
7071 INT_ON;
7072}
7073
7074/*
7075 * Add a new command entry, replacing any existing command entry for
7076 * the same name - except special builtins.
7077 */
7078static void
7079addcmdentry(char *name, struct cmdentry *entry)
7080{
7081 struct tblentry *cmdp;
7082
7083 cmdp = cmdlookup(name, 1);
7084 if (cmdp->cmdtype == CMDFUNCTION) {
7085 freefunc(cmdp->param.func);
7086 }
7087 cmdp->cmdtype = entry->cmdtype;
7088 cmdp->param = entry->u;
7089 cmdp->rehash = 0;
7090}
7091
7092static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007093hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007094{
7095 struct tblentry **pp;
7096 struct tblentry *cmdp;
7097 int c;
7098 struct cmdentry entry;
7099 char *name;
7100
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007101 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007102 clearcmdentry(0);
7103 return 0;
7104 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007105
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007106 if (*argptr == NULL) {
7107 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7108 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7109 if (cmdp->cmdtype == CMDNORMAL)
7110 printentry(cmdp);
7111 }
7112 }
7113 return 0;
7114 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007115
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007116 c = 0;
7117 while ((name = *argptr) != NULL) {
7118 cmdp = cmdlookup(name, 0);
7119 if (cmdp != NULL
7120 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007121 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7122 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007123 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007124 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007125 find_command(name, &entry, DO_ERR, pathval());
7126 if (entry.cmdtype == CMDUNKNOWN)
7127 c = 1;
7128 argptr++;
7129 }
7130 return c;
7131}
7132
7133/*
7134 * Called when a cd is done. Marks all commands so the next time they
7135 * are executed they will be rehashed.
7136 */
7137static void
7138hashcd(void)
7139{
7140 struct tblentry **pp;
7141 struct tblentry *cmdp;
7142
7143 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7144 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007145 if (cmdp->cmdtype == CMDNORMAL
7146 || (cmdp->cmdtype == CMDBUILTIN
7147 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7148 && builtinloc > 0)
7149 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007150 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007151 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007152 }
7153 }
7154}
7155
7156/*
7157 * Fix command hash table when PATH changed.
7158 * Called before PATH is changed. The argument is the new value of PATH;
7159 * pathval() still returns the old value at this point.
7160 * Called with interrupts off.
7161 */
7162static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007163changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007164{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007165 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007166 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007167 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007168 int idx_bltin;
7169
7170 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007171 firstchange = 9999; /* assume no change */
7172 idx = 0;
7173 idx_bltin = -1;
7174 for (;;) {
7175 if (*old != *new) {
7176 firstchange = idx;
7177 if ((*old == '\0' && *new == ':')
7178 || (*old == ':' && *new == '\0'))
7179 firstchange++;
7180 old = new; /* ignore subsequent differences */
7181 }
7182 if (*new == '\0')
7183 break;
7184 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7185 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007186 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007187 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007188 new++, old++;
7189 }
7190 if (builtinloc < 0 && idx_bltin >= 0)
7191 builtinloc = idx_bltin; /* zap builtins */
7192 if (builtinloc >= 0 && idx_bltin < 0)
7193 firstchange = 0;
7194 clearcmdentry(firstchange);
7195 builtinloc = idx_bltin;
7196}
7197
7198#define TEOF 0
7199#define TNL 1
7200#define TREDIR 2
7201#define TWORD 3
7202#define TSEMI 4
7203#define TBACKGND 5
7204#define TAND 6
7205#define TOR 7
7206#define TPIPE 8
7207#define TLP 9
7208#define TRP 10
7209#define TENDCASE 11
7210#define TENDBQUOTE 12
7211#define TNOT 13
7212#define TCASE 14
7213#define TDO 15
7214#define TDONE 16
7215#define TELIF 17
7216#define TELSE 18
7217#define TESAC 19
7218#define TFI 20
7219#define TFOR 21
7220#define TIF 22
7221#define TIN 23
7222#define TTHEN 24
7223#define TUNTIL 25
7224#define TWHILE 26
7225#define TBEGIN 27
7226#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007227typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007228
7229/* first char is indicating which tokens mark the end of a list */
7230static const char *const tokname_array[] = {
7231 "\1end of file",
7232 "\0newline",
7233 "\0redirection",
7234 "\0word",
7235 "\0;",
7236 "\0&",
7237 "\0&&",
7238 "\0||",
7239 "\0|",
7240 "\0(",
7241 "\1)",
7242 "\1;;",
7243 "\1`",
7244#define KWDOFFSET 13
7245 /* the following are keywords */
7246 "\0!",
7247 "\0case",
7248 "\1do",
7249 "\1done",
7250 "\1elif",
7251 "\1else",
7252 "\1esac",
7253 "\1fi",
7254 "\0for",
7255 "\0if",
7256 "\0in",
7257 "\1then",
7258 "\0until",
7259 "\0while",
7260 "\0{",
7261 "\1}",
7262};
7263
7264static const char *
7265tokname(int tok)
7266{
7267 static char buf[16];
7268
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007269//try this:
7270//if (tok < TSEMI) return tokname_array[tok] + 1;
7271//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7272//return buf;
7273
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007274 if (tok >= TSEMI)
7275 buf[0] = '"';
7276 sprintf(buf + (tok >= TSEMI), "%s%c",
7277 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7278 return buf;
7279}
7280
7281/* Wrapper around strcmp for qsort/bsearch/... */
7282static int
7283pstrcmp(const void *a, const void *b)
7284{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007285 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007286}
7287
7288static const char *const *
7289findkwd(const char *s)
7290{
7291 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007292 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7293 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294}
7295
7296/*
7297 * Locate and print what a word is...
7298 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007299static int
7300describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007301{
7302 struct cmdentry entry;
7303 struct tblentry *cmdp;
7304#if ENABLE_ASH_ALIAS
7305 const struct alias *ap;
7306#endif
7307 const char *path = pathval();
7308
7309 if (describe_command_verbose) {
7310 out1str(command);
7311 }
7312
7313 /* First look at the keywords */
7314 if (findkwd(command)) {
7315 out1str(describe_command_verbose ? " is a shell keyword" : command);
7316 goto out;
7317 }
7318
7319#if ENABLE_ASH_ALIAS
7320 /* Then look at the aliases */
7321 ap = lookupalias(command, 0);
7322 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007323 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007324 out1str("alias ");
7325 printalias(ap);
7326 return 0;
7327 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007328 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007329 goto out;
7330 }
7331#endif
7332 /* Then check if it is a tracked alias */
7333 cmdp = cmdlookup(command, 0);
7334 if (cmdp != NULL) {
7335 entry.cmdtype = cmdp->cmdtype;
7336 entry.u = cmdp->param;
7337 } else {
7338 /* Finally use brute force */
7339 find_command(command, &entry, DO_ABS, path);
7340 }
7341
7342 switch (entry.cmdtype) {
7343 case CMDNORMAL: {
7344 int j = entry.u.index;
7345 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007346 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007347 p = command;
7348 } else {
7349 do {
7350 p = padvance(&path, command);
7351 stunalloc(p);
7352 } while (--j >= 0);
7353 }
7354 if (describe_command_verbose) {
7355 out1fmt(" is%s %s",
7356 (cmdp ? " a tracked alias for" : nullstr), p
7357 );
7358 } else {
7359 out1str(p);
7360 }
7361 break;
7362 }
7363
7364 case CMDFUNCTION:
7365 if (describe_command_verbose) {
7366 out1str(" is a shell function");
7367 } else {
7368 out1str(command);
7369 }
7370 break;
7371
7372 case CMDBUILTIN:
7373 if (describe_command_verbose) {
7374 out1fmt(" is a %sshell builtin",
7375 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7376 "special " : nullstr
7377 );
7378 } else {
7379 out1str(command);
7380 }
7381 break;
7382
7383 default:
7384 if (describe_command_verbose) {
7385 out1str(": not found\n");
7386 }
7387 return 127;
7388 }
7389 out:
7390 outstr("\n", stdout);
7391 return 0;
7392}
7393
7394static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007395typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007396{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007397 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007399 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007400
Denis Vlasenko46846e22007-05-20 13:08:31 +00007401 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007402 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007403 i++;
7404 verbose = 0;
7405 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007406 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007407 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408 }
7409 return err;
7410}
7411
7412#if ENABLE_ASH_CMDCMD
7413static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007414commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415{
7416 int c;
7417 enum {
7418 VERIFY_BRIEF = 1,
7419 VERIFY_VERBOSE = 2,
7420 } verify = 0;
7421
7422 while ((c = nextopt("pvV")) != '\0')
7423 if (c == 'V')
7424 verify |= VERIFY_VERBOSE;
7425 else if (c == 'v')
7426 verify |= VERIFY_BRIEF;
7427#if DEBUG
7428 else if (c != 'p')
7429 abort();
7430#endif
7431 if (verify)
7432 return describe_command(*argptr, verify - VERIFY_BRIEF);
7433
7434 return 0;
7435}
7436#endif
7437
7438
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007439/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007440
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441static int funcblocksize; /* size of structures in function */
7442static int funcstringsize; /* size of strings in node */
7443static void *funcblock; /* block to allocate function from */
7444static char *funcstring; /* block to allocate strings from */
7445
Eric Andersencb57d552001-06-28 07:25:16 +00007446/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007447#define EV_EXIT 01 /* exit after evaluating tree */
7448#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7449#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007450
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007451static const short nodesize[26] = {
7452 SHELL_ALIGN(sizeof(struct ncmd)),
7453 SHELL_ALIGN(sizeof(struct npipe)),
7454 SHELL_ALIGN(sizeof(struct nredir)),
7455 SHELL_ALIGN(sizeof(struct nredir)),
7456 SHELL_ALIGN(sizeof(struct nredir)),
7457 SHELL_ALIGN(sizeof(struct nbinary)),
7458 SHELL_ALIGN(sizeof(struct nbinary)),
7459 SHELL_ALIGN(sizeof(struct nbinary)),
7460 SHELL_ALIGN(sizeof(struct nif)),
7461 SHELL_ALIGN(sizeof(struct nbinary)),
7462 SHELL_ALIGN(sizeof(struct nbinary)),
7463 SHELL_ALIGN(sizeof(struct nfor)),
7464 SHELL_ALIGN(sizeof(struct ncase)),
7465 SHELL_ALIGN(sizeof(struct nclist)),
7466 SHELL_ALIGN(sizeof(struct narg)),
7467 SHELL_ALIGN(sizeof(struct narg)),
7468 SHELL_ALIGN(sizeof(struct nfile)),
7469 SHELL_ALIGN(sizeof(struct nfile)),
7470 SHELL_ALIGN(sizeof(struct nfile)),
7471 SHELL_ALIGN(sizeof(struct nfile)),
7472 SHELL_ALIGN(sizeof(struct nfile)),
7473 SHELL_ALIGN(sizeof(struct ndup)),
7474 SHELL_ALIGN(sizeof(struct ndup)),
7475 SHELL_ALIGN(sizeof(struct nhere)),
7476 SHELL_ALIGN(sizeof(struct nhere)),
7477 SHELL_ALIGN(sizeof(struct nnot)),
7478};
7479
7480static void calcsize(union node *n);
7481
7482static void
7483sizenodelist(struct nodelist *lp)
7484{
7485 while (lp) {
7486 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7487 calcsize(lp->n);
7488 lp = lp->next;
7489 }
7490}
7491
7492static void
7493calcsize(union node *n)
7494{
7495 if (n == NULL)
7496 return;
7497 funcblocksize += nodesize[n->type];
7498 switch (n->type) {
7499 case NCMD:
7500 calcsize(n->ncmd.redirect);
7501 calcsize(n->ncmd.args);
7502 calcsize(n->ncmd.assign);
7503 break;
7504 case NPIPE:
7505 sizenodelist(n->npipe.cmdlist);
7506 break;
7507 case NREDIR:
7508 case NBACKGND:
7509 case NSUBSHELL:
7510 calcsize(n->nredir.redirect);
7511 calcsize(n->nredir.n);
7512 break;
7513 case NAND:
7514 case NOR:
7515 case NSEMI:
7516 case NWHILE:
7517 case NUNTIL:
7518 calcsize(n->nbinary.ch2);
7519 calcsize(n->nbinary.ch1);
7520 break;
7521 case NIF:
7522 calcsize(n->nif.elsepart);
7523 calcsize(n->nif.ifpart);
7524 calcsize(n->nif.test);
7525 break;
7526 case NFOR:
7527 funcstringsize += strlen(n->nfor.var) + 1;
7528 calcsize(n->nfor.body);
7529 calcsize(n->nfor.args);
7530 break;
7531 case NCASE:
7532 calcsize(n->ncase.cases);
7533 calcsize(n->ncase.expr);
7534 break;
7535 case NCLIST:
7536 calcsize(n->nclist.body);
7537 calcsize(n->nclist.pattern);
7538 calcsize(n->nclist.next);
7539 break;
7540 case NDEFUN:
7541 case NARG:
7542 sizenodelist(n->narg.backquote);
7543 funcstringsize += strlen(n->narg.text) + 1;
7544 calcsize(n->narg.next);
7545 break;
7546 case NTO:
7547 case NCLOBBER:
7548 case NFROM:
7549 case NFROMTO:
7550 case NAPPEND:
7551 calcsize(n->nfile.fname);
7552 calcsize(n->nfile.next);
7553 break;
7554 case NTOFD:
7555 case NFROMFD:
7556 calcsize(n->ndup.vname);
7557 calcsize(n->ndup.next);
7558 break;
7559 case NHERE:
7560 case NXHERE:
7561 calcsize(n->nhere.doc);
7562 calcsize(n->nhere.next);
7563 break;
7564 case NNOT:
7565 calcsize(n->nnot.com);
7566 break;
7567 };
7568}
7569
7570static char *
7571nodeckstrdup(char *s)
7572{
7573 char *rtn = funcstring;
7574
7575 strcpy(funcstring, s);
7576 funcstring += strlen(s) + 1;
7577 return rtn;
7578}
7579
7580static union node *copynode(union node *);
7581
7582static struct nodelist *
7583copynodelist(struct nodelist *lp)
7584{
7585 struct nodelist *start;
7586 struct nodelist **lpp;
7587
7588 lpp = &start;
7589 while (lp) {
7590 *lpp = funcblock;
7591 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7592 (*lpp)->n = copynode(lp->n);
7593 lp = lp->next;
7594 lpp = &(*lpp)->next;
7595 }
7596 *lpp = NULL;
7597 return start;
7598}
7599
7600static union node *
7601copynode(union node *n)
7602{
7603 union node *new;
7604
7605 if (n == NULL)
7606 return NULL;
7607 new = funcblock;
7608 funcblock = (char *) funcblock + nodesize[n->type];
7609
7610 switch (n->type) {
7611 case NCMD:
7612 new->ncmd.redirect = copynode(n->ncmd.redirect);
7613 new->ncmd.args = copynode(n->ncmd.args);
7614 new->ncmd.assign = copynode(n->ncmd.assign);
7615 break;
7616 case NPIPE:
7617 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7618 new->npipe.backgnd = n->npipe.backgnd;
7619 break;
7620 case NREDIR:
7621 case NBACKGND:
7622 case NSUBSHELL:
7623 new->nredir.redirect = copynode(n->nredir.redirect);
7624 new->nredir.n = copynode(n->nredir.n);
7625 break;
7626 case NAND:
7627 case NOR:
7628 case NSEMI:
7629 case NWHILE:
7630 case NUNTIL:
7631 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7632 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7633 break;
7634 case NIF:
7635 new->nif.elsepart = copynode(n->nif.elsepart);
7636 new->nif.ifpart = copynode(n->nif.ifpart);
7637 new->nif.test = copynode(n->nif.test);
7638 break;
7639 case NFOR:
7640 new->nfor.var = nodeckstrdup(n->nfor.var);
7641 new->nfor.body = copynode(n->nfor.body);
7642 new->nfor.args = copynode(n->nfor.args);
7643 break;
7644 case NCASE:
7645 new->ncase.cases = copynode(n->ncase.cases);
7646 new->ncase.expr = copynode(n->ncase.expr);
7647 break;
7648 case NCLIST:
7649 new->nclist.body = copynode(n->nclist.body);
7650 new->nclist.pattern = copynode(n->nclist.pattern);
7651 new->nclist.next = copynode(n->nclist.next);
7652 break;
7653 case NDEFUN:
7654 case NARG:
7655 new->narg.backquote = copynodelist(n->narg.backquote);
7656 new->narg.text = nodeckstrdup(n->narg.text);
7657 new->narg.next = copynode(n->narg.next);
7658 break;
7659 case NTO:
7660 case NCLOBBER:
7661 case NFROM:
7662 case NFROMTO:
7663 case NAPPEND:
7664 new->nfile.fname = copynode(n->nfile.fname);
7665 new->nfile.fd = n->nfile.fd;
7666 new->nfile.next = copynode(n->nfile.next);
7667 break;
7668 case NTOFD:
7669 case NFROMFD:
7670 new->ndup.vname = copynode(n->ndup.vname);
7671 new->ndup.dupfd = n->ndup.dupfd;
7672 new->ndup.fd = n->ndup.fd;
7673 new->ndup.next = copynode(n->ndup.next);
7674 break;
7675 case NHERE:
7676 case NXHERE:
7677 new->nhere.doc = copynode(n->nhere.doc);
7678 new->nhere.fd = n->nhere.fd;
7679 new->nhere.next = copynode(n->nhere.next);
7680 break;
7681 case NNOT:
7682 new->nnot.com = copynode(n->nnot.com);
7683 break;
7684 };
7685 new->type = n->type;
7686 return new;
7687}
7688
7689/*
7690 * Make a copy of a parse tree.
7691 */
7692static struct funcnode *
7693copyfunc(union node *n)
7694{
7695 struct funcnode *f;
7696 size_t blocksize;
7697
7698 funcblocksize = offsetof(struct funcnode, n);
7699 funcstringsize = 0;
7700 calcsize(n);
7701 blocksize = funcblocksize;
7702 f = ckmalloc(blocksize + funcstringsize);
7703 funcblock = (char *) f + offsetof(struct funcnode, n);
7704 funcstring = (char *) f + blocksize;
7705 copynode(n);
7706 f->count = 0;
7707 return f;
7708}
7709
7710/*
7711 * Define a shell function.
7712 */
7713static void
7714defun(char *name, union node *func)
7715{
7716 struct cmdentry entry;
7717
7718 INT_OFF;
7719 entry.cmdtype = CMDFUNCTION;
7720 entry.u.func = copyfunc(func);
7721 addcmdentry(name, &entry);
7722 INT_ON;
7723}
7724
7725static int evalskip; /* set if we are skipping commands */
7726/* reasons for skipping commands (see comment on breakcmd routine) */
7727#define SKIPBREAK (1 << 0)
7728#define SKIPCONT (1 << 1)
7729#define SKIPFUNC (1 << 2)
7730#define SKIPFILE (1 << 3)
7731#define SKIPEVAL (1 << 4)
7732static int skipcount; /* number of levels to skip */
7733static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007734static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007735
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007736/* forward decl way out to parsing code - dotrap needs it */
7737static int evalstring(char *s, int mask);
7738
7739/*
7740 * Called to execute a trap. Perhaps we should avoid entering new trap
7741 * handlers while we are executing a trap handler.
7742 */
7743static int
7744dotrap(void)
7745{
7746 char *p;
7747 char *q;
7748 int i;
7749 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007750 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007751
7752 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007753 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007754 xbarrier();
7755
7756 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7757 if (!*q)
7758 continue;
7759 *q = '\0';
7760
7761 p = trap[i + 1];
7762 if (!p)
7763 continue;
7764 skip = evalstring(p, SKIPEVAL);
7765 exitstatus = savestatus;
7766 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007767 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007768 }
7769
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007770 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007771}
7772
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007773/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007774static void evalloop(union node *, int);
7775static void evalfor(union node *, int);
7776static void evalcase(union node *, int);
7777static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007778static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007779static void evalpipe(union node *, int);
7780static void evalcommand(union node *, int);
7781static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007782static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007783
Eric Andersen62483552001-07-10 06:09:16 +00007784/*
Eric Andersenc470f442003-07-28 09:56:35 +00007785 * Evaluate a parse tree. The value is left in the global variable
7786 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007787 */
Eric Andersenc470f442003-07-28 09:56:35 +00007788static void
7789evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007790{
Eric Andersenc470f442003-07-28 09:56:35 +00007791 int checkexit = 0;
7792 void (*evalfn)(union node *, int);
7793 unsigned isor;
7794 int status;
7795 if (n == NULL) {
7796 TRACE(("evaltree(NULL) called\n"));
7797 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007798 }
Eric Andersenc470f442003-07-28 09:56:35 +00007799 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007800 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007801 switch (n->type) {
7802 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007803#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007804 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007805 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007806 break;
7807#endif
7808 case NNOT:
7809 evaltree(n->nnot.com, EV_TESTED);
7810 status = !exitstatus;
7811 goto setstatus;
7812 case NREDIR:
7813 expredir(n->nredir.redirect);
7814 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7815 if (!status) {
7816 evaltree(n->nredir.n, flags & EV_TESTED);
7817 status = exitstatus;
7818 }
7819 popredir(0);
7820 goto setstatus;
7821 case NCMD:
7822 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007823 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007824 if (eflag && !(flags & EV_TESTED))
7825 checkexit = ~0;
7826 goto calleval;
7827 case NFOR:
7828 evalfn = evalfor;
7829 goto calleval;
7830 case NWHILE:
7831 case NUNTIL:
7832 evalfn = evalloop;
7833 goto calleval;
7834 case NSUBSHELL:
7835 case NBACKGND:
7836 evalfn = evalsubshell;
7837 goto calleval;
7838 case NPIPE:
7839 evalfn = evalpipe;
7840 goto checkexit;
7841 case NCASE:
7842 evalfn = evalcase;
7843 goto calleval;
7844 case NAND:
7845 case NOR:
7846 case NSEMI:
7847#if NAND + 1 != NOR
7848#error NAND + 1 != NOR
7849#endif
7850#if NOR + 1 != NSEMI
7851#error NOR + 1 != NSEMI
7852#endif
7853 isor = n->type - NAND;
7854 evaltree(
7855 n->nbinary.ch1,
7856 (flags | ((isor >> 1) - 1)) & EV_TESTED
7857 );
7858 if (!exitstatus == isor)
7859 break;
7860 if (!evalskip) {
7861 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007862 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007863 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007864 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007865 evalfn(n, flags);
7866 break;
7867 }
7868 break;
7869 case NIF:
7870 evaltree(n->nif.test, EV_TESTED);
7871 if (evalskip)
7872 break;
7873 if (exitstatus == 0) {
7874 n = n->nif.ifpart;
7875 goto evaln;
7876 } else if (n->nif.elsepart) {
7877 n = n->nif.elsepart;
7878 goto evaln;
7879 }
7880 goto success;
7881 case NDEFUN:
7882 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007883 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007884 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007885 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007886 exitstatus = status;
7887 break;
7888 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007889 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007890 if ((checkexit & exitstatus))
7891 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007892 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007893 goto exexit;
7894
7895 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007896 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007897 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007898 }
Eric Andersen62483552001-07-10 06:09:16 +00007899}
7900
Eric Andersenc470f442003-07-28 09:56:35 +00007901#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7902static
7903#endif
7904void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7905
Eric Andersenc470f442003-07-28 09:56:35 +00007906static void
7907evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007908{
7909 int status;
7910
7911 loopnest++;
7912 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007913 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007914 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007915 int i;
7916
Eric Andersencb57d552001-06-28 07:25:16 +00007917 evaltree(n->nbinary.ch1, EV_TESTED);
7918 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007919 skipping:
7920 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007921 evalskip = 0;
7922 continue;
7923 }
7924 if (evalskip == SKIPBREAK && --skipcount <= 0)
7925 evalskip = 0;
7926 break;
7927 }
Eric Andersenc470f442003-07-28 09:56:35 +00007928 i = exitstatus;
7929 if (n->type != NWHILE)
7930 i = !i;
7931 if (i != 0)
7932 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007933 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007934 status = exitstatus;
7935 if (evalskip)
7936 goto skipping;
7937 }
7938 loopnest--;
7939 exitstatus = status;
7940}
7941
Eric Andersenc470f442003-07-28 09:56:35 +00007942static void
7943evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007944{
7945 struct arglist arglist;
7946 union node *argp;
7947 struct strlist *sp;
7948 struct stackmark smark;
7949
7950 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007951 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007952 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007953 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007954 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007955 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007956 if (evalskip)
7957 goto out;
7958 }
7959 *arglist.lastp = NULL;
7960
7961 exitstatus = 0;
7962 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007963 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007964 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007965 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007966 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007967 if (evalskip) {
7968 if (evalskip == SKIPCONT && --skipcount <= 0) {
7969 evalskip = 0;
7970 continue;
7971 }
7972 if (evalskip == SKIPBREAK && --skipcount <= 0)
7973 evalskip = 0;
7974 break;
7975 }
7976 }
7977 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007978 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007979 popstackmark(&smark);
7980}
7981
Eric Andersenc470f442003-07-28 09:56:35 +00007982static void
7983evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007984{
7985 union node *cp;
7986 union node *patp;
7987 struct arglist arglist;
7988 struct stackmark smark;
7989
7990 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007991 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007992 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007993 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007994 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007995 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7996 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007997 if (casematch(patp, arglist.list->text)) {
7998 if (evalskip == 0) {
7999 evaltree(cp->nclist.body, flags);
8000 }
8001 goto out;
8002 }
8003 }
8004 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008005 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008006 popstackmark(&smark);
8007}
8008
Eric Andersenc470f442003-07-28 09:56:35 +00008009/*
8010 * Kick off a subshell to evaluate a tree.
8011 */
Eric Andersenc470f442003-07-28 09:56:35 +00008012static void
8013evalsubshell(union node *n, int flags)
8014{
8015 struct job *jp;
8016 int backgnd = (n->type == NBACKGND);
8017 int status;
8018
8019 expredir(n->nredir.redirect);
8020 if (!backgnd && flags & EV_EXIT && !trap[0])
8021 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008022 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008023 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008024 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008025 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008026 flags |= EV_EXIT;
8027 if (backgnd)
8028 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008029 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008030 redirect(n->nredir.redirect, 0);
8031 evaltreenr(n->nredir.n, flags);
8032 /* never returns */
8033 }
8034 status = 0;
8035 if (! backgnd)
8036 status = waitforjob(jp);
8037 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008038 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008039}
8040
Eric Andersenc470f442003-07-28 09:56:35 +00008041/*
8042 * Compute the names of the files in a redirection list.
8043 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008044static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008045static void
8046expredir(union node *n)
8047{
8048 union node *redir;
8049
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008050 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008051 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008052
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008053 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008054 fn.lastp = &fn.list;
8055 switch (redir->type) {
8056 case NFROMTO:
8057 case NFROM:
8058 case NTO:
8059 case NCLOBBER:
8060 case NAPPEND:
8061 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8062 redir->nfile.expfname = fn.list->text;
8063 break;
8064 case NFROMFD:
8065 case NTOFD:
8066 if (redir->ndup.vname) {
8067 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008068 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008069 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008070 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008071 }
8072 break;
8073 }
8074 }
8075}
8076
Eric Andersencb57d552001-06-28 07:25:16 +00008077/*
Eric Andersencb57d552001-06-28 07:25:16 +00008078 * Evaluate a pipeline. All the processes in the pipeline are children
8079 * of the process creating the pipeline. (This differs from some versions
8080 * of the shell, which make the last process in a pipeline the parent
8081 * of all the rest.)
8082 */
Eric Andersenc470f442003-07-28 09:56:35 +00008083static void
8084evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008085{
8086 struct job *jp;
8087 struct nodelist *lp;
8088 int pipelen;
8089 int prevfd;
8090 int pip[2];
8091
Eric Andersenc470f442003-07-28 09:56:35 +00008092 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008093 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008094 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008095 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008096 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008097 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008098 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008099 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008100 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008101 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008102 pip[1] = -1;
8103 if (lp->next) {
8104 if (pipe(pip) < 0) {
8105 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008106 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008107 }
8108 }
8109 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008110 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008111 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008112 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008113 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008114 if (prevfd > 0) {
8115 dup2(prevfd, 0);
8116 close(prevfd);
8117 }
8118 if (pip[1] > 1) {
8119 dup2(pip[1], 1);
8120 close(pip[1]);
8121 }
Eric Andersenc470f442003-07-28 09:56:35 +00008122 evaltreenr(lp->n, flags);
8123 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008124 }
8125 if (prevfd >= 0)
8126 close(prevfd);
8127 prevfd = pip[0];
8128 close(pip[1]);
8129 }
Eric Andersencb57d552001-06-28 07:25:16 +00008130 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008131 exitstatus = waitforjob(jp);
8132 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008133 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008134 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008135}
8136
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008137/*
8138 * Controls whether the shell is interactive or not.
8139 */
8140static void
8141setinteractive(int on)
8142{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008143 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008144
8145 if (++on == is_interactive)
8146 return;
8147 is_interactive = on;
8148 setsignal(SIGINT);
8149 setsignal(SIGQUIT);
8150 setsignal(SIGTERM);
8151#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8152 if (is_interactive > 1) {
8153 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008154 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008155
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008156 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008157 out1fmt(
8158 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008159 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008160 "Enter 'help' for a list of built-in commands."
8161 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008162 bb_banner);
8163 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008164 }
8165 }
8166#endif
8167}
8168
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008169static void
8170optschanged(void)
8171{
8172#if DEBUG
8173 opentrace();
8174#endif
8175 setinteractive(iflag);
8176 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008177#if ENABLE_FEATURE_EDITING_VI
8178 if (viflag)
8179 line_input_state->flags |= VI_MODE;
8180 else
8181 line_input_state->flags &= ~VI_MODE;
8182#else
8183 viflag = 0; /* forcibly keep the option off */
8184#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008185}
8186
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008187static struct localvar *localvars;
8188
8189/*
8190 * Called after a function returns.
8191 * Interrupts must be off.
8192 */
8193static void
8194poplocalvars(void)
8195{
8196 struct localvar *lvp;
8197 struct var *vp;
8198
8199 while ((lvp = localvars) != NULL) {
8200 localvars = lvp->next;
8201 vp = lvp->vp;
8202 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8203 if (vp == NULL) { /* $- saved */
8204 memcpy(optlist, lvp->text, sizeof(optlist));
8205 free((char*)lvp->text);
8206 optschanged();
8207 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8208 unsetvar(vp->text);
8209 } else {
8210 if (vp->func)
8211 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8212 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8213 free((char*)vp->text);
8214 vp->flags = lvp->flags;
8215 vp->text = lvp->text;
8216 }
8217 free(lvp);
8218 }
8219}
8220
8221static int
8222evalfun(struct funcnode *func, int argc, char **argv, int flags)
8223{
8224 volatile struct shparam saveparam;
8225 struct localvar *volatile savelocalvars;
8226 struct jmploc *volatile savehandler;
8227 struct jmploc jmploc;
8228 int e;
8229
8230 saveparam = shellparam;
8231 savelocalvars = localvars;
8232 e = setjmp(jmploc.loc);
8233 if (e) {
8234 goto funcdone;
8235 }
8236 INT_OFF;
8237 savehandler = exception_handler;
8238 exception_handler = &jmploc;
8239 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008240 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008241 func->count++;
8242 funcnest++;
8243 INT_ON;
8244 shellparam.nparam = argc - 1;
8245 shellparam.p = argv + 1;
8246#if ENABLE_ASH_GETOPTS
8247 shellparam.optind = 1;
8248 shellparam.optoff = -1;
8249#endif
8250 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008251 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008252 INT_OFF;
8253 funcnest--;
8254 freefunc(func);
8255 poplocalvars();
8256 localvars = savelocalvars;
8257 freeparam(&shellparam);
8258 shellparam = saveparam;
8259 exception_handler = savehandler;
8260 INT_ON;
8261 evalskip &= ~SKIPFUNC;
8262 return e;
8263}
8264
Denis Vlasenko131ae172007-02-18 13:00:19 +00008265#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008266static char **
8267parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008268{
8269 char *cp, c;
8270
8271 for (;;) {
8272 cp = *++argv;
8273 if (!cp)
8274 return 0;
8275 if (*cp++ != '-')
8276 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008277 c = *cp++;
8278 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008279 break;
8280 if (c == '-' && !*cp) {
8281 argv++;
8282 break;
8283 }
8284 do {
8285 switch (c) {
8286 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008287 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008288 break;
8289 default:
8290 /* run 'typecmd' for other options */
8291 return 0;
8292 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008293 c = *cp++;
8294 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008295 }
8296 return argv;
8297}
8298#endif
8299
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008300/*
8301 * Make a variable a local variable. When a variable is made local, it's
8302 * value and flags are saved in a localvar structure. The saved values
8303 * will be restored when the shell function returns. We handle the name
8304 * "-" as a special case.
8305 */
8306static void
8307mklocal(char *name)
8308{
8309 struct localvar *lvp;
8310 struct var **vpp;
8311 struct var *vp;
8312
8313 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008314 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008315 if (LONE_DASH(name)) {
8316 char *p;
8317 p = ckmalloc(sizeof(optlist));
8318 lvp->text = memcpy(p, optlist, sizeof(optlist));
8319 vp = NULL;
8320 } else {
8321 char *eq;
8322
8323 vpp = hashvar(name);
8324 vp = *findvar(vpp, name);
8325 eq = strchr(name, '=');
8326 if (vp == NULL) {
8327 if (eq)
8328 setvareq(name, VSTRFIXED);
8329 else
8330 setvar(name, NULL, VSTRFIXED);
8331 vp = *vpp; /* the new variable */
8332 lvp->flags = VUNSET;
8333 } else {
8334 lvp->text = vp->text;
8335 lvp->flags = vp->flags;
8336 vp->flags |= VSTRFIXED|VTEXTFIXED;
8337 if (eq)
8338 setvareq(name, 0);
8339 }
8340 }
8341 lvp->vp = vp;
8342 lvp->next = localvars;
8343 localvars = lvp;
8344 INT_ON;
8345}
8346
8347/*
8348 * The "local" command.
8349 */
8350static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008351localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008352{
8353 char *name;
8354
8355 argv = argptr;
8356 while ((name = *argv++) != NULL) {
8357 mklocal(name);
8358 }
8359 return 0;
8360}
8361
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008362static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008363falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008364{
8365 return 1;
8366}
8367
8368static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008369truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008370{
8371 return 0;
8372}
8373
8374static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008375execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008376{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008377 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008378 iflag = 0; /* exit on error */
8379 mflag = 0;
8380 optschanged();
8381 shellexec(argv + 1, pathval(), 0);
8382 }
8383 return 0;
8384}
8385
8386/*
8387 * The return command.
8388 */
8389static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008390returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008391{
8392 /*
8393 * If called outside a function, do what ksh does;
8394 * skip the rest of the file.
8395 */
8396 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8397 return argv[1] ? number(argv[1]) : exitstatus;
8398}
8399
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008400/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008401static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008402static int dotcmd(int, char **);
8403static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008404static int exitcmd(int, char **);
8405static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008406#if ENABLE_ASH_GETOPTS
8407static int getoptscmd(int, char **);
8408#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008409#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008410static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008411#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008412#if ENABLE_ASH_MATH_SUPPORT
8413static int letcmd(int, char **);
8414#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008416static int setcmd(int, char **);
8417static int shiftcmd(int, char **);
8418static int timescmd(int, char **);
8419static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008420static int umaskcmd(int, char **);
8421static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008422static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008423
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008424#define BUILTIN_NOSPEC "0"
8425#define BUILTIN_SPECIAL "1"
8426#define BUILTIN_REGULAR "2"
8427#define BUILTIN_SPEC_REG "3"
8428#define BUILTIN_ASSIGN "4"
8429#define BUILTIN_SPEC_ASSG "5"
8430#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008431#define BUILTIN_SPEC_REG_ASSG "7"
8432
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008433/* We do not handle [[ expr ]] bashism bash-compatibly,
8434 * we make it a synonym of [ expr ].
8435 * Basically, word splitting and pathname expansion should NOT be performed
8436 * Examples:
8437 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8438 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8439 * Additional operators:
8440 * || and && should work as -o and -a
8441 * =~ regexp match
8442 * Apart from the above, [[ expr ]] should work as [ expr ]
8443 */
8444
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008445#define echocmd echo_main
8446#define printfcmd printf_main
8447#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008448
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008449/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008450static const struct builtincmd builtintab[] = {
8451 { BUILTIN_SPEC_REG ".", dotcmd },
8452 { BUILTIN_SPEC_REG ":", truecmd },
8453#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008454 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008455#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008456 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008457#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008458#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008459#if ENABLE_ASH_ALIAS
8460 { BUILTIN_REG_ASSG "alias", aliascmd },
8461#endif
8462#if JOBS
8463 { BUILTIN_REGULAR "bg", fg_bgcmd },
8464#endif
8465 { BUILTIN_SPEC_REG "break", breakcmd },
8466 { BUILTIN_REGULAR "cd", cdcmd },
8467 { BUILTIN_NOSPEC "chdir", cdcmd },
8468#if ENABLE_ASH_CMDCMD
8469 { BUILTIN_REGULAR "command", commandcmd },
8470#endif
8471 { BUILTIN_SPEC_REG "continue", breakcmd },
8472#if ENABLE_ASH_BUILTIN_ECHO
8473 { BUILTIN_REGULAR "echo", echocmd },
8474#endif
8475 { BUILTIN_SPEC_REG "eval", evalcmd },
8476 { BUILTIN_SPEC_REG "exec", execcmd },
8477 { BUILTIN_SPEC_REG "exit", exitcmd },
8478 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8479 { BUILTIN_REGULAR "false", falsecmd },
8480#if JOBS
8481 { BUILTIN_REGULAR "fg", fg_bgcmd },
8482#endif
8483#if ENABLE_ASH_GETOPTS
8484 { BUILTIN_REGULAR "getopts", getoptscmd },
8485#endif
8486 { BUILTIN_NOSPEC "hash", hashcmd },
8487#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8488 { BUILTIN_NOSPEC "help", helpcmd },
8489#endif
8490#if JOBS
8491 { BUILTIN_REGULAR "jobs", jobscmd },
8492 { BUILTIN_REGULAR "kill", killcmd },
8493#endif
8494#if ENABLE_ASH_MATH_SUPPORT
8495 { BUILTIN_NOSPEC "let", letcmd },
8496#endif
8497 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008498#if ENABLE_ASH_BUILTIN_PRINTF
8499 { BUILTIN_REGULAR "printf", printfcmd },
8500#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008501 { BUILTIN_NOSPEC "pwd", pwdcmd },
8502 { BUILTIN_REGULAR "read", readcmd },
8503 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8504 { BUILTIN_SPEC_REG "return", returncmd },
8505 { BUILTIN_SPEC_REG "set", setcmd },
8506 { BUILTIN_SPEC_REG "shift", shiftcmd },
8507 { BUILTIN_SPEC_REG "source", dotcmd },
8508#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008509 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008510#endif
8511 { BUILTIN_SPEC_REG "times", timescmd },
8512 { BUILTIN_SPEC_REG "trap", trapcmd },
8513 { BUILTIN_REGULAR "true", truecmd },
8514 { BUILTIN_NOSPEC "type", typecmd },
8515 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8516 { BUILTIN_REGULAR "umask", umaskcmd },
8517#if ENABLE_ASH_ALIAS
8518 { BUILTIN_REGULAR "unalias", unaliascmd },
8519#endif
8520 { BUILTIN_SPEC_REG "unset", unsetcmd },
8521 { BUILTIN_REGULAR "wait", waitcmd },
8522};
8523
Denis Vlasenko80591b02008-03-25 07:49:43 +00008524/* Should match the above table! */
8525#define COMMANDCMD (builtintab + \
8526 2 + \
8527 1 * ENABLE_ASH_BUILTIN_TEST + \
8528 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8529 1 * ENABLE_ASH_ALIAS + \
8530 1 * ENABLE_ASH_JOB_CONTROL + \
8531 3)
8532#define EXECCMD (builtintab + \
8533 2 + \
8534 1 * ENABLE_ASH_BUILTIN_TEST + \
8535 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8536 1 * ENABLE_ASH_ALIAS + \
8537 1 * ENABLE_ASH_JOB_CONTROL + \
8538 3 + \
8539 1 * ENABLE_ASH_CMDCMD + \
8540 1 + \
8541 ENABLE_ASH_BUILTIN_ECHO + \
8542 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008543
8544/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008545 * Search the table of builtin commands.
8546 */
8547static struct builtincmd *
8548find_builtin(const char *name)
8549{
8550 struct builtincmd *bp;
8551
8552 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008553 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008554 pstrcmp
8555 );
8556 return bp;
8557}
8558
8559/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008560 * Execute a simple command.
8561 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008562static int
8563isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008564{
8565 const char *q = endofname(p);
8566 if (p == q)
8567 return 0;
8568 return *q == '=';
8569}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008570static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008571bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008572{
8573 /* Preserve exitstatus of a previous possible redirection
8574 * as POSIX mandates */
8575 return back_exitstatus;
8576}
Eric Andersenc470f442003-07-28 09:56:35 +00008577static void
8578evalcommand(union node *cmd, int flags)
8579{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008580 static const struct builtincmd null_bltin = {
8581 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008582 };
Eric Andersenc470f442003-07-28 09:56:35 +00008583 struct stackmark smark;
8584 union node *argp;
8585 struct arglist arglist;
8586 struct arglist varlist;
8587 char **argv;
8588 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008589 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008590 struct cmdentry cmdentry;
8591 struct job *jp;
8592 char *lastarg;
8593 const char *path;
8594 int spclbltin;
8595 int cmd_is_exec;
8596 int status;
8597 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008598 struct builtincmd *bcmd;
8599 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008600
8601 /* First expand the arguments. */
8602 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8603 setstackmark(&smark);
8604 back_exitstatus = 0;
8605
8606 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008607 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008608 varlist.lastp = &varlist.list;
8609 *varlist.lastp = NULL;
8610 arglist.lastp = &arglist.list;
8611 *arglist.lastp = NULL;
8612
8613 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008614 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008615 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8616 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8617 }
8618
Eric Andersenc470f442003-07-28 09:56:35 +00008619 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8620 struct strlist **spp;
8621
8622 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008623 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008624 expandarg(argp, &arglist, EXP_VARTILDE);
8625 else
8626 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8627
Eric Andersenc470f442003-07-28 09:56:35 +00008628 for (sp = *spp; sp; sp = sp->next)
8629 argc++;
8630 }
8631
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008632 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008633 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008634 TRACE(("evalcommand arg: %s\n", sp->text));
8635 *nargv++ = sp->text;
8636 }
8637 *nargv = NULL;
8638
8639 lastarg = NULL;
8640 if (iflag && funcnest == 0 && argc > 0)
8641 lastarg = nargv[-1];
8642
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008643 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008644 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008645 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008646
8647 path = vpath.text;
8648 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8649 struct strlist **spp;
8650 char *p;
8651
8652 spp = varlist.lastp;
8653 expandarg(argp, &varlist, EXP_VARTILDE);
8654
8655 /*
8656 * Modify the command lookup path, if a PATH= assignment
8657 * is present
8658 */
8659 p = (*spp)->text;
8660 if (varequal(p, path))
8661 path = p;
8662 }
8663
8664 /* Print the command if xflag is set. */
8665 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008666 int n;
8667 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008668
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008669 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008670 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008671
8672 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008673 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008674 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008675 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008676 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008677 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008678 p--;
8679 }
8680 }
8681 sp = arglist.list;
8682 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008683 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008684 }
8685
8686 cmd_is_exec = 0;
8687 spclbltin = -1;
8688
8689 /* Now locate the command. */
8690 if (argc) {
8691 const char *oldpath;
8692 int cmd_flag = DO_ERR;
8693
8694 path += 5;
8695 oldpath = path;
8696 for (;;) {
8697 find_command(argv[0], &cmdentry, cmd_flag, path);
8698 if (cmdentry.cmdtype == CMDUNKNOWN) {
8699 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008700 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008701 goto bail;
8702 }
8703
8704 /* implement bltin and command here */
8705 if (cmdentry.cmdtype != CMDBUILTIN)
8706 break;
8707 if (spclbltin < 0)
8708 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8709 if (cmdentry.u.cmd == EXECCMD)
8710 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008711#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008712 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008713 path = oldpath;
8714 nargv = parse_command_args(argv, &path);
8715 if (!nargv)
8716 break;
8717 argc -= nargv - argv;
8718 argv = nargv;
8719 cmd_flag |= DO_NOFUNC;
8720 } else
8721#endif
8722 break;
8723 }
8724 }
8725
8726 if (status) {
8727 /* We have a redirection error. */
8728 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008729 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008730 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008731 exitstatus = status;
8732 goto out;
8733 }
8734
8735 /* Execute the command. */
8736 switch (cmdentry.cmdtype) {
8737 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008738#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008739 {
8740 /* find_command() encodes applet_no as (-2 - applet_no) */
8741 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008742 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008743 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008744 /* run <applet>_main() */
8745 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008746 break;
8747 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008748 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008749#endif
8750
Eric Andersenc470f442003-07-28 09:56:35 +00008751 /* Fork off a child process if necessary. */
8752 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008753 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008754 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008755 if (forkshell(jp, cmd, FORK_FG) != 0) {
8756 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008757 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008758 break;
8759 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008760 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008761 }
8762 listsetvar(varlist.list, VEXPORT|VSTACK);
8763 shellexec(argv, path, cmdentry.u.index);
8764 /* NOTREACHED */
8765
8766 case CMDBUILTIN:
8767 cmdenviron = varlist.list;
8768 if (cmdenviron) {
8769 struct strlist *list = cmdenviron;
8770 int i = VNOSET;
8771 if (spclbltin > 0 || argc == 0) {
8772 i = 0;
8773 if (cmd_is_exec && argc > 1)
8774 i = VEXPORT;
8775 }
8776 listsetvar(list, i);
8777 }
8778 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8779 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008780 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008781 if (i == EXEXIT)
8782 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008783 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008784 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008785 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008786 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008787 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008788 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008789 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008790 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008791 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008792 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008793 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008794 }
8795 break;
8796
8797 case CMDFUNCTION:
8798 listsetvar(varlist.list, 0);
8799 if (evalfun(cmdentry.u.func, argc, argv, flags))
8800 goto raise;
8801 break;
8802 }
8803
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008804 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008805 popredir(cmd_is_exec);
8806 if (lastarg)
8807 /* dsl: I think this is intended to be used to support
8808 * '_' in 'vi' command mode during line editing...
8809 * However I implemented that within libedit itself.
8810 */
8811 setvar("_", lastarg, 0);
8812 popstackmark(&smark);
8813}
8814
8815static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008816evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8817{
Eric Andersenc470f442003-07-28 09:56:35 +00008818 char *volatile savecmdname;
8819 struct jmploc *volatile savehandler;
8820 struct jmploc jmploc;
8821 int i;
8822
8823 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008824 i = setjmp(jmploc.loc);
8825 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008826 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008827 savehandler = exception_handler;
8828 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008829 commandname = argv[0];
8830 argptr = argv + 1;
8831 optptr = NULL; /* initialize nextopt */
8832 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008833 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008834 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008835 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008836 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008837 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008838// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008839 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008840
8841 return i;
8842}
8843
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008844static int
8845goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008846{
8847 return !*endofname(p);
8848}
8849
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008850
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008851/*
8852 * Search for a command. This is called before we fork so that the
8853 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008854 * the child. The check for "goodname" is an overly conservative
8855 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008856 */
Eric Andersenc470f442003-07-28 09:56:35 +00008857static void
8858prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008859{
8860 struct cmdentry entry;
8861
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008862 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8863 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008864}
8865
Eric Andersencb57d552001-06-28 07:25:16 +00008866
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008867/* ============ Builtin commands
8868 *
8869 * Builtin commands whose functions are closely tied to evaluation
8870 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008871 */
8872
8873/*
Eric Andersencb57d552001-06-28 07:25:16 +00008874 * Handle break and continue commands. Break, continue, and return are
8875 * all handled by setting the evalskip flag. The evaluation routines
8876 * above all check this flag, and if it is set they start skipping
8877 * commands rather than executing them. The variable skipcount is
8878 * the number of loops to break/continue, or the number of function
8879 * levels to return. (The latter is always 1.) It should probably
8880 * be an error to break out of more loops than exist, but it isn't
8881 * in the standard shell so we don't make it one here.
8882 */
Eric Andersenc470f442003-07-28 09:56:35 +00008883static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008884breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008885{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008886 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008887
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008888 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008889 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008890 if (n > loopnest)
8891 n = loopnest;
8892 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008893 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008894 skipcount = n;
8895 }
8896 return 0;
8897}
8898
Eric Andersenc470f442003-07-28 09:56:35 +00008899
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008900/* ============ input.c
8901 *
Eric Andersen90898442003-08-06 11:20:52 +00008902 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008903 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008904
Eric Andersenc470f442003-07-28 09:56:35 +00008905#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008906
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008907enum {
8908 INPUT_PUSH_FILE = 1,
8909 INPUT_NOFILE_OK = 2,
8910};
Eric Andersencb57d552001-06-28 07:25:16 +00008911
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008912static int plinno = 1; /* input line number */
8913/* number of characters left in input buffer */
8914static int parsenleft; /* copy of parsefile->nleft */
8915static int parselleft; /* copy of parsefile->lleft */
8916/* next character in input buffer */
8917static char *parsenextc; /* copy of parsefile->nextc */
8918
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008919static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008920/* values of checkkwd variable */
8921#define CHKALIAS 0x1
8922#define CHKKWD 0x2
8923#define CHKNL 0x4
8924
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008925static void
8926popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008927{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008928 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008929
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008930 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008931#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008932 if (sp->ap) {
8933 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8934 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008935 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008936 if (sp->string != sp->ap->val) {
8937 free(sp->string);
8938 }
8939 sp->ap->flag &= ~ALIASINUSE;
8940 if (sp->ap->flag & ALIASDEAD) {
8941 unalias(sp->ap->name);
8942 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008943 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008944#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008945 parsenextc = sp->prevstring;
8946 parsenleft = sp->prevnleft;
8947/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008948 g_parsefile->strpush = sp->prev;
8949 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008950 free(sp);
8951 INT_ON;
8952}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008953
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008954static int
8955preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008956{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008957 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008958 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008959 parsenextc = buf;
8960
Denis Vlasenko38f63192007-01-22 09:03:07 +00008961#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008962 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008963 if (!iflag || g_parsefile->fd)
8964 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008965 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008966#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008967 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008968#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008969 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8970 if (nr == 0) {
8971 /* Ctrl+C pressed */
8972 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008973 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008974 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008975 raise(SIGINT);
8976 return 1;
8977 }
Eric Andersenc470f442003-07-28 09:56:35 +00008978 goto retry;
8979 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008980 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008981 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008982 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008983 }
Eric Andersencb57d552001-06-28 07:25:16 +00008984 }
8985#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008986 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008987#endif
8988
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008989#if 0
8990/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008991 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008992 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008993 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008994 if (flags >= 0 && (flags & O_NONBLOCK)) {
8995 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008996 if (fcntl(0, F_SETFL, flags) >= 0) {
8997 out2str("sh: turning off NDELAY mode\n");
8998 goto retry;
8999 }
9000 }
9001 }
9002 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009003#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009004 return nr;
9005}
9006
9007/*
9008 * Refill the input buffer and return the next input character:
9009 *
9010 * 1) If a string was pushed back on the input, pop it;
9011 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9012 * from a string so we can't refill the buffer, return EOF.
9013 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9014 * 4) Process input up to the next newline, deleting nul characters.
9015 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009016static int
Eric Andersenc470f442003-07-28 09:56:35 +00009017preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009018{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009019 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009020 int more;
9021 char savec;
9022
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009023 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009024#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009025 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009026 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009027 return PEOA;
9028 }
Eric Andersen2870d962001-07-02 17:27:21 +00009029#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009030 popstring();
9031 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009032 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009033 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009034 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009035 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009036 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009037
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009038 more = parselleft;
9039 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009040 again:
9041 more = preadfd();
9042 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009043 parselleft = parsenleft = EOF_NLEFT;
9044 return PEOF;
9045 }
9046 }
9047
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009048 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009049
9050 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009051 for (;;) {
9052 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009053
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009054 more--;
9055 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009056
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009057 if (!c)
9058 memmove(q, q + 1, more);
9059 else {
9060 q++;
9061 if (c == '\n') {
9062 parsenleft = q - parsenextc - 1;
9063 break;
9064 }
Eric Andersencb57d552001-06-28 07:25:16 +00009065 }
9066
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009067 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009068 parsenleft = q - parsenextc - 1;
9069 if (parsenleft < 0)
9070 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009071 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009072 }
9073 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009074 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009075
9076 savec = *q;
9077 *q = '\0';
9078
9079 if (vflag) {
9080 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009081 }
9082
9083 *q = savec;
9084
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009085 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009086}
9087
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009088#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009089static int
9090pgetc(void)
9091{
9092 return pgetc_as_macro();
9093}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009094
9095#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9096#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009097#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009098#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009099#endif
9100
9101/*
9102 * Same as pgetc(), but ignores PEOA.
9103 */
9104#if ENABLE_ASH_ALIAS
9105static int
9106pgetc2(void)
9107{
9108 int c;
9109
9110 do {
9111 c = pgetc_macro();
9112 } while (c == PEOA);
9113 return c;
9114}
9115#else
9116static int
9117pgetc2(void)
9118{
9119 return pgetc_macro();
9120}
9121#endif
9122
9123/*
9124 * Read a line from the script.
9125 */
9126static char *
9127pfgets(char *line, int len)
9128{
9129 char *p = line;
9130 int nleft = len;
9131 int c;
9132
9133 while (--nleft > 0) {
9134 c = pgetc2();
9135 if (c == PEOF) {
9136 if (p == line)
9137 return NULL;
9138 break;
9139 }
9140 *p++ = c;
9141 if (c == '\n')
9142 break;
9143 }
9144 *p = '\0';
9145 return line;
9146}
9147
Eric Andersenc470f442003-07-28 09:56:35 +00009148/*
9149 * Undo the last call to pgetc. Only one character may be pushed back.
9150 * PEOF may be pushed back.
9151 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009152static void
Eric Andersenc470f442003-07-28 09:56:35 +00009153pungetc(void)
9154{
9155 parsenleft++;
9156 parsenextc--;
9157}
Eric Andersencb57d552001-06-28 07:25:16 +00009158
9159/*
9160 * Push a string back onto the input at this current parsefile level.
9161 * We handle aliases this way.
9162 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009163#if !ENABLE_ASH_ALIAS
9164#define pushstring(s, ap) pushstring(s)
9165#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009166static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009167pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009168{
Eric Andersencb57d552001-06-28 07:25:16 +00009169 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009170 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009171
Eric Andersenc470f442003-07-28 09:56:35 +00009172 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009173 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009174/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009175 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009176 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009177 sp->prev = g_parsefile->strpush;
9178 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009179 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009180 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009181 sp->prevstring = parsenextc;
9182 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009183#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009184 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009185 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009186 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009187 sp->string = s;
9188 }
Eric Andersen2870d962001-07-02 17:27:21 +00009189#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009190 parsenextc = s;
9191 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009192 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009193}
9194
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009195/*
9196 * To handle the "." command, a stack of input files is used. Pushfile
9197 * adds a new entry to the stack and popfile restores the previous level.
9198 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009199static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009200pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009201{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009202 struct parsefile *pf;
9203
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009204 g_parsefile->nleft = parsenleft;
9205 g_parsefile->lleft = parselleft;
9206 g_parsefile->nextc = parsenextc;
9207 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009208 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009209 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009210 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009211 /*pf->strpush = NULL; - ckzalloc did it */
9212 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009213 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009214}
9215
9216static void
9217popfile(void)
9218{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009219 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009220
Denis Vlasenkob012b102007-02-19 22:43:01 +00009221 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009222 if (pf->fd >= 0)
9223 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009224 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009225 while (pf->strpush)
9226 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009227 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009228 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009229 parsenleft = g_parsefile->nleft;
9230 parselleft = g_parsefile->lleft;
9231 parsenextc = g_parsefile->nextc;
9232 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009233 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009234}
9235
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009236/*
9237 * Return to top level.
9238 */
9239static void
9240popallfiles(void)
9241{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009242 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009243 popfile();
9244}
9245
9246/*
9247 * Close the file(s) that the shell is reading commands from. Called
9248 * after a fork is done.
9249 */
9250static void
9251closescript(void)
9252{
9253 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009254 if (g_parsefile->fd > 0) {
9255 close(g_parsefile->fd);
9256 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009257 }
9258}
9259
9260/*
9261 * Like setinputfile, but takes an open file descriptor. Call this with
9262 * interrupts off.
9263 */
9264static void
9265setinputfd(int fd, int push)
9266{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009267 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009268 if (push) {
9269 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009270 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009271 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009272 g_parsefile->fd = fd;
9273 if (g_parsefile->buf == NULL)
9274 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009275 parselleft = parsenleft = 0;
9276 plinno = 1;
9277}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009278
Eric Andersenc470f442003-07-28 09:56:35 +00009279/*
9280 * Set the input to take input from a file. If push is set, push the
9281 * old input onto the stack first.
9282 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009283static int
9284setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009285{
9286 int fd;
9287 int fd2;
9288
Denis Vlasenkob012b102007-02-19 22:43:01 +00009289 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009290 fd = open(fname, O_RDONLY);
9291 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009292 if (flags & INPUT_NOFILE_OK)
9293 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009294 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009295 }
Eric Andersenc470f442003-07-28 09:56:35 +00009296 if (fd < 10) {
9297 fd2 = copyfd(fd, 10);
9298 close(fd);
9299 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009300 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009301 fd = fd2;
9302 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009303 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009304 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009305 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009306 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009307}
9308
Eric Andersencb57d552001-06-28 07:25:16 +00009309/*
9310 * Like setinputfile, but takes input from a string.
9311 */
Eric Andersenc470f442003-07-28 09:56:35 +00009312static void
9313setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009314{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009315 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009316 pushfile();
9317 parsenextc = string;
9318 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009319 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009320 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009321 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009322}
9323
9324
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009325/* ============ mail.c
9326 *
9327 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009328 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009329
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009330#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Eric Andersencb57d552001-06-28 07:25:16 +00009332#define MAXMBOXES 10
9333
Eric Andersenc470f442003-07-28 09:56:35 +00009334/* times of mailboxes */
9335static time_t mailtime[MAXMBOXES];
9336/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009337static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009338
Eric Andersencb57d552001-06-28 07:25:16 +00009339/*
Eric Andersenc470f442003-07-28 09:56:35 +00009340 * Print appropriate message(s) if mail has arrived.
9341 * If mail_var_path_changed is set,
9342 * then the value of MAIL has mail_var_path_changed,
9343 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009344 */
Eric Andersenc470f442003-07-28 09:56:35 +00009345static void
9346chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009347{
Eric Andersencb57d552001-06-28 07:25:16 +00009348 const char *mpath;
9349 char *p;
9350 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009351 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009352 struct stackmark smark;
9353 struct stat statb;
9354
Eric Andersencb57d552001-06-28 07:25:16 +00009355 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009356 mpath = mpathset() ? mpathval() : mailval();
9357 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009358 p = padvance(&mpath, nullstr);
9359 if (p == NULL)
9360 break;
9361 if (*p == '\0')
9362 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009363 for (q = p; *q; q++)
9364 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009365#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009366 if (q[-1] != '/')
9367 abort();
9368#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009369 q[-1] = '\0'; /* delete trailing '/' */
9370 if (stat(p, &statb) < 0) {
9371 *mtp = 0;
9372 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009373 }
Eric Andersenc470f442003-07-28 09:56:35 +00009374 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9375 fprintf(
9376 stderr, snlfmt,
9377 pathopt ? pathopt : "you have mail"
9378 );
9379 }
9380 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009381 }
Eric Andersenc470f442003-07-28 09:56:35 +00009382 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009383 popstackmark(&smark);
9384}
Eric Andersencb57d552001-06-28 07:25:16 +00009385
Eric Andersenc470f442003-07-28 09:56:35 +00009386static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009387changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009388{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009389 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009390}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009391
Denis Vlasenko131ae172007-02-18 13:00:19 +00009392#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009393
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009394
9395/* ============ ??? */
9396
Eric Andersencb57d552001-06-28 07:25:16 +00009397/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009398 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009399 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009400static void
9401setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009402{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009403 char **newparam;
9404 char **ap;
9405 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009406
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009407 for (nparam = 0; argv[nparam]; nparam++)
9408 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009409 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9410 while (*argv) {
9411 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009412 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009413 *ap = NULL;
9414 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009415 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009416 shellparam.nparam = nparam;
9417 shellparam.p = newparam;
9418#if ENABLE_ASH_GETOPTS
9419 shellparam.optind = 1;
9420 shellparam.optoff = -1;
9421#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009422}
9423
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009424/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009425 * Process shell options. The global variable argptr contains a pointer
9426 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009427 *
9428 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9429 * For a non-interactive shell, an error condition encountered
9430 * by a special built-in ... shall cause the shell to write a diagnostic message
9431 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009432 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009433 * ...
9434 * Utility syntax error (option or operand error) Shall exit
9435 * ...
9436 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9437 * we see that bash does not do that (set "finishes" with error code 1 instead,
9438 * and shell continues), and people rely on this behavior!
9439 * Testcase:
9440 * set -o barfoo 2>/dev/null
9441 * echo $?
9442 *
9443 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009444 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009445static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009446plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009447{
9448 int i;
9449
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009450 if (name) {
9451 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009452 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009453 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009454 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009455 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009456 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009457 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009458 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009459 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009460 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009461 if (val) {
9462 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9463 } else {
9464 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9465 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009466 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009467 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009468}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009469static void
9470setoption(int flag, int val)
9471{
9472 int i;
9473
9474 for (i = 0; i < NOPTS; i++) {
9475 if (optletters(i) == flag) {
9476 optlist[i] = val;
9477 return;
9478 }
9479 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009480 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009481 /* NOTREACHED */
9482}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009483static int
Eric Andersenc470f442003-07-28 09:56:35 +00009484options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009485{
9486 char *p;
9487 int val;
9488 int c;
9489
9490 if (cmdline)
9491 minusc = NULL;
9492 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009493 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009494 if (c != '-' && c != '+')
9495 break;
9496 argptr++;
9497 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009498 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009499 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009500 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009501 if (!cmdline) {
9502 /* "-" means turn off -x and -v */
9503 if (p[0] == '\0')
9504 xflag = vflag = 0;
9505 /* "--" means reset params */
9506 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009507 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009508 }
Eric Andersenc470f442003-07-28 09:56:35 +00009509 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009510 }
Eric Andersencb57d552001-06-28 07:25:16 +00009511 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009512 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009513 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009514 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009515 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009516 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009517 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009518 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009519 /* it already printed err message */
9520 return 1; /* error */
9521 }
Eric Andersencb57d552001-06-28 07:25:16 +00009522 if (*argptr)
9523 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009524 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9525 isloginsh = 1;
9526 /* bash does not accept +-login, we also won't */
9527 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009528 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009529 isloginsh = 1;
9530 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009531 } else {
9532 setoption(c, val);
9533 }
9534 }
9535 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009536 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009537}
9538
Eric Andersencb57d552001-06-28 07:25:16 +00009539/*
Eric Andersencb57d552001-06-28 07:25:16 +00009540 * The shift builtin command.
9541 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009542static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009543shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009544{
9545 int n;
9546 char **ap1, **ap2;
9547
9548 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009549 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009550 n = number(argv[1]);
9551 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009552 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009553 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009554 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009555 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009556 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009557 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009558 }
9559 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009560 while ((*ap2++ = *ap1++) != NULL)
9561 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009562#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009563 shellparam.optind = 1;
9564 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009565#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009566 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009567 return 0;
9568}
9569
Eric Andersencb57d552001-06-28 07:25:16 +00009570/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009571 * POSIX requires that 'set' (but not export or readonly) output the
9572 * variables in lexicographic order - by the locale's collating order (sigh).
9573 * Maybe we could keep them in an ordered balanced binary tree
9574 * instead of hashed lists.
9575 * For now just roll 'em through qsort for printing...
9576 */
9577static int
9578showvars(const char *sep_prefix, int on, int off)
9579{
9580 const char *sep;
9581 char **ep, **epend;
9582
9583 ep = listvars(on, off, &epend);
9584 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9585
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009586 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009587
9588 for (; ep < epend; ep++) {
9589 const char *p;
9590 const char *q;
9591
9592 p = strchrnul(*ep, '=');
9593 q = nullstr;
9594 if (*p)
9595 q = single_quote(++p);
9596 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9597 }
9598 return 0;
9599}
9600
9601/*
Eric Andersencb57d552001-06-28 07:25:16 +00009602 * The set command builtin.
9603 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009604static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009605setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009606{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009607 int retval;
9608
Denis Vlasenko68404f12008-03-17 09:00:54 +00009609 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009610 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009611 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009612 retval = 1;
9613 if (!options(0)) { /* if no parse error... */
9614 retval = 0;
9615 optschanged();
9616 if (*argptr != NULL) {
9617 setparam(argptr);
9618 }
Eric Andersencb57d552001-06-28 07:25:16 +00009619 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009620 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009621 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009622}
9623
Denis Vlasenko131ae172007-02-18 13:00:19 +00009624#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009625/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009626static void
9627change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009628{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009629 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009630 /* "get", generate */
9631 char buf[16];
9632
9633 rseed = rseed * 1103515245 + 12345;
9634 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9635 /* set without recursion */
9636 setvar(vrandom.text, buf, VNOFUNC);
9637 vrandom.flags &= ~VNOFUNC;
9638 } else {
9639 /* set/reset */
9640 rseed = strtoul(value, (char **)NULL, 10);
9641 }
Eric Andersenef02f822004-03-11 13:34:24 +00009642}
Eric Andersen16767e22004-03-16 05:14:10 +00009643#endif
9644
Denis Vlasenko131ae172007-02-18 13:00:19 +00009645#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009646static int
Eric Andersenc470f442003-07-28 09:56:35 +00009647getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009648{
9649 char *p, *q;
9650 char c = '?';
9651 int done = 0;
9652 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009653 char s[12];
9654 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009655
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009656 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009657 return 1;
9658 optnext = optfirst + *param_optind - 1;
9659
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009660 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009661 p = NULL;
9662 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009663 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009664 if (p == NULL || *p == '\0') {
9665 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009666 p = *optnext;
9667 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009668 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009669 p = NULL;
9670 done = 1;
9671 goto out;
9672 }
9673 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009674 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009675 goto atend;
9676 }
9677
9678 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009679 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009680 if (*q == '\0') {
9681 if (optstr[0] == ':') {
9682 s[0] = c;
9683 s[1] = '\0';
9684 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009685 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009686 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009687 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009688 }
9689 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009690 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009691 }
9692 if (*++q == ':')
9693 q++;
9694 }
9695
9696 if (*++q == ':') {
9697 if (*p == '\0' && (p = *optnext) == NULL) {
9698 if (optstr[0] == ':') {
9699 s[0] = c;
9700 s[1] = '\0';
9701 err |= setvarsafe("OPTARG", s, 0);
9702 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009703 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009704 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009705 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009706 c = '?';
9707 }
Eric Andersenc470f442003-07-28 09:56:35 +00009708 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009709 }
9710
9711 if (p == *optnext)
9712 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009713 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009714 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009715 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009716 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009717 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009718 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009719 *param_optind = optnext - optfirst + 1;
9720 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009721 err |= setvarsafe("OPTIND", s, VNOFUNC);
9722 s[0] = c;
9723 s[1] = '\0';
9724 err |= setvarsafe(optvar, s, 0);
9725 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009726 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009727 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009728 flush_stdout_stderr();
9729 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009730 }
9731 return done;
9732}
Eric Andersenc470f442003-07-28 09:56:35 +00009733
9734/*
9735 * The getopts builtin. Shellparam.optnext points to the next argument
9736 * to be processed. Shellparam.optptr points to the next character to
9737 * be processed in the current argument. If shellparam.optnext is NULL,
9738 * then it's the first time getopts has been called.
9739 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009740static int
Eric Andersenc470f442003-07-28 09:56:35 +00009741getoptscmd(int argc, char **argv)
9742{
9743 char **optbase;
9744
9745 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009746 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009747 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009748 optbase = shellparam.p;
9749 if (shellparam.optind > shellparam.nparam + 1) {
9750 shellparam.optind = 1;
9751 shellparam.optoff = -1;
9752 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009753 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009754 optbase = &argv[3];
9755 if (shellparam.optind > argc - 2) {
9756 shellparam.optind = 1;
9757 shellparam.optoff = -1;
9758 }
9759 }
9760
9761 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009762 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009763}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009764#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009765
Eric Andersencb57d552001-06-28 07:25:16 +00009766
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009767/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009768
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009769struct heredoc {
9770 struct heredoc *next; /* next here document in list */
9771 union node *here; /* redirection node */
9772 char *eofmark; /* string indicating end of input */
9773 smallint striptabs; /* if set, strip leading tabs */
9774};
9775
9776static smallint tokpushback; /* last token pushed back */
9777static smallint parsebackquote; /* nonzero if we are inside backquotes */
9778static smallint quoteflag; /* set if (part of) last token was quoted */
9779static token_id_t lasttoken; /* last token read (integer id Txxx) */
9780static struct heredoc *heredoclist; /* list of here documents to read */
9781static char *wordtext; /* text of last word returned by readtoken */
9782static struct nodelist *backquotelist;
9783static union node *redirnode;
9784static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009785/*
9786 * NEOF is returned by parsecmd when it encounters an end of file. It
9787 * must be distinct from NULL, so we use the address of a variable that
9788 * happens to be handy.
9789 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009790#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009791
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009792static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9793static void
9794raise_error_syntax(const char *msg)
9795{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009796 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009797 /* NOTREACHED */
9798}
9799
9800/*
9801 * Called when an unexpected token is read during the parse. The argument
9802 * is the token that is expected, or -1 if more than one type of token can
9803 * occur at this point.
9804 */
9805static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9806static void
9807raise_error_unexpected_syntax(int token)
9808{
9809 char msg[64];
9810 int l;
9811
9812 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9813 if (token >= 0)
9814 sprintf(msg + l, " (expecting %s)", tokname(token));
9815 raise_error_syntax(msg);
9816 /* NOTREACHED */
9817}
Eric Andersencb57d552001-06-28 07:25:16 +00009818
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009819#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009820
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009821/* parsing is heavily cross-recursive, need these forward decls */
9822static union node *andor(void);
9823static union node *pipeline(void);
9824static union node *parse_command(void);
9825static void parseheredoc(void);
9826static char peektoken(void);
9827static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009828
Eric Andersenc470f442003-07-28 09:56:35 +00009829static union node *
9830list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009831{
9832 union node *n1, *n2, *n3;
9833 int tok;
9834
Eric Andersenc470f442003-07-28 09:56:35 +00009835 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9836 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009837 return NULL;
9838 n1 = NULL;
9839 for (;;) {
9840 n2 = andor();
9841 tok = readtoken();
9842 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009843 if (n2->type == NPIPE) {
9844 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009845 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009846 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009847 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009848 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009849 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009850 n2 = n3;
9851 }
9852 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009853 }
9854 }
9855 if (n1 == NULL) {
9856 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009857 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009858 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009859 n3->type = NSEMI;
9860 n3->nbinary.ch1 = n1;
9861 n3->nbinary.ch2 = n2;
9862 n1 = n3;
9863 }
9864 switch (tok) {
9865 case TBACKGND:
9866 case TSEMI:
9867 tok = readtoken();
9868 /* fall through */
9869 case TNL:
9870 if (tok == TNL) {
9871 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009872 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009873 return n1;
9874 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009875 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009876 }
Eric Andersenc470f442003-07-28 09:56:35 +00009877 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009878 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009879 return n1;
9880 break;
9881 case TEOF:
9882 if (heredoclist)
9883 parseheredoc();
9884 else
Eric Andersenc470f442003-07-28 09:56:35 +00009885 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009886 return n1;
9887 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009888 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009889 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009890 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009891 return n1;
9892 }
9893 }
9894}
9895
Eric Andersenc470f442003-07-28 09:56:35 +00009896static union node *
9897andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009898{
Eric Andersencb57d552001-06-28 07:25:16 +00009899 union node *n1, *n2, *n3;
9900 int t;
9901
Eric Andersencb57d552001-06-28 07:25:16 +00009902 n1 = pipeline();
9903 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009904 t = readtoken();
9905 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009906 t = NAND;
9907 } else if (t == TOR) {
9908 t = NOR;
9909 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009910 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 return n1;
9912 }
Eric Andersenc470f442003-07-28 09:56:35 +00009913 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009914 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009915 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009916 n3->type = t;
9917 n3->nbinary.ch1 = n1;
9918 n3->nbinary.ch2 = n2;
9919 n1 = n3;
9920 }
9921}
9922
Eric Andersenc470f442003-07-28 09:56:35 +00009923static union node *
9924pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009925{
Eric Andersencb57d552001-06-28 07:25:16 +00009926 union node *n1, *n2, *pipenode;
9927 struct nodelist *lp, *prev;
9928 int negate;
9929
9930 negate = 0;
9931 TRACE(("pipeline: entered\n"));
9932 if (readtoken() == TNOT) {
9933 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009934 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009935 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009936 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009937 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009938 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009939 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009940 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009941 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009942 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009943 pipenode->npipe.cmdlist = lp;
9944 lp->n = n1;
9945 do {
9946 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009947 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009948 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009949 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009950 prev->next = lp;
9951 } while (readtoken() == TPIPE);
9952 lp->next = NULL;
9953 n1 = pipenode;
9954 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009955 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009957 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009958 n2->type = NNOT;
9959 n2->nnot.com = n1;
9960 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009961 }
9962 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009963}
9964
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009965static union node *
9966makename(void)
9967{
9968 union node *n;
9969
Denis Vlasenko597906c2008-02-20 16:38:54 +00009970 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009971 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009972 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009973 n->narg.text = wordtext;
9974 n->narg.backquote = backquotelist;
9975 return n;
9976}
9977
9978static void
9979fixredir(union node *n, const char *text, int err)
9980{
9981 TRACE(("Fix redir %s %d\n", text, err));
9982 if (!err)
9983 n->ndup.vname = NULL;
9984
9985 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009986 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009987 else if (LONE_DASH(text))
9988 n->ndup.dupfd = -1;
9989 else {
9990 if (err)
9991 raise_error_syntax("Bad fd number");
9992 n->ndup.vname = makename();
9993 }
9994}
9995
9996/*
9997 * Returns true if the text contains nothing to expand (no dollar signs
9998 * or backquotes).
9999 */
10000static int
10001noexpand(char *text)
10002{
10003 char *p;
10004 char c;
10005
10006 p = text;
10007 while ((c = *p++) != '\0') {
10008 if (c == CTLQUOTEMARK)
10009 continue;
10010 if (c == CTLESC)
10011 p++;
10012 else if (SIT(c, BASESYNTAX) == CCTL)
10013 return 0;
10014 }
10015 return 1;
10016}
10017
10018static void
10019parsefname(void)
10020{
10021 union node *n = redirnode;
10022
10023 if (readtoken() != TWORD)
10024 raise_error_unexpected_syntax(-1);
10025 if (n->type == NHERE) {
10026 struct heredoc *here = heredoc;
10027 struct heredoc *p;
10028 int i;
10029
10030 if (quoteflag == 0)
10031 n->type = NXHERE;
10032 TRACE(("Here document %d\n", n->type));
10033 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10034 raise_error_syntax("Illegal eof marker for << redirection");
10035 rmescapes(wordtext);
10036 here->eofmark = wordtext;
10037 here->next = NULL;
10038 if (heredoclist == NULL)
10039 heredoclist = here;
10040 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010041 for (p = heredoclist; p->next; p = p->next)
10042 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010043 p->next = here;
10044 }
10045 } else if (n->type == NTOFD || n->type == NFROMFD) {
10046 fixredir(n, wordtext, 0);
10047 } else {
10048 n->nfile.fname = makename();
10049 }
10050}
Eric Andersencb57d552001-06-28 07:25:16 +000010051
Eric Andersenc470f442003-07-28 09:56:35 +000010052static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010053simplecmd(void)
10054{
10055 union node *args, **app;
10056 union node *n = NULL;
10057 union node *vars, **vpp;
10058 union node **rpp, *redir;
10059 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010060#if ENABLE_ASH_BASH_COMPAT
10061 smallint double_brackets_flag = 0;
10062#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010063
10064 args = NULL;
10065 app = &args;
10066 vars = NULL;
10067 vpp = &vars;
10068 redir = NULL;
10069 rpp = &redir;
10070
10071 savecheckkwd = CHKALIAS;
10072 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010073 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010074 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010075 t = readtoken();
10076 switch (t) {
10077#if ENABLE_ASH_BASH_COMPAT
10078 case TAND: /* "&&" */
10079 case TOR: /* "||" */
10080 if (!double_brackets_flag) {
10081 tokpushback = 1;
10082 goto out;
10083 }
10084 wordtext = (char *) (t == TAND ? "-a" : "-o");
10085#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010086 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010087 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010088 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010089 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010090 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010091#if ENABLE_ASH_BASH_COMPAT
10092 if (strcmp("[[", wordtext) == 0)
10093 double_brackets_flag = 1;
10094 else if (strcmp("]]", wordtext) == 0)
10095 double_brackets_flag = 0;
10096#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010097 n->narg.backquote = backquotelist;
10098 if (savecheckkwd && isassignment(wordtext)) {
10099 *vpp = n;
10100 vpp = &n->narg.next;
10101 } else {
10102 *app = n;
10103 app = &n->narg.next;
10104 savecheckkwd = 0;
10105 }
10106 break;
10107 case TREDIR:
10108 *rpp = n = redirnode;
10109 rpp = &n->nfile.next;
10110 parsefname(); /* read name of redirection file */
10111 break;
10112 case TLP:
10113 if (args && app == &args->narg.next
10114 && !vars && !redir
10115 ) {
10116 struct builtincmd *bcmd;
10117 const char *name;
10118
10119 /* We have a function */
10120 if (readtoken() != TRP)
10121 raise_error_unexpected_syntax(TRP);
10122 name = n->narg.text;
10123 if (!goodname(name)
10124 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10125 ) {
10126 raise_error_syntax("Bad function name");
10127 }
10128 n->type = NDEFUN;
10129 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10130 n->narg.next = parse_command();
10131 return n;
10132 }
10133 /* fall through */
10134 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010135 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010136 goto out;
10137 }
10138 }
10139 out:
10140 *app = NULL;
10141 *vpp = NULL;
10142 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010143 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010144 n->type = NCMD;
10145 n->ncmd.args = args;
10146 n->ncmd.assign = vars;
10147 n->ncmd.redirect = redir;
10148 return n;
10149}
10150
10151static union node *
10152parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010153{
Eric Andersencb57d552001-06-28 07:25:16 +000010154 union node *n1, *n2;
10155 union node *ap, **app;
10156 union node *cp, **cpp;
10157 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010158 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010159 int t;
10160
10161 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010162 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010163
Eric Andersencb57d552001-06-28 07:25:16 +000010164 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010165 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010166 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010167 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010168 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010169 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010170 n1->type = NIF;
10171 n1->nif.test = list(0);
10172 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010173 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010174 n1->nif.ifpart = list(0);
10175 n2 = n1;
10176 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010177 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010178 n2 = n2->nif.elsepart;
10179 n2->type = NIF;
10180 n2->nif.test = list(0);
10181 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010182 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010183 n2->nif.ifpart = list(0);
10184 }
10185 if (lasttoken == TELSE)
10186 n2->nif.elsepart = list(0);
10187 else {
10188 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010189 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010190 }
Eric Andersenc470f442003-07-28 09:56:35 +000010191 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010192 break;
10193 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010194 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010195 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010196 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010197 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010198 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010199 got = readtoken();
10200 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010201 TRACE(("expecting DO got %s %s\n", tokname(got),
10202 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010203 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010204 }
10205 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010206 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010207 break;
10208 }
10209 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010210 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010211 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010212 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010213 n1->type = NFOR;
10214 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010215 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010216 if (readtoken() == TIN) {
10217 app = &ap;
10218 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010219 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010220 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010221 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010222 n2->narg.text = wordtext;
10223 n2->narg.backquote = backquotelist;
10224 *app = n2;
10225 app = &n2->narg.next;
10226 }
10227 *app = NULL;
10228 n1->nfor.args = ap;
10229 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010230 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010231 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010232 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010233 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010234 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010235 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010236 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010237 n1->nfor.args = n2;
10238 /*
10239 * Newline or semicolon here is optional (but note
10240 * that the original Bourne shell only allowed NL).
10241 */
10242 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010243 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010244 }
Eric Andersenc470f442003-07-28 09:56:35 +000010245 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010246 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010247 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010248 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010249 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010250 break;
10251 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010252 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010253 n1->type = NCASE;
10254 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010255 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010256 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010257 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010258 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010259 n2->narg.text = wordtext;
10260 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010261 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010262 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010263 } while (readtoken() == TNL);
10264 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010265 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010266 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010267 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010268 checkkwd = CHKNL | CHKKWD;
10269 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010270 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010271 if (lasttoken == TLP)
10272 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010273 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010274 cp->type = NCLIST;
10275 app = &cp->nclist.pattern;
10276 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010277 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010278 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010279 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010280 ap->narg.text = wordtext;
10281 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010282 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010283 break;
10284 app = &ap->narg.next;
10285 readtoken();
10286 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010287 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010288 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010289 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010290 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010291
Eric Andersenc470f442003-07-28 09:56:35 +000010292 cpp = &cp->nclist.next;
10293
10294 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010295 t = readtoken();
10296 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010297 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010298 raise_error_unexpected_syntax(TENDCASE);
10299 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010300 }
Eric Andersenc470f442003-07-28 09:56:35 +000010301 }
Eric Andersencb57d552001-06-28 07:25:16 +000010302 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010303 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010304 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010305 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010306 n1->type = NSUBSHELL;
10307 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010308 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010309 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010310 break;
10311 case TBEGIN:
10312 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010313 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010314 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010315 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010316 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010317 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010318 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010319 }
10320
Eric Andersenc470f442003-07-28 09:56:35 +000010321 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010322 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010323
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010324 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010325 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010326 checkkwd = CHKKWD | CHKALIAS;
10327 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010328 while (readtoken() == TREDIR) {
10329 *rpp = n2 = redirnode;
10330 rpp = &n2->nfile.next;
10331 parsefname();
10332 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010333 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010334 *rpp = NULL;
10335 if (redir) {
10336 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010337 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010338 n2->type = NREDIR;
10339 n2->nredir.n = n1;
10340 n1 = n2;
10341 }
10342 n1->nredir.redirect = redir;
10343 }
Eric Andersencb57d552001-06-28 07:25:16 +000010344 return n1;
10345}
10346
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010347#if ENABLE_ASH_BASH_COMPAT
10348static int decode_dollar_squote(void)
10349{
10350 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10351 int c, cnt;
10352 char *p;
10353 char buf[4];
10354
10355 c = pgetc();
10356 p = strchr(C_escapes, c);
10357 if (p) {
10358 buf[0] = c;
10359 p = buf;
10360 cnt = 3;
10361 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10362 do {
10363 c = pgetc();
10364 *++p = c;
10365 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10366 pungetc();
10367 } else if (c == 'x') { /* \xHH */
10368 do {
10369 c = pgetc();
10370 *++p = c;
10371 } while (isxdigit(c) && --cnt);
10372 pungetc();
10373 if (cnt == 3) { /* \x but next char is "bad" */
10374 c = 'x';
10375 goto unrecognized;
10376 }
10377 } else { /* simple seq like \\ or \t */
10378 p++;
10379 }
10380 *p = '\0';
10381 p = buf;
10382 c = bb_process_escape_sequence((void*)&p);
10383 } else { /* unrecognized "\z": print both chars unless ' or " */
10384 if (c != '\'' && c != '"') {
10385 unrecognized:
10386 c |= 0x100; /* "please encode \, then me" */
10387 }
10388 }
10389 return c;
10390}
10391#endif
10392
Eric Andersencb57d552001-06-28 07:25:16 +000010393/*
10394 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10395 * is not NULL, read a here document. In the latter case, eofmark is the
10396 * word which marks the end of the document and striptabs is true if
10397 * leading tabs should be stripped from the document. The argument firstc
10398 * is the first character of the input token or document.
10399 *
10400 * Because C does not have internal subroutines, I have simulated them
10401 * using goto's to implement the subroutine linkage. The following macros
10402 * will run code that appears at the end of readtoken1.
10403 */
Eric Andersen2870d962001-07-02 17:27:21 +000010404#define CHECKEND() {goto checkend; checkend_return:;}
10405#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10406#define PARSESUB() {goto parsesub; parsesub_return:;}
10407#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10408#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10409#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010410static int
Eric Andersenc470f442003-07-28 09:56:35 +000010411readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010412{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010413 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010414 int c = firstc;
10415 char *out;
10416 int len;
10417 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010418 struct nodelist *bqlist;
10419 smallint quotef;
10420 smallint dblquote;
10421 smallint oldstyle;
10422 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010423#if ENABLE_ASH_EXPAND_PRMT
10424 smallint pssyntax; /* we are expanding a prompt string */
10425#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010426 int varnest; /* levels of variables expansion */
10427 int arinest; /* levels of arithmetic expansion */
10428 int parenlevel; /* levels of parens in arithmetic */
10429 int dqvarnest; /* levels of variables expansion within double quotes */
10430
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010431 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10432
Eric Andersencb57d552001-06-28 07:25:16 +000010433#if __GNUC__
10434 /* Avoid longjmp clobbering */
10435 (void) &out;
10436 (void) &quotef;
10437 (void) &dblquote;
10438 (void) &varnest;
10439 (void) &arinest;
10440 (void) &parenlevel;
10441 (void) &dqvarnest;
10442 (void) &oldstyle;
10443 (void) &prevsyntax;
10444 (void) &syntax;
10445#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010446 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010448 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010449 oldstyle = 0;
10450 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010451#if ENABLE_ASH_EXPAND_PRMT
10452 pssyntax = (syntax == PSSYNTAX);
10453 if (pssyntax)
10454 syntax = DQSYNTAX;
10455#endif
10456 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010457 varnest = 0;
10458 arinest = 0;
10459 parenlevel = 0;
10460 dqvarnest = 0;
10461
10462 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010463 loop: { /* for each line, until end of word */
10464 CHECKEND(); /* set c to PEOF if at end of here document */
10465 for (;;) { /* until end of line or end of word */
10466 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010467 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010468 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010469 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010470 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010471 USTPUTC(c, out);
10472 plinno++;
10473 if (doprompt)
10474 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010475 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010476 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010477 case CWORD:
10478 USTPUTC(c, out);
10479 break;
10480 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010481 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010482 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010483#if ENABLE_ASH_BASH_COMPAT
10484 if (c == '\\' && bash_dollar_squote) {
10485 c = decode_dollar_squote();
10486 if (c & 0x100) {
10487 USTPUTC('\\', out);
10488 c = (unsigned char)c;
10489 }
10490 }
10491#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010492 USTPUTC(c, out);
10493 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010494 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010495 c = pgetc2();
10496 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010497 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010498 USTPUTC('\\', out);
10499 pungetc();
10500 } else if (c == '\n') {
10501 if (doprompt)
10502 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010503 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010504#if ENABLE_ASH_EXPAND_PRMT
10505 if (c == '$' && pssyntax) {
10506 USTPUTC(CTLESC, out);
10507 USTPUTC('\\', out);
10508 }
10509#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010510 if (dblquote && c != '\\'
10511 && c != '`' && c != '$'
10512 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010513 ) {
10514 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010515 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010516 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010517 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010518 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010519 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010520 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010521 }
10522 break;
10523 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010524 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010525 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010526 if (eofmark == NULL) {
10527 USTPUTC(CTLQUOTEMARK, out);
10528 }
Eric Andersencb57d552001-06-28 07:25:16 +000010529 break;
10530 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010531 syntax = DQSYNTAX;
10532 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010533 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010534 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010535 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010536 if (eofmark != NULL && arinest == 0
10537 && varnest == 0
10538 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010539 USTPUTC(c, out);
10540 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010541 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010542 syntax = BASESYNTAX;
10543 dblquote = 0;
10544 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010545 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010546 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010547 }
10548 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010549 case CVAR: /* '$' */
10550 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010551 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010552 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010553 if (varnest > 0) {
10554 varnest--;
10555 if (dqvarnest > 0) {
10556 dqvarnest--;
10557 }
10558 USTPUTC(CTLENDVAR, out);
10559 } else {
10560 USTPUTC(c, out);
10561 }
10562 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010563#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010564 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010565 parenlevel++;
10566 USTPUTC(c, out);
10567 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010568 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010569 if (parenlevel > 0) {
10570 USTPUTC(c, out);
10571 --parenlevel;
10572 } else {
10573 if (pgetc() == ')') {
10574 if (--arinest == 0) {
10575 USTPUTC(CTLENDARI, out);
10576 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010577 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010578 } else
10579 USTPUTC(')', out);
10580 } else {
10581 /*
10582 * unbalanced parens
10583 * (don't 2nd guess - no error)
10584 */
10585 pungetc();
10586 USTPUTC(')', out);
10587 }
10588 }
10589 break;
10590#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010591 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010592 PARSEBACKQOLD();
10593 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010594 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010595 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010596 case CIGN:
10597 break;
10598 default:
10599 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010600 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010601#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010602 if (c != PEOA)
10603#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010604 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010605
Eric Andersencb57d552001-06-28 07:25:16 +000010606 }
10607 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010608 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010609 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010610 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010611#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010612 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010613 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010614#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010615 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010616 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010617 if (varnest != 0) {
10618 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010619 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010620 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010621 }
10622 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010623 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010624 out = stackblock();
10625 if (eofmark == NULL) {
10626 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010627 && quotef == 0
10628 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010629 && (*out == '\0' || isdigit(*out))
10630 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010631 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010632 lasttoken = TREDIR;
10633 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010634 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010635 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010636 }
10637 quoteflag = quotef;
10638 backquotelist = bqlist;
10639 grabstackblock(len);
10640 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010641 lasttoken = TWORD;
10642 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010643/* end of readtoken routine */
10644
Eric Andersencb57d552001-06-28 07:25:16 +000010645/*
10646 * Check to see whether we are at the end of the here document. When this
10647 * is called, c is set to the first character of the next input line. If
10648 * we are at the end of the here document, this routine sets the c to PEOF.
10649 */
Eric Andersenc470f442003-07-28 09:56:35 +000010650checkend: {
10651 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010652#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010653 if (c == PEOA) {
10654 c = pgetc2();
10655 }
10656#endif
10657 if (striptabs) {
10658 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010659 c = pgetc2();
10660 }
Eric Andersenc470f442003-07-28 09:56:35 +000010661 }
10662 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010663 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010664 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010665
Eric Andersenc470f442003-07-28 09:56:35 +000010666 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010667 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10668 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010669 if (*p == '\n' && *q == '\0') {
10670 c = PEOF;
10671 plinno++;
10672 needprompt = doprompt;
10673 } else {
10674 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010675 }
10676 }
10677 }
10678 }
Eric Andersenc470f442003-07-28 09:56:35 +000010679 goto checkend_return;
10680}
Eric Andersencb57d552001-06-28 07:25:16 +000010681
Eric Andersencb57d552001-06-28 07:25:16 +000010682/*
10683 * Parse a redirection operator. The variable "out" points to a string
10684 * specifying the fd to be redirected. The variable "c" contains the
10685 * first character of the redirection operator.
10686 */
Eric Andersenc470f442003-07-28 09:56:35 +000010687parseredir: {
10688 char fd = *out;
10689 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010690
Denis Vlasenko597906c2008-02-20 16:38:54 +000010691 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010692 if (c == '>') {
10693 np->nfile.fd = 1;
10694 c = pgetc();
10695 if (c == '>')
10696 np->type = NAPPEND;
10697 else if (c == '|')
10698 np->type = NCLOBBER;
10699 else if (c == '&')
10700 np->type = NTOFD;
10701 else {
10702 np->type = NTO;
10703 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010704 }
Eric Andersenc470f442003-07-28 09:56:35 +000010705 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010706 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010707 c = pgetc();
10708 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010709 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010710 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010711 np = stzalloc(sizeof(struct nhere));
10712 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010713 }
10714 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010715 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010716 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010717 c = pgetc();
10718 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010719 heredoc->striptabs = 1;
10720 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010721 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010722 pungetc();
10723 }
10724 break;
10725
10726 case '&':
10727 np->type = NFROMFD;
10728 break;
10729
10730 case '>':
10731 np->type = NFROMTO;
10732 break;
10733
10734 default:
10735 np->type = NFROM;
10736 pungetc();
10737 break;
10738 }
Eric Andersencb57d552001-06-28 07:25:16 +000010739 }
Eric Andersenc470f442003-07-28 09:56:35 +000010740 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010741 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010742 redirnode = np;
10743 goto parseredir_return;
10744}
Eric Andersencb57d552001-06-28 07:25:16 +000010745
Eric Andersencb57d552001-06-28 07:25:16 +000010746/*
10747 * Parse a substitution. At this point, we have read the dollar sign
10748 * and nothing else.
10749 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010750
10751/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10752 * (assuming ascii char codes, as the original implementation did) */
10753#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010754 (((unsigned)(c) - 33 < 32) \
10755 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010756parsesub: {
10757 int subtype;
10758 int typeloc;
10759 int flags;
10760 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010761 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010762
Eric Andersenc470f442003-07-28 09:56:35 +000010763 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010764 if (c <= PEOA_OR_PEOF
10765 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010766 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010767#if ENABLE_ASH_BASH_COMPAT
10768 if (c == '\'')
10769 bash_dollar_squote = 1;
10770 else
10771#endif
10772 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010773 pungetc();
10774 } else if (c == '(') { /* $(command) or $((arith)) */
10775 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010776#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010777 PARSEARITH();
10778#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010779 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010780#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010781 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010782 pungetc();
10783 PARSEBACKQNEW();
10784 }
10785 } else {
10786 USTPUTC(CTLVAR, out);
10787 typeloc = out - (char *)stackblock();
10788 USTPUTC(VSNORMAL, out);
10789 subtype = VSNORMAL;
10790 if (c == '{') {
10791 c = pgetc();
10792 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010793 c = pgetc();
10794 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010795 c = '#';
10796 else
10797 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010798 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010799 subtype = 0;
10800 }
10801 if (c > PEOA_OR_PEOF && is_name(c)) {
10802 do {
10803 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010804 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010805 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010806 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010807 do {
10808 STPUTC(c, out);
10809 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010810 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010811 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010812 USTPUTC(c, out);
10813 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010814 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010815 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010816
Eric Andersenc470f442003-07-28 09:56:35 +000010817 STPUTC('=', out);
10818 flags = 0;
10819 if (subtype == 0) {
10820 switch (c) {
10821 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010822 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010823#if ENABLE_ASH_BASH_COMPAT
10824 if (c == ':' || c == '$' || isdigit(c)) {
10825 pungetc();
10826 subtype = VSSUBSTR;
10827 break;
10828 }
10829#endif
10830 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010831 /*FALLTHROUGH*/
10832 default:
10833 p = strchr(types, c);
10834 if (p == NULL)
10835 goto badsub;
10836 subtype = p - types + VSNORMAL;
10837 break;
10838 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010839 case '#': {
10840 int cc = c;
10841 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10842 c = pgetc();
10843 if (c == cc)
10844 subtype++;
10845 else
10846 pungetc();
10847 break;
10848 }
10849#if ENABLE_ASH_BASH_COMPAT
10850 case '/':
10851 subtype = VSREPLACE;
10852 c = pgetc();
10853 if (c == '/')
10854 subtype++; /* VSREPLACEALL */
10855 else
10856 pungetc();
10857 break;
10858#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010859 }
Eric Andersenc470f442003-07-28 09:56:35 +000010860 } else {
10861 pungetc();
10862 }
10863 if (dblquote || arinest)
10864 flags |= VSQUOTE;
10865 *((char *)stackblock() + typeloc) = subtype | flags;
10866 if (subtype != VSNORMAL) {
10867 varnest++;
10868 if (dblquote || arinest) {
10869 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010870 }
10871 }
10872 }
Eric Andersenc470f442003-07-28 09:56:35 +000010873 goto parsesub_return;
10874}
Eric Andersencb57d552001-06-28 07:25:16 +000010875
Eric Andersencb57d552001-06-28 07:25:16 +000010876/*
10877 * Called to parse command substitutions. Newstyle is set if the command
10878 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10879 * list of commands (passed by reference), and savelen is the number of
10880 * characters on the top of the stack which must be preserved.
10881 */
Eric Andersenc470f442003-07-28 09:56:35 +000010882parsebackq: {
10883 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010884 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010885 union node *n;
10886 char *volatile str;
10887 struct jmploc jmploc;
10888 struct jmploc *volatile savehandler;
10889 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010890 smallint saveprompt = 0;
10891
Eric Andersencb57d552001-06-28 07:25:16 +000010892#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010893 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010894#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010895 savepbq = parsebackquote;
10896 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010897 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010898 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010899 exception_handler = savehandler;
10900 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010901 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010902 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010903 str = NULL;
10904 savelen = out - (char *)stackblock();
10905 if (savelen > 0) {
10906 str = ckmalloc(savelen);
10907 memcpy(str, stackblock(), savelen);
10908 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010909 savehandler = exception_handler;
10910 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010911 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010912 if (oldstyle) {
10913 /* We must read until the closing backquote, giving special
10914 treatment to some slashes, and then push the string and
10915 reread it as input, interpreting it normally. */
10916 char *pout;
10917 int pc;
10918 size_t psavelen;
10919 char *pstr;
10920
10921
10922 STARTSTACKSTR(pout);
10923 for (;;) {
10924 if (needprompt) {
10925 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010926 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010927 pc = pgetc();
10928 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010929 case '`':
10930 goto done;
10931
10932 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010933 pc = pgetc();
10934 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010935 plinno++;
10936 if (doprompt)
10937 setprompt(2);
10938 /*
10939 * If eating a newline, avoid putting
10940 * the newline into the new character
10941 * stream (via the STPUTC after the
10942 * switch).
10943 */
10944 continue;
10945 }
10946 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010947 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010948 STPUTC('\\', pout);
10949 if (pc > PEOA_OR_PEOF) {
10950 break;
10951 }
10952 /* fall through */
10953
10954 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010955#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010956 case PEOA:
10957#endif
10958 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010959 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010960
10961 case '\n':
10962 plinno++;
10963 needprompt = doprompt;
10964 break;
10965
10966 default:
10967 break;
10968 }
10969 STPUTC(pc, pout);
10970 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010971 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010972 STPUTC('\0', pout);
10973 psavelen = pout - (char *)stackblock();
10974 if (psavelen > 0) {
10975 pstr = grabstackstr(pout);
10976 setinputstring(pstr);
10977 }
10978 }
10979 nlpp = &bqlist;
10980 while (*nlpp)
10981 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010982 *nlpp = stzalloc(sizeof(**nlpp));
10983 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010984 parsebackquote = oldstyle;
10985
10986 if (oldstyle) {
10987 saveprompt = doprompt;
10988 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010989 }
10990
Eric Andersenc470f442003-07-28 09:56:35 +000010991 n = list(2);
10992
10993 if (oldstyle)
10994 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010995 else if (readtoken() != TRP)
10996 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010997
10998 (*nlpp)->n = n;
10999 if (oldstyle) {
11000 /*
11001 * Start reading from old file again, ignoring any pushed back
11002 * tokens left from the backquote parsing
11003 */
11004 popfile();
11005 tokpushback = 0;
11006 }
11007 while (stackblocksize() <= savelen)
11008 growstackblock();
11009 STARTSTACKSTR(out);
11010 if (str) {
11011 memcpy(out, str, savelen);
11012 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011013 INT_OFF;
11014 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011015 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011016 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011017 }
11018 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011019 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011020 if (arinest || dblquote)
11021 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11022 else
11023 USTPUTC(CTLBACKQ, out);
11024 if (oldstyle)
11025 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011026 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011027}
11028
Denis Vlasenko131ae172007-02-18 13:00:19 +000011029#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011030/*
11031 * Parse an arithmetic expansion (indicate start of one and set state)
11032 */
Eric Andersenc470f442003-07-28 09:56:35 +000011033parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011034 if (++arinest == 1) {
11035 prevsyntax = syntax;
11036 syntax = ARISYNTAX;
11037 USTPUTC(CTLARI, out);
11038 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011039 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011040 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011041 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011042 } else {
11043 /*
11044 * we collapse embedded arithmetic expansion to
11045 * parenthesis, which should be equivalent
11046 */
11047 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011048 }
Eric Andersenc470f442003-07-28 09:56:35 +000011049 goto parsearith_return;
11050}
11051#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011052
Eric Andersenc470f442003-07-28 09:56:35 +000011053} /* end of readtoken */
11054
Eric Andersencb57d552001-06-28 07:25:16 +000011055/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011056 * Read the next input token.
11057 * If the token is a word, we set backquotelist to the list of cmds in
11058 * backquotes. We set quoteflag to true if any part of the word was
11059 * quoted.
11060 * If the token is TREDIR, then we set redirnode to a structure containing
11061 * the redirection.
11062 * In all cases, the variable startlinno is set to the number of the line
11063 * on which the token starts.
11064 *
11065 * [Change comment: here documents and internal procedures]
11066 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11067 * word parsing code into a separate routine. In this case, readtoken
11068 * doesn't need to have any internal procedures, but parseword does.
11069 * We could also make parseoperator in essence the main routine, and
11070 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011071 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011072#define NEW_xxreadtoken
11073#ifdef NEW_xxreadtoken
11074/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011075static const char xxreadtoken_chars[7] ALIGN1 = {
11076 '\n', '(', ')', '&', '|', ';', 0
11077};
Eric Andersencb57d552001-06-28 07:25:16 +000011078
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011079static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011080 TNL, TLP, TRP, /* only single occurrence allowed */
11081 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11082 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011083 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011084};
11085
11086#define xxreadtoken_doubles \
11087 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11088#define xxreadtoken_singles \
11089 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11090
11091static int
11092xxreadtoken(void)
11093{
11094 int c;
11095
11096 if (tokpushback) {
11097 tokpushback = 0;
11098 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011099 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011100 if (needprompt) {
11101 setprompt(2);
11102 }
11103 startlinno = plinno;
11104 for (;;) { /* until token or start of word found */
11105 c = pgetc_macro();
11106
11107 if ((c != ' ') && (c != '\t')
11108#if ENABLE_ASH_ALIAS
11109 && (c != PEOA)
11110#endif
11111 ) {
11112 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011113 while ((c = pgetc()) != '\n' && c != PEOF)
11114 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011115 pungetc();
11116 } else if (c == '\\') {
11117 if (pgetc() != '\n') {
11118 pungetc();
11119 goto READTOKEN1;
11120 }
11121 startlinno = ++plinno;
11122 if (doprompt)
11123 setprompt(2);
11124 } else {
11125 const char *p
11126 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11127
11128 if (c != PEOF) {
11129 if (c == '\n') {
11130 plinno++;
11131 needprompt = doprompt;
11132 }
11133
11134 p = strchr(xxreadtoken_chars, c);
11135 if (p == NULL) {
11136 READTOKEN1:
11137 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11138 }
11139
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011140 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011141 if (pgetc() == *p) { /* double occurrence? */
11142 p += xxreadtoken_doubles + 1;
11143 } else {
11144 pungetc();
11145 }
11146 }
11147 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011148 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11149 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011150 }
11151 }
11152 } /* for */
11153}
11154#else
11155#define RETURN(token) return lasttoken = token
11156static int
11157xxreadtoken(void)
11158{
11159 int c;
11160
11161 if (tokpushback) {
11162 tokpushback = 0;
11163 return lasttoken;
11164 }
11165 if (needprompt) {
11166 setprompt(2);
11167 }
11168 startlinno = plinno;
11169 for (;;) { /* until token or start of word found */
11170 c = pgetc_macro();
11171 switch (c) {
11172 case ' ': case '\t':
11173#if ENABLE_ASH_ALIAS
11174 case PEOA:
11175#endif
11176 continue;
11177 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011178 while ((c = pgetc()) != '\n' && c != PEOF)
11179 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011180 pungetc();
11181 continue;
11182 case '\\':
11183 if (pgetc() == '\n') {
11184 startlinno = ++plinno;
11185 if (doprompt)
11186 setprompt(2);
11187 continue;
11188 }
11189 pungetc();
11190 goto breakloop;
11191 case '\n':
11192 plinno++;
11193 needprompt = doprompt;
11194 RETURN(TNL);
11195 case PEOF:
11196 RETURN(TEOF);
11197 case '&':
11198 if (pgetc() == '&')
11199 RETURN(TAND);
11200 pungetc();
11201 RETURN(TBACKGND);
11202 case '|':
11203 if (pgetc() == '|')
11204 RETURN(TOR);
11205 pungetc();
11206 RETURN(TPIPE);
11207 case ';':
11208 if (pgetc() == ';')
11209 RETURN(TENDCASE);
11210 pungetc();
11211 RETURN(TSEMI);
11212 case '(':
11213 RETURN(TLP);
11214 case ')':
11215 RETURN(TRP);
11216 default:
11217 goto breakloop;
11218 }
11219 }
11220 breakloop:
11221 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11222#undef RETURN
11223}
11224#endif /* NEW_xxreadtoken */
11225
11226static int
11227readtoken(void)
11228{
11229 int t;
11230#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011231 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011232#endif
11233
11234#if ENABLE_ASH_ALIAS
11235 top:
11236#endif
11237
11238 t = xxreadtoken();
11239
11240 /*
11241 * eat newlines
11242 */
11243 if (checkkwd & CHKNL) {
11244 while (t == TNL) {
11245 parseheredoc();
11246 t = xxreadtoken();
11247 }
11248 }
11249
11250 if (t != TWORD || quoteflag) {
11251 goto out;
11252 }
11253
11254 /*
11255 * check for keywords
11256 */
11257 if (checkkwd & CHKKWD) {
11258 const char *const *pp;
11259
11260 pp = findkwd(wordtext);
11261 if (pp) {
11262 lasttoken = t = pp - tokname_array;
11263 TRACE(("keyword %s recognized\n", tokname(t)));
11264 goto out;
11265 }
11266 }
11267
11268 if (checkkwd & CHKALIAS) {
11269#if ENABLE_ASH_ALIAS
11270 struct alias *ap;
11271 ap = lookupalias(wordtext, 1);
11272 if (ap != NULL) {
11273 if (*ap->val) {
11274 pushstring(ap->val, ap);
11275 }
11276 goto top;
11277 }
11278#endif
11279 }
11280 out:
11281 checkkwd = 0;
11282#if DEBUG
11283 if (!alreadyseen)
11284 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11285 else
11286 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11287#endif
11288 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011289}
11290
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011291static char
11292peektoken(void)
11293{
11294 int t;
11295
11296 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011297 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011298 return tokname_array[t][0];
11299}
Eric Andersencb57d552001-06-28 07:25:16 +000011300
11301/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011302 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11303 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011304 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011305static union node *
11306parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011307{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011308 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011309
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011310 tokpushback = 0;
11311 doprompt = interact;
11312 if (doprompt)
11313 setprompt(doprompt);
11314 needprompt = 0;
11315 t = readtoken();
11316 if (t == TEOF)
11317 return NEOF;
11318 if (t == TNL)
11319 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011320 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011321 return list(1);
11322}
11323
11324/*
11325 * Input any here documents.
11326 */
11327static void
11328parseheredoc(void)
11329{
11330 struct heredoc *here;
11331 union node *n;
11332
11333 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011334 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011335
11336 while (here) {
11337 if (needprompt) {
11338 setprompt(2);
11339 }
11340 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11341 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011342 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011343 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011344 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011345 n->narg.text = wordtext;
11346 n->narg.backquote = backquotelist;
11347 here->here->nhere.doc = n;
11348 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
Eric Andersencb57d552001-06-28 07:25:16 +000011350}
11351
11352
11353/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011354 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011355 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011356#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011357static const char *
11358expandstr(const char *ps)
11359{
11360 union node n;
11361
11362 /* XXX Fix (char *) cast. */
11363 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011364 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011365 popfile();
11366
11367 n.narg.type = NARG;
11368 n.narg.next = NULL;
11369 n.narg.text = wordtext;
11370 n.narg.backquote = backquotelist;
11371
11372 expandarg(&n, NULL, 0);
11373 return stackblock();
11374}
11375#endif
11376
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011377/*
11378 * Execute a command or commands contained in a string.
11379 */
11380static int
11381evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011382{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011383 union node *n;
11384 struct stackmark smark;
11385 int skip;
11386
11387 setinputstring(s);
11388 setstackmark(&smark);
11389
11390 skip = 0;
11391 while ((n = parsecmd(0)) != NEOF) {
11392 evaltree(n, 0);
11393 popstackmark(&smark);
11394 skip = evalskip;
11395 if (skip)
11396 break;
11397 }
11398 popfile();
11399
11400 skip &= mask;
11401 evalskip = skip;
11402 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011403}
11404
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011405/*
11406 * The eval command.
11407 */
11408static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011409evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011410{
11411 char *p;
11412 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011413
Denis Vlasenko68404f12008-03-17 09:00:54 +000011414 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011415 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011416 argv += 2;
11417 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011418 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011419 for (;;) {
11420 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011421 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011422 if (p == NULL)
11423 break;
11424 STPUTC(' ', concat);
11425 }
11426 STPUTC('\0', concat);
11427 p = grabstackstr(concat);
11428 }
11429 evalstring(p, ~SKIPEVAL);
11430
11431 }
11432 return exitstatus;
11433}
11434
11435/*
11436 * Read and execute commands. "Top" is nonzero for the top level command
11437 * loop; it turns on prompting if the shell is interactive.
11438 */
11439static int
11440cmdloop(int top)
11441{
11442 union node *n;
11443 struct stackmark smark;
11444 int inter;
11445 int numeof = 0;
11446
11447 TRACE(("cmdloop(%d) called\n", top));
11448 for (;;) {
11449 int skip;
11450
11451 setstackmark(&smark);
11452#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011453 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011454 showjobs(stderr, SHOW_CHANGED);
11455#endif
11456 inter = 0;
11457 if (iflag && top) {
11458 inter++;
11459#if ENABLE_ASH_MAIL
11460 chkmail();
11461#endif
11462 }
11463 n = parsecmd(inter);
11464 /* showtree(n); DEBUG */
11465 if (n == NEOF) {
11466 if (!top || numeof >= 50)
11467 break;
11468 if (!stoppedjobs()) {
11469 if (!Iflag)
11470 break;
11471 out2str("\nUse \"exit\" to leave shell.\n");
11472 }
11473 numeof++;
11474 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011475 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11476 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011477 numeof = 0;
11478 evaltree(n, 0);
11479 }
11480 popstackmark(&smark);
11481 skip = evalskip;
11482
11483 if (skip) {
11484 evalskip = 0;
11485 return skip & SKIPEVAL;
11486 }
11487 }
11488 return 0;
11489}
11490
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011491/*
11492 * Take commands from a file. To be compatible we should do a path
11493 * search for the file, which is necessary to find sub-commands.
11494 */
11495static char *
11496find_dot_file(char *name)
11497{
11498 char *fullname;
11499 const char *path = pathval();
11500 struct stat statb;
11501
11502 /* don't try this for absolute or relative paths */
11503 if (strchr(name, '/'))
11504 return name;
11505
11506 while ((fullname = padvance(&path, name)) != NULL) {
11507 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11508 /*
11509 * Don't bother freeing here, since it will
11510 * be freed by the caller.
11511 */
11512 return fullname;
11513 }
11514 stunalloc(fullname);
11515 }
11516
11517 /* not found in the PATH */
11518 ash_msg_and_raise_error("%s: not found", name);
11519 /* NOTREACHED */
11520}
11521
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011522static int
11523dotcmd(int argc, char **argv)
11524{
11525 struct strlist *sp;
11526 volatile struct shparam saveparam;
11527 int status = 0;
11528
11529 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011530 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011531
Denis Vlasenko68404f12008-03-17 09:00:54 +000011532 if (argv[1]) { /* That's what SVR2 does */
11533 char *fullname = find_dot_file(argv[1]);
11534 argv += 2;
11535 argc -= 2;
11536 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011537 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011538 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011539 shellparam.nparam = argc;
11540 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011541 };
11542
11543 setinputfile(fullname, INPUT_PUSH_FILE);
11544 commandname = fullname;
11545 cmdloop(0);
11546 popfile();
11547
Denis Vlasenko68404f12008-03-17 09:00:54 +000011548 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011549 freeparam(&shellparam);
11550 shellparam = saveparam;
11551 };
11552 status = exitstatus;
11553 }
11554 return status;
11555}
11556
11557static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011558exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011559{
11560 if (stoppedjobs())
11561 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011562 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011563 exitstatus = number(argv[1]);
11564 raise_exception(EXEXIT);
11565 /* NOTREACHED */
11566}
11567
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011568/*
11569 * Read a file containing shell functions.
11570 */
11571static void
11572readcmdfile(char *name)
11573{
11574 setinputfile(name, INPUT_PUSH_FILE);
11575 cmdloop(0);
11576 popfile();
11577}
11578
11579
Denis Vlasenkocc571512007-02-23 21:10:35 +000011580/* ============ find_command inplementation */
11581
11582/*
11583 * Resolve a command name. If you change this routine, you may have to
11584 * change the shellexec routine as well.
11585 */
11586static void
11587find_command(char *name, struct cmdentry *entry, int act, const char *path)
11588{
11589 struct tblentry *cmdp;
11590 int idx;
11591 int prev;
11592 char *fullname;
11593 struct stat statb;
11594 int e;
11595 int updatetbl;
11596 struct builtincmd *bcmd;
11597
11598 /* If name contains a slash, don't use PATH or hash table */
11599 if (strchr(name, '/') != NULL) {
11600 entry->u.index = -1;
11601 if (act & DO_ABS) {
11602 while (stat(name, &statb) < 0) {
11603#ifdef SYSV
11604 if (errno == EINTR)
11605 continue;
11606#endif
11607 entry->cmdtype = CMDUNKNOWN;
11608 return;
11609 }
11610 }
11611 entry->cmdtype = CMDNORMAL;
11612 return;
11613 }
11614
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011615/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011616
11617 updatetbl = (path == pathval());
11618 if (!updatetbl) {
11619 act |= DO_ALTPATH;
11620 if (strstr(path, "%builtin") != NULL)
11621 act |= DO_ALTBLTIN;
11622 }
11623
11624 /* If name is in the table, check answer will be ok */
11625 cmdp = cmdlookup(name, 0);
11626 if (cmdp != NULL) {
11627 int bit;
11628
11629 switch (cmdp->cmdtype) {
11630 default:
11631#if DEBUG
11632 abort();
11633#endif
11634 case CMDNORMAL:
11635 bit = DO_ALTPATH;
11636 break;
11637 case CMDFUNCTION:
11638 bit = DO_NOFUNC;
11639 break;
11640 case CMDBUILTIN:
11641 bit = DO_ALTBLTIN;
11642 break;
11643 }
11644 if (act & bit) {
11645 updatetbl = 0;
11646 cmdp = NULL;
11647 } else if (cmdp->rehash == 0)
11648 /* if not invalidated by cd, we're done */
11649 goto success;
11650 }
11651
11652 /* If %builtin not in path, check for builtin next */
11653 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011654 if (bcmd) {
11655 if (IS_BUILTIN_REGULAR(bcmd))
11656 goto builtin_success;
11657 if (act & DO_ALTPATH) {
11658 if (!(act & DO_ALTBLTIN))
11659 goto builtin_success;
11660 } else if (builtinloc <= 0) {
11661 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011662 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011663 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011664
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011665#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011666 {
11667 int applet_no = find_applet_by_name(name);
11668 if (applet_no >= 0) {
11669 entry->cmdtype = CMDNORMAL;
11670 entry->u.index = -2 - applet_no;
11671 return;
11672 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011673 }
11674#endif
11675
Denis Vlasenkocc571512007-02-23 21:10:35 +000011676 /* We have to search path. */
11677 prev = -1; /* where to start */
11678 if (cmdp && cmdp->rehash) { /* doing a rehash */
11679 if (cmdp->cmdtype == CMDBUILTIN)
11680 prev = builtinloc;
11681 else
11682 prev = cmdp->param.index;
11683 }
11684
11685 e = ENOENT;
11686 idx = -1;
11687 loop:
11688 while ((fullname = padvance(&path, name)) != NULL) {
11689 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011690 /* NB: code below will still use fullname
11691 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011692 idx++;
11693 if (pathopt) {
11694 if (prefix(pathopt, "builtin")) {
11695 if (bcmd)
11696 goto builtin_success;
11697 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011698 }
11699 if ((act & DO_NOFUNC)
11700 || !prefix(pathopt, "func")
11701 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011702 continue;
11703 }
11704 }
11705 /* if rehash, don't redo absolute path names */
11706 if (fullname[0] == '/' && idx <= prev) {
11707 if (idx < prev)
11708 continue;
11709 TRACE(("searchexec \"%s\": no change\n", name));
11710 goto success;
11711 }
11712 while (stat(fullname, &statb) < 0) {
11713#ifdef SYSV
11714 if (errno == EINTR)
11715 continue;
11716#endif
11717 if (errno != ENOENT && errno != ENOTDIR)
11718 e = errno;
11719 goto loop;
11720 }
11721 e = EACCES; /* if we fail, this will be the error */
11722 if (!S_ISREG(statb.st_mode))
11723 continue;
11724 if (pathopt) { /* this is a %func directory */
11725 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011726 /* NB: stalloc will return space pointed by fullname
11727 * (because we don't have any intervening allocations
11728 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011729 readcmdfile(fullname);
11730 cmdp = cmdlookup(name, 0);
11731 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11732 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11733 stunalloc(fullname);
11734 goto success;
11735 }
11736 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11737 if (!updatetbl) {
11738 entry->cmdtype = CMDNORMAL;
11739 entry->u.index = idx;
11740 return;
11741 }
11742 INT_OFF;
11743 cmdp = cmdlookup(name, 1);
11744 cmdp->cmdtype = CMDNORMAL;
11745 cmdp->param.index = idx;
11746 INT_ON;
11747 goto success;
11748 }
11749
11750 /* We failed. If there was an entry for this command, delete it */
11751 if (cmdp && updatetbl)
11752 delete_cmd_entry();
11753 if (act & DO_ERR)
11754 ash_msg("%s: %s", name, errmsg(e, "not found"));
11755 entry->cmdtype = CMDUNKNOWN;
11756 return;
11757
11758 builtin_success:
11759 if (!updatetbl) {
11760 entry->cmdtype = CMDBUILTIN;
11761 entry->u.cmd = bcmd;
11762 return;
11763 }
11764 INT_OFF;
11765 cmdp = cmdlookup(name, 1);
11766 cmdp->cmdtype = CMDBUILTIN;
11767 cmdp->param.cmd = bcmd;
11768 INT_ON;
11769 success:
11770 cmdp->rehash = 0;
11771 entry->cmdtype = cmdp->cmdtype;
11772 entry->u = cmdp->param;
11773}
11774
11775
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011776/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011777
Eric Andersencb57d552001-06-28 07:25:16 +000011778/*
Eric Andersencb57d552001-06-28 07:25:16 +000011779 * The trap builtin.
11780 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011781static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011782trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011783{
11784 char *action;
11785 char **ap;
11786 int signo;
11787
Eric Andersenc470f442003-07-28 09:56:35 +000011788 nextopt(nullstr);
11789 ap = argptr;
11790 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011791 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011792 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011793 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011794
Rob Landleyc9c1a412006-07-12 19:17:55 +000011795 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011796 out1fmt("trap -- %s %s\n",
11797 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011798 }
11799 }
11800 return 0;
11801 }
Eric Andersenc470f442003-07-28 09:56:35 +000011802 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011803 action = NULL;
11804 else
11805 action = *ap++;
11806 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011807 signo = get_signum(*ap);
11808 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011809 ash_msg_and_raise_error("%s: bad trap", *ap);
11810 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011811 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011812 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011813 action = NULL;
11814 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011815 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011816 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011817 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011818 trap[signo] = action;
11819 if (signo != 0)
11820 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011821 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011822 ap++;
11823 }
11824 return 0;
11825}
11826
Eric Andersenc470f442003-07-28 09:56:35 +000011827
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011828/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011829
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011830#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011831/*
11832 * Lists available builtins
11833 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011834static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011835helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011836{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011837 unsigned col;
11838 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011839
11840 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011841 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011842 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011843 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011844 if (col > 60) {
11845 out1fmt("\n");
11846 col = 0;
11847 }
11848 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011849#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011850 {
11851 const char *a = applet_names;
11852 while (*a) {
11853 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11854 if (col > 60) {
11855 out1fmt("\n");
11856 col = 0;
11857 }
11858 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011859 }
11860 }
11861#endif
11862 out1fmt("\n\n");
11863 return EXIT_SUCCESS;
11864}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011865#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011866
Eric Andersencb57d552001-06-28 07:25:16 +000011867/*
Eric Andersencb57d552001-06-28 07:25:16 +000011868 * The export and readonly commands.
11869 */
Eric Andersenc470f442003-07-28 09:56:35 +000011870static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011871exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011872{
11873 struct var *vp;
11874 char *name;
11875 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011876 char **aptr;
11877 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011878
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011879 if (nextopt("p") != 'p') {
11880 aptr = argptr;
11881 name = *aptr;
11882 if (name) {
11883 do {
11884 p = strchr(name, '=');
11885 if (p != NULL) {
11886 p++;
11887 } else {
11888 vp = *findvar(hashvar(name), name);
11889 if (vp) {
11890 vp->flags |= flag;
11891 continue;
11892 }
Eric Andersencb57d552001-06-28 07:25:16 +000011893 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011894 setvar(name, p, flag);
11895 } while ((name = *++aptr) != NULL);
11896 return 0;
11897 }
Eric Andersencb57d552001-06-28 07:25:16 +000011898 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011899 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011900 return 0;
11901}
11902
Eric Andersencb57d552001-06-28 07:25:16 +000011903/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011904 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011905 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011906static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011907unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011908{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011909 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011910
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011911 cmdp = cmdlookup(name, 0);
11912 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11913 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011914}
11915
Eric Andersencb57d552001-06-28 07:25:16 +000011916/*
Eric Andersencb57d552001-06-28 07:25:16 +000011917 * The unset builtin command. We unset the function before we unset the
11918 * variable to allow a function to be unset when there is a readonly variable
11919 * with the same name.
11920 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011921static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011922unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011923{
11924 char **ap;
11925 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011926 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011927 int ret = 0;
11928
11929 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011930 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011931 }
Eric Andersencb57d552001-06-28 07:25:16 +000011932
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011933 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011934 if (flag != 'f') {
11935 i = unsetvar(*ap);
11936 ret |= i;
11937 if (!(i & 2))
11938 continue;
11939 }
11940 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011941 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011942 }
Eric Andersenc470f442003-07-28 09:56:35 +000011943 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011944}
11945
11946
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011947/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011948
Eric Andersenc470f442003-07-28 09:56:35 +000011949#include <sys/times.h>
11950
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011951static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011952 ' ', offsetof(struct tms, tms_utime),
11953 '\n', offsetof(struct tms, tms_stime),
11954 ' ', offsetof(struct tms, tms_cutime),
11955 '\n', offsetof(struct tms, tms_cstime),
11956 0
11957};
Eric Andersencb57d552001-06-28 07:25:16 +000011958
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011959static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011960timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011961{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011962 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011963 const unsigned char *p;
11964 struct tms buf;
11965
11966 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011967 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011968
11969 p = timescmd_str;
11970 do {
11971 t = *(clock_t *)(((char *) &buf) + p[1]);
11972 s = t / clk_tck;
11973 out1fmt("%ldm%ld.%.3lds%c",
11974 s/60, s%60,
11975 ((t - s * clk_tck) * 1000) / clk_tck,
11976 p[0]);
11977 } while (*(p += 2));
11978
Eric Andersencb57d552001-06-28 07:25:16 +000011979 return 0;
11980}
11981
Denis Vlasenko131ae172007-02-18 13:00:19 +000011982#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011983static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011984dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011985{
Eric Andersened9ecf72004-06-22 08:29:45 +000011986 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011987 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011988
Denis Vlasenkob012b102007-02-19 22:43:01 +000011989 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011990 result = arith(s, &errcode);
11991 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011992 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011993 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011994 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011995 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011996 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011997 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011998 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011999 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012000 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012001
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012002 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012003}
Eric Andersenc470f442003-07-28 09:56:35 +000012004
Eric Andersenc470f442003-07-28 09:56:35 +000012005/*
Eric Andersen90898442003-08-06 11:20:52 +000012006 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12007 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12008 *
12009 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012010 */
12011static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012012letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012013{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012014 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012015
Denis Vlasenko68404f12008-03-17 09:00:54 +000012016 argv++;
12017 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012018 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012019 do {
12020 i = dash_arith(*argv);
12021 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012022
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012023 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012024}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012025#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012026
Eric Andersenc470f442003-07-28 09:56:35 +000012027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012028/* ============ miscbltin.c
12029 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012030 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012031 */
12032
12033#undef rflag
12034
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012035#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012036typedef enum __rlimit_resource rlim_t;
12037#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012038
Eric Andersenc470f442003-07-28 09:56:35 +000012039/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012040 * The read builtin. Options:
12041 * -r Do not interpret '\' specially
12042 * -s Turn off echo (tty only)
12043 * -n NCHARS Read NCHARS max
12044 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12045 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12046 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012047 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012048 * TODO: bash also has:
12049 * -a ARRAY Read into array[0],[1],etc
12050 * -d DELIM End on DELIM char, not newline
12051 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012052 */
Eric Andersenc470f442003-07-28 09:56:35 +000012053static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012054readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012055{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012056 static const char *const arg_REPLY[] = { "REPLY", NULL };
12057
Eric Andersenc470f442003-07-28 09:56:35 +000012058 char **ap;
12059 int backslash;
12060 char c;
12061 int rflag;
12062 char *prompt;
12063 const char *ifs;
12064 char *p;
12065 int startword;
12066 int status;
12067 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012068 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012069#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012070 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012071 int silent = 0;
12072 struct termios tty, old_tty;
12073#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012074#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012075 unsigned end_ms = 0;
12076 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012077#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012078
12079 rflag = 0;
12080 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012081 while ((i = nextopt("p:u:r"
12082 USE_ASH_READ_TIMEOUT("t:")
12083 USE_ASH_READ_NCHARS("n:s")
12084 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012085 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012086 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012087 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012088 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012089#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012090 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012091 nchars = bb_strtou(optionarg, NULL, 10);
12092 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012093 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012094 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012095 break;
12096 case 's':
12097 silent = 1;
12098 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012099#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012100#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012101 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012102 timeout = bb_strtou(optionarg, NULL, 10);
12103 if (errno || timeout > UINT_MAX / 2048)
12104 ash_msg_and_raise_error("invalid timeout");
12105 timeout *= 1000;
12106#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012107 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012108 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012109 /* EINVAL means number is ok, but not terminated by NUL */
12110 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012111 char *p2;
12112 if (*++p) {
12113 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012114 ts.tv_usec = bb_strtou(p, &p2, 10);
12115 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012116 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012117 scale = p2 - p;
12118 /* normalize to usec */
12119 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012120 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012121 while (scale++ < 6)
12122 ts.tv_usec *= 10;
12123 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012124 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012125 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012126 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012127 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012128 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012129 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012130#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012131 break;
12132#endif
12133 case 'r':
12134 rflag = 1;
12135 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012136 case 'u':
12137 fd = bb_strtou(optionarg, NULL, 10);
12138 if (fd < 0 || errno)
12139 ash_msg_and_raise_error("invalid file descriptor");
12140 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012141 default:
12142 break;
12143 }
Eric Andersenc470f442003-07-28 09:56:35 +000012144 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012145 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012146 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012147 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012148 ap = argptr;
12149 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012150 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012151 ifs = bltinlookup("IFS");
12152 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012153 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012154#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012155 tcgetattr(fd, &tty);
12156 old_tty = tty;
12157 if (nchars || silent) {
12158 if (nchars) {
12159 tty.c_lflag &= ~ICANON;
12160 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012161 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012162 if (silent) {
12163 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12164 }
12165 /* if tcgetattr failed, tcsetattr will fail too.
12166 * Ignoring, it's harmless. */
12167 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012168 }
12169#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012170
Eric Andersenc470f442003-07-28 09:56:35 +000012171 status = 0;
12172 startword = 1;
12173 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012174#if ENABLE_ASH_READ_TIMEOUT
12175 if (timeout) /* NB: ensuring end_ms is nonzero */
12176 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12177#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012178 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012179 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012180#if ENABLE_ASH_READ_TIMEOUT
12181 if (end_ms) {
12182 struct pollfd pfd[1];
12183 pfd[0].fd = fd;
12184 pfd[0].events = POLLIN;
12185 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12186 if ((int)timeout <= 0 /* already late? */
12187 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12188 ) { /* timed out! */
12189#if ENABLE_ASH_READ_NCHARS
12190 tcsetattr(fd, TCSANOW, &old_tty);
12191#endif
12192 return 1;
12193 }
12194 }
12195#endif
12196 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012197 status = 1;
12198 break;
12199 }
12200 if (c == '\0')
12201 continue;
12202 if (backslash) {
12203 backslash = 0;
12204 if (c != '\n')
12205 goto put;
12206 continue;
12207 }
12208 if (!rflag && c == '\\') {
12209 backslash++;
12210 continue;
12211 }
12212 if (c == '\n')
12213 break;
12214 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12215 continue;
12216 }
12217 startword = 0;
12218 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12219 STACKSTRNUL(p);
12220 setvar(*ap, stackblock(), 0);
12221 ap++;
12222 startword = 1;
12223 STARTSTACKSTR(p);
12224 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012225 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012226 STPUTC(c, p);
12227 }
12228 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012229/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012230#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012231 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012232#else
12233 while (1);
12234#endif
12235
12236#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012237 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012238#endif
12239
Eric Andersenc470f442003-07-28 09:56:35 +000012240 STACKSTRNUL(p);
12241 /* Remove trailing blanks */
12242 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12243 *p = '\0';
12244 setvar(*ap, stackblock(), 0);
12245 while (*++ap != NULL)
12246 setvar(*ap, nullstr, 0);
12247 return status;
12248}
12249
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012250static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012251umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012252{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012253 static const char permuser[3] ALIGN1 = "ugo";
12254 static const char permmode[3] ALIGN1 = "rwx";
12255 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012256 S_IRUSR, S_IWUSR, S_IXUSR,
12257 S_IRGRP, S_IWGRP, S_IXGRP,
12258 S_IROTH, S_IWOTH, S_IXOTH
12259 };
12260
12261 char *ap;
12262 mode_t mask;
12263 int i;
12264 int symbolic_mode = 0;
12265
12266 while (nextopt("S") != '\0') {
12267 symbolic_mode = 1;
12268 }
12269
Denis Vlasenkob012b102007-02-19 22:43:01 +000012270 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012271 mask = umask(0);
12272 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012273 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012274
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012275 ap = *argptr;
12276 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012277 if (symbolic_mode) {
12278 char buf[18];
12279 char *p = buf;
12280
12281 for (i = 0; i < 3; i++) {
12282 int j;
12283
12284 *p++ = permuser[i];
12285 *p++ = '=';
12286 for (j = 0; j < 3; j++) {
12287 if ((mask & permmask[3 * i + j]) == 0) {
12288 *p++ = permmode[j];
12289 }
12290 }
12291 *p++ = ',';
12292 }
12293 *--p = 0;
12294 puts(buf);
12295 } else {
12296 out1fmt("%.4o\n", mask);
12297 }
12298 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012299 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012300 mask = 0;
12301 do {
12302 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012303 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012304 mask = (mask << 3) + (*ap - '0');
12305 } while (*++ap != '\0');
12306 umask(mask);
12307 } else {
12308 mask = ~mask & 0777;
12309 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012310 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012311 }
12312 umask(~mask & 0777);
12313 }
12314 }
12315 return 0;
12316}
12317
12318/*
12319 * ulimit builtin
12320 *
12321 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12322 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12323 * ash by J.T. Conklin.
12324 *
12325 * Public domain.
12326 */
12327
12328struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012329 uint8_t cmd; /* RLIMIT_xxx fit into it */
12330 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012331 char option;
12332};
12333
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012334static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012335#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012336 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012337#endif
12338#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012339 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012340#endif
12341#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012342 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012343#endif
12344#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012345 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012346#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012347#ifdef RLIMIT_CORE
12348 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012349#endif
12350#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012351 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012352#endif
12353#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012354 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012355#endif
12356#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012357 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012358#endif
12359#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012360 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012361#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012362#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012363 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012364#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012365#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012366 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012367#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012368};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012369static const char limits_name[] =
12370#ifdef RLIMIT_CPU
12371 "time(seconds)" "\0"
12372#endif
12373#ifdef RLIMIT_FSIZE
12374 "file(blocks)" "\0"
12375#endif
12376#ifdef RLIMIT_DATA
12377 "data(kb)" "\0"
12378#endif
12379#ifdef RLIMIT_STACK
12380 "stack(kb)" "\0"
12381#endif
12382#ifdef RLIMIT_CORE
12383 "coredump(blocks)" "\0"
12384#endif
12385#ifdef RLIMIT_RSS
12386 "memory(kb)" "\0"
12387#endif
12388#ifdef RLIMIT_MEMLOCK
12389 "locked memory(kb)" "\0"
12390#endif
12391#ifdef RLIMIT_NPROC
12392 "process" "\0"
12393#endif
12394#ifdef RLIMIT_NOFILE
12395 "nofiles" "\0"
12396#endif
12397#ifdef RLIMIT_AS
12398 "vmemory(kb)" "\0"
12399#endif
12400#ifdef RLIMIT_LOCKS
12401 "locks" "\0"
12402#endif
12403;
Eric Andersenc470f442003-07-28 09:56:35 +000012404
Glenn L McGrath76620622004-01-13 10:19:37 +000012405enum limtype { SOFT = 0x1, HARD = 0x2 };
12406
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012407static void
12408printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012409 const struct limits *l)
12410{
12411 rlim_t val;
12412
12413 val = limit->rlim_max;
12414 if (how & SOFT)
12415 val = limit->rlim_cur;
12416
12417 if (val == RLIM_INFINITY)
12418 out1fmt("unlimited\n");
12419 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012420 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012421 out1fmt("%lld\n", (long long) val);
12422 }
12423}
12424
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012425static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012426ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012427{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012428 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012429 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012430 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012431 const struct limits *l;
12432 int set, all = 0;
12433 int optc, what;
12434 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012435
12436 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012437 while ((optc = nextopt("HSa"
12438#ifdef RLIMIT_CPU
12439 "t"
12440#endif
12441#ifdef RLIMIT_FSIZE
12442 "f"
12443#endif
12444#ifdef RLIMIT_DATA
12445 "d"
12446#endif
12447#ifdef RLIMIT_STACK
12448 "s"
12449#endif
12450#ifdef RLIMIT_CORE
12451 "c"
12452#endif
12453#ifdef RLIMIT_RSS
12454 "m"
12455#endif
12456#ifdef RLIMIT_MEMLOCK
12457 "l"
12458#endif
12459#ifdef RLIMIT_NPROC
12460 "p"
12461#endif
12462#ifdef RLIMIT_NOFILE
12463 "n"
12464#endif
12465#ifdef RLIMIT_AS
12466 "v"
12467#endif
12468#ifdef RLIMIT_LOCKS
12469 "w"
12470#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012471 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012472 switch (optc) {
12473 case 'H':
12474 how = HARD;
12475 break;
12476 case 'S':
12477 how = SOFT;
12478 break;
12479 case 'a':
12480 all = 1;
12481 break;
12482 default:
12483 what = optc;
12484 }
12485
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012486 for (l = limits_tbl; l->option != what; l++)
12487 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012488
12489 set = *argptr ? 1 : 0;
12490 if (set) {
12491 char *p = *argptr;
12492
12493 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012494 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012495 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012496 val = RLIM_INFINITY;
12497 else {
12498 val = (rlim_t) 0;
12499
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012500 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012501 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012502 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012503 if (val < (rlim_t) 0)
12504 break;
12505 }
12506 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012507 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012508 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012509 }
12510 }
12511 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012512 const char *lname = limits_name;
12513 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012514 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012515 out1fmt("%-20s ", lname);
12516 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012517 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012518 }
12519 return 0;
12520 }
12521
12522 getrlimit(l->cmd, &limit);
12523 if (set) {
12524 if (how & HARD)
12525 limit.rlim_max = val;
12526 if (how & SOFT)
12527 limit.rlim_cur = val;
12528 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012529 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012530 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012531 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012532 }
12533 return 0;
12534}
12535
Eric Andersen90898442003-08-06 11:20:52 +000012536
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012537/* ============ Math support */
12538
Denis Vlasenko131ae172007-02-18 13:00:19 +000012539#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012540
12541/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12542
12543 Permission is hereby granted, free of charge, to any person obtaining
12544 a copy of this software and associated documentation files (the
12545 "Software"), to deal in the Software without restriction, including
12546 without limitation the rights to use, copy, modify, merge, publish,
12547 distribute, sublicense, and/or sell copies of the Software, and to
12548 permit persons to whom the Software is furnished to do so, subject to
12549 the following conditions:
12550
12551 The above copyright notice and this permission notice shall be
12552 included in all copies or substantial portions of the Software.
12553
12554 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12555 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12556 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12557 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12558 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12559 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12560 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12561*/
12562
12563/* This is my infix parser/evaluator. It is optimized for size, intended
12564 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012565 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012566 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012567 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012568 * be that which POSIX specifies for shells. */
12569
12570/* The code uses a simple two-stack algorithm. See
12571 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012572 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012573 * this is based (this code differs in that it applies operators immediately
12574 * to the stack instead of adding them to a queue to end up with an
12575 * expression). */
12576
12577/* To use the routine, call it with an expression string and error return
12578 * pointer */
12579
12580/*
12581 * Aug 24, 2001 Manuel Novoa III
12582 *
12583 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12584 *
12585 * 1) In arith_apply():
12586 * a) Cached values of *numptr and &(numptr[-1]).
12587 * b) Removed redundant test for zero denominator.
12588 *
12589 * 2) In arith():
12590 * a) Eliminated redundant code for processing operator tokens by moving
12591 * to a table-based implementation. Also folded handling of parens
12592 * into the table.
12593 * b) Combined all 3 loops which called arith_apply to reduce generated
12594 * code size at the cost of speed.
12595 *
12596 * 3) The following expressions were treated as valid by the original code:
12597 * 1() , 0! , 1 ( *3 ) .
12598 * These bugs have been fixed by internally enclosing the expression in
12599 * parens and then checking that all binary ops and right parens are
12600 * preceded by a valid expression (NUM_TOKEN).
12601 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012602 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012603 * ctype's isspace() if it is used by another busybox applet or if additional
12604 * whitespace chars should be considered. Look below the "#include"s for a
12605 * precompiler test.
12606 */
12607
12608/*
12609 * Aug 26, 2001 Manuel Novoa III
12610 *
12611 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12612 *
12613 * Merge in Aaron's comments previously posted to the busybox list,
12614 * modified slightly to take account of my changes to the code.
12615 *
12616 */
12617
12618/*
12619 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12620 *
12621 * - allow access to variable,
12622 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12623 * - realize assign syntax (VAR=expr, +=, *= etc)
12624 * - realize exponentiation (** operator)
12625 * - realize comma separated - expr, expr
12626 * - realise ++expr --expr expr++ expr--
12627 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012628 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012629 * - was restored loses XOR operator
12630 * - remove one goto label, added three ;-)
12631 * - protect $((num num)) as true zero expr (Manuel`s error)
12632 * - always use special isspace(), see comment from bash ;-)
12633 */
12634
Eric Andersen90898442003-08-06 11:20:52 +000012635#define arith_isspace(arithval) \
12636 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12637
Eric Andersen90898442003-08-06 11:20:52 +000012638typedef unsigned char operator;
12639
12640/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012641 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012642 * precedence. The ID portion is so that multiple operators can have the
12643 * same precedence, ensuring that the leftmost one is evaluated first.
12644 * Consider * and /. */
12645
12646#define tok_decl(prec,id) (((id)<<5)|(prec))
12647#define PREC(op) ((op) & 0x1F)
12648
12649#define TOK_LPAREN tok_decl(0,0)
12650
12651#define TOK_COMMA tok_decl(1,0)
12652
12653#define TOK_ASSIGN tok_decl(2,0)
12654#define TOK_AND_ASSIGN tok_decl(2,1)
12655#define TOK_OR_ASSIGN tok_decl(2,2)
12656#define TOK_XOR_ASSIGN tok_decl(2,3)
12657#define TOK_PLUS_ASSIGN tok_decl(2,4)
12658#define TOK_MINUS_ASSIGN tok_decl(2,5)
12659#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12660#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12661
12662#define TOK_MUL_ASSIGN tok_decl(3,0)
12663#define TOK_DIV_ASSIGN tok_decl(3,1)
12664#define TOK_REM_ASSIGN tok_decl(3,2)
12665
12666/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012667#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012668
12669/* conditional is right associativity too */
12670#define TOK_CONDITIONAL tok_decl(4,0)
12671#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12672
12673#define TOK_OR tok_decl(5,0)
12674
12675#define TOK_AND tok_decl(6,0)
12676
12677#define TOK_BOR tok_decl(7,0)
12678
12679#define TOK_BXOR tok_decl(8,0)
12680
12681#define TOK_BAND tok_decl(9,0)
12682
12683#define TOK_EQ tok_decl(10,0)
12684#define TOK_NE tok_decl(10,1)
12685
12686#define TOK_LT tok_decl(11,0)
12687#define TOK_GT tok_decl(11,1)
12688#define TOK_GE tok_decl(11,2)
12689#define TOK_LE tok_decl(11,3)
12690
12691#define TOK_LSHIFT tok_decl(12,0)
12692#define TOK_RSHIFT tok_decl(12,1)
12693
12694#define TOK_ADD tok_decl(13,0)
12695#define TOK_SUB tok_decl(13,1)
12696
12697#define TOK_MUL tok_decl(14,0)
12698#define TOK_DIV tok_decl(14,1)
12699#define TOK_REM tok_decl(14,2)
12700
12701/* exponent is right associativity */
12702#define TOK_EXPONENT tok_decl(15,1)
12703
12704/* For now unary operators. */
12705#define UNARYPREC 16
12706#define TOK_BNOT tok_decl(UNARYPREC,0)
12707#define TOK_NOT tok_decl(UNARYPREC,1)
12708
12709#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12710#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12711
12712#define PREC_PRE (UNARYPREC+2)
12713
12714#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12715#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12716
12717#define PREC_POST (UNARYPREC+3)
12718
12719#define TOK_POST_INC tok_decl(PREC_POST, 0)
12720#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12721
12722#define SPEC_PREC (UNARYPREC+4)
12723
12724#define TOK_NUM tok_decl(SPEC_PREC, 0)
12725#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12726
12727#define NUMPTR (*numstackptr)
12728
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012729static int
12730tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012731{
12732 operator prec = PREC(op);
12733
12734 convert_prec_is_assing(prec);
12735 return (prec == PREC(TOK_ASSIGN) ||
12736 prec == PREC_PRE || prec == PREC_POST);
12737}
12738
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012739static int
12740is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012741{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012742 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12743 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012744}
12745
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012746typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012747 arith_t val;
12748 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012749 char contidional_second_val_initialized;
12750 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012751 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012752} v_n_t;
12753
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012754typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012755 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012756 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012757} chk_var_recursive_looped_t;
12758
12759static chk_var_recursive_looped_t *prev_chk_var_recursive;
12760
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012761static int
12762arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012763{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012764 if (t->var) {
12765 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012766
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012767 if (p) {
12768 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012769
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012770 /* recursive try as expression */
12771 chk_var_recursive_looped_t *cur;
12772 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012773
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012774 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12775 if (strcmp(cur->var, t->var) == 0) {
12776 /* expression recursion loop detected */
12777 return -5;
12778 }
12779 }
12780 /* save current lookuped var name */
12781 cur = prev_chk_var_recursive;
12782 cur_save.var = t->var;
12783 cur_save.next = cur;
12784 prev_chk_var_recursive = &cur_save;
12785
12786 t->val = arith (p, &errcode);
12787 /* restore previous ptr after recursiving */
12788 prev_chk_var_recursive = cur;
12789 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012790 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012791 /* allow undefined var as 0 */
12792 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012793 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012794 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012795}
12796
12797/* "applying" a token means performing it on the top elements on the integer
12798 * stack. For a unary operator it will only change the top element, but a
12799 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012800static int
12801arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012802{
Eric Andersen90898442003-08-06 11:20:52 +000012803 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012804 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012805 int ret_arith_lookup_val;
12806
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012807 /* There is no operator that can work without arguments */
12808 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012809 numptr_m1 = NUMPTR - 1;
12810
12811 /* check operand is var with noninteger value */
12812 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012813 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012814 return ret_arith_lookup_val;
12815
12816 rez = numptr_m1->val;
12817 if (op == TOK_UMINUS)
12818 rez *= -1;
12819 else if (op == TOK_NOT)
12820 rez = !rez;
12821 else if (op == TOK_BNOT)
12822 rez = ~rez;
12823 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12824 rez++;
12825 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12826 rez--;
12827 else if (op != TOK_UPLUS) {
12828 /* Binary operators */
12829
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012830 /* check and binary operators need two arguments */
12831 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012832
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012833 /* ... and they pop one */
12834 --NUMPTR;
12835 numptr_val = rez;
12836 if (op == TOK_CONDITIONAL) {
12837 if (! numptr_m1->contidional_second_val_initialized) {
12838 /* protect $((expr1 ? expr2)) without ": expr" */
12839 goto err;
12840 }
12841 rez = numptr_m1->contidional_second_val;
12842 } else if (numptr_m1->contidional_second_val_initialized) {
12843 /* protect $((expr1 : expr2)) without "expr ? " */
12844 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012845 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012846 numptr_m1 = NUMPTR - 1;
12847 if (op != TOK_ASSIGN) {
12848 /* check operand is var with noninteger value for not '=' */
12849 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12850 if (ret_arith_lookup_val)
12851 return ret_arith_lookup_val;
12852 }
12853 if (op == TOK_CONDITIONAL) {
12854 numptr_m1->contidional_second_val = rez;
12855 }
12856 rez = numptr_m1->val;
12857 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012858 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012859 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012860 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012861 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012862 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012863 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012864 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012865 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012866 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012867 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012868 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012869 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012870 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012871 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012872 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012873 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012874 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012875 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012876 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012877 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012878 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012879 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012880 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012881 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012882 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012883 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012884 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012885 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012886 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012887 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012888 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012889 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012890 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012891 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012892 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012893 /* protect $((expr : expr)) without "expr ? " */
12894 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012895 }
12896 numptr_m1->contidional_second_val_initialized = op;
12897 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012898 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012899 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012900 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012901 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012902 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012903 return -3; /* exponent less than 0 */
12904 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012905 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012906
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 if (numptr_val)
12908 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012909 c *= rez;
12910 rez = c;
12911 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012912 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012913 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012914 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012915 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012916 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012917 rez %= numptr_val;
12918 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012919 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012920 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012921
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012922 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012923 /* Hmm, 1=2 ? */
12924 goto err;
12925 }
12926 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012927#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012928 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012929#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012930 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012931#endif
Eric Andersen90898442003-08-06 11:20:52 +000012932 setvar(numptr_m1->var, buf, 0);
12933 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012934 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012935 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012936 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012937 rez++;
12938 }
12939 numptr_m1->val = rez;
12940 /* protect geting var value, is number now */
12941 numptr_m1->var = NULL;
12942 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012943 err:
12944 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012945}
12946
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012947/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012948static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012949 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12950 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12951 '<','<', 0, TOK_LSHIFT,
12952 '>','>', 0, TOK_RSHIFT,
12953 '|','|', 0, TOK_OR,
12954 '&','&', 0, TOK_AND,
12955 '!','=', 0, TOK_NE,
12956 '<','=', 0, TOK_LE,
12957 '>','=', 0, TOK_GE,
12958 '=','=', 0, TOK_EQ,
12959 '|','=', 0, TOK_OR_ASSIGN,
12960 '&','=', 0, TOK_AND_ASSIGN,
12961 '*','=', 0, TOK_MUL_ASSIGN,
12962 '/','=', 0, TOK_DIV_ASSIGN,
12963 '%','=', 0, TOK_REM_ASSIGN,
12964 '+','=', 0, TOK_PLUS_ASSIGN,
12965 '-','=', 0, TOK_MINUS_ASSIGN,
12966 '-','-', 0, TOK_POST_DEC,
12967 '^','=', 0, TOK_XOR_ASSIGN,
12968 '+','+', 0, TOK_POST_INC,
12969 '*','*', 0, TOK_EXPONENT,
12970 '!', 0, TOK_NOT,
12971 '<', 0, TOK_LT,
12972 '>', 0, TOK_GT,
12973 '=', 0, TOK_ASSIGN,
12974 '|', 0, TOK_BOR,
12975 '&', 0, TOK_BAND,
12976 '*', 0, TOK_MUL,
12977 '/', 0, TOK_DIV,
12978 '%', 0, TOK_REM,
12979 '+', 0, TOK_ADD,
12980 '-', 0, TOK_SUB,
12981 '^', 0, TOK_BXOR,
12982 /* uniq */
12983 '~', 0, TOK_BNOT,
12984 ',', 0, TOK_COMMA,
12985 '?', 0, TOK_CONDITIONAL,
12986 ':', 0, TOK_CONDITIONAL_SEP,
12987 ')', 0, TOK_RPAREN,
12988 '(', 0, TOK_LPAREN,
12989 0
12990};
12991/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012992#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000012993
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012994static arith_t
12995arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012996{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012997 char arithval; /* Current character under analysis */
12998 operator lasttok, op;
12999 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013000 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013001 const char *p = endexpression;
13002 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013003 v_n_t *numstack, *numstackptr;
13004 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013005
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013006 /* Stack of integers */
13007 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13008 * in any given correct or incorrect expression is left as an exercise to
13009 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013010 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013012 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013013
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13015 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013016
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013018 arithval = *expr;
13019 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013020 if (p == endexpression) {
13021 /* Null expression. */
13022 return 0;
13023 }
13024
13025 /* This is only reached after all tokens have been extracted from the
13026 * input stream. If there are still tokens on the operator stack, they
13027 * are to be applied in order. At the end, there should be a final
13028 * result on the integer stack */
13029
13030 if (expr != endexpression + 1) {
13031 /* If we haven't done so already, */
13032 /* append a closing right paren */
13033 expr = endexpression;
13034 /* and let the loop process it. */
13035 continue;
13036 }
13037 /* At this point, we're done with the expression. */
13038 if (numstackptr != numstack+1) {
13039 /* ... but if there isn't, it's bad */
13040 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013041 *perrcode = -1;
13042 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013043 }
13044 if (numstack->var) {
13045 /* expression is $((var)) only, lookup now */
13046 errcode = arith_lookup_val(numstack);
13047 }
13048 ret:
13049 *perrcode = errcode;
13050 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013051 }
13052
Eric Andersen90898442003-08-06 11:20:52 +000013053 /* Continue processing the expression. */
13054 if (arith_isspace(arithval)) {
13055 /* Skip whitespace */
13056 goto prologue;
13057 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013058 p = endofname(expr);
13059 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013060 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013061
13062 numstackptr->var = alloca(var_name_size);
13063 safe_strncpy(numstackptr->var, expr, var_name_size);
13064 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013065 num:
Eric Andersen90898442003-08-06 11:20:52 +000013066 numstackptr->contidional_second_val_initialized = 0;
13067 numstackptr++;
13068 lasttok = TOK_NUM;
13069 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013070 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013071 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013072 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013073#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013074 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013075#else
13076 numstackptr->val = strtol(expr, (char **) &expr, 0);
13077#endif
Eric Andersen90898442003-08-06 11:20:52 +000013078 goto num;
13079 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013080 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013081 const char *o;
13082
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013083 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013084 /* strange operator not found */
13085 goto err;
13086 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013087 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013088 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013089 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013090 /* found */
13091 expr = o - 1;
13092 break;
13093 }
13094 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013095 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013096 p++;
13097 /* skip zero delim */
13098 p++;
13099 }
13100 op = p[1];
13101
13102 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013103 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13104 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013105
13106 /* Plus and minus are binary (not unary) _only_ if the last
13107 * token was as number, or a right paren (which pretends to be
13108 * a number, since it evaluates to one). Think about it.
13109 * It makes sense. */
13110 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013111 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013112 case TOK_ADD:
13113 op = TOK_UPLUS;
13114 break;
13115 case TOK_SUB:
13116 op = TOK_UMINUS;
13117 break;
13118 case TOK_POST_INC:
13119 op = TOK_PRE_INC;
13120 break;
13121 case TOK_POST_DEC:
13122 op = TOK_PRE_DEC;
13123 break;
Eric Andersen90898442003-08-06 11:20:52 +000013124 }
13125 }
13126 /* We don't want a unary operator to cause recursive descent on the
13127 * stack, because there can be many in a row and it could cause an
13128 * operator to be evaluated before its argument is pushed onto the
13129 * integer stack. */
13130 /* But for binary operators, "apply" everything on the operator
13131 * stack until we find an operator with a lesser priority than the
13132 * one we have just extracted. */
13133 /* Left paren is given the lowest priority so it will never be
13134 * "applied" in this way.
13135 * if associativity is right and priority eq, applied also skip
13136 */
13137 prec = PREC(op);
13138 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13139 /* not left paren or unary */
13140 if (lasttok != TOK_NUM) {
13141 /* binary op must be preceded by a num */
13142 goto err;
13143 }
13144 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013145 if (op == TOK_RPAREN) {
13146 /* The algorithm employed here is simple: while we don't
13147 * hit an open paren nor the bottom of the stack, pop
13148 * tokens and apply them */
13149 if (stackptr[-1] == TOK_LPAREN) {
13150 --stackptr;
13151 /* Any operator directly after a */
13152 lasttok = TOK_NUM;
13153 /* close paren should consider itself binary */
13154 goto prologue;
13155 }
13156 } else {
13157 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013158
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013159 convert_prec_is_assing(prec);
13160 convert_prec_is_assing(prev_prec);
13161 if (prev_prec < prec)
13162 break;
13163 /* check right assoc */
13164 if (prev_prec == prec && is_right_associativity(prec))
13165 break;
13166 }
13167 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13168 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013169 }
13170 if (op == TOK_RPAREN) {
13171 goto err;
13172 }
13173 }
13174
13175 /* Push this operator to the stack and remember it. */
13176 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013177 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013178 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013179 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013180}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013181#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013182
13183
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013184/* ============ main() and helpers */
13185
13186/*
13187 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013188 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013189static void exitshell(void) ATTRIBUTE_NORETURN;
13190static void
13191exitshell(void)
13192{
13193 struct jmploc loc;
13194 char *p;
13195 int status;
13196
13197 status = exitstatus;
13198 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13199 if (setjmp(loc.loc)) {
13200 if (exception == EXEXIT)
13201/* dash bug: it just does _exit(exitstatus) here
13202 * but we have to do setjobctl(0) first!
13203 * (bug is still not fixed in dash-0.5.3 - if you run dash
13204 * under Midnight Commander, on exit from dash MC is backgrounded) */
13205 status = exitstatus;
13206 goto out;
13207 }
13208 exception_handler = &loc;
13209 p = trap[0];
13210 if (p) {
13211 trap[0] = NULL;
13212 evalstring(p, 0);
13213 }
13214 flush_stdout_stderr();
13215 out:
13216 setjobctl(0);
13217 _exit(status);
13218 /* NOTREACHED */
13219}
13220
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013221static void
13222init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013223{
13224 /* from input.c: */
13225 basepf.nextc = basepf.buf = basebuf;
13226
13227 /* from trap.c: */
13228 signal(SIGCHLD, SIG_DFL);
13229
13230 /* from var.c: */
13231 {
13232 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013233 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013234 const char *p;
13235 struct stat st1, st2;
13236
13237 initvar();
13238 for (envp = environ; envp && *envp; envp++) {
13239 if (strchr(*envp, '=')) {
13240 setvareq(*envp, VEXPORT|VTEXTFIXED);
13241 }
13242 }
13243
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013244 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013245 setvar("PPID", ppid, 0);
13246
13247 p = lookupvar("PWD");
13248 if (p)
13249 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13250 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13251 p = '\0';
13252 setpwd(p, 0);
13253 }
13254}
13255
13256/*
13257 * Process the shell command line arguments.
13258 */
13259static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013260procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013261{
13262 int i;
13263 const char *xminusc;
13264 char **xargv;
13265
13266 xargv = argv;
13267 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013268 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013269 xargv++;
13270 for (i = 0; i < NOPTS; i++)
13271 optlist[i] = 2;
13272 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013273 if (options(1)) {
13274 /* it already printed err message */
13275 raise_exception(EXERROR);
13276 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013277 xargv = argptr;
13278 xminusc = minusc;
13279 if (*xargv == NULL) {
13280 if (xminusc)
13281 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13282 sflag = 1;
13283 }
13284 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13285 iflag = 1;
13286 if (mflag == 2)
13287 mflag = iflag;
13288 for (i = 0; i < NOPTS; i++)
13289 if (optlist[i] == 2)
13290 optlist[i] = 0;
13291#if DEBUG == 2
13292 debug = 1;
13293#endif
13294 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13295 if (xminusc) {
13296 minusc = *xargv++;
13297 if (*xargv)
13298 goto setarg0;
13299 } else if (!sflag) {
13300 setinputfile(*xargv, 0);
13301 setarg0:
13302 arg0 = *xargv++;
13303 commandname = arg0;
13304 }
13305
13306 shellparam.p = xargv;
13307#if ENABLE_ASH_GETOPTS
13308 shellparam.optind = 1;
13309 shellparam.optoff = -1;
13310#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013311 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013312 while (*xargv) {
13313 shellparam.nparam++;
13314 xargv++;
13315 }
13316 optschanged();
13317}
13318
13319/*
13320 * Read /etc/profile or .profile.
13321 */
13322static void
13323read_profile(const char *name)
13324{
13325 int skip;
13326
13327 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13328 return;
13329 skip = cmdloop(0);
13330 popfile();
13331 if (skip)
13332 exitshell();
13333}
13334
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013335/*
13336 * This routine is called when an error or an interrupt occurs in an
13337 * interactive shell and control is returned to the main command loop.
13338 */
13339static void
13340reset(void)
13341{
13342 /* from eval.c: */
13343 evalskip = 0;
13344 loopnest = 0;
13345 /* from input.c: */
13346 parselleft = parsenleft = 0; /* clear input buffer */
13347 popallfiles();
13348 /* from parser.c: */
13349 tokpushback = 0;
13350 checkkwd = 0;
13351 /* from redir.c: */
13352 clearredir(0);
13353}
13354
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013355#if PROFILE
13356static short profile_buf[16384];
13357extern int etext();
13358#endif
13359
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013360/*
13361 * Main routine. We initialize things, parse the arguments, execute
13362 * profiles if we're a login shell, and then call cmdloop to execute
13363 * commands. The setjmp call sets up the location to jump to when an
13364 * exception occurs. When an exception occurs the variable "state"
13365 * is used to figure out how far we had gotten.
13366 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013367int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013368int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013369{
13370 char *shinit;
13371 volatile int state;
13372 struct jmploc jmploc;
13373 struct stackmark smark;
13374
Denis Vlasenko01631112007-12-16 17:20:38 +000013375 /* Initialize global data */
13376 INIT_G_misc();
13377 INIT_G_memstack();
13378 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013379#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013380 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013381#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013382 INIT_G_cmdtable();
13383
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013384#if PROFILE
13385 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13386#endif
13387
13388#if ENABLE_FEATURE_EDITING
13389 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13390#endif
13391 state = 0;
13392 if (setjmp(jmploc.loc)) {
13393 int e;
13394 int s;
13395
13396 reset();
13397
13398 e = exception;
13399 if (e == EXERROR)
13400 exitstatus = 2;
13401 s = state;
13402 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13403 exitshell();
13404
13405 if (e == EXINT) {
13406 outcslow('\n', stderr);
13407 }
13408 popstackmark(&smark);
13409 FORCE_INT_ON; /* enable interrupts */
13410 if (s == 1)
13411 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013412 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013413 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013414 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013415 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013416 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013417 }
13418 exception_handler = &jmploc;
13419#if DEBUG
13420 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013421 trace_puts("Shell args: ");
13422 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013423#endif
13424 rootpid = getpid();
13425
13426#if ENABLE_ASH_RANDOM_SUPPORT
13427 rseed = rootpid + time(NULL);
13428#endif
13429 init();
13430 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013431 procargs(argv);
13432
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013433#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13434 if (iflag) {
13435 const char *hp = lookupvar("HISTFILE");
13436
13437 if (hp == NULL) {
13438 hp = lookupvar("HOME");
13439 if (hp != NULL) {
13440 char *defhp = concat_path_file(hp, ".ash_history");
13441 setvar("HISTFILE", defhp, 0);
13442 free(defhp);
13443 }
13444 }
13445 }
13446#endif
13447 if (argv[0] && argv[0][0] == '-')
13448 isloginsh = 1;
13449 if (isloginsh) {
13450 state = 1;
13451 read_profile("/etc/profile");
13452 state1:
13453 state = 2;
13454 read_profile(".profile");
13455 }
13456 state2:
13457 state = 3;
13458 if (
13459#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013460 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013461#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013462 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013463 ) {
13464 shinit = lookupvar("ENV");
13465 if (shinit != NULL && *shinit != '\0') {
13466 read_profile(shinit);
13467 }
13468 }
13469 state3:
13470 state = 4;
13471 if (minusc)
13472 evalstring(minusc, 0);
13473
13474 if (sflag || minusc == NULL) {
13475#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013476 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013477 const char *hp = lookupvar("HISTFILE");
13478
13479 if (hp != NULL)
13480 line_input_state->hist_file = hp;
13481 }
13482#endif
13483 state4: /* XXX ??? - why isn't this before the "if" statement */
13484 cmdloop(1);
13485 }
13486#if PROFILE
13487 monitor(0);
13488#endif
13489#ifdef GPROF
13490 {
13491 extern void _mcleanup(void);
13492 _mcleanup();
13493 }
13494#endif
13495 exitshell();
13496 /* NOTREACHED */
13497}
13498
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013499#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013500const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013501int main(int argc, char **argv)
13502{
13503 return ash_main(argc, argv);
13504}
13505#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013506
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013507
Eric Andersendf82f612001-06-28 07:46:40 +000013508/*-
13509 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013510 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013511 *
13512 * This code is derived from software contributed to Berkeley by
13513 * Kenneth Almquist.
13514 *
13515 * Redistribution and use in source and binary forms, with or without
13516 * modification, are permitted provided that the following conditions
13517 * are met:
13518 * 1. Redistributions of source code must retain the above copyright
13519 * notice, this list of conditions and the following disclaimer.
13520 * 2. Redistributions in binary form must reproduce the above copyright
13521 * notice, this list of conditions and the following disclaimer in the
13522 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013523 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013524 * may be used to endorse or promote products derived from this software
13525 * without specific prior written permission.
13526 *
13527 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13528 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13529 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13530 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13531 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13532 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13533 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13534 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13535 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13536 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13537 * SUCH DAMAGE.
13538 */