blob: 0634f1868589b7eacb3fb5bad88bce7ef793f317 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000054#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000055#define _GNU_SOURCE
56#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000057#endif
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000058#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000059#include <paths.h>
60#include <setjmp.h>
61#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000062#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000063#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000064#endif
65
Denis Vlasenkob012b102007-02-19 22:43:01 +000066#if defined(__uClinux__)
67#error "Do not even bother, ash will not run on uClinux"
68#endif
69
Denis Vlasenkob012b102007-02-19 22:43:01 +000070
Denis Vlasenko01631112007-12-16 17:20:38 +000071/* ============ Hash table sizes. Configurable. */
72
73#define VTABSIZE 39
74#define ATABSIZE 39
75#define CMDTABLESIZE 31 /* should be prime */
76
77
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000078/* ============ Misc helpers */
79
80#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
81
82/* C99 say: "char" declaration may be signed or unsigned default */
83#define signed_char2int(sc) ((int)((signed char)sc))
84
85
Denis Vlasenkob012b102007-02-19 22:43:01 +000086/* ============ Shell options */
87
88static const char *const optletters_optnames[] = {
89 "e" "errexit",
90 "f" "noglob",
91 "I" "ignoreeof",
92 "i" "interactive",
93 "m" "monitor",
94 "n" "noexec",
95 "s" "stdin",
96 "x" "xtrace",
97 "v" "verbose",
98 "C" "noclobber",
99 "a" "allexport",
100 "b" "notify",
101 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000102 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000103#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000104 ,"\0" "nolog"
105 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000106#endif
107};
108
109#define optletters(n) optletters_optnames[(n)][0]
110#define optnames(n) (&optletters_optnames[(n)][1])
111
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000112enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000113
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000114static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000115
116#define eflag optlist[0]
117#define fflag optlist[1]
118#define Iflag optlist[2]
119#define iflag optlist[3]
120#define mflag optlist[4]
121#define nflag optlist[5]
122#define sflag optlist[6]
123#define xflag optlist[7]
124#define vflag optlist[8]
125#define Cflag optlist[9]
126#define aflag optlist[10]
127#define bflag optlist[11]
128#define uflag optlist[12]
129#define viflag optlist[13]
130#if DEBUG
131#define nolog optlist[14]
132#define debug optlist[15]
133#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000134
135
Denis Vlasenkob012b102007-02-19 22:43:01 +0000136/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000137
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000138static const char homestr[] ALIGN1 = "HOME";
139static const char snlfmt[] ALIGN1 = "%s\n";
140static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000141
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000142/*
Eric Andersenc470f442003-07-28 09:56:35 +0000143 * We enclose jmp_buf in a structure so that we can declare pointers to
144 * jump locations. The global variable handler contains the location to
145 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000146 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000147 * exception handlers, the user should save the value of handler on entry
148 * to an inner scope, set handler to point to a jmploc structure for the
149 * inner scope, and restore handler on exit from the scope.
150 */
Eric Andersenc470f442003-07-28 09:56:35 +0000151struct jmploc {
152 jmp_buf loc;
153};
Denis Vlasenko01631112007-12-16 17:20:38 +0000154
155struct globals_misc {
156 /* pid of main shell */
157 int rootpid;
158 /* shell level: 0 for the main shell, 1 for its children, and so on */
159 int shlvl;
160#define rootshell (!shlvl)
161 char *minusc; /* argument to -c option */
162
163 char *curdir; // = nullstr; /* current working directory */
164 char *physdir; // = nullstr; /* physical working directory */
165
166 char *arg0; /* value of $0 */
167
168 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000169
170// disabled by vda: cannot understand how it was supposed to work -
171// cannot fix bugs. That's why you have to explain your non-trivial designs!
172// /* do we generate EXSIG events */
173// int exsig; /* counter */
174 volatile int suppressint; /* counter */
175 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
176 /* last pending signal */
177 volatile /*sig_atomic_t*/ smallint pendingsig;
178 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000179 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000180#define EXINT 0 /* SIGINT received */
181#define EXERROR 1 /* a generic error */
182#define EXSHELLPROC 2 /* execute a shell procedure */
183#define EXEXEC 3 /* command execution failed */
184#define EXEXIT 4 /* exit the shell */
185#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000186
Denis Vlasenko01631112007-12-16 17:20:38 +0000187 /* trap handler commands */
188 char *trap[NSIG];
189 smallint isloginsh;
190 char nullstr[1]; /* zero length string */
191 /*
192 * Sigmode records the current value of the signal handlers for the various
193 * modes. A value of zero means that the current handler is not known.
194 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
195 */
196 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000197#define S_DFL 1 /* default signal handling (SIG_DFL) */
198#define S_CATCH 2 /* signal is caught */
199#define S_IGN 3 /* signal is ignored (SIG_IGN) */
200#define S_HARD_IGN 4 /* signal is ignored permenantly */
201#define S_RESET 5 /* temporary - to reset a hard ignored sig */
202
Denis Vlasenko01631112007-12-16 17:20:38 +0000203 /* indicates specified signal received */
204 char gotsig[NSIG - 1];
205};
206/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
207static struct globals_misc *const ptr_to_globals_misc __attribute__ ((section (".data")));
208#define G_misc (*ptr_to_globals_misc)
209#define rootpid (G_misc.rootpid )
210#define shlvl (G_misc.shlvl )
211#define minusc (G_misc.minusc )
212#define curdir (G_misc.curdir )
213#define physdir (G_misc.physdir )
214#define arg0 (G_misc.arg0 )
215#define exception_handler (G_misc.exception_handler)
216#define exception (G_misc.exception )
217#define suppressint (G_misc.suppressint )
218#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000219//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000220#define pendingsig (G_misc.pendingsig )
221#define trap (G_misc.trap )
222#define isloginsh (G_misc.isloginsh)
223#define nullstr (G_misc.nullstr )
224#define sigmode (G_misc.sigmode )
225#define gotsig (G_misc.gotsig )
226#define INIT_G_misc() do { \
227 (*(struct globals_misc**)&ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
228 curdir = nullstr; \
229 physdir = nullstr; \
230} while (0)
231
232
233/* ============ Interrupts / exceptions */
234
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000235/*
Eric Andersen2870d962001-07-02 17:27:21 +0000236 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000237 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000238 * much more efficient and portable. (But hacking the kernel is so much
239 * more fun than worrying about efficiency and portability. :-))
240 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000241#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000242 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000243 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000244 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000245 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000246
247/*
248 * Called to raise an exception. Since C doesn't include exceptions, we
249 * just do a longjmp to the exception handler. The type of exception is
250 * stored in the global variable "exception".
251 */
252static void raise_exception(int) ATTRIBUTE_NORETURN;
253static void
254raise_exception(int e)
255{
256#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000257 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000258 abort();
259#endif
260 INT_OFF;
261 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000262 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000263}
264
265/*
266 * Called from trap.c when a SIGINT is received. (If the user specifies
267 * that SIGINT is to be trapped or ignored using the trap builtin, then
268 * this routine is not called.) Suppressint is nonzero when interrupts
269 * are held using the INT_OFF macro. (The test for iflag is just
270 * defensive programming.)
271 */
272static void raise_interrupt(void) ATTRIBUTE_NORETURN;
273static void
274raise_interrupt(void)
275{
276 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000277 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000278
279 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000280 /* Signal is not automatically unmasked after it is raised,
281 * do it ourself - unmask all signals */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000282 sigemptyset(&mask);
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000283 sigprocmask(SIG_SETMASK, &mask, NULL);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000284 /* pendingsig = 0; - now done in onsig() */
285
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286 i = EXSIG;
287 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
288 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000289 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000290 signal(SIGINT, SIG_DFL);
291 raise(SIGINT);
292 }
293 i = EXINT;
294 }
295 raise_exception(i);
296 /* NOTREACHED */
297}
298
299#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000300static void
301int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000302{
303 if (--suppressint == 0 && intpending) {
304 raise_interrupt();
305 }
306}
307#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000308static void
309force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000310{
311 suppressint = 0;
312 if (intpending)
313 raise_interrupt();
314}
315#define FORCE_INT_ON force_int_on()
316#else
317#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000318 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000319 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000320 if (--suppressint == 0 && intpending) \
321 raise_interrupt(); \
322 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000324 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000325 xbarrier(); \
326 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000327 if (intpending) \
328 raise_interrupt(); \
329 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000330#endif /* ASH_OPTIMIZE_FOR_SIZE */
331
332#define SAVE_INT(v) ((v) = suppressint)
333
334#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000335 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000336 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000337 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000338 if (suppressint == 0 && intpending) \
339 raise_interrupt(); \
340 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000341
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000342/*
343 * Ignore a signal. Only one usage site - in forkchild()
344 */
345static void
346ignoresig(int signo)
347{
348 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
349 signal(signo, SIG_IGN);
350 }
351 sigmode[signo - 1] = S_HARD_IGN;
352}
353
354/*
355 * Signal handler. Only one usage site - in setsignal()
356 */
357static void
358onsig(int signo)
359{
360 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000361 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000362
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000363 if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000364 if (!suppressint) {
365 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000366 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000367 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000368 intpending = 1;
369 }
370}
371
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000372
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000373/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000374
Eric Andersenc470f442003-07-28 09:56:35 +0000375static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000376outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000377{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000378 INT_OFF;
379 fputs(p, file);
380 INT_ON;
381}
382
383static void
384flush_stdout_stderr(void)
385{
386 INT_OFF;
387 fflush(stdout);
388 fflush(stderr);
389 INT_ON;
390}
391
392static void
393flush_stderr(void)
394{
395 INT_OFF;
396 fflush(stderr);
397 INT_ON;
398}
399
400static void
401outcslow(int c, FILE *dest)
402{
403 INT_OFF;
404 putc(c, dest);
405 fflush(dest);
406 INT_ON;
407}
408
409static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
410static int
411out1fmt(const char *fmt, ...)
412{
413 va_list ap;
414 int r;
415
416 INT_OFF;
417 va_start(ap, fmt);
418 r = vprintf(fmt, ap);
419 va_end(ap);
420 INT_ON;
421 return r;
422}
423
424static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
425static int
426fmtstr(char *outbuf, size_t length, const char *fmt, ...)
427{
428 va_list ap;
429 int ret;
430
431 va_start(ap, fmt);
432 INT_OFF;
433 ret = vsnprintf(outbuf, length, fmt, ap);
434 va_end(ap);
435 INT_ON;
436 return ret;
437}
438
439static void
440out1str(const char *p)
441{
442 outstr(p, stdout);
443}
444
445static void
446out2str(const char *p)
447{
448 outstr(p, stderr);
449 flush_stderr();
450}
451
452
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000453/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000454
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000455/* control characters in argument strings */
456#define CTLESC '\201' /* escape next character */
457#define CTLVAR '\202' /* variable defn */
458#define CTLENDVAR '\203'
459#define CTLBACKQ '\204'
460#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
461/* CTLBACKQ | CTLQUOTE == '\205' */
462#define CTLARI '\206' /* arithmetic expression */
463#define CTLENDARI '\207'
464#define CTLQUOTEMARK '\210'
465
466/* variable substitution byte (follows CTLVAR) */
467#define VSTYPE 0x0f /* type of variable substitution */
468#define VSNUL 0x10 /* colon--treat the empty string as unset */
469#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
470
471/* values of VSTYPE field */
472#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
473#define VSMINUS 0x2 /* ${var-text} */
474#define VSPLUS 0x3 /* ${var+text} */
475#define VSQUESTION 0x4 /* ${var?message} */
476#define VSASSIGN 0x5 /* ${var=text} */
477#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
478#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
479#define VSTRIMLEFT 0x8 /* ${var#pattern} */
480#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
481#define VSLENGTH 0xa /* ${#var} */
482
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000483static const char dolatstr[] ALIGN1 = {
484 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
485};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000486
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000487#define NCMD 0
488#define NPIPE 1
489#define NREDIR 2
490#define NBACKGND 3
491#define NSUBSHELL 4
492#define NAND 5
493#define NOR 6
494#define NSEMI 7
495#define NIF 8
496#define NWHILE 9
497#define NUNTIL 10
498#define NFOR 11
499#define NCASE 12
500#define NCLIST 13
501#define NDEFUN 14
502#define NARG 15
503#define NTO 16
504#define NCLOBBER 17
505#define NFROM 18
506#define NFROMTO 19
507#define NAPPEND 20
508#define NTOFD 21
509#define NFROMFD 22
510#define NHERE 23
511#define NXHERE 24
512#define NNOT 25
513
514union node;
515
516struct ncmd {
517 int type;
518 union node *assign;
519 union node *args;
520 union node *redirect;
521};
522
523struct npipe {
524 int type;
525 int backgnd;
526 struct nodelist *cmdlist;
527};
528
529struct nredir {
530 int type;
531 union node *n;
532 union node *redirect;
533};
534
535struct nbinary {
536 int type;
537 union node *ch1;
538 union node *ch2;
539};
540
541struct nif {
542 int type;
543 union node *test;
544 union node *ifpart;
545 union node *elsepart;
546};
547
548struct nfor {
549 int type;
550 union node *args;
551 union node *body;
552 char *var;
553};
554
555struct ncase {
556 int type;
557 union node *expr;
558 union node *cases;
559};
560
561struct nclist {
562 int type;
563 union node *next;
564 union node *pattern;
565 union node *body;
566};
567
568struct narg {
569 int type;
570 union node *next;
571 char *text;
572 struct nodelist *backquote;
573};
574
575struct nfile {
576 int type;
577 union node *next;
578 int fd;
579 union node *fname;
580 char *expfname;
581};
582
583struct ndup {
584 int type;
585 union node *next;
586 int fd;
587 int dupfd;
588 union node *vname;
589};
590
591struct nhere {
592 int type;
593 union node *next;
594 int fd;
595 union node *doc;
596};
597
598struct nnot {
599 int type;
600 union node *com;
601};
602
603union node {
604 int type;
605 struct ncmd ncmd;
606 struct npipe npipe;
607 struct nredir nredir;
608 struct nbinary nbinary;
609 struct nif nif;
610 struct nfor nfor;
611 struct ncase ncase;
612 struct nclist nclist;
613 struct narg narg;
614 struct nfile nfile;
615 struct ndup ndup;
616 struct nhere nhere;
617 struct nnot nnot;
618};
619
620struct nodelist {
621 struct nodelist *next;
622 union node *n;
623};
624
625struct funcnode {
626 int count;
627 union node n;
628};
629
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000630/*
631 * Free a parse tree.
632 */
633static void
634freefunc(struct funcnode *f)
635{
636 if (f && --f->count < 0)
637 free(f);
638}
639
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000640
641/* ============ Debugging output */
642
643#if DEBUG
644
645static FILE *tracefile;
646
647static void
648trace_printf(const char *fmt, ...)
649{
650 va_list va;
651
652 if (debug != 1)
653 return;
654 va_start(va, fmt);
655 vfprintf(tracefile, fmt, va);
656 va_end(va);
657}
658
659static void
660trace_vprintf(const char *fmt, va_list va)
661{
662 if (debug != 1)
663 return;
664 vfprintf(tracefile, fmt, va);
665}
666
667static void
668trace_puts(const char *s)
669{
670 if (debug != 1)
671 return;
672 fputs(s, tracefile);
673}
674
675static void
676trace_puts_quoted(char *s)
677{
678 char *p;
679 char c;
680
681 if (debug != 1)
682 return;
683 putc('"', tracefile);
684 for (p = s; *p; p++) {
685 switch (*p) {
686 case '\n': c = 'n'; goto backslash;
687 case '\t': c = 't'; goto backslash;
688 case '\r': c = 'r'; goto backslash;
689 case '"': c = '"'; goto backslash;
690 case '\\': c = '\\'; goto backslash;
691 case CTLESC: c = 'e'; goto backslash;
692 case CTLVAR: c = 'v'; goto backslash;
693 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
694 case CTLBACKQ: c = 'q'; goto backslash;
695 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
696 backslash:
697 putc('\\', tracefile);
698 putc(c, tracefile);
699 break;
700 default:
701 if (*p >= ' ' && *p <= '~')
702 putc(*p, tracefile);
703 else {
704 putc('\\', tracefile);
705 putc(*p >> 6 & 03, tracefile);
706 putc(*p >> 3 & 07, tracefile);
707 putc(*p & 07, tracefile);
708 }
709 break;
710 }
711 }
712 putc('"', tracefile);
713}
714
715static void
716trace_puts_args(char **ap)
717{
718 if (debug != 1)
719 return;
720 if (!*ap)
721 return;
722 while (1) {
723 trace_puts_quoted(*ap);
724 if (!*++ap) {
725 putc('\n', tracefile);
726 break;
727 }
728 putc(' ', tracefile);
729 }
730}
731
732static void
733opentrace(void)
734{
735 char s[100];
736#ifdef O_APPEND
737 int flags;
738#endif
739
740 if (debug != 1) {
741 if (tracefile)
742 fflush(tracefile);
743 /* leave open because libedit might be using it */
744 return;
745 }
746 strcpy(s, "./trace");
747 if (tracefile) {
748 if (!freopen(s, "a", tracefile)) {
749 fprintf(stderr, "Can't re-open %s\n", s);
750 debug = 0;
751 return;
752 }
753 } else {
754 tracefile = fopen(s, "a");
755 if (tracefile == NULL) {
756 fprintf(stderr, "Can't open %s\n", s);
757 debug = 0;
758 return;
759 }
760 }
761#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000762 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000763 if (flags >= 0)
764 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
765#endif
766 setlinebuf(tracefile);
767 fputs("\nTracing started.\n", tracefile);
768}
769
770static void
771indent(int amount, char *pfx, FILE *fp)
772{
773 int i;
774
775 for (i = 0; i < amount; i++) {
776 if (pfx && i == amount - 1)
777 fputs(pfx, fp);
778 putc('\t', fp);
779 }
780}
781
782/* little circular references here... */
783static void shtree(union node *n, int ind, char *pfx, FILE *fp);
784
785static void
786sharg(union node *arg, FILE *fp)
787{
788 char *p;
789 struct nodelist *bqlist;
790 int subtype;
791
792 if (arg->type != NARG) {
793 out1fmt("<node type %d>\n", arg->type);
794 abort();
795 }
796 bqlist = arg->narg.backquote;
797 for (p = arg->narg.text; *p; p++) {
798 switch (*p) {
799 case CTLESC:
800 putc(*++p, fp);
801 break;
802 case CTLVAR:
803 putc('$', fp);
804 putc('{', fp);
805 subtype = *++p;
806 if (subtype == VSLENGTH)
807 putc('#', fp);
808
809 while (*p != '=')
810 putc(*p++, fp);
811
812 if (subtype & VSNUL)
813 putc(':', fp);
814
815 switch (subtype & VSTYPE) {
816 case VSNORMAL:
817 putc('}', fp);
818 break;
819 case VSMINUS:
820 putc('-', fp);
821 break;
822 case VSPLUS:
823 putc('+', fp);
824 break;
825 case VSQUESTION:
826 putc('?', fp);
827 break;
828 case VSASSIGN:
829 putc('=', fp);
830 break;
831 case VSTRIMLEFT:
832 putc('#', fp);
833 break;
834 case VSTRIMLEFTMAX:
835 putc('#', fp);
836 putc('#', fp);
837 break;
838 case VSTRIMRIGHT:
839 putc('%', fp);
840 break;
841 case VSTRIMRIGHTMAX:
842 putc('%', fp);
843 putc('%', fp);
844 break;
845 case VSLENGTH:
846 break;
847 default:
848 out1fmt("<subtype %d>", subtype);
849 }
850 break;
851 case CTLENDVAR:
852 putc('}', fp);
853 break;
854 case CTLBACKQ:
855 case CTLBACKQ|CTLQUOTE:
856 putc('$', fp);
857 putc('(', fp);
858 shtree(bqlist->n, -1, NULL, fp);
859 putc(')', fp);
860 break;
861 default:
862 putc(*p, fp);
863 break;
864 }
865 }
866}
867
868static void
869shcmd(union node *cmd, FILE *fp)
870{
871 union node *np;
872 int first;
873 const char *s;
874 int dftfd;
875
876 first = 1;
877 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000878 if (!first)
879 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000880 sharg(np, fp);
881 first = 0;
882 }
883 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000884 if (!first)
885 putc(' ', fp);
886 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000887 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000888 case NTO: s = ">>"+1; dftfd = 1; break;
889 case NCLOBBER: s = ">|"; dftfd = 1; break;
890 case NAPPEND: s = ">>"; dftfd = 1; break;
891 case NTOFD: s = ">&"; dftfd = 1; break;
892 case NFROM: s = "<"; break;
893 case NFROMFD: s = "<&"; break;
894 case NFROMTO: s = "<>"; break;
895 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000896 }
897 if (np->nfile.fd != dftfd)
898 fprintf(fp, "%d", np->nfile.fd);
899 fputs(s, fp);
900 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
901 fprintf(fp, "%d", np->ndup.dupfd);
902 } else {
903 sharg(np->nfile.fname, fp);
904 }
905 first = 0;
906 }
907}
908
909static void
910shtree(union node *n, int ind, char *pfx, FILE *fp)
911{
912 struct nodelist *lp;
913 const char *s;
914
915 if (n == NULL)
916 return;
917
918 indent(ind, pfx, fp);
919 switch (n->type) {
920 case NSEMI:
921 s = "; ";
922 goto binop;
923 case NAND:
924 s = " && ";
925 goto binop;
926 case NOR:
927 s = " || ";
928 binop:
929 shtree(n->nbinary.ch1, ind, NULL, fp);
930 /* if (ind < 0) */
931 fputs(s, fp);
932 shtree(n->nbinary.ch2, ind, NULL, fp);
933 break;
934 case NCMD:
935 shcmd(n, fp);
936 if (ind >= 0)
937 putc('\n', fp);
938 break;
939 case NPIPE:
940 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
941 shcmd(lp->n, fp);
942 if (lp->next)
943 fputs(" | ", fp);
944 }
945 if (n->npipe.backgnd)
946 fputs(" &", fp);
947 if (ind >= 0)
948 putc('\n', fp);
949 break;
950 default:
951 fprintf(fp, "<node type %d>", n->type);
952 if (ind >= 0)
953 putc('\n', fp);
954 break;
955 }
956}
957
958static void
959showtree(union node *n)
960{
961 trace_puts("showtree called\n");
962 shtree(n, 1, NULL, stdout);
963}
964
965#define TRACE(param) trace_printf param
966#define TRACEV(param) trace_vprintf param
967
968#else
969
970#define TRACE(param)
971#define TRACEV(param)
972
973#endif /* DEBUG */
974
975
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000976/* ============ Parser data */
977
978/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000979 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
980 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000981struct strlist {
982 struct strlist *next;
983 char *text;
984};
985
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000986#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000987struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000988#endif
989
Denis Vlasenkob012b102007-02-19 22:43:01 +0000990struct strpush {
991 struct strpush *prev; /* preceding string on stack */
992 char *prevstring;
993 int prevnleft;
994#if ENABLE_ASH_ALIAS
995 struct alias *ap; /* if push was associated with an alias */
996#endif
997 char *string; /* remember the string since it may change */
998};
999
1000struct parsefile {
1001 struct parsefile *prev; /* preceding file on stack */
1002 int linno; /* current line */
1003 int fd; /* file descriptor (or -1 if string) */
1004 int nleft; /* number of chars left in this line */
1005 int lleft; /* number of chars left in this buffer */
1006 char *nextc; /* next char in buffer */
1007 char *buf; /* input buffer */
1008 struct strpush *strpush; /* for pushing strings at this level */
1009 struct strpush basestrpush; /* so pushing one is fast */
1010};
1011
1012static struct parsefile basepf; /* top level input file */
1013static struct parsefile *parsefile = &basepf; /* current input file */
1014static int startlinno; /* line # where last token started */
1015static char *commandname; /* currently executing command */
1016static struct strlist *cmdenviron; /* environment for builtin command */
1017static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001018
1019
1020/* ============ Message printing */
1021
1022static void
1023ash_vmsg(const char *msg, va_list ap)
1024{
1025 fprintf(stderr, "%s: ", arg0);
1026 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001027 if (strcmp(arg0, commandname))
1028 fprintf(stderr, "%s: ", commandname);
1029 if (!iflag || parsefile->fd)
1030 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001031 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001032 vfprintf(stderr, msg, ap);
1033 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001034}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001035
1036/*
1037 * Exverror is called to raise the error exception. If the second argument
1038 * is not NULL then error prints an error message using printf style
1039 * formatting. It then raises the error exception.
1040 */
1041static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1042static void
1043ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001044{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001045#if DEBUG
1046 if (msg) {
1047 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1048 TRACEV((msg, ap));
1049 TRACE(("\") pid=%d\n", getpid()));
1050 } else
1051 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1052 if (msg)
1053#endif
1054 ash_vmsg(msg, ap);
1055
1056 flush_stdout_stderr();
1057 raise_exception(cond);
1058 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001059}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001060
1061static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1062static void
1063ash_msg_and_raise_error(const char *msg, ...)
1064{
1065 va_list ap;
1066
1067 va_start(ap, msg);
1068 ash_vmsg_and_raise(EXERROR, msg, ap);
1069 /* NOTREACHED */
1070 va_end(ap);
1071}
1072
1073static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1074static void
1075ash_msg_and_raise(int cond, const char *msg, ...)
1076{
1077 va_list ap;
1078
1079 va_start(ap, msg);
1080 ash_vmsg_and_raise(cond, msg, ap);
1081 /* NOTREACHED */
1082 va_end(ap);
1083}
1084
1085/*
1086 * error/warning routines for external builtins
1087 */
1088static void
1089ash_msg(const char *fmt, ...)
1090{
1091 va_list ap;
1092
1093 va_start(ap, fmt);
1094 ash_vmsg(fmt, ap);
1095 va_end(ap);
1096}
1097
1098/*
1099 * Return a string describing an error. The returned string may be a
1100 * pointer to a static buffer that will be overwritten on the next call.
1101 * Action describes the operation that got the error.
1102 */
1103static const char *
1104errmsg(int e, const char *em)
1105{
1106 if (e == ENOENT || e == ENOTDIR) {
1107 return em;
1108 }
1109 return strerror(e);
1110}
1111
1112
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001113/* ============ Memory allocation */
1114
1115/*
1116 * It appears that grabstackstr() will barf with such alignments
1117 * because stalloc() will return a string allocated in a new stackblock.
1118 */
1119#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1120enum {
1121 /* Most machines require the value returned from malloc to be aligned
1122 * in some way. The following macro will get this right
1123 * on many machines. */
1124 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1125 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001126 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001127};
1128
1129struct stack_block {
1130 struct stack_block *prev;
1131 char space[MINSIZE];
1132};
1133
1134struct stackmark {
1135 struct stack_block *stackp;
1136 char *stacknxt;
1137 size_t stacknleft;
1138 struct stackmark *marknext;
1139};
1140
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001141
Denis Vlasenko01631112007-12-16 17:20:38 +00001142struct globals_memstack {
1143 struct stack_block *g_stackp; // = &stackbase;
1144 struct stackmark *markp;
1145 char *g_stacknxt; // = stackbase.space;
1146 char *sstrend; // = stackbase.space + MINSIZE;
1147 size_t g_stacknleft; // = MINSIZE;
1148 int herefd; // = -1;
1149 struct stack_block stackbase;
1150};
1151/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1152static struct globals_memstack *const ptr_to_globals_memstack __attribute__ ((section (".data")));
1153#define G_memstack (*ptr_to_globals_memstack)
1154#define g_stackp (G_memstack.g_stackp )
1155#define markp (G_memstack.markp )
1156#define g_stacknxt (G_memstack.g_stacknxt )
1157#define sstrend (G_memstack.sstrend )
1158#define g_stacknleft (G_memstack.g_stacknleft)
1159#define herefd (G_memstack.herefd )
1160#define stackbase (G_memstack.stackbase )
1161#define INIT_G_memstack() do { \
1162 (*(struct globals_memstack**)&ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1163 g_stackp = &stackbase; \
1164 g_stacknxt = stackbase.space; \
1165 g_stacknleft = MINSIZE; \
1166 sstrend = stackbase.space + MINSIZE; \
1167 herefd = -1; \
1168} while (0)
1169
1170#define stackblock() ((void *)g_stacknxt)
1171#define stackblocksize() g_stacknleft
1172
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001173
1174static void *
1175ckrealloc(void * p, size_t nbytes)
1176{
1177 p = realloc(p, nbytes);
1178 if (!p)
1179 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1180 return p;
1181}
1182
1183static void *
1184ckmalloc(size_t nbytes)
1185{
1186 return ckrealloc(NULL, nbytes);
1187}
1188
1189/*
1190 * Make a copy of a string in safe storage.
1191 */
1192static char *
1193ckstrdup(const char *s)
1194{
1195 char *p = strdup(s);
1196 if (!p)
1197 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1198 return p;
1199}
1200
1201/*
1202 * Parse trees for commands are allocated in lifo order, so we use a stack
1203 * to make this more efficient, and also to avoid all sorts of exception
1204 * handling code to handle interrupts in the middle of a parse.
1205 *
1206 * The size 504 was chosen because the Ultrix malloc handles that size
1207 * well.
1208 */
1209static void *
1210stalloc(size_t nbytes)
1211{
1212 char *p;
1213 size_t aligned;
1214
1215 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001216 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001217 size_t len;
1218 size_t blocksize;
1219 struct stack_block *sp;
1220
1221 blocksize = aligned;
1222 if (blocksize < MINSIZE)
1223 blocksize = MINSIZE;
1224 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1225 if (len < blocksize)
1226 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1227 INT_OFF;
1228 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001229 sp->prev = g_stackp;
1230 g_stacknxt = sp->space;
1231 g_stacknleft = blocksize;
1232 sstrend = g_stacknxt + blocksize;
1233 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001234 INT_ON;
1235 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001236 p = g_stacknxt;
1237 g_stacknxt += aligned;
1238 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001239 return p;
1240}
1241
1242static void
1243stunalloc(void *p)
1244{
1245#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001246 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001247 write(2, "stunalloc\n", 10);
1248 abort();
1249 }
1250#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001251 g_stacknleft += g_stacknxt - (char *)p;
1252 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001253}
1254
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001255/*
1256 * Like strdup but works with the ash stack.
1257 */
1258static char *
1259ststrdup(const char *p)
1260{
1261 size_t len = strlen(p) + 1;
1262 return memcpy(stalloc(len), p, len);
1263}
1264
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001265static void
1266setstackmark(struct stackmark *mark)
1267{
Denis Vlasenko01631112007-12-16 17:20:38 +00001268 mark->stackp = g_stackp;
1269 mark->stacknxt = g_stacknxt;
1270 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001271 mark->marknext = markp;
1272 markp = mark;
1273}
1274
1275static void
1276popstackmark(struct stackmark *mark)
1277{
1278 struct stack_block *sp;
1279
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001280 if (!mark->stackp)
1281 return;
1282
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001283 INT_OFF;
1284 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001285 while (g_stackp != mark->stackp) {
1286 sp = g_stackp;
1287 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001288 free(sp);
1289 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001290 g_stacknxt = mark->stacknxt;
1291 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001292 sstrend = mark->stacknxt + mark->stacknleft;
1293 INT_ON;
1294}
1295
1296/*
1297 * When the parser reads in a string, it wants to stick the string on the
1298 * stack and only adjust the stack pointer when it knows how big the
1299 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1300 * of space on top of the stack and stackblocklen returns the length of
1301 * this block. Growstackblock will grow this space by at least one byte,
1302 * possibly moving it (like realloc). Grabstackblock actually allocates the
1303 * part of the block that has been used.
1304 */
1305static void
1306growstackblock(void)
1307{
1308 size_t newlen;
1309
Denis Vlasenko01631112007-12-16 17:20:38 +00001310 newlen = g_stacknleft * 2;
1311 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001312 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1313 if (newlen < 128)
1314 newlen += 128;
1315
Denis Vlasenko01631112007-12-16 17:20:38 +00001316 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001317 struct stack_block *oldstackp;
1318 struct stackmark *xmark;
1319 struct stack_block *sp;
1320 struct stack_block *prevstackp;
1321 size_t grosslen;
1322
1323 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001324 oldstackp = g_stackp;
1325 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 prevstackp = sp->prev;
1327 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1328 sp = ckrealloc(sp, grosslen);
1329 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001330 g_stackp = sp;
1331 g_stacknxt = sp->space;
1332 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333 sstrend = sp->space + newlen;
1334
1335 /*
1336 * Stack marks pointing to the start of the old block
1337 * must be relocated to point to the new block
1338 */
1339 xmark = markp;
1340 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001341 xmark->stackp = g_stackp;
1342 xmark->stacknxt = g_stacknxt;
1343 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001344 xmark = xmark->marknext;
1345 }
1346 INT_ON;
1347 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001348 char *oldspace = g_stacknxt;
1349 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001350 char *p = stalloc(newlen);
1351
1352 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001353 g_stacknxt = memcpy(p, oldspace, oldlen);
1354 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001355 }
1356}
1357
1358static void
1359grabstackblock(size_t len)
1360{
1361 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 g_stacknxt += len;
1363 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001364}
1365
1366/*
1367 * The following routines are somewhat easier to use than the above.
1368 * The user declares a variable of type STACKSTR, which may be declared
1369 * to be a register. The macro STARTSTACKSTR initializes things. Then
1370 * the user uses the macro STPUTC to add characters to the string. In
1371 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1372 * grown as necessary. When the user is done, she can just leave the
1373 * string there and refer to it using stackblock(). Or she can allocate
1374 * the space for it using grabstackstr(). If it is necessary to allow
1375 * someone else to use the stack temporarily and then continue to grow
1376 * the string, the user should use grabstack to allocate the space, and
1377 * then call ungrabstr(p) to return to the previous mode of operation.
1378 *
1379 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1380 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1381 * is space for at least one character.
1382 */
1383static void *
1384growstackstr(void)
1385{
1386 size_t len = stackblocksize();
1387 if (herefd >= 0 && len >= 1024) {
1388 full_write(herefd, stackblock(), len);
1389 return stackblock();
1390 }
1391 growstackblock();
1392 return stackblock() + len;
1393}
1394
1395/*
1396 * Called from CHECKSTRSPACE.
1397 */
1398static char *
1399makestrspace(size_t newlen, char *p)
1400{
Denis Vlasenko01631112007-12-16 17:20:38 +00001401 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001402 size_t size = stackblocksize();
1403
1404 for (;;) {
1405 size_t nleft;
1406
1407 size = stackblocksize();
1408 nleft = size - len;
1409 if (nleft >= newlen)
1410 break;
1411 growstackblock();
1412 }
1413 return stackblock() + len;
1414}
1415
1416static char *
1417stack_nputstr(const char *s, size_t n, char *p)
1418{
1419 p = makestrspace(n, p);
1420 p = memcpy(p, s, n) + n;
1421 return p;
1422}
1423
1424static char *
1425stack_putstr(const char *s, char *p)
1426{
1427 return stack_nputstr(s, strlen(s), p);
1428}
1429
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001430static char *
1431_STPUTC(int c, char *p)
1432{
1433 if (p == sstrend)
1434 p = growstackstr();
1435 *p++ = c;
1436 return p;
1437}
1438
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001439#define STARTSTACKSTR(p) ((p) = stackblock())
1440#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001441#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001442 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001443 char *q = (p); \
1444 size_t l = (n); \
1445 size_t m = sstrend - q; \
1446 if (l > m) \
1447 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001448 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001449#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001450#define STACKSTRNUL(p) \
1451 do { \
1452 if ((p) == sstrend) \
1453 p = growstackstr(); \
1454 *p = '\0'; \
1455 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001456#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001457#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001458#define STADJUST(amount, p) (p += (amount))
1459
1460#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1461#define ungrabstackstr(s, p) stunalloc((s))
1462#define stackstrend() ((void *)sstrend)
1463
1464
1465/* ============ String helpers */
1466
1467/*
1468 * prefix -- see if pfx is a prefix of string.
1469 */
1470static char *
1471prefix(const char *string, const char *pfx)
1472{
1473 while (*pfx) {
1474 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001475 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001476 }
1477 return (char *) string;
1478}
1479
1480/*
1481 * Check for a valid number. This should be elsewhere.
1482 */
1483static int
1484is_number(const char *p)
1485{
1486 do {
1487 if (!isdigit(*p))
1488 return 0;
1489 } while (*++p != '\0');
1490 return 1;
1491}
1492
1493/*
1494 * Convert a string of digits to an integer, printing an error message on
1495 * failure.
1496 */
1497static int
1498number(const char *s)
1499{
1500 if (!is_number(s))
1501 ash_msg_and_raise_error(illnum, s);
1502 return atoi(s);
1503}
1504
1505/*
1506 * Produce a possibly single quoted string suitable as input to the shell.
1507 * The return string is allocated on the stack.
1508 */
1509static char *
1510single_quote(const char *s)
1511{
1512 char *p;
1513
1514 STARTSTACKSTR(p);
1515
1516 do {
1517 char *q;
1518 size_t len;
1519
1520 len = strchrnul(s, '\'') - s;
1521
1522 q = p = makestrspace(len + 3, p);
1523
1524 *q++ = '\'';
1525 q = memcpy(q, s, len) + len;
1526 *q++ = '\'';
1527 s += len;
1528
1529 STADJUST(q - p, p);
1530
1531 len = strspn(s, "'");
1532 if (!len)
1533 break;
1534
1535 q = p = makestrspace(len + 3, p);
1536
1537 *q++ = '"';
1538 q = memcpy(q, s, len) + len;
1539 *q++ = '"';
1540 s += len;
1541
1542 STADJUST(q - p, p);
1543 } while (*s);
1544
1545 USTPUTC(0, p);
1546
1547 return stackblock();
1548}
1549
1550
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001551/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001552
1553static char **argptr; /* argument list for builtin commands */
1554static char *optionarg; /* set by nextopt (like getopt) */
1555static char *optptr; /* used by nextopt */
1556
1557/*
1558 * XXX - should get rid of. have all builtins use getopt(3). the
1559 * library getopt must have the BSD extension static variable "optreset"
1560 * otherwise it can't be used within the shell safely.
1561 *
1562 * Standard option processing (a la getopt) for builtin routines. The
1563 * only argument that is passed to nextopt is the option string; the
1564 * other arguments are unnecessary. It return the character, or '\0' on
1565 * end of input.
1566 */
1567static int
1568nextopt(const char *optstring)
1569{
1570 char *p;
1571 const char *q;
1572 char c;
1573
1574 p = optptr;
1575 if (p == NULL || *p == '\0') {
1576 p = *argptr;
1577 if (p == NULL || *p != '-' || *++p == '\0')
1578 return '\0';
1579 argptr++;
1580 if (LONE_DASH(p)) /* check for "--" */
1581 return '\0';
1582 }
1583 c = *p++;
1584 for (q = optstring; *q != c; ) {
1585 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001586 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001587 if (*++q == ':')
1588 q++;
1589 }
1590 if (*++q == ':') {
1591 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001592 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001593 optionarg = p;
1594 p = NULL;
1595 }
1596 optptr = p;
1597 return c;
1598}
1599
1600
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001601/* ============ Math support definitions */
1602
1603#if ENABLE_ASH_MATH_SUPPORT_64
1604typedef int64_t arith_t;
1605#define arith_t_type long long
1606#else
1607typedef long arith_t;
1608#define arith_t_type long
1609#endif
1610
1611#if ENABLE_ASH_MATH_SUPPORT
1612static arith_t dash_arith(const char *);
1613static arith_t arith(const char *expr, int *perrcode);
1614#endif
1615
1616#if ENABLE_ASH_RANDOM_SUPPORT
1617static unsigned long rseed;
1618#ifndef DYNAMIC_VAR
1619#define DYNAMIC_VAR
1620#endif
1621#endif
1622
1623
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001624/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625
Denis Vlasenko01631112007-12-16 17:20:38 +00001626/*
1627 * The parsefile structure pointed to by the global variable parsefile
1628 * contains information about the current file being read.
1629 */
1630struct redirtab {
1631 struct redirtab *next;
1632 int renamed[10];
1633 int nullredirs;
1634};
1635
1636struct shparam {
1637 int nparam; /* # of positional parameters (without $0) */
1638#if ENABLE_ASH_GETOPTS
1639 int optind; /* next parameter to be processed by getopts */
1640 int optoff; /* used by getopts */
1641#endif
1642 unsigned char malloced; /* if parameter list dynamically allocated */
1643 char **p; /* parameter list */
1644};
1645
1646/*
1647 * Free the list of positional parameters.
1648 */
1649static void
1650freeparam(volatile struct shparam *param)
1651{
1652 char **ap;
1653
1654 if (param->malloced) {
1655 for (ap = param->p; *ap; ap++)
1656 free(*ap);
1657 free(param->p);
1658 }
1659}
1660
1661#if ENABLE_ASH_GETOPTS
1662static void getoptsreset(const char *value);
1663#endif
1664
1665struct var {
1666 struct var *next; /* next entry in hash list */
1667 int flags; /* flags are defined above */
1668 const char *text; /* name=value */
1669 void (*func)(const char *); /* function to be called when */
1670 /* the variable gets set/unset */
1671};
1672
1673struct localvar {
1674 struct localvar *next; /* next local variable in list */
1675 struct var *vp; /* the variable that was made local */
1676 int flags; /* saved flags */
1677 const char *text; /* saved text */
1678};
1679
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001680/* flags */
1681#define VEXPORT 0x01 /* variable is exported */
1682#define VREADONLY 0x02 /* variable cannot be modified */
1683#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1684#define VTEXTFIXED 0x08 /* text is statically allocated */
1685#define VSTACK 0x10 /* text is allocated on the stack */
1686#define VUNSET 0x20 /* the variable is not set */
1687#define VNOFUNC 0x40 /* don't call the callback function */
1688#define VNOSET 0x80 /* do not set variable - just readonly test */
1689#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1690#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001691# define VDYNAMIC 0x200 /* dynamic variable */
1692#else
1693# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001694#endif
1695
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001696#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001697static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001698#define defifs (defifsvar + 4)
1699#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001700static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#endif
1702
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001703
Denis Vlasenko01631112007-12-16 17:20:38 +00001704/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001705#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001706static void
1707change_lc_all(const char *value)
1708{
1709 if (value && *value != '\0')
1710 setlocale(LC_ALL, value);
1711}
1712static void
1713change_lc_ctype(const char *value)
1714{
1715 if (value && *value != '\0')
1716 setlocale(LC_CTYPE, value);
1717}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001718#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001719#if ENABLE_ASH_MAIL
1720static void chkmail(void);
1721static void changemail(const char *);
1722#endif
1723static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001724#if ENABLE_ASH_RANDOM_SUPPORT
1725static void change_random(const char *);
1726#endif
1727
Denis Vlasenko01631112007-12-16 17:20:38 +00001728static const struct {
1729 int flags;
1730 const char *text;
1731 void (*func)(const char *);
1732} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001734 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001736 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001739 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1740 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001741#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001742 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1743 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1744 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1745 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001747 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
1749#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#endif
1752#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1754 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
1756#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001757 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#endif
1759};
1760
Denis Vlasenko01631112007-12-16 17:20:38 +00001761
1762struct globals_var {
1763 struct shparam shellparam; /* $@ current positional parameters */
1764 struct redirtab *redirlist;
1765 int g_nullredirs;
1766 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1767 struct var *vartab[VTABSIZE];
1768 struct var varinit[ARRAY_SIZE(varinit_data)];
1769};
1770/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1771static struct globals_var *const ptr_to_globals_var __attribute__ ((section (".data")));
1772#define G_var (*ptr_to_globals_var)
1773#define shellparam (G_var.shellparam )
1774#define redirlist (G_var.redirlist )
1775#define g_nullredirs (G_var.g_nullredirs )
1776#define preverrout_fd (G_var.preverrout_fd)
1777#define vartab (G_var.vartab )
1778#define varinit (G_var.varinit )
1779#define INIT_G_var() do { \
1780 int i; \
1781 (*(struct globals_var**)&ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1782 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1783 varinit[i].flags = varinit_data[i].flags; \
1784 varinit[i].text = varinit_data[i].text; \
1785 varinit[i].func = varinit_data[i].func; \
1786 } \
1787} while (0)
1788
1789#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001791#define vmail (&vifs)[1]
1792#define vmpath (&vmail)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001794#define vmpath vifs
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001795#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001796#define vpath (&vmpath)[1]
1797#define vps1 (&vpath)[1]
1798#define vps2 (&vps1)[1]
1799#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800#define voptind (&vps4)[1]
1801#if ENABLE_ASH_GETOPTS
1802#define vrandom (&voptind)[1]
1803#else
1804#define vrandom (&vps4)[1]
1805#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001806
1807/*
1808 * The following macros access the values of the above variables.
1809 * They have to skip over the name. They return the null string
1810 * for unset variables.
1811 */
1812#define ifsval() (vifs.text + 4)
1813#define ifsset() ((vifs.flags & VUNSET) == 0)
1814#define mailval() (vmail.text + 5)
1815#define mpathval() (vmpath.text + 9)
1816#define pathval() (vpath.text + 5)
1817#define ps1val() (vps1.text + 4)
1818#define ps2val() (vps2.text + 4)
1819#define ps4val() (vps4.text + 4)
1820#define optindval() (voptind.text + 7)
1821
1822#define mpathset() ((vmpath.flags & VUNSET) == 0)
1823
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001824
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001825#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1826#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1827
Denis Vlasenko01631112007-12-16 17:20:38 +00001828#if ENABLE_ASH_GETOPTS
1829static void
1830getoptsreset(const char *value)
1831{
1832 shellparam.optind = number(value);
1833 shellparam.optoff = -1;
1834}
1835#endif
1836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837/*
1838 * Return of a legal variable name (a letter or underscore followed by zero or
1839 * more letters, underscores, and digits).
1840 */
1841static char *
1842endofname(const char *name)
1843{
1844 char *p;
1845
1846 p = (char *) name;
1847 if (!is_name(*p))
1848 return p;
1849 while (*++p) {
1850 if (!is_in_name(*p))
1851 break;
1852 }
1853 return p;
1854}
1855
1856/*
1857 * Compares two strings up to the first = or '\0'. The first
1858 * string must be terminated by '='; the second may be terminated by
1859 * either '=' or '\0'.
1860 */
1861static int
1862varcmp(const char *p, const char *q)
1863{
1864 int c, d;
1865
1866 while ((c = *p) == (d = *q)) {
1867 if (!c || c == '=')
1868 goto out;
1869 p++;
1870 q++;
1871 }
1872 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001873 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001874 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001875 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001876 out:
1877 return c - d;
1878}
1879
1880static int
1881varequal(const char *a, const char *b)
1882{
1883 return !varcmp(a, b);
1884}
1885
1886/*
1887 * Find the appropriate entry in the hash table from the name.
1888 */
1889static struct var **
1890hashvar(const char *p)
1891{
1892 unsigned hashval;
1893
1894 hashval = ((unsigned char) *p) << 4;
1895 while (*p && *p != '=')
1896 hashval += (unsigned char) *p++;
1897 return &vartab[hashval % VTABSIZE];
1898}
1899
1900static int
1901vpcmp(const void *a, const void *b)
1902{
1903 return varcmp(*(const char **)a, *(const char **)b);
1904}
1905
1906/*
1907 * This routine initializes the builtin variables.
1908 */
1909static void
1910initvar(void)
1911{
1912 struct var *vp;
1913 struct var *end;
1914 struct var **vpp;
1915
1916 /*
1917 * PS1 depends on uid
1918 */
1919#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1920 vps1.text = "PS1=\\w \\$ ";
1921#else
1922 if (!geteuid())
1923 vps1.text = "PS1=# ";
1924#endif
1925 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001926 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001927 do {
1928 vpp = hashvar(vp->text);
1929 vp->next = *vpp;
1930 *vpp = vp;
1931 } while (++vp < end);
1932}
1933
1934static struct var **
1935findvar(struct var **vpp, const char *name)
1936{
1937 for (; *vpp; vpp = &(*vpp)->next) {
1938 if (varequal((*vpp)->text, name)) {
1939 break;
1940 }
1941 }
1942 return vpp;
1943}
1944
1945/*
1946 * Find the value of a variable. Returns NULL if not set.
1947 */
1948static char *
1949lookupvar(const char *name)
1950{
1951 struct var *v;
1952
1953 v = *findvar(hashvar(name), name);
1954 if (v) {
1955#ifdef DYNAMIC_VAR
1956 /*
1957 * Dynamic variables are implemented roughly the same way they are
1958 * in bash. Namely, they're "special" so long as they aren't unset.
1959 * As soon as they're unset, they're no longer dynamic, and dynamic
1960 * lookup will no longer happen at that point. -- PFM.
1961 */
1962 if ((v->flags & VDYNAMIC))
1963 (*v->func)(NULL);
1964#endif
1965 if (!(v->flags & VUNSET))
1966 return strchrnul(v->text, '=') + 1;
1967 }
1968 return NULL;
1969}
1970
1971/*
1972 * Search the environment of a builtin command.
1973 */
1974static char *
1975bltinlookup(const char *name)
1976{
1977 struct strlist *sp;
1978
1979 for (sp = cmdenviron; sp; sp = sp->next) {
1980 if (varequal(sp->text, name))
1981 return strchrnul(sp->text, '=') + 1;
1982 }
1983 return lookupvar(name);
1984}
1985
1986/*
1987 * Same as setvar except that the variable and value are passed in
1988 * the first argument as name=value. Since the first argument will
1989 * be actually stored in the table, it should not be a string that
1990 * will go away.
1991 * Called with interrupts off.
1992 */
1993static void
1994setvareq(char *s, int flags)
1995{
1996 struct var *vp, **vpp;
1997
1998 vpp = hashvar(s);
1999 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2000 vp = *findvar(vpp, s);
2001 if (vp) {
2002 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2003 const char *n;
2004
2005 if (flags & VNOSAVE)
2006 free(s);
2007 n = vp->text;
2008 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2009 }
2010
2011 if (flags & VNOSET)
2012 return;
2013
2014 if (vp->func && (flags & VNOFUNC) == 0)
2015 (*vp->func)(strchrnul(s, '=') + 1);
2016
2017 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2018 free((char*)vp->text);
2019
2020 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2021 } else {
2022 if (flags & VNOSET)
2023 return;
2024 /* not found */
2025 vp = ckmalloc(sizeof(*vp));
2026 vp->next = *vpp;
2027 vp->func = NULL;
2028 *vpp = vp;
2029 }
2030 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2031 s = ckstrdup(s);
2032 vp->text = s;
2033 vp->flags = flags;
2034}
2035
2036/*
2037 * Set the value of a variable. The flags argument is ored with the
2038 * flags of the variable. If val is NULL, the variable is unset.
2039 */
2040static void
2041setvar(const char *name, const char *val, int flags)
2042{
2043 char *p, *q;
2044 size_t namelen;
2045 char *nameeq;
2046 size_t vallen;
2047
2048 q = endofname(name);
2049 p = strchrnul(q, '=');
2050 namelen = p - name;
2051 if (!namelen || p != q)
2052 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2053 vallen = 0;
2054 if (val == NULL) {
2055 flags |= VUNSET;
2056 } else {
2057 vallen = strlen(val);
2058 }
2059 INT_OFF;
2060 nameeq = ckmalloc(namelen + vallen + 2);
2061 p = memcpy(nameeq, name, namelen) + namelen;
2062 if (val) {
2063 *p++ = '=';
2064 p = memcpy(p, val, vallen) + vallen;
2065 }
2066 *p = '\0';
2067 setvareq(nameeq, flags | VNOSAVE);
2068 INT_ON;
2069}
2070
2071#if ENABLE_ASH_GETOPTS
2072/*
2073 * Safe version of setvar, returns 1 on success 0 on failure.
2074 */
2075static int
2076setvarsafe(const char *name, const char *val, int flags)
2077{
2078 int err;
2079 volatile int saveint;
2080 struct jmploc *volatile savehandler = exception_handler;
2081 struct jmploc jmploc;
2082
2083 SAVE_INT(saveint);
2084 if (setjmp(jmploc.loc))
2085 err = 1;
2086 else {
2087 exception_handler = &jmploc;
2088 setvar(name, val, flags);
2089 err = 0;
2090 }
2091 exception_handler = savehandler;
2092 RESTORE_INT(saveint);
2093 return err;
2094}
2095#endif
2096
2097/*
2098 * Unset the specified variable.
2099 */
2100static int
2101unsetvar(const char *s)
2102{
2103 struct var **vpp;
2104 struct var *vp;
2105 int retval;
2106
2107 vpp = findvar(hashvar(s), s);
2108 vp = *vpp;
2109 retval = 2;
2110 if (vp) {
2111 int flags = vp->flags;
2112
2113 retval = 1;
2114 if (flags & VREADONLY)
2115 goto out;
2116#ifdef DYNAMIC_VAR
2117 vp->flags &= ~VDYNAMIC;
2118#endif
2119 if (flags & VUNSET)
2120 goto ok;
2121 if ((flags & VSTRFIXED) == 0) {
2122 INT_OFF;
2123 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2124 free((char*)vp->text);
2125 *vpp = vp->next;
2126 free(vp);
2127 INT_ON;
2128 } else {
2129 setvar(s, 0, 0);
2130 vp->flags &= ~VEXPORT;
2131 }
2132 ok:
2133 retval = 0;
2134 }
2135 out:
2136 return retval;
2137}
2138
2139/*
2140 * Process a linked list of variable assignments.
2141 */
2142static void
2143listsetvar(struct strlist *list_set_var, int flags)
2144{
2145 struct strlist *lp = list_set_var;
2146
2147 if (!lp)
2148 return;
2149 INT_OFF;
2150 do {
2151 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002152 lp = lp->next;
2153 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002154 INT_ON;
2155}
2156
2157/*
2158 * Generate a list of variables satisfying the given conditions.
2159 */
2160static char **
2161listvars(int on, int off, char ***end)
2162{
2163 struct var **vpp;
2164 struct var *vp;
2165 char **ep;
2166 int mask;
2167
2168 STARTSTACKSTR(ep);
2169 vpp = vartab;
2170 mask = on | off;
2171 do {
2172 for (vp = *vpp; vp; vp = vp->next) {
2173 if ((vp->flags & mask) == on) {
2174 if (ep == stackstrend())
2175 ep = growstackstr();
2176 *ep++ = (char *) vp->text;
2177 }
2178 }
2179 } while (++vpp < vartab + VTABSIZE);
2180 if (ep == stackstrend())
2181 ep = growstackstr();
2182 if (end)
2183 *end = ep;
2184 *ep++ = NULL;
2185 return grabstackstr(ep);
2186}
2187
2188
2189/* ============ Path search helper
2190 *
2191 * The variable path (passed by reference) should be set to the start
2192 * of the path before the first call; padvance will update
2193 * this value as it proceeds. Successive calls to padvance will return
2194 * the possible path expansions in sequence. If an option (indicated by
2195 * a percent sign) appears in the path entry then the global variable
2196 * pathopt will be set to point to it; otherwise pathopt will be set to
2197 * NULL.
2198 */
2199static const char *pathopt; /* set by padvance */
2200
2201static char *
2202padvance(const char **path, const char *name)
2203{
2204 const char *p;
2205 char *q;
2206 const char *start;
2207 size_t len;
2208
2209 if (*path == NULL)
2210 return NULL;
2211 start = *path;
2212 for (p = start; *p && *p != ':' && *p != '%'; p++);
2213 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2214 while (stackblocksize() < len)
2215 growstackblock();
2216 q = stackblock();
2217 if (p != start) {
2218 memcpy(q, start, p - start);
2219 q += p - start;
2220 *q++ = '/';
2221 }
2222 strcpy(q, name);
2223 pathopt = NULL;
2224 if (*p == '%') {
2225 pathopt = ++p;
2226 while (*p && *p != ':') p++;
2227 }
2228 if (*p == ':')
2229 *path = p + 1;
2230 else
2231 *path = NULL;
2232 return stalloc(len);
2233}
2234
2235
2236/* ============ Prompt */
2237
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002238static smallint doprompt; /* if set, prompt the user */
2239static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002240
2241#if ENABLE_FEATURE_EDITING
2242static line_input_t *line_input_state;
2243static const char *cmdedit_prompt;
2244static void
2245putprompt(const char *s)
2246{
2247 if (ENABLE_ASH_EXPAND_PRMT) {
2248 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002249 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002250 return;
2251 }
2252 cmdedit_prompt = s;
2253}
2254#else
2255static void
2256putprompt(const char *s)
2257{
2258 out2str(s);
2259}
2260#endif
2261
2262#if ENABLE_ASH_EXPAND_PRMT
2263/* expandstr() needs parsing machinery, so it is far away ahead... */
2264static const char *expandstr(const char *ps);
2265#else
2266#define expandstr(s) s
2267#endif
2268
2269static void
2270setprompt(int whichprompt)
2271{
2272 const char *prompt;
2273#if ENABLE_ASH_EXPAND_PRMT
2274 struct stackmark smark;
2275#endif
2276
2277 needprompt = 0;
2278
2279 switch (whichprompt) {
2280 case 1:
2281 prompt = ps1val();
2282 break;
2283 case 2:
2284 prompt = ps2val();
2285 break;
2286 default: /* 0 */
2287 prompt = nullstr;
2288 }
2289#if ENABLE_ASH_EXPAND_PRMT
2290 setstackmark(&smark);
2291 stalloc(stackblocksize());
2292#endif
2293 putprompt(expandstr(prompt));
2294#if ENABLE_ASH_EXPAND_PRMT
2295 popstackmark(&smark);
2296#endif
2297}
2298
2299
2300/* ============ The cd and pwd commands */
2301
2302#define CD_PHYSICAL 1
2303#define CD_PRINT 2
2304
2305static int docd(const char *, int);
2306
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002307static int
2308cdopt(void)
2309{
2310 int flags = 0;
2311 int i, j;
2312
2313 j = 'L';
2314 while ((i = nextopt("LP"))) {
2315 if (i != j) {
2316 flags ^= CD_PHYSICAL;
2317 j = i;
2318 }
2319 }
2320
2321 return flags;
2322}
2323
2324/*
2325 * Update curdir (the name of the current directory) in response to a
2326 * cd command.
2327 */
2328static const char *
2329updatepwd(const char *dir)
2330{
2331 char *new;
2332 char *p;
2333 char *cdcomppath;
2334 const char *lim;
2335
2336 cdcomppath = ststrdup(dir);
2337 STARTSTACKSTR(new);
2338 if (*dir != '/') {
2339 if (curdir == nullstr)
2340 return 0;
2341 new = stack_putstr(curdir, new);
2342 }
2343 new = makestrspace(strlen(dir) + 2, new);
2344 lim = stackblock() + 1;
2345 if (*dir != '/') {
2346 if (new[-1] != '/')
2347 USTPUTC('/', new);
2348 if (new > lim && *lim == '/')
2349 lim++;
2350 } else {
2351 USTPUTC('/', new);
2352 cdcomppath++;
2353 if (dir[1] == '/' && dir[2] != '/') {
2354 USTPUTC('/', new);
2355 cdcomppath++;
2356 lim++;
2357 }
2358 }
2359 p = strtok(cdcomppath, "/");
2360 while (p) {
2361 switch (*p) {
2362 case '.':
2363 if (p[1] == '.' && p[2] == '\0') {
2364 while (new > lim) {
2365 STUNPUTC(new);
2366 if (new[-1] == '/')
2367 break;
2368 }
2369 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002370 }
2371 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002372 break;
2373 /* fall through */
2374 default:
2375 new = stack_putstr(p, new);
2376 USTPUTC('/', new);
2377 }
2378 p = strtok(0, "/");
2379 }
2380 if (new > lim)
2381 STUNPUTC(new);
2382 *new = 0;
2383 return stackblock();
2384}
2385
2386/*
2387 * Find out what the current directory is. If we already know the current
2388 * directory, this routine returns immediately.
2389 */
2390static char *
2391getpwd(void)
2392{
Denis Vlasenko01631112007-12-16 17:20:38 +00002393 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002394 return dir ? dir : nullstr;
2395}
2396
2397static void
2398setpwd(const char *val, int setold)
2399{
2400 char *oldcur, *dir;
2401
2402 oldcur = dir = curdir;
2403
2404 if (setold) {
2405 setvar("OLDPWD", oldcur, VEXPORT);
2406 }
2407 INT_OFF;
2408 if (physdir != nullstr) {
2409 if (physdir != oldcur)
2410 free(physdir);
2411 physdir = nullstr;
2412 }
2413 if (oldcur == val || !val) {
2414 char *s = getpwd();
2415 physdir = s;
2416 if (!val)
2417 dir = s;
2418 } else
2419 dir = ckstrdup(val);
2420 if (oldcur != dir && oldcur != nullstr) {
2421 free(oldcur);
2422 }
2423 curdir = dir;
2424 INT_ON;
2425 setvar("PWD", dir, VEXPORT);
2426}
2427
2428static void hashcd(void);
2429
2430/*
2431 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2432 * know that the current directory has changed.
2433 */
2434static int
2435docd(const char *dest, int flags)
2436{
2437 const char *dir = 0;
2438 int err;
2439
2440 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2441
2442 INT_OFF;
2443 if (!(flags & CD_PHYSICAL)) {
2444 dir = updatepwd(dest);
2445 if (dir)
2446 dest = dir;
2447 }
2448 err = chdir(dest);
2449 if (err)
2450 goto out;
2451 setpwd(dir, 1);
2452 hashcd();
2453 out:
2454 INT_ON;
2455 return err;
2456}
2457
2458static int
2459cdcmd(int argc, char **argv)
2460{
2461 const char *dest;
2462 const char *path;
2463 const char *p;
2464 char c;
2465 struct stat statb;
2466 int flags;
2467
2468 flags = cdopt();
2469 dest = *argptr;
2470 if (!dest)
2471 dest = bltinlookup(homestr);
2472 else if (LONE_DASH(dest)) {
2473 dest = bltinlookup("OLDPWD");
2474 flags |= CD_PRINT;
2475 }
2476 if (!dest)
2477 dest = nullstr;
2478 if (*dest == '/')
2479 goto step7;
2480 if (*dest == '.') {
2481 c = dest[1];
2482 dotdot:
2483 switch (c) {
2484 case '\0':
2485 case '/':
2486 goto step6;
2487 case '.':
2488 c = dest[2];
2489 if (c != '.')
2490 goto dotdot;
2491 }
2492 }
2493 if (!*dest)
2494 dest = ".";
2495 path = bltinlookup("CDPATH");
2496 if (!path) {
2497 step6:
2498 step7:
2499 p = dest;
2500 goto docd;
2501 }
2502 do {
2503 c = *path;
2504 p = padvance(&path, dest);
2505 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2506 if (c && c != ':')
2507 flags |= CD_PRINT;
2508 docd:
2509 if (!docd(p, flags))
2510 goto out;
2511 break;
2512 }
2513 } while (path);
2514 ash_msg_and_raise_error("can't cd to %s", dest);
2515 /* NOTREACHED */
2516 out:
2517 if (flags & CD_PRINT)
2518 out1fmt(snlfmt, curdir);
2519 return 0;
2520}
2521
2522static int
2523pwdcmd(int argc, char **argv)
2524{
2525 int flags;
2526 const char *dir = curdir;
2527
2528 flags = cdopt();
2529 if (flags) {
2530 if (physdir == nullstr)
2531 setpwd(dir, 0);
2532 dir = physdir;
2533 }
2534 out1fmt(snlfmt, dir);
2535 return 0;
2536}
2537
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002538
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002539/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002540
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002541#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002542#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002543
Eric Andersenc470f442003-07-28 09:56:35 +00002544/* Syntax classes */
2545#define CWORD 0 /* character is nothing special */
2546#define CNL 1 /* newline character */
2547#define CBACK 2 /* a backslash character */
2548#define CSQUOTE 3 /* single quote */
2549#define CDQUOTE 4 /* double quote */
2550#define CENDQUOTE 5 /* a terminating quote */
2551#define CBQUOTE 6 /* backwards single quote */
2552#define CVAR 7 /* a dollar sign */
2553#define CENDVAR 8 /* a '}' character */
2554#define CLP 9 /* a left paren in arithmetic */
2555#define CRP 10 /* a right paren in arithmetic */
2556#define CENDFILE 11 /* end of file */
2557#define CCTL 12 /* like CWORD, except it must be escaped */
2558#define CSPCL 13 /* these terminate a word */
2559#define CIGN 14 /* character should be ignored */
2560
Denis Vlasenko131ae172007-02-18 13:00:19 +00002561#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002562#define SYNBASE 130
2563#define PEOF -130
2564#define PEOA -129
2565#define PEOA_OR_PEOF PEOA
2566#else
2567#define SYNBASE 129
2568#define PEOF -129
2569#define PEOA_OR_PEOF PEOF
2570#endif
2571
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002572/* number syntax index */
2573#define BASESYNTAX 0 /* not in quotes */
2574#define DQSYNTAX 1 /* in double quotes */
2575#define SQSYNTAX 2 /* in single quotes */
2576#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002577#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002578
Denis Vlasenko131ae172007-02-18 13:00:19 +00002579#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002580#define USE_SIT_FUNCTION
2581#endif
2582
Denis Vlasenko131ae172007-02-18 13:00:19 +00002583#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002584static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002585#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002586 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002587#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002588 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2589 { CNL, CNL, CNL, CNL }, /* 2, \n */
2590 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2591 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2592 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2593 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2594 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2595 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2596 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2597 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2598 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002599#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002600 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2601 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2602 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002603#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002604};
Eric Andersenc470f442003-07-28 09:56:35 +00002605#else
2606static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002607#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002608 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002609#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002610 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2611 { CNL, CNL, CNL }, /* 2, \n */
2612 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2613 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2614 { CVAR, CVAR, CWORD }, /* 5, $ */
2615 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2616 { CSPCL, CWORD, CWORD }, /* 7, ( */
2617 { CSPCL, CWORD, CWORD }, /* 8, ) */
2618 { CBACK, CBACK, CCTL }, /* 9, \ */
2619 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2620 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002621#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002622 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2623 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2624 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002625#endif
2626};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002627#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002628
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002629#ifdef USE_SIT_FUNCTION
2630
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002631static int
2632SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002633{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002634 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002635#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002636 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002637 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2638 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2639 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2640 11, 3 /* "}~" */
2641 };
2642#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002643 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002644 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2645 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2646 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2647 10, 2 /* "}~" */
2648 };
2649#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002650 const char *s;
2651 int indx;
2652
Eric Andersenc470f442003-07-28 09:56:35 +00002653 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002654 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002655#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002656 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002657 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002658 else
2659#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002660#define U_C(c) ((unsigned char)(c))
2661
2662 if ((unsigned char)c >= (unsigned char)(CTLESC)
2663 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2664 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002665 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002666 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002667 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002668 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002670 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002671 }
2672 return S_I_T[indx][syntax];
2673}
2674
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002675#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002676
Denis Vlasenko131ae172007-02-18 13:00:19 +00002677#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002678#define CSPCL_CIGN_CIGN_CIGN 0
2679#define CSPCL_CWORD_CWORD_CWORD 1
2680#define CNL_CNL_CNL_CNL 2
2681#define CWORD_CCTL_CCTL_CWORD 3
2682#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2683#define CVAR_CVAR_CWORD_CVAR 5
2684#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2685#define CSPCL_CWORD_CWORD_CLP 7
2686#define CSPCL_CWORD_CWORD_CRP 8
2687#define CBACK_CBACK_CCTL_CBACK 9
2688#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2689#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2690#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2691#define CWORD_CWORD_CWORD_CWORD 13
2692#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002693#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694#define CSPCL_CWORD_CWORD_CWORD 0
2695#define CNL_CNL_CNL_CNL 1
2696#define CWORD_CCTL_CCTL_CWORD 2
2697#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2698#define CVAR_CVAR_CWORD_CVAR 4
2699#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2700#define CSPCL_CWORD_CWORD_CLP 6
2701#define CSPCL_CWORD_CWORD_CRP 7
2702#define CBACK_CBACK_CCTL_CBACK 8
2703#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2704#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2705#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2706#define CWORD_CWORD_CWORD_CWORD 12
2707#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002708#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002709
2710static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002711 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002712 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002713#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002714 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2715#endif
2716 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2718 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2719 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2720 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2721 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2722 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2723 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2724 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002725 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2854 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2855 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002878 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002879 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2881 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002883 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002884 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2885 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2886 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2887 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2889 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2891 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2904 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2905 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2907 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2937 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2941 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2969 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2970 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2971 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002972};
2973
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002974#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2975
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002976#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002977
Eric Andersen2870d962001-07-02 17:27:21 +00002978
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002979/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002980
Denis Vlasenko131ae172007-02-18 13:00:19 +00002981#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002982
2983#define ALIASINUSE 1
2984#define ALIASDEAD 2
2985
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002986struct alias {
2987 struct alias *next;
2988 char *name;
2989 char *val;
2990 int flag;
2991};
2992
Denis Vlasenko01631112007-12-16 17:20:38 +00002993
2994static struct alias **atab; // [ATABSIZE];
2995#define INIT_G_alias() do { \
2996 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
2997} while (0)
2998
Eric Andersen2870d962001-07-02 17:27:21 +00002999
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003000static struct alias **
3001__lookupalias(const char *name) {
3002 unsigned int hashval;
3003 struct alias **app;
3004 const char *p;
3005 unsigned int ch;
3006
3007 p = name;
3008
3009 ch = (unsigned char)*p;
3010 hashval = ch << 4;
3011 while (ch) {
3012 hashval += ch;
3013 ch = (unsigned char)*++p;
3014 }
3015 app = &atab[hashval % ATABSIZE];
3016
3017 for (; *app; app = &(*app)->next) {
3018 if (strcmp(name, (*app)->name) == 0) {
3019 break;
3020 }
3021 }
3022
3023 return app;
3024}
3025
3026static struct alias *
3027lookupalias(const char *name, int check)
3028{
3029 struct alias *ap = *__lookupalias(name);
3030
3031 if (check && ap && (ap->flag & ALIASINUSE))
3032 return NULL;
3033 return ap;
3034}
3035
3036static struct alias *
3037freealias(struct alias *ap)
3038{
3039 struct alias *next;
3040
3041 if (ap->flag & ALIASINUSE) {
3042 ap->flag |= ALIASDEAD;
3043 return ap;
3044 }
3045
3046 next = ap->next;
3047 free(ap->name);
3048 free(ap->val);
3049 free(ap);
3050 return next;
3051}
Eric Andersencb57d552001-06-28 07:25:16 +00003052
Eric Andersenc470f442003-07-28 09:56:35 +00003053static void
3054setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003055{
3056 struct alias *ap, **app;
3057
3058 app = __lookupalias(name);
3059 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003060 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003061 if (ap) {
3062 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003063 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003064 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003065 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003066 ap->flag &= ~ALIASDEAD;
3067 } else {
3068 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003069 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003070 ap->name = ckstrdup(name);
3071 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003072 ap->flag = 0;
3073 ap->next = 0;
3074 *app = ap;
3075 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003076 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003077}
3078
Eric Andersenc470f442003-07-28 09:56:35 +00003079static int
3080unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003081{
Eric Andersencb57d552001-06-28 07:25:16 +00003082 struct alias **app;
3083
3084 app = __lookupalias(name);
3085
3086 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003087 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003088 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003089 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003090 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003091 }
3092
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003093 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003094}
3095
Eric Andersenc470f442003-07-28 09:56:35 +00003096static void
3097rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003098{
Eric Andersencb57d552001-06-28 07:25:16 +00003099 struct alias *ap, **app;
3100 int i;
3101
Denis Vlasenkob012b102007-02-19 22:43:01 +00003102 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003103 for (i = 0; i < ATABSIZE; i++) {
3104 app = &atab[i];
3105 for (ap = *app; ap; ap = *app) {
3106 *app = freealias(*app);
3107 if (ap == *app) {
3108 app = &ap->next;
3109 }
3110 }
3111 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003112 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003113}
3114
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003115static void
3116printalias(const struct alias *ap)
3117{
3118 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3119}
3120
Eric Andersencb57d552001-06-28 07:25:16 +00003121/*
3122 * TODO - sort output
3123 */
Eric Andersenc470f442003-07-28 09:56:35 +00003124static int
3125aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003126{
3127 char *n, *v;
3128 int ret = 0;
3129 struct alias *ap;
3130
3131 if (argc == 1) {
3132 int i;
3133
3134 for (i = 0; i < ATABSIZE; i++)
3135 for (ap = atab[i]; ap; ap = ap->next) {
3136 printalias(ap);
3137 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003138 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003139 }
3140 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003141 v = strchr(n+1, '=');
3142 if (v == NULL) { /* n+1: funny ksh stuff */
3143 ap = *__lookupalias(n);
3144 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003145 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003146 ret = 1;
3147 } else
3148 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003149 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003150 *v++ = '\0';
3151 setalias(n, v);
3152 }
3153 }
3154
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003155 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003156}
3157
Eric Andersenc470f442003-07-28 09:56:35 +00003158static int
3159unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003160{
3161 int i;
3162
3163 while ((i = nextopt("a")) != '\0') {
3164 if (i == 'a') {
3165 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003166 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003167 }
3168 }
3169 for (i = 0; *argptr; argptr++) {
3170 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003171 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003172 i = 1;
3173 }
3174 }
3175
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003176 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003177}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003178
Denis Vlasenko131ae172007-02-18 13:00:19 +00003179#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003180
Eric Andersenc470f442003-07-28 09:56:35 +00003181
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003182/* ============ jobs.c */
3183
3184/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3185#define FORK_FG 0
3186#define FORK_BG 1
3187#define FORK_NOJOB 2
3188
3189/* mode flags for showjob(s) */
3190#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3191#define SHOW_PID 0x04 /* include process pid */
3192#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3193
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003194/*
3195 * A job structure contains information about a job. A job is either a
3196 * single process or a set of processes contained in a pipeline. In the
3197 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3198 * array of pids.
3199 */
3200
3201struct procstat {
3202 pid_t pid; /* process id */
3203 int status; /* last process status from wait() */
3204 char *cmd; /* text of command being run */
3205};
3206
3207struct job {
3208 struct procstat ps0; /* status of process */
3209 struct procstat *ps; /* status or processes when more than one */
3210#if JOBS
3211 int stopstatus; /* status of a stopped job */
3212#endif
3213 uint32_t
3214 nprocs: 16, /* number of processes */
3215 state: 8,
3216#define JOBRUNNING 0 /* at least one proc running */
3217#define JOBSTOPPED 1 /* all procs are stopped */
3218#define JOBDONE 2 /* all procs are completed */
3219#if JOBS
3220 sigint: 1, /* job was killed by SIGINT */
3221 jobctl: 1, /* job running under job control */
3222#endif
3223 waited: 1, /* true if this entry has been waited for */
3224 used: 1, /* true if this entry is in used */
3225 changed: 1; /* true if status has changed */
3226 struct job *prev_job; /* previous job */
3227};
3228
3229static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003230static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003231
3232static struct job *makejob(union node *, int);
3233static int forkshell(struct job *, union node *, int);
3234static int waitforjob(struct job *);
3235
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003236#if !JOBS
3237enum { jobctl = 0 };
3238#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003239#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003240static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003241static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003242#endif
3243
3244/*
3245 * Set the signal handler for the specified signal. The routine figures
3246 * out what it should be set to.
3247 */
3248static void
3249setsignal(int signo)
3250{
3251 int action;
3252 char *t, tsig;
3253 struct sigaction act;
3254
3255 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003256 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003257 if (t == NULL)
3258 action = S_DFL;
3259 else if (*t != '\0')
3260 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003261 if (rootshell && action == S_DFL) {
3262 switch (signo) {
3263 case SIGINT:
3264 if (iflag || minusc || sflag == 0)
3265 action = S_CATCH;
3266 break;
3267 case SIGQUIT:
3268#if DEBUG
3269 if (debug)
3270 break;
3271#endif
3272 /* FALLTHROUGH */
3273 case SIGTERM:
3274 if (iflag)
3275 action = S_IGN;
3276 break;
3277#if JOBS
3278 case SIGTSTP:
3279 case SIGTTOU:
3280 if (mflag)
3281 action = S_IGN;
3282 break;
3283#endif
3284 }
3285 }
3286
3287 t = &sigmode[signo - 1];
3288 tsig = *t;
3289 if (tsig == 0) {
3290 /*
3291 * current setting unknown
3292 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003293 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003294 /*
3295 * Pretend it worked; maybe we should give a warning
3296 * here, but other shells don't. We don't alter
3297 * sigmode, so that we retry every time.
3298 */
3299 return;
3300 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003301 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003302 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003303 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003304 if (mflag
3305 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3306 ) {
3307 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003308 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309 }
3310 }
3311 if (tsig == S_HARD_IGN || tsig == action)
3312 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003313 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314 switch (action) {
3315 case S_CATCH:
3316 act.sa_handler = onsig;
3317 break;
3318 case S_IGN:
3319 act.sa_handler = SIG_IGN;
3320 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321 }
3322 *t = action;
3323 act.sa_flags = 0;
3324 sigfillset(&act.sa_mask);
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003325 sigaction(signo, &act, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326}
3327
3328/* mode flags for set_curjob */
3329#define CUR_DELETE 2
3330#define CUR_RUNNING 1
3331#define CUR_STOPPED 0
3332
3333/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003334#define DOWAIT_NONBLOCK WNOHANG
3335#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336
3337#if JOBS
3338/* pgrp of shell on invocation */
3339static int initialpgrp;
3340static int ttyfd = -1;
3341#endif
3342/* array of jobs */
3343static struct job *jobtab;
3344/* size of array */
3345static unsigned njobs;
3346/* current job */
3347static struct job *curjob;
3348/* number of presumed living untracked jobs */
3349static int jobless;
3350
3351static void
3352set_curjob(struct job *jp, unsigned mode)
3353{
3354 struct job *jp1;
3355 struct job **jpp, **curp;
3356
3357 /* first remove from list */
3358 jpp = curp = &curjob;
3359 do {
3360 jp1 = *jpp;
3361 if (jp1 == jp)
3362 break;
3363 jpp = &jp1->prev_job;
3364 } while (1);
3365 *jpp = jp1->prev_job;
3366
3367 /* Then re-insert in correct position */
3368 jpp = curp;
3369 switch (mode) {
3370 default:
3371#if DEBUG
3372 abort();
3373#endif
3374 case CUR_DELETE:
3375 /* job being deleted */
3376 break;
3377 case CUR_RUNNING:
3378 /* newly created job or backgrounded job,
3379 put after all stopped jobs. */
3380 do {
3381 jp1 = *jpp;
3382#if JOBS
3383 if (!jp1 || jp1->state != JOBSTOPPED)
3384#endif
3385 break;
3386 jpp = &jp1->prev_job;
3387 } while (1);
3388 /* FALLTHROUGH */
3389#if JOBS
3390 case CUR_STOPPED:
3391#endif
3392 /* newly stopped job - becomes curjob */
3393 jp->prev_job = *jpp;
3394 *jpp = jp;
3395 break;
3396 }
3397}
3398
3399#if JOBS || DEBUG
3400static int
3401jobno(const struct job *jp)
3402{
3403 return jp - jobtab + 1;
3404}
3405#endif
3406
3407/*
3408 * Convert a job name to a job structure.
3409 */
3410static struct job *
3411getjob(const char *name, int getctl)
3412{
3413 struct job *jp;
3414 struct job *found;
3415 const char *err_msg = "No such job: %s";
3416 unsigned num;
3417 int c;
3418 const char *p;
3419 char *(*match)(const char *, const char *);
3420
3421 jp = curjob;
3422 p = name;
3423 if (!p)
3424 goto currentjob;
3425
3426 if (*p != '%')
3427 goto err;
3428
3429 c = *++p;
3430 if (!c)
3431 goto currentjob;
3432
3433 if (!p[1]) {
3434 if (c == '+' || c == '%') {
3435 currentjob:
3436 err_msg = "No current job";
3437 goto check;
3438 }
3439 if (c == '-') {
3440 if (jp)
3441 jp = jp->prev_job;
3442 err_msg = "No previous job";
3443 check:
3444 if (!jp)
3445 goto err;
3446 goto gotit;
3447 }
3448 }
3449
3450 if (is_number(p)) {
3451 num = atoi(p);
3452 if (num < njobs) {
3453 jp = jobtab + num - 1;
3454 if (jp->used)
3455 goto gotit;
3456 goto err;
3457 }
3458 }
3459
3460 match = prefix;
3461 if (*p == '?') {
3462 match = strstr;
3463 p++;
3464 }
3465
3466 found = 0;
3467 while (1) {
3468 if (!jp)
3469 goto err;
3470 if (match(jp->ps[0].cmd, p)) {
3471 if (found)
3472 goto err;
3473 found = jp;
3474 err_msg = "%s: ambiguous";
3475 }
3476 jp = jp->prev_job;
3477 }
3478
3479 gotit:
3480#if JOBS
3481 err_msg = "job %s not created under job control";
3482 if (getctl && jp->jobctl == 0)
3483 goto err;
3484#endif
3485 return jp;
3486 err:
3487 ash_msg_and_raise_error(err_msg, name);
3488}
3489
3490/*
3491 * Mark a job structure as unused.
3492 */
3493static void
3494freejob(struct job *jp)
3495{
3496 struct procstat *ps;
3497 int i;
3498
3499 INT_OFF;
3500 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3501 if (ps->cmd != nullstr)
3502 free(ps->cmd);
3503 }
3504 if (jp->ps != &jp->ps0)
3505 free(jp->ps);
3506 jp->used = 0;
3507 set_curjob(jp, CUR_DELETE);
3508 INT_ON;
3509}
3510
3511#if JOBS
3512static void
3513xtcsetpgrp(int fd, pid_t pgrp)
3514{
3515 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003516 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517}
3518
3519/*
3520 * Turn job control on and off.
3521 *
3522 * Note: This code assumes that the third arg to ioctl is a character
3523 * pointer, which is true on Berkeley systems but not System V. Since
3524 * System V doesn't have job control yet, this isn't a problem now.
3525 *
3526 * Called with interrupts off.
3527 */
3528static void
3529setjobctl(int on)
3530{
3531 int fd;
3532 int pgrp;
3533
3534 if (on == jobctl || rootshell == 0)
3535 return;
3536 if (on) {
3537 int ofd;
3538 ofd = fd = open(_PATH_TTY, O_RDWR);
3539 if (fd < 0) {
3540 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3541 * That sometimes helps to acquire controlling tty.
3542 * Obviously, a workaround for bugs when someone
3543 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003544 fd = 2;
3545 while (!isatty(fd))
3546 if (--fd < 0)
3547 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548 }
3549 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003550 if (ofd >= 0)
3551 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003552 if (fd < 0)
3553 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003554 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003555 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003556 do { /* while we are in the background */
3557 pgrp = tcgetpgrp(fd);
3558 if (pgrp < 0) {
3559 out:
3560 ash_msg("can't access tty; job control turned off");
3561 mflag = on = 0;
3562 goto close;
3563 }
3564 if (pgrp == getpgrp())
3565 break;
3566 killpg(0, SIGTTIN);
3567 } while (1);
3568 initialpgrp = pgrp;
3569
3570 setsignal(SIGTSTP);
3571 setsignal(SIGTTOU);
3572 setsignal(SIGTTIN);
3573 pgrp = rootpid;
3574 setpgid(0, pgrp);
3575 xtcsetpgrp(fd, pgrp);
3576 } else {
3577 /* turning job control off */
3578 fd = ttyfd;
3579 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003580 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003581 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003582 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003583 setpgid(0, pgrp);
3584 setsignal(SIGTSTP);
3585 setsignal(SIGTTOU);
3586 setsignal(SIGTTIN);
3587 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003588 if (fd >= 0)
3589 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003590 fd = -1;
3591 }
3592 ttyfd = fd;
3593 jobctl = on;
3594}
3595
3596static int
3597killcmd(int argc, char **argv)
3598{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003599 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3600 int i = 1;
3601 do {
3602 if (argv[i][0] == '%') {
3603 struct job *jp = getjob(argv[i], 0);
3604 unsigned pid = jp->ps[0].pid;
3605 /* Enough space for ' -NNN<nul>' */
3606 argv[i] = alloca(sizeof(int)*3 + 3);
3607 /* kill_main has matching code to expect
3608 * leading space. Needed to not confuse
3609 * negative pids with "kill -SIGNAL_NO" syntax */
3610 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003611 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003612 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003613 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003614 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003615}
3616
3617static void
3618showpipe(struct job *jp, FILE *out)
3619{
3620 struct procstat *sp;
3621 struct procstat *spend;
3622
3623 spend = jp->ps + jp->nprocs;
3624 for (sp = jp->ps + 1; sp < spend; sp++)
3625 fprintf(out, " | %s", sp->cmd);
3626 outcslow('\n', out);
3627 flush_stdout_stderr();
3628}
3629
3630
3631static int
3632restartjob(struct job *jp, int mode)
3633{
3634 struct procstat *ps;
3635 int i;
3636 int status;
3637 pid_t pgid;
3638
3639 INT_OFF;
3640 if (jp->state == JOBDONE)
3641 goto out;
3642 jp->state = JOBRUNNING;
3643 pgid = jp->ps->pid;
3644 if (mode == FORK_FG)
3645 xtcsetpgrp(ttyfd, pgid);
3646 killpg(pgid, SIGCONT);
3647 ps = jp->ps;
3648 i = jp->nprocs;
3649 do {
3650 if (WIFSTOPPED(ps->status)) {
3651 ps->status = -1;
3652 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003653 ps++;
3654 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655 out:
3656 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3657 INT_ON;
3658 return status;
3659}
3660
3661static int
3662fg_bgcmd(int argc, char **argv)
3663{
3664 struct job *jp;
3665 FILE *out;
3666 int mode;
3667 int retval;
3668
3669 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3670 nextopt(nullstr);
3671 argv = argptr;
3672 out = stdout;
3673 do {
3674 jp = getjob(*argv, 1);
3675 if (mode == FORK_BG) {
3676 set_curjob(jp, CUR_RUNNING);
3677 fprintf(out, "[%d] ", jobno(jp));
3678 }
3679 outstr(jp->ps->cmd, out);
3680 showpipe(jp, out);
3681 retval = restartjob(jp, mode);
3682 } while (*argv && *++argv);
3683 return retval;
3684}
3685#endif
3686
3687static int
3688sprint_status(char *s, int status, int sigonly)
3689{
3690 int col;
3691 int st;
3692
3693 col = 0;
3694 if (!WIFEXITED(status)) {
3695#if JOBS
3696 if (WIFSTOPPED(status))
3697 st = WSTOPSIG(status);
3698 else
3699#endif
3700 st = WTERMSIG(status);
3701 if (sigonly) {
3702 if (st == SIGINT || st == SIGPIPE)
3703 goto out;
3704#if JOBS
3705 if (WIFSTOPPED(status))
3706 goto out;
3707#endif
3708 }
3709 st &= 0x7f;
3710 col = fmtstr(s, 32, strsignal(st));
3711 if (WCOREDUMP(status)) {
3712 col += fmtstr(s + col, 16, " (core dumped)");
3713 }
3714 } else if (!sigonly) {
3715 st = WEXITSTATUS(status);
3716 if (st)
3717 col = fmtstr(s, 16, "Done(%d)", st);
3718 else
3719 col = fmtstr(s, 16, "Done");
3720 }
3721 out:
3722 return col;
3723}
3724
3725/*
3726 * Do a wait system call. If job control is compiled in, we accept
3727 * stopped processes. If block is zero, we return a value of zero
3728 * rather than blocking.
3729 *
3730 * System V doesn't have a non-blocking wait system call. It does
3731 * have a SIGCLD signal that is sent to a process when one of it's
3732 * children dies. The obvious way to use SIGCLD would be to install
3733 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3734 * was received, and have waitproc bump another counter when it got
3735 * the status of a process. Waitproc would then know that a wait
3736 * system call would not block if the two counters were different.
3737 * This approach doesn't work because if a process has children that
3738 * have not been waited for, System V will send it a SIGCLD when it
3739 * installs a signal handler for SIGCLD. What this means is that when
3740 * a child exits, the shell will be sent SIGCLD signals continuously
3741 * until is runs out of stack space, unless it does a wait call before
3742 * restoring the signal handler. The code below takes advantage of
3743 * this (mis)feature by installing a signal handler for SIGCLD and
3744 * then checking to see whether it was called. If there are any
3745 * children to be waited for, it will be.
3746 *
3747 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3748 * waits at all. In this case, the user will not be informed when
3749 * a background process until the next time she runs a real program
3750 * (as opposed to running a builtin command or just typing return),
3751 * and the jobs command may give out of date information.
3752 */
3753static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003754waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003756#if JOBS
3757 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003758 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003760 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3761 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762}
3763
3764/*
3765 * Wait for a process to terminate.
3766 */
3767static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003768dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003769{
3770 int pid;
3771 int status;
3772 struct job *jp;
3773 struct job *thisjob;
3774 int state;
3775
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003776 TRACE(("dowait(%d) called\n", wait_flags));
3777 pid = waitproc(wait_flags, &status);
3778 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003779 if (pid <= 0) {
3780 /* If we were doing blocking wait and (probably) got EINTR,
3781 * check for pending sigs received while waiting.
3782 * (NB: can be moved into callers if needed) */
3783 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3784 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003786 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787 INT_OFF;
3788 thisjob = NULL;
3789 for (jp = curjob; jp; jp = jp->prev_job) {
3790 struct procstat *sp;
3791 struct procstat *spend;
3792 if (jp->state == JOBDONE)
3793 continue;
3794 state = JOBDONE;
3795 spend = jp->ps + jp->nprocs;
3796 sp = jp->ps;
3797 do {
3798 if (sp->pid == pid) {
3799 TRACE(("Job %d: changing status of proc %d "
3800 "from 0x%x to 0x%x\n",
3801 jobno(jp), pid, sp->status, status));
3802 sp->status = status;
3803 thisjob = jp;
3804 }
3805 if (sp->status == -1)
3806 state = JOBRUNNING;
3807#if JOBS
3808 if (state == JOBRUNNING)
3809 continue;
3810 if (WIFSTOPPED(sp->status)) {
3811 jp->stopstatus = sp->status;
3812 state = JOBSTOPPED;
3813 }
3814#endif
3815 } while (++sp < spend);
3816 if (thisjob)
3817 goto gotjob;
3818 }
3819#if JOBS
3820 if (!WIFSTOPPED(status))
3821#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003822 jobless--;
3823 goto out;
3824
3825 gotjob:
3826 if (state != JOBRUNNING) {
3827 thisjob->changed = 1;
3828
3829 if (thisjob->state != state) {
3830 TRACE(("Job %d: changing state from %d to %d\n",
3831 jobno(thisjob), thisjob->state, state));
3832 thisjob->state = state;
3833#if JOBS
3834 if (state == JOBSTOPPED) {
3835 set_curjob(thisjob, CUR_STOPPED);
3836 }
3837#endif
3838 }
3839 }
3840
3841 out:
3842 INT_ON;
3843
3844 if (thisjob && thisjob == job) {
3845 char s[48 + 1];
3846 int len;
3847
3848 len = sprint_status(s, status, 1);
3849 if (len) {
3850 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003851 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003852 out2str(s);
3853 }
3854 }
3855 return pid;
3856}
3857
3858#if JOBS
3859static void
3860showjob(FILE *out, struct job *jp, int mode)
3861{
3862 struct procstat *ps;
3863 struct procstat *psend;
3864 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003865 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003866 char s[80];
3867
3868 ps = jp->ps;
3869
3870 if (mode & SHOW_PGID) {
3871 /* just output process (group) id of pipeline */
3872 fprintf(out, "%d\n", ps->pid);
3873 return;
3874 }
3875
3876 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003877 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878
3879 if (jp == curjob)
3880 s[col - 2] = '+';
3881 else if (curjob && jp == curjob->prev_job)
3882 s[col - 2] = '-';
3883
3884 if (mode & SHOW_PID)
3885 col += fmtstr(s + col, 16, "%d ", ps->pid);
3886
3887 psend = ps + jp->nprocs;
3888
3889 if (jp->state == JOBRUNNING) {
3890 strcpy(s + col, "Running");
3891 col += sizeof("Running") - 1;
3892 } else {
3893 int status = psend[-1].status;
3894 if (jp->state == JOBSTOPPED)
3895 status = jp->stopstatus;
3896 col += sprint_status(s + col, status, 0);
3897 }
3898
3899 goto start;
3900
3901 do {
3902 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003903 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003904 start:
3905 fprintf(out, "%s%*c%s",
3906 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3907 );
3908 if (!(mode & SHOW_PID)) {
3909 showpipe(jp, out);
3910 break;
3911 }
3912 if (++ps == psend) {
3913 outcslow('\n', out);
3914 break;
3915 }
3916 } while (1);
3917
3918 jp->changed = 0;
3919
3920 if (jp->state == JOBDONE) {
3921 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3922 freejob(jp);
3923 }
3924}
3925
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003926/*
3927 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3928 * statuses have changed since the last call to showjobs.
3929 */
3930static void
3931showjobs(FILE *out, int mode)
3932{
3933 struct job *jp;
3934
3935 TRACE(("showjobs(%x) called\n", mode));
3936
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003937 /* If not even one job changed, there is nothing to do */
3938 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003939 continue;
3940
3941 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003942 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003944 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003945 }
3946}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003947
3948static int
3949jobscmd(int argc, char **argv)
3950{
3951 int mode, m;
3952
3953 mode = 0;
3954 while ((m = nextopt("lp"))) {
3955 if (m == 'l')
3956 mode = SHOW_PID;
3957 else
3958 mode = SHOW_PGID;
3959 }
3960
3961 argv = argptr;
3962 if (*argv) {
3963 do
3964 showjob(stdout, getjob(*argv,0), mode);
3965 while (*++argv);
3966 } else
3967 showjobs(stdout, mode);
3968
3969 return 0;
3970}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971#endif /* JOBS */
3972
3973static int
3974getstatus(struct job *job)
3975{
3976 int status;
3977 int retval;
3978
3979 status = job->ps[job->nprocs - 1].status;
3980 retval = WEXITSTATUS(status);
3981 if (!WIFEXITED(status)) {
3982#if JOBS
3983 retval = WSTOPSIG(status);
3984 if (!WIFSTOPPED(status))
3985#endif
3986 {
3987 /* XXX: limits number of signals */
3988 retval = WTERMSIG(status);
3989#if JOBS
3990 if (retval == SIGINT)
3991 job->sigint = 1;
3992#endif
3993 }
3994 retval += 128;
3995 }
3996 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3997 jobno(job), job->nprocs, status, retval));
3998 return retval;
3999}
4000
4001static int
4002waitcmd(int argc, char **argv)
4003{
4004 struct job *job;
4005 int retval;
4006 struct job *jp;
4007
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004008// exsig++;
4009// xbarrier();
4010 if (pendingsig)
4011 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004012
4013 nextopt(nullstr);
4014 retval = 0;
4015
4016 argv = argptr;
4017 if (!*argv) {
4018 /* wait for all jobs */
4019 for (;;) {
4020 jp = curjob;
4021 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004022 if (!jp) /* no running procs */
4023 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004024 if (jp->state == JOBRUNNING)
4025 break;
4026 jp->waited = 1;
4027 jp = jp->prev_job;
4028 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004029 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030 }
4031 }
4032
4033 retval = 127;
4034 do {
4035 if (**argv != '%') {
4036 pid_t pid = number(*argv);
4037 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004038 while (1) {
4039 if (!job)
4040 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041 if (job->ps[job->nprocs - 1].pid == pid)
4042 break;
4043 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004044 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004045 } else
4046 job = getjob(*argv, 0);
4047 /* loop until process terminated or stopped */
4048 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004049 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 job->waited = 1;
4051 retval = getstatus(job);
4052 repeat:
4053 ;
4054 } while (*++argv);
4055
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004056 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004057 return retval;
4058}
4059
4060static struct job *
4061growjobtab(void)
4062{
4063 size_t len;
4064 ptrdiff_t offset;
4065 struct job *jp, *jq;
4066
4067 len = njobs * sizeof(*jp);
4068 jq = jobtab;
4069 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4070
4071 offset = (char *)jp - (char *)jq;
4072 if (offset) {
4073 /* Relocate pointers */
4074 size_t l = len;
4075
4076 jq = (struct job *)((char *)jq + l);
4077 while (l) {
4078 l -= sizeof(*jp);
4079 jq--;
4080#define joff(p) ((struct job *)((char *)(p) + l))
4081#define jmove(p) (p) = (void *)((char *)(p) + offset)
4082 if (joff(jp)->ps == &jq->ps0)
4083 jmove(joff(jp)->ps);
4084 if (joff(jp)->prev_job)
4085 jmove(joff(jp)->prev_job);
4086 }
4087 if (curjob)
4088 jmove(curjob);
4089#undef joff
4090#undef jmove
4091 }
4092
4093 njobs += 4;
4094 jobtab = jp;
4095 jp = (struct job *)((char *)jp + len);
4096 jq = jp + 3;
4097 do {
4098 jq->used = 0;
4099 } while (--jq >= jp);
4100 return jp;
4101}
4102
4103/*
4104 * Return a new job structure.
4105 * Called with interrupts off.
4106 */
4107static struct job *
4108makejob(union node *node, int nprocs)
4109{
4110 int i;
4111 struct job *jp;
4112
4113 for (i = njobs, jp = jobtab; ; jp++) {
4114 if (--i < 0) {
4115 jp = growjobtab();
4116 break;
4117 }
4118 if (jp->used == 0)
4119 break;
4120 if (jp->state != JOBDONE || !jp->waited)
4121 continue;
4122#if JOBS
4123 if (jobctl)
4124 continue;
4125#endif
4126 freejob(jp);
4127 break;
4128 }
4129 memset(jp, 0, sizeof(*jp));
4130#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004131 /* jp->jobctl is a bitfield.
4132 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 if (jobctl)
4134 jp->jobctl = 1;
4135#endif
4136 jp->prev_job = curjob;
4137 curjob = jp;
4138 jp->used = 1;
4139 jp->ps = &jp->ps0;
4140 if (nprocs > 1) {
4141 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4142 }
4143 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4144 jobno(jp)));
4145 return jp;
4146}
4147
4148#if JOBS
4149/*
4150 * Return a string identifying a command (to be printed by the
4151 * jobs command).
4152 */
4153static char *cmdnextc;
4154
4155static void
4156cmdputs(const char *s)
4157{
4158 const char *p, *str;
4159 char c, cc[2] = " ";
4160 char *nextc;
4161 int subtype = 0;
4162 int quoted = 0;
4163 static const char vstype[VSTYPE + 1][4] = {
4164 "", "}", "-", "+", "?", "=",
4165 "%", "%%", "#", "##"
4166 };
4167
4168 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4169 p = s;
4170 while ((c = *p++) != 0) {
4171 str = 0;
4172 switch (c) {
4173 case CTLESC:
4174 c = *p++;
4175 break;
4176 case CTLVAR:
4177 subtype = *p++;
4178 if ((subtype & VSTYPE) == VSLENGTH)
4179 str = "${#";
4180 else
4181 str = "${";
4182 if (!(subtype & VSQUOTE) == !(quoted & 1))
4183 goto dostr;
4184 quoted ^= 1;
4185 c = '"';
4186 break;
4187 case CTLENDVAR:
4188 str = "\"}" + !(quoted & 1);
4189 quoted >>= 1;
4190 subtype = 0;
4191 goto dostr;
4192 case CTLBACKQ:
4193 str = "$(...)";
4194 goto dostr;
4195 case CTLBACKQ+CTLQUOTE:
4196 str = "\"$(...)\"";
4197 goto dostr;
4198#if ENABLE_ASH_MATH_SUPPORT
4199 case CTLARI:
4200 str = "$((";
4201 goto dostr;
4202 case CTLENDARI:
4203 str = "))";
4204 goto dostr;
4205#endif
4206 case CTLQUOTEMARK:
4207 quoted ^= 1;
4208 c = '"';
4209 break;
4210 case '=':
4211 if (subtype == 0)
4212 break;
4213 if ((subtype & VSTYPE) != VSNORMAL)
4214 quoted <<= 1;
4215 str = vstype[subtype & VSTYPE];
4216 if (subtype & VSNUL)
4217 c = ':';
4218 else
4219 goto checkstr;
4220 break;
4221 case '\'':
4222 case '\\':
4223 case '"':
4224 case '$':
4225 /* These can only happen inside quotes */
4226 cc[0] = c;
4227 str = cc;
4228 c = '\\';
4229 break;
4230 default:
4231 break;
4232 }
4233 USTPUTC(c, nextc);
4234 checkstr:
4235 if (!str)
4236 continue;
4237 dostr:
4238 while ((c = *str++)) {
4239 USTPUTC(c, nextc);
4240 }
4241 }
4242 if (quoted & 1) {
4243 USTPUTC('"', nextc);
4244 }
4245 *nextc = 0;
4246 cmdnextc = nextc;
4247}
4248
4249/* cmdtxt() and cmdlist() call each other */
4250static void cmdtxt(union node *n);
4251
4252static void
4253cmdlist(union node *np, int sep)
4254{
4255 for (; np; np = np->narg.next) {
4256 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004257 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004258 cmdtxt(np);
4259 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004260 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004261 }
4262}
4263
4264static void
4265cmdtxt(union node *n)
4266{
4267 union node *np;
4268 struct nodelist *lp;
4269 const char *p;
4270 char s[2];
4271
4272 if (!n)
4273 return;
4274 switch (n->type) {
4275 default:
4276#if DEBUG
4277 abort();
4278#endif
4279 case NPIPE:
4280 lp = n->npipe.cmdlist;
4281 for (;;) {
4282 cmdtxt(lp->n);
4283 lp = lp->next;
4284 if (!lp)
4285 break;
4286 cmdputs(" | ");
4287 }
4288 break;
4289 case NSEMI:
4290 p = "; ";
4291 goto binop;
4292 case NAND:
4293 p = " && ";
4294 goto binop;
4295 case NOR:
4296 p = " || ";
4297 binop:
4298 cmdtxt(n->nbinary.ch1);
4299 cmdputs(p);
4300 n = n->nbinary.ch2;
4301 goto donode;
4302 case NREDIR:
4303 case NBACKGND:
4304 n = n->nredir.n;
4305 goto donode;
4306 case NNOT:
4307 cmdputs("!");
4308 n = n->nnot.com;
4309 donode:
4310 cmdtxt(n);
4311 break;
4312 case NIF:
4313 cmdputs("if ");
4314 cmdtxt(n->nif.test);
4315 cmdputs("; then ");
4316 n = n->nif.ifpart;
4317 if (n->nif.elsepart) {
4318 cmdtxt(n);
4319 cmdputs("; else ");
4320 n = n->nif.elsepart;
4321 }
4322 p = "; fi";
4323 goto dotail;
4324 case NSUBSHELL:
4325 cmdputs("(");
4326 n = n->nredir.n;
4327 p = ")";
4328 goto dotail;
4329 case NWHILE:
4330 p = "while ";
4331 goto until;
4332 case NUNTIL:
4333 p = "until ";
4334 until:
4335 cmdputs(p);
4336 cmdtxt(n->nbinary.ch1);
4337 n = n->nbinary.ch2;
4338 p = "; done";
4339 dodo:
4340 cmdputs("; do ");
4341 dotail:
4342 cmdtxt(n);
4343 goto dotail2;
4344 case NFOR:
4345 cmdputs("for ");
4346 cmdputs(n->nfor.var);
4347 cmdputs(" in ");
4348 cmdlist(n->nfor.args, 1);
4349 n = n->nfor.body;
4350 p = "; done";
4351 goto dodo;
4352 case NDEFUN:
4353 cmdputs(n->narg.text);
4354 p = "() { ... }";
4355 goto dotail2;
4356 case NCMD:
4357 cmdlist(n->ncmd.args, 1);
4358 cmdlist(n->ncmd.redirect, 0);
4359 break;
4360 case NARG:
4361 p = n->narg.text;
4362 dotail2:
4363 cmdputs(p);
4364 break;
4365 case NHERE:
4366 case NXHERE:
4367 p = "<<...";
4368 goto dotail2;
4369 case NCASE:
4370 cmdputs("case ");
4371 cmdputs(n->ncase.expr->narg.text);
4372 cmdputs(" in ");
4373 for (np = n->ncase.cases; np; np = np->nclist.next) {
4374 cmdtxt(np->nclist.pattern);
4375 cmdputs(") ");
4376 cmdtxt(np->nclist.body);
4377 cmdputs(";; ");
4378 }
4379 p = "esac";
4380 goto dotail2;
4381 case NTO:
4382 p = ">";
4383 goto redir;
4384 case NCLOBBER:
4385 p = ">|";
4386 goto redir;
4387 case NAPPEND:
4388 p = ">>";
4389 goto redir;
4390 case NTOFD:
4391 p = ">&";
4392 goto redir;
4393 case NFROM:
4394 p = "<";
4395 goto redir;
4396 case NFROMFD:
4397 p = "<&";
4398 goto redir;
4399 case NFROMTO:
4400 p = "<>";
4401 redir:
4402 s[0] = n->nfile.fd + '0';
4403 s[1] = '\0';
4404 cmdputs(s);
4405 cmdputs(p);
4406 if (n->type == NTOFD || n->type == NFROMFD) {
4407 s[0] = n->ndup.dupfd + '0';
4408 p = s;
4409 goto dotail2;
4410 }
4411 n = n->nfile.fname;
4412 goto donode;
4413 }
4414}
4415
4416static char *
4417commandtext(union node *n)
4418{
4419 char *name;
4420
4421 STARTSTACKSTR(cmdnextc);
4422 cmdtxt(n);
4423 name = stackblock();
4424 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4425 name, cmdnextc, cmdnextc));
4426 return ckstrdup(name);
4427}
4428#endif /* JOBS */
4429
4430/*
4431 * Fork off a subshell. If we are doing job control, give the subshell its
4432 * own process group. Jp is a job structure that the job is to be added to.
4433 * N is the command that will be evaluated by the child. Both jp and n may
4434 * be NULL. The mode parameter can be one of the following:
4435 * FORK_FG - Fork off a foreground process.
4436 * FORK_BG - Fork off a background process.
4437 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4438 * process group even if job control is on.
4439 *
4440 * When job control is turned off, background processes have their standard
4441 * input redirected to /dev/null (except for the second and later processes
4442 * in a pipeline).
4443 *
4444 * Called with interrupts off.
4445 */
4446/*
4447 * Clear traps on a fork.
4448 */
4449static void
4450clear_traps(void)
4451{
4452 char **tp;
4453
4454 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004455 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004456 INT_OFF;
4457 free(*tp);
4458 *tp = NULL;
4459 if (tp != &trap[0])
4460 setsignal(tp - trap);
4461 INT_ON;
4462 }
4463 }
4464}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004465
4466/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004467static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004468
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004469/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004470static void
4471forkchild(struct job *jp, union node *n, int mode)
4472{
4473 int oldlvl;
4474
4475 TRACE(("Child shell %d\n", getpid()));
4476 oldlvl = shlvl;
4477 shlvl++;
4478
4479 closescript();
4480 clear_traps();
4481#if JOBS
4482 /* do job control only in root shell */
4483 jobctl = 0;
4484 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4485 pid_t pgrp;
4486
4487 if (jp->nprocs == 0)
4488 pgrp = getpid();
4489 else
4490 pgrp = jp->ps[0].pid;
4491 /* This can fail because we are doing it in the parent also */
4492 (void)setpgid(0, pgrp);
4493 if (mode == FORK_FG)
4494 xtcsetpgrp(ttyfd, pgrp);
4495 setsignal(SIGTSTP);
4496 setsignal(SIGTTOU);
4497 } else
4498#endif
4499 if (mode == FORK_BG) {
4500 ignoresig(SIGINT);
4501 ignoresig(SIGQUIT);
4502 if (jp->nprocs == 0) {
4503 close(0);
4504 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004505 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004506 }
4507 }
4508 if (!oldlvl && iflag) {
4509 setsignal(SIGINT);
4510 setsignal(SIGQUIT);
4511 setsignal(SIGTERM);
4512 }
4513 for (jp = curjob; jp; jp = jp->prev_job)
4514 freejob(jp);
4515 jobless = 0;
4516}
4517
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004518/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004519static void
4520forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4521{
4522 TRACE(("In parent shell: child = %d\n", pid));
4523 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004524 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4525 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526 jobless++;
4527 return;
4528 }
4529#if JOBS
4530 if (mode != FORK_NOJOB && jp->jobctl) {
4531 int pgrp;
4532
4533 if (jp->nprocs == 0)
4534 pgrp = pid;
4535 else
4536 pgrp = jp->ps[0].pid;
4537 /* This can fail because we are doing it in the child also */
4538 setpgid(pid, pgrp);
4539 }
4540#endif
4541 if (mode == FORK_BG) {
4542 backgndpid = pid; /* set $! */
4543 set_curjob(jp, CUR_RUNNING);
4544 }
4545 if (jp) {
4546 struct procstat *ps = &jp->ps[jp->nprocs++];
4547 ps->pid = pid;
4548 ps->status = -1;
4549 ps->cmd = nullstr;
4550#if JOBS
4551 if (jobctl && n)
4552 ps->cmd = commandtext(n);
4553#endif
4554 }
4555}
4556
4557static int
4558forkshell(struct job *jp, union node *n, int mode)
4559{
4560 int pid;
4561
4562 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4563 pid = fork();
4564 if (pid < 0) {
4565 TRACE(("Fork failed, errno=%d", errno));
4566 if (jp)
4567 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004568 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004569 }
4570 if (pid == 0)
4571 forkchild(jp, n, mode);
4572 else
4573 forkparent(jp, n, mode, pid);
4574 return pid;
4575}
4576
4577/*
4578 * Wait for job to finish.
4579 *
4580 * Under job control we have the problem that while a child process is
4581 * running interrupts generated by the user are sent to the child but not
4582 * to the shell. This means that an infinite loop started by an inter-
4583 * active user may be hard to kill. With job control turned off, an
4584 * interactive user may place an interactive program inside a loop. If
4585 * the interactive program catches interrupts, the user doesn't want
4586 * these interrupts to also abort the loop. The approach we take here
4587 * is to have the shell ignore interrupt signals while waiting for a
4588 * foreground process to terminate, and then send itself an interrupt
4589 * signal if the child process was terminated by an interrupt signal.
4590 * Unfortunately, some programs want to do a bit of cleanup and then
4591 * exit on interrupt; unless these processes terminate themselves by
4592 * sending a signal to themselves (instead of calling exit) they will
4593 * confuse this approach.
4594 *
4595 * Called with interrupts off.
4596 */
4597static int
4598waitforjob(struct job *jp)
4599{
4600 int st;
4601
4602 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4603 while (jp->state == JOBRUNNING) {
4604 dowait(DOWAIT_BLOCK, jp);
4605 }
4606 st = getstatus(jp);
4607#if JOBS
4608 if (jp->jobctl) {
4609 xtcsetpgrp(ttyfd, rootpid);
4610 /*
4611 * This is truly gross.
4612 * If we're doing job control, then we did a TIOCSPGRP which
4613 * caused us (the shell) to no longer be in the controlling
4614 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4615 * intuit from the subprocess exit status whether a SIGINT
4616 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4617 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004618 if (jp->sigint) /* TODO: do the same with all signals */
4619 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004620 }
4621 if (jp->state == JOBDONE)
4622#endif
4623 freejob(jp);
4624 return st;
4625}
4626
4627/*
4628 * return 1 if there are stopped jobs, otherwise 0
4629 */
4630static int
4631stoppedjobs(void)
4632{
4633 struct job *jp;
4634 int retval;
4635
4636 retval = 0;
4637 if (job_warning)
4638 goto out;
4639 jp = curjob;
4640 if (jp && jp->state == JOBSTOPPED) {
4641 out2str("You have stopped jobs.\n");
4642 job_warning = 2;
4643 retval++;
4644 }
4645 out:
4646 return retval;
4647}
4648
4649
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004650/* ============ redir.c
4651 *
4652 * Code for dealing with input/output redirection.
4653 */
4654
4655#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004656#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004657#ifndef PIPE_BUF
4658# define PIPESIZE 4096 /* amount of buffering in a pipe */
4659#else
4660# define PIPESIZE PIPE_BUF
4661#endif
4662
4663/*
4664 * Open a file in noclobber mode.
4665 * The code was copied from bash.
4666 */
4667static int
4668noclobberopen(const char *fname)
4669{
4670 int r, fd;
4671 struct stat finfo, finfo2;
4672
4673 /*
4674 * If the file exists and is a regular file, return an error
4675 * immediately.
4676 */
4677 r = stat(fname, &finfo);
4678 if (r == 0 && S_ISREG(finfo.st_mode)) {
4679 errno = EEXIST;
4680 return -1;
4681 }
4682
4683 /*
4684 * If the file was not present (r != 0), make sure we open it
4685 * exclusively so that if it is created before we open it, our open
4686 * will fail. Make sure that we do not truncate an existing file.
4687 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4688 * file was not a regular file, we leave O_EXCL off.
4689 */
4690 if (r != 0)
4691 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4692 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4693
4694 /* If the open failed, return the file descriptor right away. */
4695 if (fd < 0)
4696 return fd;
4697
4698 /*
4699 * OK, the open succeeded, but the file may have been changed from a
4700 * non-regular file to a regular file between the stat and the open.
4701 * We are assuming that the O_EXCL open handles the case where FILENAME
4702 * did not exist and is symlinked to an existing file between the stat
4703 * and open.
4704 */
4705
4706 /*
4707 * If we can open it and fstat the file descriptor, and neither check
4708 * revealed that it was a regular file, and the file has not been
4709 * replaced, return the file descriptor.
4710 */
4711 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4712 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4713 return fd;
4714
4715 /* The file has been replaced. badness. */
4716 close(fd);
4717 errno = EEXIST;
4718 return -1;
4719}
4720
4721/*
4722 * Handle here documents. Normally we fork off a process to write the
4723 * data to a pipe. If the document is short, we can stuff the data in
4724 * the pipe without forking.
4725 */
4726/* openhere needs this forward reference */
4727static void expandhere(union node *arg, int fd);
4728static int
4729openhere(union node *redir)
4730{
4731 int pip[2];
4732 size_t len = 0;
4733
4734 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004735 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004736 if (redir->type == NHERE) {
4737 len = strlen(redir->nhere.doc->narg.text);
4738 if (len <= PIPESIZE) {
4739 full_write(pip[1], redir->nhere.doc->narg.text, len);
4740 goto out;
4741 }
4742 }
4743 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4744 close(pip[0]);
4745 signal(SIGINT, SIG_IGN);
4746 signal(SIGQUIT, SIG_IGN);
4747 signal(SIGHUP, SIG_IGN);
4748#ifdef SIGTSTP
4749 signal(SIGTSTP, SIG_IGN);
4750#endif
4751 signal(SIGPIPE, SIG_DFL);
4752 if (redir->type == NHERE)
4753 full_write(pip[1], redir->nhere.doc->narg.text, len);
4754 else
4755 expandhere(redir->nhere.doc, pip[1]);
4756 _exit(0);
4757 }
4758 out:
4759 close(pip[1]);
4760 return pip[0];
4761}
4762
4763static int
4764openredirect(union node *redir)
4765{
4766 char *fname;
4767 int f;
4768
4769 switch (redir->nfile.type) {
4770 case NFROM:
4771 fname = redir->nfile.expfname;
4772 f = open(fname, O_RDONLY);
4773 if (f < 0)
4774 goto eopen;
4775 break;
4776 case NFROMTO:
4777 fname = redir->nfile.expfname;
4778 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4779 if (f < 0)
4780 goto ecreate;
4781 break;
4782 case NTO:
4783 /* Take care of noclobber mode. */
4784 if (Cflag) {
4785 fname = redir->nfile.expfname;
4786 f = noclobberopen(fname);
4787 if (f < 0)
4788 goto ecreate;
4789 break;
4790 }
4791 /* FALLTHROUGH */
4792 case NCLOBBER:
4793 fname = redir->nfile.expfname;
4794 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4795 if (f < 0)
4796 goto ecreate;
4797 break;
4798 case NAPPEND:
4799 fname = redir->nfile.expfname;
4800 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4801 if (f < 0)
4802 goto ecreate;
4803 break;
4804 default:
4805#if DEBUG
4806 abort();
4807#endif
4808 /* Fall through to eliminate warning. */
4809 case NTOFD:
4810 case NFROMFD:
4811 f = -1;
4812 break;
4813 case NHERE:
4814 case NXHERE:
4815 f = openhere(redir);
4816 break;
4817 }
4818
4819 return f;
4820 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004821 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004822 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004823 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004824}
4825
4826/*
4827 * Copy a file descriptor to be >= to. Returns -1
4828 * if the source file descriptor is closed, EMPTY if there are no unused
4829 * file descriptors left.
4830 */
4831static int
4832copyfd(int from, int to)
4833{
4834 int newfd;
4835
4836 newfd = fcntl(from, F_DUPFD, to);
4837 if (newfd < 0) {
4838 if (errno == EMFILE)
4839 return EMPTY;
4840 ash_msg_and_raise_error("%d: %m", from);
4841 }
4842 return newfd;
4843}
4844
4845static void
4846dupredirect(union node *redir, int f)
4847{
4848 int fd = redir->nfile.fd;
4849
4850 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4851 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4852 copyfd(redir->ndup.dupfd, fd);
4853 }
4854 return;
4855 }
4856
4857 if (f != fd) {
4858 copyfd(f, fd);
4859 close(f);
4860 }
4861}
4862
4863/*
4864 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4865 * old file descriptors are stashed away so that the redirection can be
4866 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4867 * standard output, and the standard error if it becomes a duplicate of
4868 * stdout, is saved in memory.
4869 */
4870/* flags passed to redirect */
4871#define REDIR_PUSH 01 /* save previous values of file descriptors */
4872#define REDIR_SAVEFD2 03 /* set preverrout */
4873static void
4874redirect(union node *redir, int flags)
4875{
4876 union node *n;
4877 struct redirtab *sv;
4878 int i;
4879 int fd;
4880 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004881
Denis Vlasenko01631112007-12-16 17:20:38 +00004882 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004883 if (!redir) {
4884 return;
4885 }
4886 sv = NULL;
4887 INT_OFF;
4888 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004889 sv = ckmalloc(sizeof(*sv));
4890 sv->next = redirlist;
4891 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004892 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004893 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004894 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004895 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004896 }
4897 n = redir;
4898 do {
4899 fd = n->nfile.fd;
4900 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4901 && n->ndup.dupfd == fd)
4902 continue; /* redirect from/to same file descriptor */
4903
4904 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004905 if (fd == newfd) {
4906 /* Descriptor wasn't open before redirect.
4907 * Mark it for close in the future */
4908 if (sv && sv->renamed[fd] == EMPTY)
4909 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004910 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004911 }
4912 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004913 i = fcntl(fd, F_DUPFD, 10);
4914
4915 if (i == -1) {
4916 i = errno;
4917 if (i != EBADF) {
4918 close(newfd);
4919 errno = i;
4920 ash_msg_and_raise_error("%d: %m", fd);
4921 /* NOTREACHED */
4922 }
4923 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004924 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004925 close(fd);
4926 }
4927 } else {
4928 close(fd);
4929 }
4930 dupredirect(n, newfd);
4931 } while ((n = n->nfile.next));
4932 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004933 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004934 preverrout_fd = sv->renamed[2];
4935}
4936
4937/*
4938 * Undo the effects of the last redirection.
4939 */
4940static void
4941popredir(int drop)
4942{
4943 struct redirtab *rp;
4944 int i;
4945
Denis Vlasenko01631112007-12-16 17:20:38 +00004946 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004947 return;
4948 INT_OFF;
4949 rp = redirlist;
4950 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004951 if (rp->renamed[i] == CLOSED) {
4952 if (!drop)
4953 close(i);
4954 continue;
4955 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956 if (rp->renamed[i] != EMPTY) {
4957 if (!drop) {
4958 close(i);
4959 copyfd(rp->renamed[i], i);
4960 }
4961 close(rp->renamed[i]);
4962 }
4963 }
4964 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004965 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004966 free(rp);
4967 INT_ON;
4968}
4969
4970/*
4971 * Undo all redirections. Called on error or interrupt.
4972 */
4973
4974/*
4975 * Discard all saved file descriptors.
4976 */
4977static void
4978clearredir(int drop)
4979{
4980 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004981 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982 if (!redirlist)
4983 break;
4984 popredir(drop);
4985 }
4986}
4987
4988static int
4989redirectsafe(union node *redir, int flags)
4990{
4991 int err;
4992 volatile int saveint;
4993 struct jmploc *volatile savehandler = exception_handler;
4994 struct jmploc jmploc;
4995
4996 SAVE_INT(saveint);
4997 err = setjmp(jmploc.loc) * 2;
4998 if (!err) {
4999 exception_handler = &jmploc;
5000 redirect(redir, flags);
5001 }
5002 exception_handler = savehandler;
5003 if (err && exception != EXERROR)
5004 longjmp(exception_handler->loc, 1);
5005 RESTORE_INT(saveint);
5006 return err;
5007}
5008
5009
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005010/* ============ Routines to expand arguments to commands
5011 *
5012 * We have to deal with backquotes, shell variables, and file metacharacters.
5013 */
5014
5015/*
5016 * expandarg flags
5017 */
5018#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5019#define EXP_TILDE 0x2 /* do normal tilde expansion */
5020#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5021#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5022#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5023#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5024#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5025#define EXP_WORD 0x80 /* expand word in parameter expansion */
5026#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5027/*
5028 * _rmescape() flags
5029 */
5030#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5031#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5032#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5033#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5034#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5035
5036/*
5037 * Structure specifying which parts of the string should be searched
5038 * for IFS characters.
5039 */
5040struct ifsregion {
5041 struct ifsregion *next; /* next region in list */
5042 int begoff; /* offset of start of region */
5043 int endoff; /* offset of end of region */
5044 int nulonly; /* search for nul bytes only */
5045};
5046
5047struct arglist {
5048 struct strlist *list;
5049 struct strlist **lastp;
5050};
5051
5052/* output of current string */
5053static char *expdest;
5054/* list of back quote expressions */
5055static struct nodelist *argbackq;
5056/* first struct in list of ifs regions */
5057static struct ifsregion ifsfirst;
5058/* last struct in list */
5059static struct ifsregion *ifslastp;
5060/* holds expanded arg list */
5061static struct arglist exparg;
5062
5063/*
5064 * Our own itoa().
5065 */
5066static int
5067cvtnum(arith_t num)
5068{
5069 int len;
5070
5071 expdest = makestrspace(32, expdest);
5072#if ENABLE_ASH_MATH_SUPPORT_64
5073 len = fmtstr(expdest, 32, "%lld", (long long) num);
5074#else
5075 len = fmtstr(expdest, 32, "%ld", num);
5076#endif
5077 STADJUST(len, expdest);
5078 return len;
5079}
5080
5081static size_t
5082esclen(const char *start, const char *p)
5083{
5084 size_t esc = 0;
5085
5086 while (p > start && *--p == CTLESC) {
5087 esc++;
5088 }
5089 return esc;
5090}
5091
5092/*
5093 * Remove any CTLESC characters from a string.
5094 */
5095static char *
5096_rmescapes(char *str, int flag)
5097{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005098 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005099
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005100 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005101 unsigned inquotes;
5102 int notescaped;
5103 int globbing;
5104
5105 p = strpbrk(str, qchars);
5106 if (!p) {
5107 return str;
5108 }
5109 q = p;
5110 r = str;
5111 if (flag & RMESCAPE_ALLOC) {
5112 size_t len = p - str;
5113 size_t fulllen = len + strlen(p) + 1;
5114
5115 if (flag & RMESCAPE_GROW) {
5116 r = makestrspace(fulllen, expdest);
5117 } else if (flag & RMESCAPE_HEAP) {
5118 r = ckmalloc(fulllen);
5119 } else {
5120 r = stalloc(fulllen);
5121 }
5122 q = r;
5123 if (len > 0) {
5124 q = memcpy(q, str, len) + len;
5125 }
5126 }
5127 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5128 globbing = flag & RMESCAPE_GLOB;
5129 notescaped = globbing;
5130 while (*p) {
5131 if (*p == CTLQUOTEMARK) {
5132 inquotes = ~inquotes;
5133 p++;
5134 notescaped = globbing;
5135 continue;
5136 }
5137 if (*p == '\\') {
5138 /* naked back slash */
5139 notescaped = 0;
5140 goto copy;
5141 }
5142 if (*p == CTLESC) {
5143 p++;
5144 if (notescaped && inquotes && *p != '/') {
5145 *q++ = '\\';
5146 }
5147 }
5148 notescaped = globbing;
5149 copy:
5150 *q++ = *p++;
5151 }
5152 *q = '\0';
5153 if (flag & RMESCAPE_GROW) {
5154 expdest = r;
5155 STADJUST(q - r + 1, expdest);
5156 }
5157 return r;
5158}
5159#define rmescapes(p) _rmescapes((p), 0)
5160
5161#define pmatch(a, b) !fnmatch((a), (b), 0)
5162
5163/*
5164 * Prepare a pattern for a expmeta (internal glob(3)) call.
5165 *
5166 * Returns an stalloced string.
5167 */
5168static char *
5169preglob(const char *pattern, int quoted, int flag)
5170{
5171 flag |= RMESCAPE_GLOB;
5172 if (quoted) {
5173 flag |= RMESCAPE_QUOTED;
5174 }
5175 return _rmescapes((char *)pattern, flag);
5176}
5177
5178/*
5179 * Put a string on the stack.
5180 */
5181static void
5182memtodest(const char *p, size_t len, int syntax, int quotes)
5183{
5184 char *q = expdest;
5185
5186 q = makestrspace(len * 2, q);
5187
5188 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005189 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005190 if (!c)
5191 continue;
5192 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5193 USTPUTC(CTLESC, q);
5194 USTPUTC(c, q);
5195 }
5196
5197 expdest = q;
5198}
5199
5200static void
5201strtodest(const char *p, int syntax, int quotes)
5202{
5203 memtodest(p, strlen(p), syntax, quotes);
5204}
5205
5206/*
5207 * Record the fact that we have to scan this region of the
5208 * string for IFS characters.
5209 */
5210static void
5211recordregion(int start, int end, int nulonly)
5212{
5213 struct ifsregion *ifsp;
5214
5215 if (ifslastp == NULL) {
5216 ifsp = &ifsfirst;
5217 } else {
5218 INT_OFF;
5219 ifsp = ckmalloc(sizeof(*ifsp));
5220 ifsp->next = NULL;
5221 ifslastp->next = ifsp;
5222 INT_ON;
5223 }
5224 ifslastp = ifsp;
5225 ifslastp->begoff = start;
5226 ifslastp->endoff = end;
5227 ifslastp->nulonly = nulonly;
5228}
5229
5230static void
5231removerecordregions(int endoff)
5232{
5233 if (ifslastp == NULL)
5234 return;
5235
5236 if (ifsfirst.endoff > endoff) {
5237 while (ifsfirst.next != NULL) {
5238 struct ifsregion *ifsp;
5239 INT_OFF;
5240 ifsp = ifsfirst.next->next;
5241 free(ifsfirst.next);
5242 ifsfirst.next = ifsp;
5243 INT_ON;
5244 }
5245 if (ifsfirst.begoff > endoff)
5246 ifslastp = NULL;
5247 else {
5248 ifslastp = &ifsfirst;
5249 ifsfirst.endoff = endoff;
5250 }
5251 return;
5252 }
5253
5254 ifslastp = &ifsfirst;
5255 while (ifslastp->next && ifslastp->next->begoff < endoff)
5256 ifslastp=ifslastp->next;
5257 while (ifslastp->next != NULL) {
5258 struct ifsregion *ifsp;
5259 INT_OFF;
5260 ifsp = ifslastp->next->next;
5261 free(ifslastp->next);
5262 ifslastp->next = ifsp;
5263 INT_ON;
5264 }
5265 if (ifslastp->endoff > endoff)
5266 ifslastp->endoff = endoff;
5267}
5268
5269static char *
5270exptilde(char *startp, char *p, int flag)
5271{
5272 char c;
5273 char *name;
5274 struct passwd *pw;
5275 const char *home;
5276 int quotes = flag & (EXP_FULL | EXP_CASE);
5277 int startloc;
5278
5279 name = p + 1;
5280
5281 while ((c = *++p) != '\0') {
5282 switch (c) {
5283 case CTLESC:
5284 return startp;
5285 case CTLQUOTEMARK:
5286 return startp;
5287 case ':':
5288 if (flag & EXP_VARTILDE)
5289 goto done;
5290 break;
5291 case '/':
5292 case CTLENDVAR:
5293 goto done;
5294 }
5295 }
5296 done:
5297 *p = '\0';
5298 if (*name == '\0') {
5299 home = lookupvar(homestr);
5300 } else {
5301 pw = getpwnam(name);
5302 if (pw == NULL)
5303 goto lose;
5304 home = pw->pw_dir;
5305 }
5306 if (!home || !*home)
5307 goto lose;
5308 *p = c;
5309 startloc = expdest - (char *)stackblock();
5310 strtodest(home, SQSYNTAX, quotes);
5311 recordregion(startloc, expdest - (char *)stackblock(), 0);
5312 return p;
5313 lose:
5314 *p = c;
5315 return startp;
5316}
5317
5318/*
5319 * Execute a command inside back quotes. If it's a builtin command, we
5320 * want to save its output in a block obtained from malloc. Otherwise
5321 * we fork off a subprocess and get the output of the command via a pipe.
5322 * Should be called with interrupts off.
5323 */
5324struct backcmd { /* result of evalbackcmd */
5325 int fd; /* file descriptor to read from */
5326 char *buf; /* buffer */
5327 int nleft; /* number of chars in buffer */
5328 struct job *jp; /* job structure for command */
5329};
5330
5331/* These forward decls are needed to use "eval" code for backticks handling: */
5332static int back_exitstatus; /* exit status of backquoted command */
5333#define EV_EXIT 01 /* exit after evaluating tree */
5334static void evaltree(union node *, int);
5335
5336static void
5337evalbackcmd(union node *n, struct backcmd *result)
5338{
5339 int saveherefd;
5340
5341 result->fd = -1;
5342 result->buf = NULL;
5343 result->nleft = 0;
5344 result->jp = NULL;
5345 if (n == NULL) {
5346 goto out;
5347 }
5348
5349 saveherefd = herefd;
5350 herefd = -1;
5351
5352 {
5353 int pip[2];
5354 struct job *jp;
5355
5356 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005357 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005358 jp = makejob(n, 1);
5359 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5360 FORCE_INT_ON;
5361 close(pip[0]);
5362 if (pip[1] != 1) {
5363 close(1);
5364 copyfd(pip[1], 1);
5365 close(pip[1]);
5366 }
5367 eflag = 0;
5368 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5369 /* NOTREACHED */
5370 }
5371 close(pip[1]);
5372 result->fd = pip[0];
5373 result->jp = jp;
5374 }
5375 herefd = saveherefd;
5376 out:
5377 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5378 result->fd, result->buf, result->nleft, result->jp));
5379}
5380
5381/*
5382 * Expand stuff in backwards quotes.
5383 */
5384static void
5385expbackq(union node *cmd, int quoted, int quotes)
5386{
5387 struct backcmd in;
5388 int i;
5389 char buf[128];
5390 char *p;
5391 char *dest;
5392 int startloc;
5393 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5394 struct stackmark smark;
5395
5396 INT_OFF;
5397 setstackmark(&smark);
5398 dest = expdest;
5399 startloc = dest - (char *)stackblock();
5400 grabstackstr(dest);
5401 evalbackcmd(cmd, &in);
5402 popstackmark(&smark);
5403
5404 p = in.buf;
5405 i = in.nleft;
5406 if (i == 0)
5407 goto read;
5408 for (;;) {
5409 memtodest(p, i, syntax, quotes);
5410 read:
5411 if (in.fd < 0)
5412 break;
5413 i = safe_read(in.fd, buf, sizeof(buf));
5414 TRACE(("expbackq: read returns %d\n", i));
5415 if (i <= 0)
5416 break;
5417 p = buf;
5418 }
5419
Denis Vlasenko60818682007-09-28 22:07:23 +00005420 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005421 if (in.fd >= 0) {
5422 close(in.fd);
5423 back_exitstatus = waitforjob(in.jp);
5424 }
5425 INT_ON;
5426
5427 /* Eat all trailing newlines */
5428 dest = expdest;
5429 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5430 STUNPUTC(dest);
5431 expdest = dest;
5432
5433 if (quoted == 0)
5434 recordregion(startloc, dest - (char *)stackblock(), 0);
5435 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5436 (dest - (char *)stackblock()) - startloc,
5437 (dest - (char *)stackblock()) - startloc,
5438 stackblock() + startloc));
5439}
5440
5441#if ENABLE_ASH_MATH_SUPPORT
5442/*
5443 * Expand arithmetic expression. Backup to start of expression,
5444 * evaluate, place result in (backed up) result, adjust string position.
5445 */
5446static void
5447expari(int quotes)
5448{
5449 char *p, *start;
5450 int begoff;
5451 int flag;
5452 int len;
5453
5454 /* ifsfree(); */
5455
5456 /*
5457 * This routine is slightly over-complicated for
5458 * efficiency. Next we scan backwards looking for the
5459 * start of arithmetic.
5460 */
5461 start = stackblock();
5462 p = expdest - 1;
5463 *p = '\0';
5464 p--;
5465 do {
5466 int esc;
5467
5468 while (*p != CTLARI) {
5469 p--;
5470#if DEBUG
5471 if (p < start) {
5472 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5473 }
5474#endif
5475 }
5476
5477 esc = esclen(start, p);
5478 if (!(esc % 2)) {
5479 break;
5480 }
5481
5482 p -= esc + 1;
5483 } while (1);
5484
5485 begoff = p - start;
5486
5487 removerecordregions(begoff);
5488
5489 flag = p[1];
5490
5491 expdest = p;
5492
5493 if (quotes)
5494 rmescapes(p + 2);
5495
5496 len = cvtnum(dash_arith(p + 2));
5497
5498 if (flag != '"')
5499 recordregion(begoff, begoff + len, 0);
5500}
5501#endif
5502
5503/* argstr needs it */
5504static char *evalvar(char *p, int flag);
5505
5506/*
5507 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5508 * characters to allow for further processing. Otherwise treat
5509 * $@ like $* since no splitting will be performed.
5510 */
5511static void
5512argstr(char *p, int flag)
5513{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005514 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005515 '=',
5516 ':',
5517 CTLQUOTEMARK,
5518 CTLENDVAR,
5519 CTLESC,
5520 CTLVAR,
5521 CTLBACKQ,
5522 CTLBACKQ | CTLQUOTE,
5523#if ENABLE_ASH_MATH_SUPPORT
5524 CTLENDARI,
5525#endif
5526 0
5527 };
5528 const char *reject = spclchars;
5529 int c;
5530 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5531 int breakall = flag & EXP_WORD;
5532 int inquotes;
5533 size_t length;
5534 int startloc;
5535
5536 if (!(flag & EXP_VARTILDE)) {
5537 reject += 2;
5538 } else if (flag & EXP_VARTILDE2) {
5539 reject++;
5540 }
5541 inquotes = 0;
5542 length = 0;
5543 if (flag & EXP_TILDE) {
5544 char *q;
5545
5546 flag &= ~EXP_TILDE;
5547 tilde:
5548 q = p;
5549 if (*q == CTLESC && (flag & EXP_QWORD))
5550 q++;
5551 if (*q == '~')
5552 p = exptilde(p, q, flag);
5553 }
5554 start:
5555 startloc = expdest - (char *)stackblock();
5556 for (;;) {
5557 length += strcspn(p + length, reject);
5558 c = p[length];
5559 if (c && (!(c & 0x80)
5560#if ENABLE_ASH_MATH_SUPPORT
5561 || c == CTLENDARI
5562#endif
5563 )) {
5564 /* c == '=' || c == ':' || c == CTLENDARI */
5565 length++;
5566 }
5567 if (length > 0) {
5568 int newloc;
5569 expdest = stack_nputstr(p, length, expdest);
5570 newloc = expdest - (char *)stackblock();
5571 if (breakall && !inquotes && newloc > startloc) {
5572 recordregion(startloc, newloc, 0);
5573 }
5574 startloc = newloc;
5575 }
5576 p += length + 1;
5577 length = 0;
5578
5579 switch (c) {
5580 case '\0':
5581 goto breakloop;
5582 case '=':
5583 if (flag & EXP_VARTILDE2) {
5584 p--;
5585 continue;
5586 }
5587 flag |= EXP_VARTILDE2;
5588 reject++;
5589 /* fall through */
5590 case ':':
5591 /*
5592 * sort of a hack - expand tildes in variable
5593 * assignments (after the first '=' and after ':'s).
5594 */
5595 if (*--p == '~') {
5596 goto tilde;
5597 }
5598 continue;
5599 }
5600
5601 switch (c) {
5602 case CTLENDVAR: /* ??? */
5603 goto breakloop;
5604 case CTLQUOTEMARK:
5605 /* "$@" syntax adherence hack */
5606 if (
5607 !inquotes &&
5608 !memcmp(p, dolatstr, 4) &&
5609 (p[4] == CTLQUOTEMARK || (
5610 p[4] == CTLENDVAR &&
5611 p[5] == CTLQUOTEMARK
5612 ))
5613 ) {
5614 p = evalvar(p + 1, flag) + 1;
5615 goto start;
5616 }
5617 inquotes = !inquotes;
5618 addquote:
5619 if (quotes) {
5620 p--;
5621 length++;
5622 startloc++;
5623 }
5624 break;
5625 case CTLESC:
5626 startloc++;
5627 length++;
5628 goto addquote;
5629 case CTLVAR:
5630 p = evalvar(p, flag);
5631 goto start;
5632 case CTLBACKQ:
5633 c = 0;
5634 case CTLBACKQ|CTLQUOTE:
5635 expbackq(argbackq->n, c, quotes);
5636 argbackq = argbackq->next;
5637 goto start;
5638#if ENABLE_ASH_MATH_SUPPORT
5639 case CTLENDARI:
5640 p--;
5641 expari(quotes);
5642 goto start;
5643#endif
5644 }
5645 }
5646 breakloop:
5647 ;
5648}
5649
5650static char *
5651scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5652 int zero)
5653{
5654 char *loc;
5655 char *loc2;
5656 char c;
5657
5658 loc = startp;
5659 loc2 = rmesc;
5660 do {
5661 int match;
5662 const char *s = loc2;
5663 c = *loc2;
5664 if (zero) {
5665 *loc2 = '\0';
5666 s = rmesc;
5667 }
5668 match = pmatch(str, s);
5669 *loc2 = c;
5670 if (match)
5671 return loc;
5672 if (quotes && *loc == CTLESC)
5673 loc++;
5674 loc++;
5675 loc2++;
5676 } while (c);
5677 return 0;
5678}
5679
5680static char *
5681scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5682 int zero)
5683{
5684 int esc = 0;
5685 char *loc;
5686 char *loc2;
5687
5688 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5689 int match;
5690 char c = *loc2;
5691 const char *s = loc2;
5692 if (zero) {
5693 *loc2 = '\0';
5694 s = rmesc;
5695 }
5696 match = pmatch(str, s);
5697 *loc2 = c;
5698 if (match)
5699 return loc;
5700 loc--;
5701 if (quotes) {
5702 if (--esc < 0) {
5703 esc = esclen(startp, loc);
5704 }
5705 if (esc % 2) {
5706 esc--;
5707 loc--;
5708 }
5709 }
5710 }
5711 return 0;
5712}
5713
5714static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5715static void
5716varunset(const char *end, const char *var, const char *umsg, int varflags)
5717{
5718 const char *msg;
5719 const char *tail;
5720
5721 tail = nullstr;
5722 msg = "parameter not set";
5723 if (umsg) {
5724 if (*end == CTLENDVAR) {
5725 if (varflags & VSNUL)
5726 tail = " or null";
5727 } else
5728 msg = umsg;
5729 }
5730 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5731}
5732
5733static const char *
5734subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5735{
5736 char *startp;
5737 char *loc;
5738 int saveherefd = herefd;
5739 struct nodelist *saveargbackq = argbackq;
5740 int amount;
5741 char *rmesc, *rmescend;
5742 int zero;
5743 char *(*scan)(char *, char *, char *, char *, int , int);
5744
5745 herefd = -1;
5746 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5747 STPUTC('\0', expdest);
5748 herefd = saveherefd;
5749 argbackq = saveargbackq;
5750 startp = stackblock() + startloc;
5751
5752 switch (subtype) {
5753 case VSASSIGN:
5754 setvar(str, startp, 0);
5755 amount = startp - expdest;
5756 STADJUST(amount, expdest);
5757 return startp;
5758
5759 case VSQUESTION:
5760 varunset(p, str, startp, varflags);
5761 /* NOTREACHED */
5762 }
5763
5764 subtype -= VSTRIMRIGHT;
5765#if DEBUG
5766 if (subtype < 0 || subtype > 3)
5767 abort();
5768#endif
5769
5770 rmesc = startp;
5771 rmescend = stackblock() + strloc;
5772 if (quotes) {
5773 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5774 if (rmesc != startp) {
5775 rmescend = expdest;
5776 startp = stackblock() + startloc;
5777 }
5778 }
5779 rmescend--;
5780 str = stackblock() + strloc;
5781 preglob(str, varflags & VSQUOTE, 0);
5782
5783 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5784 zero = subtype >> 1;
5785 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5786 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5787
5788 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5789 if (loc) {
5790 if (zero) {
5791 memmove(startp, loc, str - loc);
5792 loc = startp + (str - loc) - 1;
5793 }
5794 *loc = '\0';
5795 amount = loc - expdest;
5796 STADJUST(amount, expdest);
5797 }
5798 return loc;
5799}
5800
5801/*
5802 * Add the value of a specialized variable to the stack string.
5803 */
5804static ssize_t
5805varvalue(char *name, int varflags, int flags)
5806{
5807 int num;
5808 char *p;
5809 int i;
5810 int sep = 0;
5811 int sepq = 0;
5812 ssize_t len = 0;
5813 char **ap;
5814 int syntax;
5815 int quoted = varflags & VSQUOTE;
5816 int subtype = varflags & VSTYPE;
5817 int quotes = flags & (EXP_FULL | EXP_CASE);
5818
5819 if (quoted && (flags & EXP_FULL))
5820 sep = 1 << CHAR_BIT;
5821
5822 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5823 switch (*name) {
5824 case '$':
5825 num = rootpid;
5826 goto numvar;
5827 case '?':
5828 num = exitstatus;
5829 goto numvar;
5830 case '#':
5831 num = shellparam.nparam;
5832 goto numvar;
5833 case '!':
5834 num = backgndpid;
5835 if (num == 0)
5836 return -1;
5837 numvar:
5838 len = cvtnum(num);
5839 break;
5840 case '-':
5841 p = makestrspace(NOPTS, expdest);
5842 for (i = NOPTS - 1; i >= 0; i--) {
5843 if (optlist[i]) {
5844 USTPUTC(optletters(i), p);
5845 len++;
5846 }
5847 }
5848 expdest = p;
5849 break;
5850 case '@':
5851 if (sep)
5852 goto param;
5853 /* fall through */
5854 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005855 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5857 sepq = 1;
5858 param:
5859 ap = shellparam.p;
5860 if (!ap)
5861 return -1;
5862 while ((p = *ap++)) {
5863 size_t partlen;
5864
5865 partlen = strlen(p);
5866 len += partlen;
5867
5868 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5869 memtodest(p, partlen, syntax, quotes);
5870
5871 if (*ap && sep) {
5872 char *q;
5873
5874 len++;
5875 if (subtype == VSPLUS || subtype == VSLENGTH) {
5876 continue;
5877 }
5878 q = expdest;
5879 if (sepq)
5880 STPUTC(CTLESC, q);
5881 STPUTC(sep, q);
5882 expdest = q;
5883 }
5884 }
5885 return len;
5886 case '0':
5887 case '1':
5888 case '2':
5889 case '3':
5890 case '4':
5891 case '5':
5892 case '6':
5893 case '7':
5894 case '8':
5895 case '9':
5896 num = atoi(name);
5897 if (num < 0 || num > shellparam.nparam)
5898 return -1;
5899 p = num ? shellparam.p[num - 1] : arg0;
5900 goto value;
5901 default:
5902 p = lookupvar(name);
5903 value:
5904 if (!p)
5905 return -1;
5906
5907 len = strlen(p);
5908 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5909 memtodest(p, len, syntax, quotes);
5910 return len;
5911 }
5912
5913 if (subtype == VSPLUS || subtype == VSLENGTH)
5914 STADJUST(-len, expdest);
5915 return len;
5916}
5917
5918/*
5919 * Expand a variable, and return a pointer to the next character in the
5920 * input string.
5921 */
5922static char *
5923evalvar(char *p, int flag)
5924{
5925 int subtype;
5926 int varflags;
5927 char *var;
5928 int patloc;
5929 int c;
5930 int startloc;
5931 ssize_t varlen;
5932 int easy;
5933 int quotes;
5934 int quoted;
5935
5936 quotes = flag & (EXP_FULL | EXP_CASE);
5937 varflags = *p++;
5938 subtype = varflags & VSTYPE;
5939 quoted = varflags & VSQUOTE;
5940 var = p;
5941 easy = (!quoted || (*var == '@' && shellparam.nparam));
5942 startloc = expdest - (char *)stackblock();
5943 p = strchr(p, '=') + 1;
5944
5945 again:
5946 varlen = varvalue(var, varflags, flag);
5947 if (varflags & VSNUL)
5948 varlen--;
5949
5950 if (subtype == VSPLUS) {
5951 varlen = -1 - varlen;
5952 goto vsplus;
5953 }
5954
5955 if (subtype == VSMINUS) {
5956 vsplus:
5957 if (varlen < 0) {
5958 argstr(
5959 p, flag | EXP_TILDE |
5960 (quoted ? EXP_QWORD : EXP_WORD)
5961 );
5962 goto end;
5963 }
5964 if (easy)
5965 goto record;
5966 goto end;
5967 }
5968
5969 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5970 if (varlen < 0) {
5971 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5972 varflags &= ~VSNUL;
5973 /*
5974 * Remove any recorded regions beyond
5975 * start of variable
5976 */
5977 removerecordregions(startloc);
5978 goto again;
5979 }
5980 goto end;
5981 }
5982 if (easy)
5983 goto record;
5984 goto end;
5985 }
5986
5987 if (varlen < 0 && uflag)
5988 varunset(p, var, 0, 0);
5989
5990 if (subtype == VSLENGTH) {
5991 cvtnum(varlen > 0 ? varlen : 0);
5992 goto record;
5993 }
5994
5995 if (subtype == VSNORMAL) {
5996 if (!easy)
5997 goto end;
5998 record:
5999 recordregion(startloc, expdest - (char *)stackblock(), quoted);
6000 goto end;
6001 }
6002
6003#if DEBUG
6004 switch (subtype) {
6005 case VSTRIMLEFT:
6006 case VSTRIMLEFTMAX:
6007 case VSTRIMRIGHT:
6008 case VSTRIMRIGHTMAX:
6009 break;
6010 default:
6011 abort();
6012 }
6013#endif
6014
6015 if (varlen >= 0) {
6016 /*
6017 * Terminate the string and start recording the pattern
6018 * right after it
6019 */
6020 STPUTC('\0', expdest);
6021 patloc = expdest - (char *)stackblock();
6022 if (subevalvar(p, NULL, patloc, subtype,
6023 startloc, varflags, quotes) == 0) {
6024 int amount = expdest - (
6025 (char *)stackblock() + patloc - 1
6026 );
6027 STADJUST(-amount, expdest);
6028 }
6029 /* Remove any recorded regions beyond start of variable */
6030 removerecordregions(startloc);
6031 goto record;
6032 }
6033
6034 end:
6035 if (subtype != VSNORMAL) { /* skip to end of alternative */
6036 int nesting = 1;
6037 for (;;) {
6038 c = *p++;
6039 if (c == CTLESC)
6040 p++;
6041 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6042 if (varlen >= 0)
6043 argbackq = argbackq->next;
6044 } else if (c == CTLVAR) {
6045 if ((*p++ & VSTYPE) != VSNORMAL)
6046 nesting++;
6047 } else if (c == CTLENDVAR) {
6048 if (--nesting == 0)
6049 break;
6050 }
6051 }
6052 }
6053 return p;
6054}
6055
6056/*
6057 * Break the argument string into pieces based upon IFS and add the
6058 * strings to the argument list. The regions of the string to be
6059 * searched for IFS characters have been stored by recordregion.
6060 */
6061static void
6062ifsbreakup(char *string, struct arglist *arglist)
6063{
6064 struct ifsregion *ifsp;
6065 struct strlist *sp;
6066 char *start;
6067 char *p;
6068 char *q;
6069 const char *ifs, *realifs;
6070 int ifsspc;
6071 int nulonly;
6072
6073 start = string;
6074 if (ifslastp != NULL) {
6075 ifsspc = 0;
6076 nulonly = 0;
6077 realifs = ifsset() ? ifsval() : defifs;
6078 ifsp = &ifsfirst;
6079 do {
6080 p = string + ifsp->begoff;
6081 nulonly = ifsp->nulonly;
6082 ifs = nulonly ? nullstr : realifs;
6083 ifsspc = 0;
6084 while (p < string + ifsp->endoff) {
6085 q = p;
6086 if (*p == CTLESC)
6087 p++;
6088 if (!strchr(ifs, *p)) {
6089 p++;
6090 continue;
6091 }
6092 if (!nulonly)
6093 ifsspc = (strchr(defifs, *p) != NULL);
6094 /* Ignore IFS whitespace at start */
6095 if (q == start && ifsspc) {
6096 p++;
6097 start = p;
6098 continue;
6099 }
6100 *q = '\0';
6101 sp = stalloc(sizeof(*sp));
6102 sp->text = start;
6103 *arglist->lastp = sp;
6104 arglist->lastp = &sp->next;
6105 p++;
6106 if (!nulonly) {
6107 for (;;) {
6108 if (p >= string + ifsp->endoff) {
6109 break;
6110 }
6111 q = p;
6112 if (*p == CTLESC)
6113 p++;
6114 if (strchr(ifs, *p) == NULL ) {
6115 p = q;
6116 break;
6117 } else if (strchr(defifs, *p) == NULL) {
6118 if (ifsspc) {
6119 p++;
6120 ifsspc = 0;
6121 } else {
6122 p = q;
6123 break;
6124 }
6125 } else
6126 p++;
6127 }
6128 }
6129 start = p;
6130 } /* while */
6131 ifsp = ifsp->next;
6132 } while (ifsp != NULL);
6133 if (nulonly)
6134 goto add;
6135 }
6136
6137 if (!*start)
6138 return;
6139
6140 add:
6141 sp = stalloc(sizeof(*sp));
6142 sp->text = start;
6143 *arglist->lastp = sp;
6144 arglist->lastp = &sp->next;
6145}
6146
6147static void
6148ifsfree(void)
6149{
6150 struct ifsregion *p;
6151
6152 INT_OFF;
6153 p = ifsfirst.next;
6154 do {
6155 struct ifsregion *ifsp;
6156 ifsp = p->next;
6157 free(p);
6158 p = ifsp;
6159 } while (p);
6160 ifslastp = NULL;
6161 ifsfirst.next = NULL;
6162 INT_ON;
6163}
6164
6165/*
6166 * Add a file name to the list.
6167 */
6168static void
6169addfname(const char *name)
6170{
6171 struct strlist *sp;
6172
6173 sp = stalloc(sizeof(*sp));
6174 sp->text = ststrdup(name);
6175 *exparg.lastp = sp;
6176 exparg.lastp = &sp->next;
6177}
6178
6179static char *expdir;
6180
6181/*
6182 * Do metacharacter (i.e. *, ?, [...]) expansion.
6183 */
6184static void
6185expmeta(char *enddir, char *name)
6186{
6187 char *p;
6188 const char *cp;
6189 char *start;
6190 char *endname;
6191 int metaflag;
6192 struct stat statb;
6193 DIR *dirp;
6194 struct dirent *dp;
6195 int atend;
6196 int matchdot;
6197
6198 metaflag = 0;
6199 start = name;
6200 for (p = name; *p; p++) {
6201 if (*p == '*' || *p == '?')
6202 metaflag = 1;
6203 else if (*p == '[') {
6204 char *q = p + 1;
6205 if (*q == '!')
6206 q++;
6207 for (;;) {
6208 if (*q == '\\')
6209 q++;
6210 if (*q == '/' || *q == '\0')
6211 break;
6212 if (*++q == ']') {
6213 metaflag = 1;
6214 break;
6215 }
6216 }
6217 } else if (*p == '\\')
6218 p++;
6219 else if (*p == '/') {
6220 if (metaflag)
6221 goto out;
6222 start = p + 1;
6223 }
6224 }
6225 out:
6226 if (metaflag == 0) { /* we've reached the end of the file name */
6227 if (enddir != expdir)
6228 metaflag++;
6229 p = name;
6230 do {
6231 if (*p == '\\')
6232 p++;
6233 *enddir++ = *p;
6234 } while (*p++);
6235 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6236 addfname(expdir);
6237 return;
6238 }
6239 endname = p;
6240 if (name < start) {
6241 p = name;
6242 do {
6243 if (*p == '\\')
6244 p++;
6245 *enddir++ = *p++;
6246 } while (p < start);
6247 }
6248 if (enddir == expdir) {
6249 cp = ".";
6250 } else if (enddir == expdir + 1 && *expdir == '/') {
6251 cp = "/";
6252 } else {
6253 cp = expdir;
6254 enddir[-1] = '\0';
6255 }
6256 dirp = opendir(cp);
6257 if (dirp == NULL)
6258 return;
6259 if (enddir != expdir)
6260 enddir[-1] = '/';
6261 if (*endname == 0) {
6262 atend = 1;
6263 } else {
6264 atend = 0;
6265 *endname++ = '\0';
6266 }
6267 matchdot = 0;
6268 p = start;
6269 if (*p == '\\')
6270 p++;
6271 if (*p == '.')
6272 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006273 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006274 if (dp->d_name[0] == '.' && ! matchdot)
6275 continue;
6276 if (pmatch(start, dp->d_name)) {
6277 if (atend) {
6278 strcpy(enddir, dp->d_name);
6279 addfname(expdir);
6280 } else {
6281 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6282 continue;
6283 p[-1] = '/';
6284 expmeta(p, endname);
6285 }
6286 }
6287 }
6288 closedir(dirp);
6289 if (! atend)
6290 endname[-1] = '/';
6291}
6292
6293static struct strlist *
6294msort(struct strlist *list, int len)
6295{
6296 struct strlist *p, *q = NULL;
6297 struct strlist **lpp;
6298 int half;
6299 int n;
6300
6301 if (len <= 1)
6302 return list;
6303 half = len >> 1;
6304 p = list;
6305 for (n = half; --n >= 0; ) {
6306 q = p;
6307 p = p->next;
6308 }
6309 q->next = NULL; /* terminate first half of list */
6310 q = msort(list, half); /* sort first half of list */
6311 p = msort(p, len - half); /* sort second half */
6312 lpp = &list;
6313 for (;;) {
6314#if ENABLE_LOCALE_SUPPORT
6315 if (strcoll(p->text, q->text) < 0)
6316#else
6317 if (strcmp(p->text, q->text) < 0)
6318#endif
6319 {
6320 *lpp = p;
6321 lpp = &p->next;
6322 p = *lpp;
6323 if (p == NULL) {
6324 *lpp = q;
6325 break;
6326 }
6327 } else {
6328 *lpp = q;
6329 lpp = &q->next;
6330 q = *lpp;
6331 if (q == NULL) {
6332 *lpp = p;
6333 break;
6334 }
6335 }
6336 }
6337 return list;
6338}
6339
6340/*
6341 * Sort the results of file name expansion. It calculates the number of
6342 * strings to sort and then calls msort (short for merge sort) to do the
6343 * work.
6344 */
6345static struct strlist *
6346expsort(struct strlist *str)
6347{
6348 int len;
6349 struct strlist *sp;
6350
6351 len = 0;
6352 for (sp = str; sp; sp = sp->next)
6353 len++;
6354 return msort(str, len);
6355}
6356
6357static void
6358expandmeta(struct strlist *str, int flag)
6359{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006360 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006361 '*', '?', '[', 0
6362 };
6363 /* TODO - EXP_REDIR */
6364
6365 while (str) {
6366 struct strlist **savelastp;
6367 struct strlist *sp;
6368 char *p;
6369
6370 if (fflag)
6371 goto nometa;
6372 if (!strpbrk(str->text, metachars))
6373 goto nometa;
6374 savelastp = exparg.lastp;
6375
6376 INT_OFF;
6377 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6378 {
6379 int i = strlen(str->text);
6380 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6381 }
6382
6383 expmeta(expdir, p);
6384 free(expdir);
6385 if (p != str->text)
6386 free(p);
6387 INT_ON;
6388 if (exparg.lastp == savelastp) {
6389 /*
6390 * no matches
6391 */
6392 nometa:
6393 *exparg.lastp = str;
6394 rmescapes(str->text);
6395 exparg.lastp = &str->next;
6396 } else {
6397 *exparg.lastp = NULL;
6398 *savelastp = sp = expsort(*savelastp);
6399 while (sp->next != NULL)
6400 sp = sp->next;
6401 exparg.lastp = &sp->next;
6402 }
6403 str = str->next;
6404 }
6405}
6406
6407/*
6408 * Perform variable substitution and command substitution on an argument,
6409 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6410 * perform splitting and file name expansion. When arglist is NULL, perform
6411 * here document expansion.
6412 */
6413static void
6414expandarg(union node *arg, struct arglist *arglist, int flag)
6415{
6416 struct strlist *sp;
6417 char *p;
6418
6419 argbackq = arg->narg.backquote;
6420 STARTSTACKSTR(expdest);
6421 ifsfirst.next = NULL;
6422 ifslastp = NULL;
6423 argstr(arg->narg.text, flag);
6424 p = _STPUTC('\0', expdest);
6425 expdest = p - 1;
6426 if (arglist == NULL) {
6427 return; /* here document expanded */
6428 }
6429 p = grabstackstr(p);
6430 exparg.lastp = &exparg.list;
6431 /*
6432 * TODO - EXP_REDIR
6433 */
6434 if (flag & EXP_FULL) {
6435 ifsbreakup(p, &exparg);
6436 *exparg.lastp = NULL;
6437 exparg.lastp = &exparg.list;
6438 expandmeta(exparg.list, flag);
6439 } else {
6440 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6441 rmescapes(p);
6442 sp = stalloc(sizeof(*sp));
6443 sp->text = p;
6444 *exparg.lastp = sp;
6445 exparg.lastp = &sp->next;
6446 }
6447 if (ifsfirst.next)
6448 ifsfree();
6449 *exparg.lastp = NULL;
6450 if (exparg.list) {
6451 *arglist->lastp = exparg.list;
6452 arglist->lastp = exparg.lastp;
6453 }
6454}
6455
6456/*
6457 * Expand shell variables and backquotes inside a here document.
6458 */
6459static void
6460expandhere(union node *arg, int fd)
6461{
6462 herefd = fd;
6463 expandarg(arg, (struct arglist *)NULL, 0);
6464 full_write(fd, stackblock(), expdest - (char *)stackblock());
6465}
6466
6467/*
6468 * Returns true if the pattern matches the string.
6469 */
6470static int
6471patmatch(char *pattern, const char *string)
6472{
6473 return pmatch(preglob(pattern, 0, 0), string);
6474}
6475
6476/*
6477 * See if a pattern matches in a case statement.
6478 */
6479static int
6480casematch(union node *pattern, char *val)
6481{
6482 struct stackmark smark;
6483 int result;
6484
6485 setstackmark(&smark);
6486 argbackq = pattern->narg.backquote;
6487 STARTSTACKSTR(expdest);
6488 ifslastp = NULL;
6489 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6490 STACKSTRNUL(expdest);
6491 result = patmatch(stackblock(), val);
6492 popstackmark(&smark);
6493 return result;
6494}
6495
6496
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006497/* ============ find_command */
6498
6499struct builtincmd {
6500 const char *name;
6501 int (*builtin)(int, char **);
6502 /* unsigned flags; */
6503};
6504#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006505/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006506 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006507#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006508#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006509
6510struct cmdentry {
6511 int cmdtype;
6512 union param {
6513 int index;
6514 const struct builtincmd *cmd;
6515 struct funcnode *func;
6516 } u;
6517};
6518/* values of cmdtype */
6519#define CMDUNKNOWN -1 /* no entry in table for command */
6520#define CMDNORMAL 0 /* command is an executable program */
6521#define CMDFUNCTION 1 /* command is a shell function */
6522#define CMDBUILTIN 2 /* command is a shell builtin */
6523
6524/* action to find_command() */
6525#define DO_ERR 0x01 /* prints errors */
6526#define DO_ABS 0x02 /* checks absolute paths */
6527#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6528#define DO_ALTPATH 0x08 /* using alternate path */
6529#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6530
6531static void find_command(char *, struct cmdentry *, int, const char *);
6532
6533
6534/* ============ Hashing commands */
6535
6536/*
6537 * When commands are first encountered, they are entered in a hash table.
6538 * This ensures that a full path search will not have to be done for them
6539 * on each invocation.
6540 *
6541 * We should investigate converting to a linear search, even though that
6542 * would make the command name "hash" a misnomer.
6543 */
6544
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006545#define ARB 1 /* actual size determined at run time */
6546
6547struct tblentry {
6548 struct tblentry *next; /* next entry in hash chain */
6549 union param param; /* definition of builtin function */
6550 short cmdtype; /* index identifying command */
6551 char rehash; /* if set, cd done since entry created */
6552 char cmdname[ARB]; /* name of command */
6553};
6554
Denis Vlasenko01631112007-12-16 17:20:38 +00006555static struct tblentry **cmdtable;
6556#define INIT_G_cmdtable() do { \
6557 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6558} while (0)
6559
6560static int builtinloc = -1; /* index in path of %builtin, or -1 */
6561
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006562
6563static void
6564tryexec(char *cmd, char **argv, char **envp)
6565{
6566 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006567
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006568#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006569 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006570 int a = find_applet_by_name(cmd);
6571 if (a >= 0) {
6572 if (APPLET_IS_NOEXEC(a))
6573 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006574 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006575 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006576 /* If they called chroot or otherwise made the binary no longer
6577 * executable, fall through */
6578 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006579 }
6580#endif
6581
6582 repeat:
6583#ifdef SYSV
6584 do {
6585 execve(cmd, argv, envp);
6586 } while (errno == EINTR);
6587#else
6588 execve(cmd, argv, envp);
6589#endif
6590 if (repeated++) {
6591 free(argv);
6592 } else if (errno == ENOEXEC) {
6593 char **ap;
6594 char **new;
6595
6596 for (ap = argv; *ap; ap++)
6597 ;
6598 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6599 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006600 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006601 ap += 2;
6602 argv++;
6603 while ((*ap++ = *argv++))
6604 ;
6605 argv = new;
6606 goto repeat;
6607 }
6608}
6609
6610/*
6611 * Exec a program. Never returns. If you change this routine, you may
6612 * have to change the find_command routine as well.
6613 */
6614#define environment() listvars(VEXPORT, VUNSET, 0)
6615static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6616static void
6617shellexec(char **argv, const char *path, int idx)
6618{
6619 char *cmdname;
6620 int e;
6621 char **envp;
6622 int exerrno;
6623
6624 clearredir(1);
6625 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006626 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006627#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006628 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006629#endif
6630 ) {
6631 tryexec(argv[0], argv, envp);
6632 e = errno;
6633 } else {
6634 e = ENOENT;
6635 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6636 if (--idx < 0 && pathopt == NULL) {
6637 tryexec(cmdname, argv, envp);
6638 if (errno != ENOENT && errno != ENOTDIR)
6639 e = errno;
6640 }
6641 stunalloc(cmdname);
6642 }
6643 }
6644
6645 /* Map to POSIX errors */
6646 switch (e) {
6647 case EACCES:
6648 exerrno = 126;
6649 break;
6650 case ENOENT:
6651 exerrno = 127;
6652 break;
6653 default:
6654 exerrno = 2;
6655 break;
6656 }
6657 exitstatus = exerrno;
6658 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6659 argv[0], e, suppressint ));
6660 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6661 /* NOTREACHED */
6662}
6663
6664static void
6665printentry(struct tblentry *cmdp)
6666{
6667 int idx;
6668 const char *path;
6669 char *name;
6670
6671 idx = cmdp->param.index;
6672 path = pathval();
6673 do {
6674 name = padvance(&path, cmdp->cmdname);
6675 stunalloc(name);
6676 } while (--idx >= 0);
6677 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6678}
6679
6680/*
6681 * Clear out command entries. The argument specifies the first entry in
6682 * PATH which has changed.
6683 */
6684static void
6685clearcmdentry(int firstchange)
6686{
6687 struct tblentry **tblp;
6688 struct tblentry **pp;
6689 struct tblentry *cmdp;
6690
6691 INT_OFF;
6692 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6693 pp = tblp;
6694 while ((cmdp = *pp) != NULL) {
6695 if ((cmdp->cmdtype == CMDNORMAL &&
6696 cmdp->param.index >= firstchange)
6697 || (cmdp->cmdtype == CMDBUILTIN &&
6698 builtinloc >= firstchange)
6699 ) {
6700 *pp = cmdp->next;
6701 free(cmdp);
6702 } else {
6703 pp = &cmdp->next;
6704 }
6705 }
6706 }
6707 INT_ON;
6708}
6709
6710/*
6711 * Locate a command in the command hash table. If "add" is nonzero,
6712 * add the command to the table if it is not already present. The
6713 * variable "lastcmdentry" is set to point to the address of the link
6714 * pointing to the entry, so that delete_cmd_entry can delete the
6715 * entry.
6716 *
6717 * Interrupts must be off if called with add != 0.
6718 */
6719static struct tblentry **lastcmdentry;
6720
6721static struct tblentry *
6722cmdlookup(const char *name, int add)
6723{
6724 unsigned int hashval;
6725 const char *p;
6726 struct tblentry *cmdp;
6727 struct tblentry **pp;
6728
6729 p = name;
6730 hashval = (unsigned char)*p << 4;
6731 while (*p)
6732 hashval += (unsigned char)*p++;
6733 hashval &= 0x7FFF;
6734 pp = &cmdtable[hashval % CMDTABLESIZE];
6735 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6736 if (strcmp(cmdp->cmdname, name) == 0)
6737 break;
6738 pp = &cmdp->next;
6739 }
6740 if (add && cmdp == NULL) {
6741 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6742 + strlen(name) + 1);
6743 cmdp->next = NULL;
6744 cmdp->cmdtype = CMDUNKNOWN;
6745 strcpy(cmdp->cmdname, name);
6746 }
6747 lastcmdentry = pp;
6748 return cmdp;
6749}
6750
6751/*
6752 * Delete the command entry returned on the last lookup.
6753 */
6754static void
6755delete_cmd_entry(void)
6756{
6757 struct tblentry *cmdp;
6758
6759 INT_OFF;
6760 cmdp = *lastcmdentry;
6761 *lastcmdentry = cmdp->next;
6762 if (cmdp->cmdtype == CMDFUNCTION)
6763 freefunc(cmdp->param.func);
6764 free(cmdp);
6765 INT_ON;
6766}
6767
6768/*
6769 * Add a new command entry, replacing any existing command entry for
6770 * the same name - except special builtins.
6771 */
6772static void
6773addcmdentry(char *name, struct cmdentry *entry)
6774{
6775 struct tblentry *cmdp;
6776
6777 cmdp = cmdlookup(name, 1);
6778 if (cmdp->cmdtype == CMDFUNCTION) {
6779 freefunc(cmdp->param.func);
6780 }
6781 cmdp->cmdtype = entry->cmdtype;
6782 cmdp->param = entry->u;
6783 cmdp->rehash = 0;
6784}
6785
6786static int
6787hashcmd(int argc, char **argv)
6788{
6789 struct tblentry **pp;
6790 struct tblentry *cmdp;
6791 int c;
6792 struct cmdentry entry;
6793 char *name;
6794
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006795 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006796 clearcmdentry(0);
6797 return 0;
6798 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006799
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006800 if (*argptr == NULL) {
6801 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6802 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6803 if (cmdp->cmdtype == CMDNORMAL)
6804 printentry(cmdp);
6805 }
6806 }
6807 return 0;
6808 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006809
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006810 c = 0;
6811 while ((name = *argptr) != NULL) {
6812 cmdp = cmdlookup(name, 0);
6813 if (cmdp != NULL
6814 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006815 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6816 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006817 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006818 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006819 find_command(name, &entry, DO_ERR, pathval());
6820 if (entry.cmdtype == CMDUNKNOWN)
6821 c = 1;
6822 argptr++;
6823 }
6824 return c;
6825}
6826
6827/*
6828 * Called when a cd is done. Marks all commands so the next time they
6829 * are executed they will be rehashed.
6830 */
6831static void
6832hashcd(void)
6833{
6834 struct tblentry **pp;
6835 struct tblentry *cmdp;
6836
6837 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6838 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006839 if (cmdp->cmdtype == CMDNORMAL
6840 || (cmdp->cmdtype == CMDBUILTIN
6841 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6842 && builtinloc > 0)
6843 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006844 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006845 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006846 }
6847 }
6848}
6849
6850/*
6851 * Fix command hash table when PATH changed.
6852 * Called before PATH is changed. The argument is the new value of PATH;
6853 * pathval() still returns the old value at this point.
6854 * Called with interrupts off.
6855 */
6856static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006857changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006858{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006859 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006861 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006862 int idx_bltin;
6863
6864 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006865 firstchange = 9999; /* assume no change */
6866 idx = 0;
6867 idx_bltin = -1;
6868 for (;;) {
6869 if (*old != *new) {
6870 firstchange = idx;
6871 if ((*old == '\0' && *new == ':')
6872 || (*old == ':' && *new == '\0'))
6873 firstchange++;
6874 old = new; /* ignore subsequent differences */
6875 }
6876 if (*new == '\0')
6877 break;
6878 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6879 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006880 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006881 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006882 new++, old++;
6883 }
6884 if (builtinloc < 0 && idx_bltin >= 0)
6885 builtinloc = idx_bltin; /* zap builtins */
6886 if (builtinloc >= 0 && idx_bltin < 0)
6887 firstchange = 0;
6888 clearcmdentry(firstchange);
6889 builtinloc = idx_bltin;
6890}
6891
6892#define TEOF 0
6893#define TNL 1
6894#define TREDIR 2
6895#define TWORD 3
6896#define TSEMI 4
6897#define TBACKGND 5
6898#define TAND 6
6899#define TOR 7
6900#define TPIPE 8
6901#define TLP 9
6902#define TRP 10
6903#define TENDCASE 11
6904#define TENDBQUOTE 12
6905#define TNOT 13
6906#define TCASE 14
6907#define TDO 15
6908#define TDONE 16
6909#define TELIF 17
6910#define TELSE 18
6911#define TESAC 19
6912#define TFI 20
6913#define TFOR 21
6914#define TIF 22
6915#define TIN 23
6916#define TTHEN 24
6917#define TUNTIL 25
6918#define TWHILE 26
6919#define TBEGIN 27
6920#define TEND 28
6921
6922/* first char is indicating which tokens mark the end of a list */
6923static const char *const tokname_array[] = {
6924 "\1end of file",
6925 "\0newline",
6926 "\0redirection",
6927 "\0word",
6928 "\0;",
6929 "\0&",
6930 "\0&&",
6931 "\0||",
6932 "\0|",
6933 "\0(",
6934 "\1)",
6935 "\1;;",
6936 "\1`",
6937#define KWDOFFSET 13
6938 /* the following are keywords */
6939 "\0!",
6940 "\0case",
6941 "\1do",
6942 "\1done",
6943 "\1elif",
6944 "\1else",
6945 "\1esac",
6946 "\1fi",
6947 "\0for",
6948 "\0if",
6949 "\0in",
6950 "\1then",
6951 "\0until",
6952 "\0while",
6953 "\0{",
6954 "\1}",
6955};
6956
6957static const char *
6958tokname(int tok)
6959{
6960 static char buf[16];
6961
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006962//try this:
6963//if (tok < TSEMI) return tokname_array[tok] + 1;
6964//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6965//return buf;
6966
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006967 if (tok >= TSEMI)
6968 buf[0] = '"';
6969 sprintf(buf + (tok >= TSEMI), "%s%c",
6970 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6971 return buf;
6972}
6973
6974/* Wrapper around strcmp for qsort/bsearch/... */
6975static int
6976pstrcmp(const void *a, const void *b)
6977{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006978 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006979}
6980
6981static const char *const *
6982findkwd(const char *s)
6983{
6984 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00006985 ARRAY_SIZE(tokname_array) - KWDOFFSET,
6986 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987}
6988
6989/*
6990 * Locate and print what a word is...
6991 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992static int
6993describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006994{
6995 struct cmdentry entry;
6996 struct tblentry *cmdp;
6997#if ENABLE_ASH_ALIAS
6998 const struct alias *ap;
6999#endif
7000 const char *path = pathval();
7001
7002 if (describe_command_verbose) {
7003 out1str(command);
7004 }
7005
7006 /* First look at the keywords */
7007 if (findkwd(command)) {
7008 out1str(describe_command_verbose ? " is a shell keyword" : command);
7009 goto out;
7010 }
7011
7012#if ENABLE_ASH_ALIAS
7013 /* Then look at the aliases */
7014 ap = lookupalias(command, 0);
7015 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007016 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007017 out1str("alias ");
7018 printalias(ap);
7019 return 0;
7020 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007021 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007022 goto out;
7023 }
7024#endif
7025 /* Then check if it is a tracked alias */
7026 cmdp = cmdlookup(command, 0);
7027 if (cmdp != NULL) {
7028 entry.cmdtype = cmdp->cmdtype;
7029 entry.u = cmdp->param;
7030 } else {
7031 /* Finally use brute force */
7032 find_command(command, &entry, DO_ABS, path);
7033 }
7034
7035 switch (entry.cmdtype) {
7036 case CMDNORMAL: {
7037 int j = entry.u.index;
7038 char *p;
7039 if (j == -1) {
7040 p = command;
7041 } else {
7042 do {
7043 p = padvance(&path, command);
7044 stunalloc(p);
7045 } while (--j >= 0);
7046 }
7047 if (describe_command_verbose) {
7048 out1fmt(" is%s %s",
7049 (cmdp ? " a tracked alias for" : nullstr), p
7050 );
7051 } else {
7052 out1str(p);
7053 }
7054 break;
7055 }
7056
7057 case CMDFUNCTION:
7058 if (describe_command_verbose) {
7059 out1str(" is a shell function");
7060 } else {
7061 out1str(command);
7062 }
7063 break;
7064
7065 case CMDBUILTIN:
7066 if (describe_command_verbose) {
7067 out1fmt(" is a %sshell builtin",
7068 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7069 "special " : nullstr
7070 );
7071 } else {
7072 out1str(command);
7073 }
7074 break;
7075
7076 default:
7077 if (describe_command_verbose) {
7078 out1str(": not found\n");
7079 }
7080 return 127;
7081 }
7082 out:
7083 outstr("\n", stdout);
7084 return 0;
7085}
7086
7087static int
7088typecmd(int argc, char **argv)
7089{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007090 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007091 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007092 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007093
Denis Vlasenko46846e22007-05-20 13:08:31 +00007094 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007095 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007096 i++;
7097 verbose = 0;
7098 }
7099 while (i < argc) {
7100 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007101 }
7102 return err;
7103}
7104
7105#if ENABLE_ASH_CMDCMD
7106static int
7107commandcmd(int argc, char **argv)
7108{
7109 int c;
7110 enum {
7111 VERIFY_BRIEF = 1,
7112 VERIFY_VERBOSE = 2,
7113 } verify = 0;
7114
7115 while ((c = nextopt("pvV")) != '\0')
7116 if (c == 'V')
7117 verify |= VERIFY_VERBOSE;
7118 else if (c == 'v')
7119 verify |= VERIFY_BRIEF;
7120#if DEBUG
7121 else if (c != 'p')
7122 abort();
7123#endif
7124 if (verify)
7125 return describe_command(*argptr, verify - VERIFY_BRIEF);
7126
7127 return 0;
7128}
7129#endif
7130
7131
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007132/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007133
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007134static int funcblocksize; /* size of structures in function */
7135static int funcstringsize; /* size of strings in node */
7136static void *funcblock; /* block to allocate function from */
7137static char *funcstring; /* block to allocate strings from */
7138
Eric Andersencb57d552001-06-28 07:25:16 +00007139/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007140#define EV_EXIT 01 /* exit after evaluating tree */
7141#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7142#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007143
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007144static const short nodesize[26] = {
7145 SHELL_ALIGN(sizeof(struct ncmd)),
7146 SHELL_ALIGN(sizeof(struct npipe)),
7147 SHELL_ALIGN(sizeof(struct nredir)),
7148 SHELL_ALIGN(sizeof(struct nredir)),
7149 SHELL_ALIGN(sizeof(struct nredir)),
7150 SHELL_ALIGN(sizeof(struct nbinary)),
7151 SHELL_ALIGN(sizeof(struct nbinary)),
7152 SHELL_ALIGN(sizeof(struct nbinary)),
7153 SHELL_ALIGN(sizeof(struct nif)),
7154 SHELL_ALIGN(sizeof(struct nbinary)),
7155 SHELL_ALIGN(sizeof(struct nbinary)),
7156 SHELL_ALIGN(sizeof(struct nfor)),
7157 SHELL_ALIGN(sizeof(struct ncase)),
7158 SHELL_ALIGN(sizeof(struct nclist)),
7159 SHELL_ALIGN(sizeof(struct narg)),
7160 SHELL_ALIGN(sizeof(struct narg)),
7161 SHELL_ALIGN(sizeof(struct nfile)),
7162 SHELL_ALIGN(sizeof(struct nfile)),
7163 SHELL_ALIGN(sizeof(struct nfile)),
7164 SHELL_ALIGN(sizeof(struct nfile)),
7165 SHELL_ALIGN(sizeof(struct nfile)),
7166 SHELL_ALIGN(sizeof(struct ndup)),
7167 SHELL_ALIGN(sizeof(struct ndup)),
7168 SHELL_ALIGN(sizeof(struct nhere)),
7169 SHELL_ALIGN(sizeof(struct nhere)),
7170 SHELL_ALIGN(sizeof(struct nnot)),
7171};
7172
7173static void calcsize(union node *n);
7174
7175static void
7176sizenodelist(struct nodelist *lp)
7177{
7178 while (lp) {
7179 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7180 calcsize(lp->n);
7181 lp = lp->next;
7182 }
7183}
7184
7185static void
7186calcsize(union node *n)
7187{
7188 if (n == NULL)
7189 return;
7190 funcblocksize += nodesize[n->type];
7191 switch (n->type) {
7192 case NCMD:
7193 calcsize(n->ncmd.redirect);
7194 calcsize(n->ncmd.args);
7195 calcsize(n->ncmd.assign);
7196 break;
7197 case NPIPE:
7198 sizenodelist(n->npipe.cmdlist);
7199 break;
7200 case NREDIR:
7201 case NBACKGND:
7202 case NSUBSHELL:
7203 calcsize(n->nredir.redirect);
7204 calcsize(n->nredir.n);
7205 break;
7206 case NAND:
7207 case NOR:
7208 case NSEMI:
7209 case NWHILE:
7210 case NUNTIL:
7211 calcsize(n->nbinary.ch2);
7212 calcsize(n->nbinary.ch1);
7213 break;
7214 case NIF:
7215 calcsize(n->nif.elsepart);
7216 calcsize(n->nif.ifpart);
7217 calcsize(n->nif.test);
7218 break;
7219 case NFOR:
7220 funcstringsize += strlen(n->nfor.var) + 1;
7221 calcsize(n->nfor.body);
7222 calcsize(n->nfor.args);
7223 break;
7224 case NCASE:
7225 calcsize(n->ncase.cases);
7226 calcsize(n->ncase.expr);
7227 break;
7228 case NCLIST:
7229 calcsize(n->nclist.body);
7230 calcsize(n->nclist.pattern);
7231 calcsize(n->nclist.next);
7232 break;
7233 case NDEFUN:
7234 case NARG:
7235 sizenodelist(n->narg.backquote);
7236 funcstringsize += strlen(n->narg.text) + 1;
7237 calcsize(n->narg.next);
7238 break;
7239 case NTO:
7240 case NCLOBBER:
7241 case NFROM:
7242 case NFROMTO:
7243 case NAPPEND:
7244 calcsize(n->nfile.fname);
7245 calcsize(n->nfile.next);
7246 break;
7247 case NTOFD:
7248 case NFROMFD:
7249 calcsize(n->ndup.vname);
7250 calcsize(n->ndup.next);
7251 break;
7252 case NHERE:
7253 case NXHERE:
7254 calcsize(n->nhere.doc);
7255 calcsize(n->nhere.next);
7256 break;
7257 case NNOT:
7258 calcsize(n->nnot.com);
7259 break;
7260 };
7261}
7262
7263static char *
7264nodeckstrdup(char *s)
7265{
7266 char *rtn = funcstring;
7267
7268 strcpy(funcstring, s);
7269 funcstring += strlen(s) + 1;
7270 return rtn;
7271}
7272
7273static union node *copynode(union node *);
7274
7275static struct nodelist *
7276copynodelist(struct nodelist *lp)
7277{
7278 struct nodelist *start;
7279 struct nodelist **lpp;
7280
7281 lpp = &start;
7282 while (lp) {
7283 *lpp = funcblock;
7284 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7285 (*lpp)->n = copynode(lp->n);
7286 lp = lp->next;
7287 lpp = &(*lpp)->next;
7288 }
7289 *lpp = NULL;
7290 return start;
7291}
7292
7293static union node *
7294copynode(union node *n)
7295{
7296 union node *new;
7297
7298 if (n == NULL)
7299 return NULL;
7300 new = funcblock;
7301 funcblock = (char *) funcblock + nodesize[n->type];
7302
7303 switch (n->type) {
7304 case NCMD:
7305 new->ncmd.redirect = copynode(n->ncmd.redirect);
7306 new->ncmd.args = copynode(n->ncmd.args);
7307 new->ncmd.assign = copynode(n->ncmd.assign);
7308 break;
7309 case NPIPE:
7310 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7311 new->npipe.backgnd = n->npipe.backgnd;
7312 break;
7313 case NREDIR:
7314 case NBACKGND:
7315 case NSUBSHELL:
7316 new->nredir.redirect = copynode(n->nredir.redirect);
7317 new->nredir.n = copynode(n->nredir.n);
7318 break;
7319 case NAND:
7320 case NOR:
7321 case NSEMI:
7322 case NWHILE:
7323 case NUNTIL:
7324 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7325 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7326 break;
7327 case NIF:
7328 new->nif.elsepart = copynode(n->nif.elsepart);
7329 new->nif.ifpart = copynode(n->nif.ifpart);
7330 new->nif.test = copynode(n->nif.test);
7331 break;
7332 case NFOR:
7333 new->nfor.var = nodeckstrdup(n->nfor.var);
7334 new->nfor.body = copynode(n->nfor.body);
7335 new->nfor.args = copynode(n->nfor.args);
7336 break;
7337 case NCASE:
7338 new->ncase.cases = copynode(n->ncase.cases);
7339 new->ncase.expr = copynode(n->ncase.expr);
7340 break;
7341 case NCLIST:
7342 new->nclist.body = copynode(n->nclist.body);
7343 new->nclist.pattern = copynode(n->nclist.pattern);
7344 new->nclist.next = copynode(n->nclist.next);
7345 break;
7346 case NDEFUN:
7347 case NARG:
7348 new->narg.backquote = copynodelist(n->narg.backquote);
7349 new->narg.text = nodeckstrdup(n->narg.text);
7350 new->narg.next = copynode(n->narg.next);
7351 break;
7352 case NTO:
7353 case NCLOBBER:
7354 case NFROM:
7355 case NFROMTO:
7356 case NAPPEND:
7357 new->nfile.fname = copynode(n->nfile.fname);
7358 new->nfile.fd = n->nfile.fd;
7359 new->nfile.next = copynode(n->nfile.next);
7360 break;
7361 case NTOFD:
7362 case NFROMFD:
7363 new->ndup.vname = copynode(n->ndup.vname);
7364 new->ndup.dupfd = n->ndup.dupfd;
7365 new->ndup.fd = n->ndup.fd;
7366 new->ndup.next = copynode(n->ndup.next);
7367 break;
7368 case NHERE:
7369 case NXHERE:
7370 new->nhere.doc = copynode(n->nhere.doc);
7371 new->nhere.fd = n->nhere.fd;
7372 new->nhere.next = copynode(n->nhere.next);
7373 break;
7374 case NNOT:
7375 new->nnot.com = copynode(n->nnot.com);
7376 break;
7377 };
7378 new->type = n->type;
7379 return new;
7380}
7381
7382/*
7383 * Make a copy of a parse tree.
7384 */
7385static struct funcnode *
7386copyfunc(union node *n)
7387{
7388 struct funcnode *f;
7389 size_t blocksize;
7390
7391 funcblocksize = offsetof(struct funcnode, n);
7392 funcstringsize = 0;
7393 calcsize(n);
7394 blocksize = funcblocksize;
7395 f = ckmalloc(blocksize + funcstringsize);
7396 funcblock = (char *) f + offsetof(struct funcnode, n);
7397 funcstring = (char *) f + blocksize;
7398 copynode(n);
7399 f->count = 0;
7400 return f;
7401}
7402
7403/*
7404 * Define a shell function.
7405 */
7406static void
7407defun(char *name, union node *func)
7408{
7409 struct cmdentry entry;
7410
7411 INT_OFF;
7412 entry.cmdtype = CMDFUNCTION;
7413 entry.u.func = copyfunc(func);
7414 addcmdentry(name, &entry);
7415 INT_ON;
7416}
7417
7418static int evalskip; /* set if we are skipping commands */
7419/* reasons for skipping commands (see comment on breakcmd routine) */
7420#define SKIPBREAK (1 << 0)
7421#define SKIPCONT (1 << 1)
7422#define SKIPFUNC (1 << 2)
7423#define SKIPFILE (1 << 3)
7424#define SKIPEVAL (1 << 4)
7425static int skipcount; /* number of levels to skip */
7426static int funcnest; /* depth of function calls */
7427
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007428/* forward decl way out to parsing code - dotrap needs it */
7429static int evalstring(char *s, int mask);
7430
7431/*
7432 * Called to execute a trap. Perhaps we should avoid entering new trap
7433 * handlers while we are executing a trap handler.
7434 */
7435static int
7436dotrap(void)
7437{
7438 char *p;
7439 char *q;
7440 int i;
7441 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007442 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007443
7444 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007445 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007446 xbarrier();
7447
7448 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7449 if (!*q)
7450 continue;
7451 *q = '\0';
7452
7453 p = trap[i + 1];
7454 if (!p)
7455 continue;
7456 skip = evalstring(p, SKIPEVAL);
7457 exitstatus = savestatus;
7458 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007459 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007460 }
7461
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007462 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007463}
7464
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007465/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007466static void evalloop(union node *, int);
7467static void evalfor(union node *, int);
7468static void evalcase(union node *, int);
7469static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007470static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007471static void evalpipe(union node *, int);
7472static void evalcommand(union node *, int);
7473static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007474static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007475
Eric Andersen62483552001-07-10 06:09:16 +00007476/*
Eric Andersenc470f442003-07-28 09:56:35 +00007477 * Evaluate a parse tree. The value is left in the global variable
7478 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007479 */
Eric Andersenc470f442003-07-28 09:56:35 +00007480static void
7481evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007482{
Eric Andersenc470f442003-07-28 09:56:35 +00007483 int checkexit = 0;
7484 void (*evalfn)(union node *, int);
7485 unsigned isor;
7486 int status;
7487 if (n == NULL) {
7488 TRACE(("evaltree(NULL) called\n"));
7489 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007490 }
Eric Andersenc470f442003-07-28 09:56:35 +00007491 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007492 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007493 switch (n->type) {
7494 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007495#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007496 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007497 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007498 break;
7499#endif
7500 case NNOT:
7501 evaltree(n->nnot.com, EV_TESTED);
7502 status = !exitstatus;
7503 goto setstatus;
7504 case NREDIR:
7505 expredir(n->nredir.redirect);
7506 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7507 if (!status) {
7508 evaltree(n->nredir.n, flags & EV_TESTED);
7509 status = exitstatus;
7510 }
7511 popredir(0);
7512 goto setstatus;
7513 case NCMD:
7514 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007515 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007516 if (eflag && !(flags & EV_TESTED))
7517 checkexit = ~0;
7518 goto calleval;
7519 case NFOR:
7520 evalfn = evalfor;
7521 goto calleval;
7522 case NWHILE:
7523 case NUNTIL:
7524 evalfn = evalloop;
7525 goto calleval;
7526 case NSUBSHELL:
7527 case NBACKGND:
7528 evalfn = evalsubshell;
7529 goto calleval;
7530 case NPIPE:
7531 evalfn = evalpipe;
7532 goto checkexit;
7533 case NCASE:
7534 evalfn = evalcase;
7535 goto calleval;
7536 case NAND:
7537 case NOR:
7538 case NSEMI:
7539#if NAND + 1 != NOR
7540#error NAND + 1 != NOR
7541#endif
7542#if NOR + 1 != NSEMI
7543#error NOR + 1 != NSEMI
7544#endif
7545 isor = n->type - NAND;
7546 evaltree(
7547 n->nbinary.ch1,
7548 (flags | ((isor >> 1) - 1)) & EV_TESTED
7549 );
7550 if (!exitstatus == isor)
7551 break;
7552 if (!evalskip) {
7553 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007554 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007555 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007556 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007557 evalfn(n, flags);
7558 break;
7559 }
7560 break;
7561 case NIF:
7562 evaltree(n->nif.test, EV_TESTED);
7563 if (evalskip)
7564 break;
7565 if (exitstatus == 0) {
7566 n = n->nif.ifpart;
7567 goto evaln;
7568 } else if (n->nif.elsepart) {
7569 n = n->nif.elsepart;
7570 goto evaln;
7571 }
7572 goto success;
7573 case NDEFUN:
7574 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007575 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007576 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007577 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007578 exitstatus = status;
7579 break;
7580 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007581 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007582 if ((checkexit & exitstatus))
7583 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007584 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007585 goto exexit;
7586
7587 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007588 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007589 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007590 }
Eric Andersen62483552001-07-10 06:09:16 +00007591}
7592
Eric Andersenc470f442003-07-28 09:56:35 +00007593#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7594static
7595#endif
7596void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7597
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007598static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007599
7600static void
7601evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007602{
7603 int status;
7604
7605 loopnest++;
7606 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007607 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007608 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007609 int i;
7610
Eric Andersencb57d552001-06-28 07:25:16 +00007611 evaltree(n->nbinary.ch1, EV_TESTED);
7612 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007613 skipping:
7614 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007615 evalskip = 0;
7616 continue;
7617 }
7618 if (evalskip == SKIPBREAK && --skipcount <= 0)
7619 evalskip = 0;
7620 break;
7621 }
Eric Andersenc470f442003-07-28 09:56:35 +00007622 i = exitstatus;
7623 if (n->type != NWHILE)
7624 i = !i;
7625 if (i != 0)
7626 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007627 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007628 status = exitstatus;
7629 if (evalskip)
7630 goto skipping;
7631 }
7632 loopnest--;
7633 exitstatus = status;
7634}
7635
Eric Andersenc470f442003-07-28 09:56:35 +00007636static void
7637evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007638{
7639 struct arglist arglist;
7640 union node *argp;
7641 struct strlist *sp;
7642 struct stackmark smark;
7643
7644 setstackmark(&smark);
7645 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007646 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007647 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007648 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007649 if (evalskip)
7650 goto out;
7651 }
7652 *arglist.lastp = NULL;
7653
7654 exitstatus = 0;
7655 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007656 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007657 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007658 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007659 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007660 if (evalskip) {
7661 if (evalskip == SKIPCONT && --skipcount <= 0) {
7662 evalskip = 0;
7663 continue;
7664 }
7665 if (evalskip == SKIPBREAK && --skipcount <= 0)
7666 evalskip = 0;
7667 break;
7668 }
7669 }
7670 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007671 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007672 popstackmark(&smark);
7673}
7674
Eric Andersenc470f442003-07-28 09:56:35 +00007675static void
7676evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007677{
7678 union node *cp;
7679 union node *patp;
7680 struct arglist arglist;
7681 struct stackmark smark;
7682
7683 setstackmark(&smark);
7684 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007685 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007686 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007687 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7688 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007689 if (casematch(patp, arglist.list->text)) {
7690 if (evalskip == 0) {
7691 evaltree(cp->nclist.body, flags);
7692 }
7693 goto out;
7694 }
7695 }
7696 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007697 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007698 popstackmark(&smark);
7699}
7700
Eric Andersenc470f442003-07-28 09:56:35 +00007701/*
7702 * Kick off a subshell to evaluate a tree.
7703 */
Eric Andersenc470f442003-07-28 09:56:35 +00007704static void
7705evalsubshell(union node *n, int flags)
7706{
7707 struct job *jp;
7708 int backgnd = (n->type == NBACKGND);
7709 int status;
7710
7711 expredir(n->nredir.redirect);
7712 if (!backgnd && flags & EV_EXIT && !trap[0])
7713 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007714 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007715 jp = makejob(n, 1);
7716 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007717 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007718 flags |= EV_EXIT;
7719 if (backgnd)
7720 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007721 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007722 redirect(n->nredir.redirect, 0);
7723 evaltreenr(n->nredir.n, flags);
7724 /* never returns */
7725 }
7726 status = 0;
7727 if (! backgnd)
7728 status = waitforjob(jp);
7729 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007730 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007731}
7732
Eric Andersenc470f442003-07-28 09:56:35 +00007733/*
7734 * Compute the names of the files in a redirection list.
7735 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007736static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007737static void
7738expredir(union node *n)
7739{
7740 union node *redir;
7741
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007742 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007743 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007744
7745 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007746 fn.lastp = &fn.list;
7747 switch (redir->type) {
7748 case NFROMTO:
7749 case NFROM:
7750 case NTO:
7751 case NCLOBBER:
7752 case NAPPEND:
7753 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7754 redir->nfile.expfname = fn.list->text;
7755 break;
7756 case NFROMFD:
7757 case NTOFD:
7758 if (redir->ndup.vname) {
7759 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007760 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007761 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007762 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007763 }
7764 break;
7765 }
7766 }
7767}
7768
Eric Andersencb57d552001-06-28 07:25:16 +00007769/*
Eric Andersencb57d552001-06-28 07:25:16 +00007770 * Evaluate a pipeline. All the processes in the pipeline are children
7771 * of the process creating the pipeline. (This differs from some versions
7772 * of the shell, which make the last process in a pipeline the parent
7773 * of all the rest.)
7774 */
Eric Andersenc470f442003-07-28 09:56:35 +00007775static void
7776evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007777{
7778 struct job *jp;
7779 struct nodelist *lp;
7780 int pipelen;
7781 int prevfd;
7782 int pip[2];
7783
Eric Andersenc470f442003-07-28 09:56:35 +00007784 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007785 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007786 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007787 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007788 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007789 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007790 jp = makejob(n, pipelen);
7791 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007792 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007793 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007794 pip[1] = -1;
7795 if (lp->next) {
7796 if (pipe(pip) < 0) {
7797 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007798 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007799 }
7800 }
7801 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007802 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007803 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007804 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007805 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007806 if (prevfd > 0) {
7807 dup2(prevfd, 0);
7808 close(prevfd);
7809 }
7810 if (pip[1] > 1) {
7811 dup2(pip[1], 1);
7812 close(pip[1]);
7813 }
Eric Andersenc470f442003-07-28 09:56:35 +00007814 evaltreenr(lp->n, flags);
7815 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007816 }
7817 if (prevfd >= 0)
7818 close(prevfd);
7819 prevfd = pip[0];
7820 close(pip[1]);
7821 }
Eric Andersencb57d552001-06-28 07:25:16 +00007822 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007823 exitstatus = waitforjob(jp);
7824 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007825 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007826 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007827}
7828
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007829/*
7830 * Controls whether the shell is interactive or not.
7831 */
7832static void
7833setinteractive(int on)
7834{
7835 static int is_interactive;
7836
7837 if (++on == is_interactive)
7838 return;
7839 is_interactive = on;
7840 setsignal(SIGINT);
7841 setsignal(SIGQUIT);
7842 setsignal(SIGTERM);
7843#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7844 if (is_interactive > 1) {
7845 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007846 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007847
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007848 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007849 out1fmt(
7850 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007851 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007852 "Enter 'help' for a list of built-in commands."
7853 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007854 bb_banner);
7855 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007856 }
7857 }
7858#endif
7859}
7860
7861#if ENABLE_FEATURE_EDITING_VI
7862#define setvimode(on) do { \
7863 if (on) line_input_state->flags |= VI_MODE; \
7864 else line_input_state->flags &= ~VI_MODE; \
7865} while (0)
7866#else
7867#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7868#endif
7869
7870static void
7871optschanged(void)
7872{
7873#if DEBUG
7874 opentrace();
7875#endif
7876 setinteractive(iflag);
7877 setjobctl(mflag);
7878 setvimode(viflag);
7879}
7880
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007881static struct localvar *localvars;
7882
7883/*
7884 * Called after a function returns.
7885 * Interrupts must be off.
7886 */
7887static void
7888poplocalvars(void)
7889{
7890 struct localvar *lvp;
7891 struct var *vp;
7892
7893 while ((lvp = localvars) != NULL) {
7894 localvars = lvp->next;
7895 vp = lvp->vp;
7896 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7897 if (vp == NULL) { /* $- saved */
7898 memcpy(optlist, lvp->text, sizeof(optlist));
7899 free((char*)lvp->text);
7900 optschanged();
7901 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7902 unsetvar(vp->text);
7903 } else {
7904 if (vp->func)
7905 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7906 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7907 free((char*)vp->text);
7908 vp->flags = lvp->flags;
7909 vp->text = lvp->text;
7910 }
7911 free(lvp);
7912 }
7913}
7914
7915static int
7916evalfun(struct funcnode *func, int argc, char **argv, int flags)
7917{
7918 volatile struct shparam saveparam;
7919 struct localvar *volatile savelocalvars;
7920 struct jmploc *volatile savehandler;
7921 struct jmploc jmploc;
7922 int e;
7923
7924 saveparam = shellparam;
7925 savelocalvars = localvars;
7926 e = setjmp(jmploc.loc);
7927 if (e) {
7928 goto funcdone;
7929 }
7930 INT_OFF;
7931 savehandler = exception_handler;
7932 exception_handler = &jmploc;
7933 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007934 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007935 func->count++;
7936 funcnest++;
7937 INT_ON;
7938 shellparam.nparam = argc - 1;
7939 shellparam.p = argv + 1;
7940#if ENABLE_ASH_GETOPTS
7941 shellparam.optind = 1;
7942 shellparam.optoff = -1;
7943#endif
7944 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007945 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007946 INT_OFF;
7947 funcnest--;
7948 freefunc(func);
7949 poplocalvars();
7950 localvars = savelocalvars;
7951 freeparam(&shellparam);
7952 shellparam = saveparam;
7953 exception_handler = savehandler;
7954 INT_ON;
7955 evalskip &= ~SKIPFUNC;
7956 return e;
7957}
7958
Denis Vlasenko131ae172007-02-18 13:00:19 +00007959#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007960static char **
7961parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007962{
7963 char *cp, c;
7964
7965 for (;;) {
7966 cp = *++argv;
7967 if (!cp)
7968 return 0;
7969 if (*cp++ != '-')
7970 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007971 c = *cp++;
7972 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007973 break;
7974 if (c == '-' && !*cp) {
7975 argv++;
7976 break;
7977 }
7978 do {
7979 switch (c) {
7980 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007981 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007982 break;
7983 default:
7984 /* run 'typecmd' for other options */
7985 return 0;
7986 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007987 c = *cp++;
7988 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007989 }
7990 return argv;
7991}
7992#endif
7993
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007994/*
7995 * Make a variable a local variable. When a variable is made local, it's
7996 * value and flags are saved in a localvar structure. The saved values
7997 * will be restored when the shell function returns. We handle the name
7998 * "-" as a special case.
7999 */
8000static void
8001mklocal(char *name)
8002{
8003 struct localvar *lvp;
8004 struct var **vpp;
8005 struct var *vp;
8006
8007 INT_OFF;
8008 lvp = ckmalloc(sizeof(struct localvar));
8009 if (LONE_DASH(name)) {
8010 char *p;
8011 p = ckmalloc(sizeof(optlist));
8012 lvp->text = memcpy(p, optlist, sizeof(optlist));
8013 vp = NULL;
8014 } else {
8015 char *eq;
8016
8017 vpp = hashvar(name);
8018 vp = *findvar(vpp, name);
8019 eq = strchr(name, '=');
8020 if (vp == NULL) {
8021 if (eq)
8022 setvareq(name, VSTRFIXED);
8023 else
8024 setvar(name, NULL, VSTRFIXED);
8025 vp = *vpp; /* the new variable */
8026 lvp->flags = VUNSET;
8027 } else {
8028 lvp->text = vp->text;
8029 lvp->flags = vp->flags;
8030 vp->flags |= VSTRFIXED|VTEXTFIXED;
8031 if (eq)
8032 setvareq(name, 0);
8033 }
8034 }
8035 lvp->vp = vp;
8036 lvp->next = localvars;
8037 localvars = lvp;
8038 INT_ON;
8039}
8040
8041/*
8042 * The "local" command.
8043 */
8044static int
8045localcmd(int argc, char **argv)
8046{
8047 char *name;
8048
8049 argv = argptr;
8050 while ((name = *argv++) != NULL) {
8051 mklocal(name);
8052 }
8053 return 0;
8054}
8055
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008056static int
8057falsecmd(int argc, char **argv)
8058{
8059 return 1;
8060}
8061
8062static int
8063truecmd(int argc, char **argv)
8064{
8065 return 0;
8066}
8067
8068static int
8069execcmd(int argc, char **argv)
8070{
8071 if (argc > 1) {
8072 iflag = 0; /* exit on error */
8073 mflag = 0;
8074 optschanged();
8075 shellexec(argv + 1, pathval(), 0);
8076 }
8077 return 0;
8078}
8079
8080/*
8081 * The return command.
8082 */
8083static int
8084returncmd(int argc, char **argv)
8085{
8086 /*
8087 * If called outside a function, do what ksh does;
8088 * skip the rest of the file.
8089 */
8090 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8091 return argv[1] ? number(argv[1]) : exitstatus;
8092}
8093
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008094/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008095static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008096static int dotcmd(int, char **);
8097static int evalcmd(int, char **);
8098#if ENABLE_ASH_BUILTIN_ECHO
8099static int echocmd(int, char **);
8100#endif
8101#if ENABLE_ASH_BUILTIN_TEST
8102static int testcmd(int, char **);
8103#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008104static int exitcmd(int, char **);
8105static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008106#if ENABLE_ASH_GETOPTS
8107static int getoptscmd(int, char **);
8108#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008109#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8110static int helpcmd(int argc, char **argv);
8111#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008112#if ENABLE_ASH_MATH_SUPPORT
8113static int letcmd(int, char **);
8114#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008115static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008116static int setcmd(int, char **);
8117static int shiftcmd(int, char **);
8118static int timescmd(int, char **);
8119static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008120static int umaskcmd(int, char **);
8121static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008122static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008123
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008124#define BUILTIN_NOSPEC "0"
8125#define BUILTIN_SPECIAL "1"
8126#define BUILTIN_REGULAR "2"
8127#define BUILTIN_SPEC_REG "3"
8128#define BUILTIN_ASSIGN "4"
8129#define BUILTIN_SPEC_ASSG "5"
8130#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008131#define BUILTIN_SPEC_REG_ASSG "7"
8132
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008133/* make sure to keep these in proper order since it is searched via bsearch() */
8134static const struct builtincmd builtintab[] = {
8135 { BUILTIN_SPEC_REG ".", dotcmd },
8136 { BUILTIN_SPEC_REG ":", truecmd },
8137#if ENABLE_ASH_BUILTIN_TEST
8138 { BUILTIN_REGULAR "[", testcmd },
8139 { BUILTIN_REGULAR "[[", testcmd },
8140#endif
8141#if ENABLE_ASH_ALIAS
8142 { BUILTIN_REG_ASSG "alias", aliascmd },
8143#endif
8144#if JOBS
8145 { BUILTIN_REGULAR "bg", fg_bgcmd },
8146#endif
8147 { BUILTIN_SPEC_REG "break", breakcmd },
8148 { BUILTIN_REGULAR "cd", cdcmd },
8149 { BUILTIN_NOSPEC "chdir", cdcmd },
8150#if ENABLE_ASH_CMDCMD
8151 { BUILTIN_REGULAR "command", commandcmd },
8152#endif
8153 { BUILTIN_SPEC_REG "continue", breakcmd },
8154#if ENABLE_ASH_BUILTIN_ECHO
8155 { BUILTIN_REGULAR "echo", echocmd },
8156#endif
8157 { BUILTIN_SPEC_REG "eval", evalcmd },
8158 { BUILTIN_SPEC_REG "exec", execcmd },
8159 { BUILTIN_SPEC_REG "exit", exitcmd },
8160 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8161 { BUILTIN_REGULAR "false", falsecmd },
8162#if JOBS
8163 { BUILTIN_REGULAR "fg", fg_bgcmd },
8164#endif
8165#if ENABLE_ASH_GETOPTS
8166 { BUILTIN_REGULAR "getopts", getoptscmd },
8167#endif
8168 { BUILTIN_NOSPEC "hash", hashcmd },
8169#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8170 { BUILTIN_NOSPEC "help", helpcmd },
8171#endif
8172#if JOBS
8173 { BUILTIN_REGULAR "jobs", jobscmd },
8174 { BUILTIN_REGULAR "kill", killcmd },
8175#endif
8176#if ENABLE_ASH_MATH_SUPPORT
8177 { BUILTIN_NOSPEC "let", letcmd },
8178#endif
8179 { BUILTIN_ASSIGN "local", localcmd },
8180 { BUILTIN_NOSPEC "pwd", pwdcmd },
8181 { BUILTIN_REGULAR "read", readcmd },
8182 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8183 { BUILTIN_SPEC_REG "return", returncmd },
8184 { BUILTIN_SPEC_REG "set", setcmd },
8185 { BUILTIN_SPEC_REG "shift", shiftcmd },
8186 { BUILTIN_SPEC_REG "source", dotcmd },
8187#if ENABLE_ASH_BUILTIN_TEST
8188 { BUILTIN_REGULAR "test", testcmd },
8189#endif
8190 { BUILTIN_SPEC_REG "times", timescmd },
8191 { BUILTIN_SPEC_REG "trap", trapcmd },
8192 { BUILTIN_REGULAR "true", truecmd },
8193 { BUILTIN_NOSPEC "type", typecmd },
8194 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8195 { BUILTIN_REGULAR "umask", umaskcmd },
8196#if ENABLE_ASH_ALIAS
8197 { BUILTIN_REGULAR "unalias", unaliascmd },
8198#endif
8199 { BUILTIN_SPEC_REG "unset", unsetcmd },
8200 { BUILTIN_REGULAR "wait", waitcmd },
8201};
8202
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008203
8204#define COMMANDCMD (builtintab + 5 + \
8205 2 * ENABLE_ASH_BUILTIN_TEST + \
8206 ENABLE_ASH_ALIAS + \
8207 ENABLE_ASH_JOB_CONTROL)
8208#define EXECCMD (builtintab + 7 + \
8209 2 * ENABLE_ASH_BUILTIN_TEST + \
8210 ENABLE_ASH_ALIAS + \
8211 ENABLE_ASH_JOB_CONTROL + \
8212 ENABLE_ASH_CMDCMD + \
8213 ENABLE_ASH_BUILTIN_ECHO)
8214
8215/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008216 * Search the table of builtin commands.
8217 */
8218static struct builtincmd *
8219find_builtin(const char *name)
8220{
8221 struct builtincmd *bp;
8222
8223 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008224 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008225 pstrcmp
8226 );
8227 return bp;
8228}
8229
8230/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008231 * Execute a simple command.
8232 */
8233static int back_exitstatus; /* exit status of backquoted command */
8234static int
8235isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008236{
8237 const char *q = endofname(p);
8238 if (p == q)
8239 return 0;
8240 return *q == '=';
8241}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008242static int
8243bltincmd(int argc, char **argv)
8244{
8245 /* Preserve exitstatus of a previous possible redirection
8246 * as POSIX mandates */
8247 return back_exitstatus;
8248}
Eric Andersenc470f442003-07-28 09:56:35 +00008249static void
8250evalcommand(union node *cmd, int flags)
8251{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008252 static const struct builtincmd bltin = {
8253 "\0\0", bltincmd
8254 };
Eric Andersenc470f442003-07-28 09:56:35 +00008255 struct stackmark smark;
8256 union node *argp;
8257 struct arglist arglist;
8258 struct arglist varlist;
8259 char **argv;
8260 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008261 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008262 struct cmdentry cmdentry;
8263 struct job *jp;
8264 char *lastarg;
8265 const char *path;
8266 int spclbltin;
8267 int cmd_is_exec;
8268 int status;
8269 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008270 struct builtincmd *bcmd;
8271 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008272
8273 /* First expand the arguments. */
8274 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8275 setstackmark(&smark);
8276 back_exitstatus = 0;
8277
8278 cmdentry.cmdtype = CMDBUILTIN;
8279 cmdentry.u.cmd = &bltin;
8280 varlist.lastp = &varlist.list;
8281 *varlist.lastp = NULL;
8282 arglist.lastp = &arglist.list;
8283 *arglist.lastp = NULL;
8284
8285 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008286 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008287 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8288 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8289 }
8290
Eric Andersenc470f442003-07-28 09:56:35 +00008291 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8292 struct strlist **spp;
8293
8294 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008295 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008296 expandarg(argp, &arglist, EXP_VARTILDE);
8297 else
8298 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8299
Eric Andersenc470f442003-07-28 09:56:35 +00008300 for (sp = *spp; sp; sp = sp->next)
8301 argc++;
8302 }
8303
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008304 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008305 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008306 TRACE(("evalcommand arg: %s\n", sp->text));
8307 *nargv++ = sp->text;
8308 }
8309 *nargv = NULL;
8310
8311 lastarg = NULL;
8312 if (iflag && funcnest == 0 && argc > 0)
8313 lastarg = nargv[-1];
8314
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008315 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008316 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008317 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008318
8319 path = vpath.text;
8320 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8321 struct strlist **spp;
8322 char *p;
8323
8324 spp = varlist.lastp;
8325 expandarg(argp, &varlist, EXP_VARTILDE);
8326
8327 /*
8328 * Modify the command lookup path, if a PATH= assignment
8329 * is present
8330 */
8331 p = (*spp)->text;
8332 if (varequal(p, path))
8333 path = p;
8334 }
8335
8336 /* Print the command if xflag is set. */
8337 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008338 int n;
8339 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008340
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008341 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008342 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008343
8344 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008345 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008346 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008347 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008348 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008349 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008350 p--;
8351 }
8352 }
8353 sp = arglist.list;
8354 }
Rob Landley53437472006-07-16 08:14:35 +00008355 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008356 }
8357
8358 cmd_is_exec = 0;
8359 spclbltin = -1;
8360
8361 /* Now locate the command. */
8362 if (argc) {
8363 const char *oldpath;
8364 int cmd_flag = DO_ERR;
8365
8366 path += 5;
8367 oldpath = path;
8368 for (;;) {
8369 find_command(argv[0], &cmdentry, cmd_flag, path);
8370 if (cmdentry.cmdtype == CMDUNKNOWN) {
8371 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008372 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008373 goto bail;
8374 }
8375
8376 /* implement bltin and command here */
8377 if (cmdentry.cmdtype != CMDBUILTIN)
8378 break;
8379 if (spclbltin < 0)
8380 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8381 if (cmdentry.u.cmd == EXECCMD)
8382 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008383#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008384 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008385 path = oldpath;
8386 nargv = parse_command_args(argv, &path);
8387 if (!nargv)
8388 break;
8389 argc -= nargv - argv;
8390 argv = nargv;
8391 cmd_flag |= DO_NOFUNC;
8392 } else
8393#endif
8394 break;
8395 }
8396 }
8397
8398 if (status) {
8399 /* We have a redirection error. */
8400 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008401 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008402 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008403 exitstatus = status;
8404 goto out;
8405 }
8406
8407 /* Execute the command. */
8408 switch (cmdentry.cmdtype) {
8409 default:
8410 /* Fork off a child process if necessary. */
8411 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008412 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008413 jp = makejob(cmd, 1);
8414 if (forkshell(jp, cmd, FORK_FG) != 0) {
8415 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008416 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008417 break;
8418 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008419 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008420 }
8421 listsetvar(varlist.list, VEXPORT|VSTACK);
8422 shellexec(argv, path, cmdentry.u.index);
8423 /* NOTREACHED */
8424
8425 case CMDBUILTIN:
8426 cmdenviron = varlist.list;
8427 if (cmdenviron) {
8428 struct strlist *list = cmdenviron;
8429 int i = VNOSET;
8430 if (spclbltin > 0 || argc == 0) {
8431 i = 0;
8432 if (cmd_is_exec && argc > 1)
8433 i = VEXPORT;
8434 }
8435 listsetvar(list, i);
8436 }
8437 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8438 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008439 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008440 if (i == EXEXIT)
8441 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008442 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008443 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008444 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008445 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008446 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008447 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008448 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008449 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008450 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008451 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008452 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008453 }
8454 break;
8455
8456 case CMDFUNCTION:
8457 listsetvar(varlist.list, 0);
8458 if (evalfun(cmdentry.u.func, argc, argv, flags))
8459 goto raise;
8460 break;
8461 }
8462
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008463 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008464 popredir(cmd_is_exec);
8465 if (lastarg)
8466 /* dsl: I think this is intended to be used to support
8467 * '_' in 'vi' command mode during line editing...
8468 * However I implemented that within libedit itself.
8469 */
8470 setvar("_", lastarg, 0);
8471 popstackmark(&smark);
8472}
8473
8474static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008475evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8476{
Eric Andersenc470f442003-07-28 09:56:35 +00008477 char *volatile savecmdname;
8478 struct jmploc *volatile savehandler;
8479 struct jmploc jmploc;
8480 int i;
8481
8482 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008483 i = setjmp(jmploc.loc);
8484 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008485 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008486 savehandler = exception_handler;
8487 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008488 commandname = argv[0];
8489 argptr = argv + 1;
8490 optptr = NULL; /* initialize nextopt */
8491 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008492 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008493 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008494 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008495 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008496 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008497// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008498 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008499
8500 return i;
8501}
8502
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008503static int
8504goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008505{
8506 return !*endofname(p);
8507}
8508
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008509
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008510/*
8511 * Search for a command. This is called before we fork so that the
8512 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008513 * the child. The check for "goodname" is an overly conservative
8514 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008515 */
Eric Andersenc470f442003-07-28 09:56:35 +00008516static void
8517prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008518{
8519 struct cmdentry entry;
8520
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008521 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8522 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008523}
8524
Eric Andersencb57d552001-06-28 07:25:16 +00008525
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008526/* ============ Builtin commands
8527 *
8528 * Builtin commands whose functions are closely tied to evaluation
8529 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008530 */
8531
8532/*
Eric Andersencb57d552001-06-28 07:25:16 +00008533 * Handle break and continue commands. Break, continue, and return are
8534 * all handled by setting the evalskip flag. The evaluation routines
8535 * above all check this flag, and if it is set they start skipping
8536 * commands rather than executing them. The variable skipcount is
8537 * the number of loops to break/continue, or the number of function
8538 * levels to return. (The latter is always 1.) It should probably
8539 * be an error to break out of more loops than exist, but it isn't
8540 * in the standard shell so we don't make it one here.
8541 */
Eric Andersenc470f442003-07-28 09:56:35 +00008542static int
8543breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008544{
8545 int n = argc > 1 ? number(argv[1]) : 1;
8546
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008547 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008548 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008549 if (n > loopnest)
8550 n = loopnest;
8551 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008552 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008553 skipcount = n;
8554 }
8555 return 0;
8556}
8557
Eric Andersenc470f442003-07-28 09:56:35 +00008558
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008559/* ============ input.c
8560 *
Eric Andersen90898442003-08-06 11:20:52 +00008561 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008562 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008563
Eric Andersenc470f442003-07-28 09:56:35 +00008564#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008566enum {
8567 INPUT_PUSH_FILE = 1,
8568 INPUT_NOFILE_OK = 2,
8569};
Eric Andersencb57d552001-06-28 07:25:16 +00008570
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008571static int plinno = 1; /* input line number */
8572/* number of characters left in input buffer */
8573static int parsenleft; /* copy of parsefile->nleft */
8574static int parselleft; /* copy of parsefile->lleft */
8575/* next character in input buffer */
8576static char *parsenextc; /* copy of parsefile->nextc */
8577
8578static int checkkwd;
8579/* values of checkkwd variable */
8580#define CHKALIAS 0x1
8581#define CHKKWD 0x2
8582#define CHKNL 0x4
8583
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008584static void
8585popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008586{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008587 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008588
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008590#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008591 if (sp->ap) {
8592 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8593 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008594 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008595 if (sp->string != sp->ap->val) {
8596 free(sp->string);
8597 }
8598 sp->ap->flag &= ~ALIASINUSE;
8599 if (sp->ap->flag & ALIASDEAD) {
8600 unalias(sp->ap->name);
8601 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008602 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008603#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008604 parsenextc = sp->prevstring;
8605 parsenleft = sp->prevnleft;
8606/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8607 parsefile->strpush = sp->prev;
8608 if (sp != &(parsefile->basestrpush))
8609 free(sp);
8610 INT_ON;
8611}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008612
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008613static int
8614preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008615{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008616 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008617 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008618 parsenextc = buf;
8619
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008620 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008621#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008622 if (!iflag || parsefile->fd)
8623 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8624 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008625#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008626 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008627#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008628 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8629 if (nr == 0) {
8630 /* Ctrl+C pressed */
8631 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008632 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008633 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008634 raise(SIGINT);
8635 return 1;
8636 }
Eric Andersenc470f442003-07-28 09:56:35 +00008637 goto retry;
8638 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008639 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008640 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008641 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008642 }
Eric Andersencb57d552001-06-28 07:25:16 +00008643 }
8644#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008645 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008646#endif
8647
8648 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008649 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008650 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008651 if (flags >= 0 && (flags & O_NONBLOCK)) {
8652 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008653 if (fcntl(0, F_SETFL, flags) >= 0) {
8654 out2str("sh: turning off NDELAY mode\n");
8655 goto retry;
8656 }
8657 }
8658 }
8659 }
8660 return nr;
8661}
8662
8663/*
8664 * Refill the input buffer and return the next input character:
8665 *
8666 * 1) If a string was pushed back on the input, pop it;
8667 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8668 * from a string so we can't refill the buffer, return EOF.
8669 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8670 * 4) Process input up to the next newline, deleting nul characters.
8671 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008672static int
Eric Andersenc470f442003-07-28 09:56:35 +00008673preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008674{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008675 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008676 int more;
8677 char savec;
8678
8679 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008680#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008681 if (parsenleft == -1 && parsefile->strpush->ap &&
8682 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008683 return PEOA;
8684 }
Eric Andersen2870d962001-07-02 17:27:21 +00008685#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008686 popstring();
8687 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008688 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008689 }
8690 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8691 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008692 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008693
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008694 more = parselleft;
8695 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008696 again:
8697 more = preadfd();
8698 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008699 parselleft = parsenleft = EOF_NLEFT;
8700 return PEOF;
8701 }
8702 }
8703
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008704 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008705
8706 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008707 for (;;) {
8708 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008709
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008710 more--;
8711 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008712
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008713 if (!c)
8714 memmove(q, q + 1, more);
8715 else {
8716 q++;
8717 if (c == '\n') {
8718 parsenleft = q - parsenextc - 1;
8719 break;
8720 }
Eric Andersencb57d552001-06-28 07:25:16 +00008721 }
8722
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008723 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008724 parsenleft = q - parsenextc - 1;
8725 if (parsenleft < 0)
8726 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008727 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008728 }
8729 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008730 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008731
8732 savec = *q;
8733 *q = '\0';
8734
8735 if (vflag) {
8736 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008737 }
8738
8739 *q = savec;
8740
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008741 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008742}
8743
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008744#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008745static int
8746pgetc(void)
8747{
8748 return pgetc_as_macro();
8749}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008750
8751#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8752#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008753#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008754#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008755#endif
8756
8757/*
8758 * Same as pgetc(), but ignores PEOA.
8759 */
8760#if ENABLE_ASH_ALIAS
8761static int
8762pgetc2(void)
8763{
8764 int c;
8765
8766 do {
8767 c = pgetc_macro();
8768 } while (c == PEOA);
8769 return c;
8770}
8771#else
8772static int
8773pgetc2(void)
8774{
8775 return pgetc_macro();
8776}
8777#endif
8778
8779/*
8780 * Read a line from the script.
8781 */
8782static char *
8783pfgets(char *line, int len)
8784{
8785 char *p = line;
8786 int nleft = len;
8787 int c;
8788
8789 while (--nleft > 0) {
8790 c = pgetc2();
8791 if (c == PEOF) {
8792 if (p == line)
8793 return NULL;
8794 break;
8795 }
8796 *p++ = c;
8797 if (c == '\n')
8798 break;
8799 }
8800 *p = '\0';
8801 return line;
8802}
8803
Eric Andersenc470f442003-07-28 09:56:35 +00008804/*
8805 * Undo the last call to pgetc. Only one character may be pushed back.
8806 * PEOF may be pushed back.
8807 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008808static void
Eric Andersenc470f442003-07-28 09:56:35 +00008809pungetc(void)
8810{
8811 parsenleft++;
8812 parsenextc--;
8813}
Eric Andersencb57d552001-06-28 07:25:16 +00008814
8815/*
8816 * Push a string back onto the input at this current parsefile level.
8817 * We handle aliases this way.
8818 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008819static void
Eric Andersenc470f442003-07-28 09:56:35 +00008820pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008821{
Eric Andersencb57d552001-06-28 07:25:16 +00008822 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008823 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008824
Eric Andersenc470f442003-07-28 09:56:35 +00008825 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008826 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008827/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8828 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008829 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008830 sp->prev = parsefile->strpush;
8831 parsefile->strpush = sp;
8832 } else
8833 sp = parsefile->strpush = &(parsefile->basestrpush);
8834 sp->prevstring = parsenextc;
8835 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008836#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008837 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008838 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008839 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008840 sp->string = s;
8841 }
Eric Andersen2870d962001-07-02 17:27:21 +00008842#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008843 parsenextc = s;
8844 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008845 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008846}
8847
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008848/*
8849 * To handle the "." command, a stack of input files is used. Pushfile
8850 * adds a new entry to the stack and popfile restores the previous level.
8851 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008852static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008853pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008854{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008855 struct parsefile *pf;
8856
8857 parsefile->nleft = parsenleft;
8858 parsefile->lleft = parselleft;
8859 parsefile->nextc = parsenextc;
8860 parsefile->linno = plinno;
8861 pf = ckmalloc(sizeof(*pf));
8862 pf->prev = parsefile;
8863 pf->fd = -1;
8864 pf->strpush = NULL;
8865 pf->basestrpush.prev = NULL;
8866 parsefile = pf;
8867}
8868
8869static void
8870popfile(void)
8871{
8872 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008873
Denis Vlasenkob012b102007-02-19 22:43:01 +00008874 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008875 if (pf->fd >= 0)
8876 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008877 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008878 while (pf->strpush)
8879 popstring();
8880 parsefile = pf->prev;
8881 free(pf);
8882 parsenleft = parsefile->nleft;
8883 parselleft = parsefile->lleft;
8884 parsenextc = parsefile->nextc;
8885 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008886 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008887}
8888
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008889/*
8890 * Return to top level.
8891 */
8892static void
8893popallfiles(void)
8894{
8895 while (parsefile != &basepf)
8896 popfile();
8897}
8898
8899/*
8900 * Close the file(s) that the shell is reading commands from. Called
8901 * after a fork is done.
8902 */
8903static void
8904closescript(void)
8905{
8906 popallfiles();
8907 if (parsefile->fd > 0) {
8908 close(parsefile->fd);
8909 parsefile->fd = 0;
8910 }
8911}
8912
8913/*
8914 * Like setinputfile, but takes an open file descriptor. Call this with
8915 * interrupts off.
8916 */
8917static void
8918setinputfd(int fd, int push)
8919{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008920 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008921 if (push) {
8922 pushfile();
8923 parsefile->buf = 0;
8924 }
8925 parsefile->fd = fd;
8926 if (parsefile->buf == NULL)
8927 parsefile->buf = ckmalloc(IBUFSIZ);
8928 parselleft = parsenleft = 0;
8929 plinno = 1;
8930}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008931
Eric Andersenc470f442003-07-28 09:56:35 +00008932/*
8933 * Set the input to take input from a file. If push is set, push the
8934 * old input onto the stack first.
8935 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008936static int
8937setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008938{
8939 int fd;
8940 int fd2;
8941
Denis Vlasenkob012b102007-02-19 22:43:01 +00008942 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008943 fd = open(fname, O_RDONLY);
8944 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008945 if (flags & INPUT_NOFILE_OK)
8946 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008947 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008948 }
Eric Andersenc470f442003-07-28 09:56:35 +00008949 if (fd < 10) {
8950 fd2 = copyfd(fd, 10);
8951 close(fd);
8952 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008953 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008954 fd = fd2;
8955 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008956 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008957 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008958 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008959 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008960}
8961
Eric Andersencb57d552001-06-28 07:25:16 +00008962/*
8963 * Like setinputfile, but takes input from a string.
8964 */
Eric Andersenc470f442003-07-28 09:56:35 +00008965static void
8966setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008967{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008968 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008969 pushfile();
8970 parsenextc = string;
8971 parsenleft = strlen(string);
8972 parsefile->buf = NULL;
8973 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008974 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008975}
8976
8977
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008978/* ============ mail.c
8979 *
8980 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008981 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008983#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008984
Eric Andersencb57d552001-06-28 07:25:16 +00008985#define MAXMBOXES 10
8986
Eric Andersenc470f442003-07-28 09:56:35 +00008987/* times of mailboxes */
8988static time_t mailtime[MAXMBOXES];
8989/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00008990static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008991
Eric Andersencb57d552001-06-28 07:25:16 +00008992/*
Eric Andersenc470f442003-07-28 09:56:35 +00008993 * Print appropriate message(s) if mail has arrived.
8994 * If mail_var_path_changed is set,
8995 * then the value of MAIL has mail_var_path_changed,
8996 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008997 */
Eric Andersenc470f442003-07-28 09:56:35 +00008998static void
8999chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009000{
Eric Andersencb57d552001-06-28 07:25:16 +00009001 const char *mpath;
9002 char *p;
9003 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009004 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009005 struct stackmark smark;
9006 struct stat statb;
9007
Eric Andersencb57d552001-06-28 07:25:16 +00009008 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009009 mpath = mpathset() ? mpathval() : mailval();
9010 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009011 p = padvance(&mpath, nullstr);
9012 if (p == NULL)
9013 break;
9014 if (*p == '\0')
9015 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009016 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009017#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009018 if (q[-1] != '/')
9019 abort();
9020#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009021 q[-1] = '\0'; /* delete trailing '/' */
9022 if (stat(p, &statb) < 0) {
9023 *mtp = 0;
9024 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009025 }
Eric Andersenc470f442003-07-28 09:56:35 +00009026 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9027 fprintf(
9028 stderr, snlfmt,
9029 pathopt ? pathopt : "you have mail"
9030 );
9031 }
9032 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009033 }
Eric Andersenc470f442003-07-28 09:56:35 +00009034 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009035 popstackmark(&smark);
9036}
Eric Andersencb57d552001-06-28 07:25:16 +00009037
Eric Andersenc470f442003-07-28 09:56:35 +00009038static void
9039changemail(const char *val)
9040{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009041 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009042}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009043
Denis Vlasenko131ae172007-02-18 13:00:19 +00009044#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009046
9047/* ============ ??? */
9048
Eric Andersencb57d552001-06-28 07:25:16 +00009049/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009050 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009051 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009052static void
9053setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009054{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009055 char **newparam;
9056 char **ap;
9057 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009058
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009059 for (nparam = 0; argv[nparam]; nparam++);
9060 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9061 while (*argv) {
9062 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009063 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009064 *ap = NULL;
9065 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009066 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009067 shellparam.nparam = nparam;
9068 shellparam.p = newparam;
9069#if ENABLE_ASH_GETOPTS
9070 shellparam.optind = 1;
9071 shellparam.optoff = -1;
9072#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009073}
9074
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009075/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009076 * Process shell options. The global variable argptr contains a pointer
9077 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009078 */
9079static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009080minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009081{
9082 int i;
9083
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009084 if (name) {
9085 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009086 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009087 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009088 return;
9089 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009090 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009091 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009092 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009093 out1str("Current option settings\n");
9094 for (i = 0; i < NOPTS; i++)
9095 out1fmt("%-16s%s\n", optnames(i),
9096 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009097}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009098static void
9099setoption(int flag, int val)
9100{
9101 int i;
9102
9103 for (i = 0; i < NOPTS; i++) {
9104 if (optletters(i) == flag) {
9105 optlist[i] = val;
9106 return;
9107 }
9108 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009109 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009110 /* NOTREACHED */
9111}
Eric Andersenc470f442003-07-28 09:56:35 +00009112static void
9113options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009114{
9115 char *p;
9116 int val;
9117 int c;
9118
9119 if (cmdline)
9120 minusc = NULL;
9121 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009122 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009123 if (c != '-' && c != '+')
9124 break;
9125 argptr++;
9126 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009127 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009128 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009129 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009130 if (!cmdline) {
9131 /* "-" means turn off -x and -v */
9132 if (p[0] == '\0')
9133 xflag = vflag = 0;
9134 /* "--" means reset params */
9135 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009136 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009137 }
Eric Andersenc470f442003-07-28 09:56:35 +00009138 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009139 }
Eric Andersencb57d552001-06-28 07:25:16 +00009140 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009141 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009142 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009143 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009144 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009145 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009146 } else if (c == 'o') {
9147 minus_o(*argptr, val);
9148 if (*argptr)
9149 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009150 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9151 isloginsh = 1;
9152 /* bash does not accept +-login, we also won't */
9153 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009154 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009155 isloginsh = 1;
9156 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009157 } else {
9158 setoption(c, val);
9159 }
9160 }
9161 }
9162}
9163
Eric Andersencb57d552001-06-28 07:25:16 +00009164/*
Eric Andersencb57d552001-06-28 07:25:16 +00009165 * The shift builtin command.
9166 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009167static int
Eric Andersenc470f442003-07-28 09:56:35 +00009168shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009169{
9170 int n;
9171 char **ap1, **ap2;
9172
9173 n = 1;
9174 if (argc > 1)
9175 n = number(argv[1]);
9176 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009177 ash_msg_and_raise_error("can't shift that many");
9178 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009179 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009180 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009181 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009182 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009183 }
9184 ap2 = shellparam.p;
9185 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009186#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009187 shellparam.optind = 1;
9188 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009189#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009190 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009191 return 0;
9192}
9193
Eric Andersencb57d552001-06-28 07:25:16 +00009194/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009195 * POSIX requires that 'set' (but not export or readonly) output the
9196 * variables in lexicographic order - by the locale's collating order (sigh).
9197 * Maybe we could keep them in an ordered balanced binary tree
9198 * instead of hashed lists.
9199 * For now just roll 'em through qsort for printing...
9200 */
9201static int
9202showvars(const char *sep_prefix, int on, int off)
9203{
9204 const char *sep;
9205 char **ep, **epend;
9206
9207 ep = listvars(on, off, &epend);
9208 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9209
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009210 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009211
9212 for (; ep < epend; ep++) {
9213 const char *p;
9214 const char *q;
9215
9216 p = strchrnul(*ep, '=');
9217 q = nullstr;
9218 if (*p)
9219 q = single_quote(++p);
9220 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9221 }
9222 return 0;
9223}
9224
9225/*
Eric Andersencb57d552001-06-28 07:25:16 +00009226 * The set command builtin.
9227 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009228static int
Eric Andersenc470f442003-07-28 09:56:35 +00009229setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009230{
9231 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009232 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009233 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009234 options(0);
9235 optschanged();
9236 if (*argptr != NULL) {
9237 setparam(argptr);
9238 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009239 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009240 return 0;
9241}
9242
Denis Vlasenko131ae172007-02-18 13:00:19 +00009243#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009244/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009245static void
9246change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009247{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009248 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009249 /* "get", generate */
9250 char buf[16];
9251
9252 rseed = rseed * 1103515245 + 12345;
9253 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9254 /* set without recursion */
9255 setvar(vrandom.text, buf, VNOFUNC);
9256 vrandom.flags &= ~VNOFUNC;
9257 } else {
9258 /* set/reset */
9259 rseed = strtoul(value, (char **)NULL, 10);
9260 }
Eric Andersenef02f822004-03-11 13:34:24 +00009261}
Eric Andersen16767e22004-03-16 05:14:10 +00009262#endif
9263
Denis Vlasenko131ae172007-02-18 13:00:19 +00009264#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009265static int
Eric Andersenc470f442003-07-28 09:56:35 +00009266getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009267{
9268 char *p, *q;
9269 char c = '?';
9270 int done = 0;
9271 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009272 char s[12];
9273 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009274
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009275 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009276 return 1;
9277 optnext = optfirst + *param_optind - 1;
9278
9279 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009280 p = NULL;
9281 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009282 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009283 if (p == NULL || *p == '\0') {
9284 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009285 p = *optnext;
9286 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009287 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009288 p = NULL;
9289 done = 1;
9290 goto out;
9291 }
9292 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009293 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009294 goto atend;
9295 }
9296
9297 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009298 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009299 if (*q == '\0') {
9300 if (optstr[0] == ':') {
9301 s[0] = c;
9302 s[1] = '\0';
9303 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009304 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009305 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009306 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009307 }
9308 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009309 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009310 }
9311 if (*++q == ':')
9312 q++;
9313 }
9314
9315 if (*++q == ':') {
9316 if (*p == '\0' && (p = *optnext) == NULL) {
9317 if (optstr[0] == ':') {
9318 s[0] = c;
9319 s[1] = '\0';
9320 err |= setvarsafe("OPTARG", s, 0);
9321 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009322 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009323 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009324 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009325 c = '?';
9326 }
Eric Andersenc470f442003-07-28 09:56:35 +00009327 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009328 }
9329
9330 if (p == *optnext)
9331 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009332 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009333 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009334 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009335 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009336 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009337 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009338 *param_optind = optnext - optfirst + 1;
9339 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009340 err |= setvarsafe("OPTIND", s, VNOFUNC);
9341 s[0] = c;
9342 s[1] = '\0';
9343 err |= setvarsafe(optvar, s, 0);
9344 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009345 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009346 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009347 flush_stdout_stderr();
9348 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009349 }
9350 return done;
9351}
Eric Andersenc470f442003-07-28 09:56:35 +00009352
9353/*
9354 * The getopts builtin. Shellparam.optnext points to the next argument
9355 * to be processed. Shellparam.optptr points to the next character to
9356 * be processed in the current argument. If shellparam.optnext is NULL,
9357 * then it's the first time getopts has been called.
9358 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009359static int
Eric Andersenc470f442003-07-28 09:56:35 +00009360getoptscmd(int argc, char **argv)
9361{
9362 char **optbase;
9363
9364 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009365 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009366 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009367 optbase = shellparam.p;
9368 if (shellparam.optind > shellparam.nparam + 1) {
9369 shellparam.optind = 1;
9370 shellparam.optoff = -1;
9371 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009372 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009373 optbase = &argv[3];
9374 if (shellparam.optind > argc - 2) {
9375 shellparam.optind = 1;
9376 shellparam.optoff = -1;
9377 }
9378 }
9379
9380 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009381 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009382}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009383#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009384
Eric Andersencb57d552001-06-28 07:25:16 +00009385
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009386/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009387
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009388/*
9389 * NEOF is returned by parsecmd when it encounters an end of file. It
9390 * must be distinct from NULL, so we use the address of a variable that
9391 * happens to be handy.
9392 */
9393static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009394#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009395static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009396static int lasttoken; /* last token read */
9397static char *wordtext; /* text of last word returned by readtoken */
9398static struct nodelist *backquotelist;
9399static union node *redirnode;
9400static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009401static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009402
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009403static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9404static void
9405raise_error_syntax(const char *msg)
9406{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009407 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009408 /* NOTREACHED */
9409}
9410
9411/*
9412 * Called when an unexpected token is read during the parse. The argument
9413 * is the token that is expected, or -1 if more than one type of token can
9414 * occur at this point.
9415 */
9416static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9417static void
9418raise_error_unexpected_syntax(int token)
9419{
9420 char msg[64];
9421 int l;
9422
9423 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9424 if (token >= 0)
9425 sprintf(msg + l, " (expecting %s)", tokname(token));
9426 raise_error_syntax(msg);
9427 /* NOTREACHED */
9428}
Eric Andersencb57d552001-06-28 07:25:16 +00009429
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009430#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009431
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009432struct heredoc {
9433 struct heredoc *next; /* next here document in list */
9434 union node *here; /* redirection node */
9435 char *eofmark; /* string indicating end of input */
9436 int striptabs; /* if set, strip leading tabs */
9437};
Eric Andersencb57d552001-06-28 07:25:16 +00009438
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009439static struct heredoc *heredoclist; /* list of here documents to read */
9440
9441/* parsing is heavily cross-recursive, need these forward decls */
9442static union node *andor(void);
9443static union node *pipeline(void);
9444static union node *parse_command(void);
9445static void parseheredoc(void);
9446static char peektoken(void);
9447static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009448
Eric Andersenc470f442003-07-28 09:56:35 +00009449static union node *
9450list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009451{
9452 union node *n1, *n2, *n3;
9453 int tok;
9454
Eric Andersenc470f442003-07-28 09:56:35 +00009455 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9456 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009457 return NULL;
9458 n1 = NULL;
9459 for (;;) {
9460 n2 = andor();
9461 tok = readtoken();
9462 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009463 if (n2->type == NPIPE) {
9464 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009465 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009466 if (n2->type != NREDIR) {
9467 n3 = stalloc(sizeof(struct nredir));
9468 n3->nredir.n = n2;
9469 n3->nredir.redirect = NULL;
9470 n2 = n3;
9471 }
9472 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009473 }
9474 }
9475 if (n1 == NULL) {
9476 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009477 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009478 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009479 n3->type = NSEMI;
9480 n3->nbinary.ch1 = n1;
9481 n3->nbinary.ch2 = n2;
9482 n1 = n3;
9483 }
9484 switch (tok) {
9485 case TBACKGND:
9486 case TSEMI:
9487 tok = readtoken();
9488 /* fall through */
9489 case TNL:
9490 if (tok == TNL) {
9491 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009492 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009493 return n1;
9494 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009495 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009496 }
Eric Andersenc470f442003-07-28 09:56:35 +00009497 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009498 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009499 return n1;
9500 break;
9501 case TEOF:
9502 if (heredoclist)
9503 parseheredoc();
9504 else
Eric Andersenc470f442003-07-28 09:56:35 +00009505 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009506 return n1;
9507 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009508 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009509 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009510 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009511 return n1;
9512 }
9513 }
9514}
9515
Eric Andersenc470f442003-07-28 09:56:35 +00009516static union node *
9517andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009518{
Eric Andersencb57d552001-06-28 07:25:16 +00009519 union node *n1, *n2, *n3;
9520 int t;
9521
Eric Andersencb57d552001-06-28 07:25:16 +00009522 n1 = pipeline();
9523 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009524 t = readtoken();
9525 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009526 t = NAND;
9527 } else if (t == TOR) {
9528 t = NOR;
9529 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009530 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009531 return n1;
9532 }
Eric Andersenc470f442003-07-28 09:56:35 +00009533 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009534 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009535 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009536 n3->type = t;
9537 n3->nbinary.ch1 = n1;
9538 n3->nbinary.ch2 = n2;
9539 n1 = n3;
9540 }
9541}
9542
Eric Andersenc470f442003-07-28 09:56:35 +00009543static union node *
9544pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009545{
Eric Andersencb57d552001-06-28 07:25:16 +00009546 union node *n1, *n2, *pipenode;
9547 struct nodelist *lp, *prev;
9548 int negate;
9549
9550 negate = 0;
9551 TRACE(("pipeline: entered\n"));
9552 if (readtoken() == TNOT) {
9553 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009554 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009555 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009556 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009557 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009558 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009559 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009560 pipenode->type = NPIPE;
9561 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009562 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009563 pipenode->npipe.cmdlist = lp;
9564 lp->n = n1;
9565 do {
9566 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009567 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009568 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009569 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009570 prev->next = lp;
9571 } while (readtoken() == TPIPE);
9572 lp->next = NULL;
9573 n1 = pipenode;
9574 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009575 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009576 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009577 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009578 n2->type = NNOT;
9579 n2->nnot.com = n1;
9580 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009581 }
9582 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009583}
9584
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009585static union node *
9586makename(void)
9587{
9588 union node *n;
9589
9590 n = stalloc(sizeof(struct narg));
9591 n->type = NARG;
9592 n->narg.next = NULL;
9593 n->narg.text = wordtext;
9594 n->narg.backquote = backquotelist;
9595 return n;
9596}
9597
9598static void
9599fixredir(union node *n, const char *text, int err)
9600{
9601 TRACE(("Fix redir %s %d\n", text, err));
9602 if (!err)
9603 n->ndup.vname = NULL;
9604
9605 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009606 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009607 else if (LONE_DASH(text))
9608 n->ndup.dupfd = -1;
9609 else {
9610 if (err)
9611 raise_error_syntax("Bad fd number");
9612 n->ndup.vname = makename();
9613 }
9614}
9615
9616/*
9617 * Returns true if the text contains nothing to expand (no dollar signs
9618 * or backquotes).
9619 */
9620static int
9621noexpand(char *text)
9622{
9623 char *p;
9624 char c;
9625
9626 p = text;
9627 while ((c = *p++) != '\0') {
9628 if (c == CTLQUOTEMARK)
9629 continue;
9630 if (c == CTLESC)
9631 p++;
9632 else if (SIT(c, BASESYNTAX) == CCTL)
9633 return 0;
9634 }
9635 return 1;
9636}
9637
9638static void
9639parsefname(void)
9640{
9641 union node *n = redirnode;
9642
9643 if (readtoken() != TWORD)
9644 raise_error_unexpected_syntax(-1);
9645 if (n->type == NHERE) {
9646 struct heredoc *here = heredoc;
9647 struct heredoc *p;
9648 int i;
9649
9650 if (quoteflag == 0)
9651 n->type = NXHERE;
9652 TRACE(("Here document %d\n", n->type));
9653 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9654 raise_error_syntax("Illegal eof marker for << redirection");
9655 rmescapes(wordtext);
9656 here->eofmark = wordtext;
9657 here->next = NULL;
9658 if (heredoclist == NULL)
9659 heredoclist = here;
9660 else {
9661 for (p = heredoclist; p->next; p = p->next);
9662 p->next = here;
9663 }
9664 } else if (n->type == NTOFD || n->type == NFROMFD) {
9665 fixredir(n, wordtext, 0);
9666 } else {
9667 n->nfile.fname = makename();
9668 }
9669}
Eric Andersencb57d552001-06-28 07:25:16 +00009670
Eric Andersenc470f442003-07-28 09:56:35 +00009671static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009672simplecmd(void)
9673{
9674 union node *args, **app;
9675 union node *n = NULL;
9676 union node *vars, **vpp;
9677 union node **rpp, *redir;
9678 int savecheckkwd;
9679
9680 args = NULL;
9681 app = &args;
9682 vars = NULL;
9683 vpp = &vars;
9684 redir = NULL;
9685 rpp = &redir;
9686
9687 savecheckkwd = CHKALIAS;
9688 for (;;) {
9689 checkkwd = savecheckkwd;
9690 switch (readtoken()) {
9691 case TWORD:
9692 n = stalloc(sizeof(struct narg));
9693 n->type = NARG;
9694 n->narg.text = wordtext;
9695 n->narg.backquote = backquotelist;
9696 if (savecheckkwd && isassignment(wordtext)) {
9697 *vpp = n;
9698 vpp = &n->narg.next;
9699 } else {
9700 *app = n;
9701 app = &n->narg.next;
9702 savecheckkwd = 0;
9703 }
9704 break;
9705 case TREDIR:
9706 *rpp = n = redirnode;
9707 rpp = &n->nfile.next;
9708 parsefname(); /* read name of redirection file */
9709 break;
9710 case TLP:
9711 if (args && app == &args->narg.next
9712 && !vars && !redir
9713 ) {
9714 struct builtincmd *bcmd;
9715 const char *name;
9716
9717 /* We have a function */
9718 if (readtoken() != TRP)
9719 raise_error_unexpected_syntax(TRP);
9720 name = n->narg.text;
9721 if (!goodname(name)
9722 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9723 ) {
9724 raise_error_syntax("Bad function name");
9725 }
9726 n->type = NDEFUN;
9727 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9728 n->narg.next = parse_command();
9729 return n;
9730 }
9731 /* fall through */
9732 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009733 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009734 goto out;
9735 }
9736 }
9737 out:
9738 *app = NULL;
9739 *vpp = NULL;
9740 *rpp = NULL;
9741 n = stalloc(sizeof(struct ncmd));
9742 n->type = NCMD;
9743 n->ncmd.args = args;
9744 n->ncmd.assign = vars;
9745 n->ncmd.redirect = redir;
9746 return n;
9747}
9748
9749static union node *
9750parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009751{
Eric Andersencb57d552001-06-28 07:25:16 +00009752 union node *n1, *n2;
9753 union node *ap, **app;
9754 union node *cp, **cpp;
9755 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009756 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009757 int t;
9758
9759 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009760 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009761
Eric Andersencb57d552001-06-28 07:25:16 +00009762 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009763 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009764 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009765 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009766 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009767 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009768 n1->type = NIF;
9769 n1->nif.test = list(0);
9770 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009771 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009772 n1->nif.ifpart = list(0);
9773 n2 = n1;
9774 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009775 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009776 n2 = n2->nif.elsepart;
9777 n2->type = NIF;
9778 n2->nif.test = list(0);
9779 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009780 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009781 n2->nif.ifpart = list(0);
9782 }
9783 if (lasttoken == TELSE)
9784 n2->nif.elsepart = list(0);
9785 else {
9786 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009787 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009788 }
Eric Andersenc470f442003-07-28 09:56:35 +00009789 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009790 break;
9791 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009792 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009793 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009794 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009795 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009796 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009797 got = readtoken();
9798 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009799 TRACE(("expecting DO got %s %s\n", tokname(got),
9800 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009802 }
9803 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009804 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009805 break;
9806 }
9807 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009808 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009809 raise_error_syntax("Bad for loop variable");
9810 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009811 n1->type = NFOR;
9812 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009813 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009814 if (readtoken() == TIN) {
9815 app = &ap;
9816 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009817 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009818 n2->type = NARG;
9819 n2->narg.text = wordtext;
9820 n2->narg.backquote = backquotelist;
9821 *app = n2;
9822 app = &n2->narg.next;
9823 }
9824 *app = NULL;
9825 n1->nfor.args = ap;
9826 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009827 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009828 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009829 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009830 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009831 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009832 n2->narg.backquote = NULL;
9833 n2->narg.next = NULL;
9834 n1->nfor.args = n2;
9835 /*
9836 * Newline or semicolon here is optional (but note
9837 * that the original Bourne shell only allowed NL).
9838 */
9839 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009840 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009841 }
Eric Andersenc470f442003-07-28 09:56:35 +00009842 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009843 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009844 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009845 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009846 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009847 break;
9848 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009849 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009850 n1->type = NCASE;
9851 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009852 raise_error_unexpected_syntax(TWORD);
9853 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009854 n2->type = NARG;
9855 n2->narg.text = wordtext;
9856 n2->narg.backquote = backquotelist;
9857 n2->narg.next = NULL;
9858 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009859 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009860 } while (readtoken() == TNL);
9861 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009862 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009863 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009864 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009865 checkkwd = CHKNL | CHKKWD;
9866 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009867 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009868 if (lasttoken == TLP)
9869 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009870 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009871 cp->type = NCLIST;
9872 app = &cp->nclist.pattern;
9873 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009874 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009875 ap->type = NARG;
9876 ap->narg.text = wordtext;
9877 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009878 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009879 break;
9880 app = &ap->narg.next;
9881 readtoken();
9882 }
9883 ap->narg.next = NULL;
9884 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009885 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009886 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009887
Eric Andersenc470f442003-07-28 09:56:35 +00009888 cpp = &cp->nclist.next;
9889
9890 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009891 t = readtoken();
9892 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009893 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009894 raise_error_unexpected_syntax(TENDCASE);
9895 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009896 }
Eric Andersenc470f442003-07-28 09:56:35 +00009897 }
Eric Andersencb57d552001-06-28 07:25:16 +00009898 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009899 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009900 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009901 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009902 n1->type = NSUBSHELL;
9903 n1->nredir.n = list(0);
9904 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009905 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009906 break;
9907 case TBEGIN:
9908 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009909 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009910 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009912 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009913 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009914 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009915 }
9916
Eric Andersenc470f442003-07-28 09:56:35 +00009917 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009918 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009919
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009920 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009921 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009922 checkkwd = CHKKWD | CHKALIAS;
9923 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009924 while (readtoken() == TREDIR) {
9925 *rpp = n2 = redirnode;
9926 rpp = &n2->nfile.next;
9927 parsefname();
9928 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009929 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009930 *rpp = NULL;
9931 if (redir) {
9932 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009933 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009934 n2->type = NREDIR;
9935 n2->nredir.n = n1;
9936 n1 = n2;
9937 }
9938 n1->nredir.redirect = redir;
9939 }
Eric Andersencb57d552001-06-28 07:25:16 +00009940 return n1;
9941}
9942
Eric Andersencb57d552001-06-28 07:25:16 +00009943/*
9944 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9945 * is not NULL, read a here document. In the latter case, eofmark is the
9946 * word which marks the end of the document and striptabs is true if
9947 * leading tabs should be stripped from the document. The argument firstc
9948 * is the first character of the input token or document.
9949 *
9950 * Because C does not have internal subroutines, I have simulated them
9951 * using goto's to implement the subroutine linkage. The following macros
9952 * will run code that appears at the end of readtoken1.
9953 */
9954
Eric Andersen2870d962001-07-02 17:27:21 +00009955#define CHECKEND() {goto checkend; checkend_return:;}
9956#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9957#define PARSESUB() {goto parsesub; parsesub_return:;}
9958#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9959#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9960#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009961
9962static int
Eric Andersenc470f442003-07-28 09:56:35 +00009963readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009964{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009965 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009966 int c = firstc;
9967 char *out;
9968 int len;
9969 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009970 struct nodelist *bqlist;
9971 smallint quotef;
9972 smallint dblquote;
9973 smallint oldstyle;
9974 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009975#if ENABLE_ASH_EXPAND_PRMT
9976 smallint pssyntax; /* we are expanding a prompt string */
9977#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009978 int varnest; /* levels of variables expansion */
9979 int arinest; /* levels of arithmetic expansion */
9980 int parenlevel; /* levels of parens in arithmetic */
9981 int dqvarnest; /* levels of variables expansion within double quotes */
9982
Eric Andersencb57d552001-06-28 07:25:16 +00009983#if __GNUC__
9984 /* Avoid longjmp clobbering */
9985 (void) &out;
9986 (void) &quotef;
9987 (void) &dblquote;
9988 (void) &varnest;
9989 (void) &arinest;
9990 (void) &parenlevel;
9991 (void) &dqvarnest;
9992 (void) &oldstyle;
9993 (void) &prevsyntax;
9994 (void) &syntax;
9995#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009996 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +00009997 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009998 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009999 oldstyle = 0;
10000 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010001#if ENABLE_ASH_EXPAND_PRMT
10002 pssyntax = (syntax == PSSYNTAX);
10003 if (pssyntax)
10004 syntax = DQSYNTAX;
10005#endif
10006 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010007 varnest = 0;
10008 arinest = 0;
10009 parenlevel = 0;
10010 dqvarnest = 0;
10011
10012 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010013 loop: { /* for each line, until end of word */
10014 CHECKEND(); /* set c to PEOF if at end of here document */
10015 for (;;) { /* until end of line or end of word */
10016 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010017 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010018 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010019 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010020 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010021 USTPUTC(c, out);
10022 plinno++;
10023 if (doprompt)
10024 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010025 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010026 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010027 case CWORD:
10028 USTPUTC(c, out);
10029 break;
10030 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010031 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010032 USTPUTC(CTLESC, out);
10033 USTPUTC(c, out);
10034 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010035 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010036 c = pgetc2();
10037 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010038 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010039 USTPUTC('\\', out);
10040 pungetc();
10041 } else if (c == '\n') {
10042 if (doprompt)
10043 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010044 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010045#if ENABLE_ASH_EXPAND_PRMT
10046 if (c == '$' && pssyntax) {
10047 USTPUTC(CTLESC, out);
10048 USTPUTC('\\', out);
10049 }
10050#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010051 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010052 c != '\\' && c != '`' &&
10053 c != '$' && (
10054 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010055 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010056 ) {
10057 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010058 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010059 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010060 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010061 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010062 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010063 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010064 }
10065 break;
10066 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010067 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010068 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010069 if (eofmark == NULL) {
10070 USTPUTC(CTLQUOTEMARK, out);
10071 }
Eric Andersencb57d552001-06-28 07:25:16 +000010072 break;
10073 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010074 syntax = DQSYNTAX;
10075 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010076 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010077 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010078 if (eofmark != NULL && arinest == 0
10079 && varnest == 0
10080 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010081 USTPUTC(c, out);
10082 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010083 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010084 syntax = BASESYNTAX;
10085 dblquote = 0;
10086 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010087 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010088 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010089 }
10090 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010091 case CVAR: /* '$' */
10092 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010093 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010094 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010095 if (varnest > 0) {
10096 varnest--;
10097 if (dqvarnest > 0) {
10098 dqvarnest--;
10099 }
10100 USTPUTC(CTLENDVAR, out);
10101 } else {
10102 USTPUTC(c, out);
10103 }
10104 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010105#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010106 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010107 parenlevel++;
10108 USTPUTC(c, out);
10109 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010110 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010111 if (parenlevel > 0) {
10112 USTPUTC(c, out);
10113 --parenlevel;
10114 } else {
10115 if (pgetc() == ')') {
10116 if (--arinest == 0) {
10117 USTPUTC(CTLENDARI, out);
10118 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010119 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010120 } else
10121 USTPUTC(')', out);
10122 } else {
10123 /*
10124 * unbalanced parens
10125 * (don't 2nd guess - no error)
10126 */
10127 pungetc();
10128 USTPUTC(')', out);
10129 }
10130 }
10131 break;
10132#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010133 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010134 PARSEBACKQOLD();
10135 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010136 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010137 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010138 case CIGN:
10139 break;
10140 default:
10141 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010142 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010143#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010144 if (c != PEOA)
10145#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010146 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010147
Eric Andersencb57d552001-06-28 07:25:16 +000010148 }
10149 c = pgetc_macro();
10150 }
10151 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010152 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010153#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010154 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010155 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010156#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010157 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010158 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010159 if (varnest != 0) {
10160 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010161 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010162 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010163 }
10164 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010165 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010166 out = stackblock();
10167 if (eofmark == NULL) {
10168 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010169 && quotef == 0
10170 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010171 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010172 PARSEREDIR();
10173 return lasttoken = TREDIR;
10174 } else {
10175 pungetc();
10176 }
10177 }
10178 quoteflag = quotef;
10179 backquotelist = bqlist;
10180 grabstackblock(len);
10181 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010182 lasttoken = TWORD;
10183 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010184/* end of readtoken routine */
10185
Eric Andersencb57d552001-06-28 07:25:16 +000010186/*
10187 * Check to see whether we are at the end of the here document. When this
10188 * is called, c is set to the first character of the next input line. If
10189 * we are at the end of the here document, this routine sets the c to PEOF.
10190 */
Eric Andersenc470f442003-07-28 09:56:35 +000010191checkend: {
10192 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010193#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010194 if (c == PEOA) {
10195 c = pgetc2();
10196 }
10197#endif
10198 if (striptabs) {
10199 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010200 c = pgetc2();
10201 }
Eric Andersenc470f442003-07-28 09:56:35 +000010202 }
10203 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010204 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010205 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010206
Eric Andersenc470f442003-07-28 09:56:35 +000010207 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010208 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010209 if (*p == '\n' && *q == '\0') {
10210 c = PEOF;
10211 plinno++;
10212 needprompt = doprompt;
10213 } else {
10214 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010215 }
10216 }
10217 }
10218 }
Eric Andersenc470f442003-07-28 09:56:35 +000010219 goto checkend_return;
10220}
Eric Andersencb57d552001-06-28 07:25:16 +000010221
Eric Andersencb57d552001-06-28 07:25:16 +000010222/*
10223 * Parse a redirection operator. The variable "out" points to a string
10224 * specifying the fd to be redirected. The variable "c" contains the
10225 * first character of the redirection operator.
10226 */
Eric Andersenc470f442003-07-28 09:56:35 +000010227parseredir: {
10228 char fd = *out;
10229 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010230
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010231 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010232 if (c == '>') {
10233 np->nfile.fd = 1;
10234 c = pgetc();
10235 if (c == '>')
10236 np->type = NAPPEND;
10237 else if (c == '|')
10238 np->type = NCLOBBER;
10239 else if (c == '&')
10240 np->type = NTOFD;
10241 else {
10242 np->type = NTO;
10243 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010244 }
Eric Andersenc470f442003-07-28 09:56:35 +000010245 } else { /* c == '<' */
10246 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010247 c = pgetc();
10248 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010249 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010250 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010251 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010252 np->nfile.fd = 0;
10253 }
10254 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010255 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010256 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010257 c = pgetc();
10258 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010259 heredoc->striptabs = 1;
10260 } else {
10261 heredoc->striptabs = 0;
10262 pungetc();
10263 }
10264 break;
10265
10266 case '&':
10267 np->type = NFROMFD;
10268 break;
10269
10270 case '>':
10271 np->type = NFROMTO;
10272 break;
10273
10274 default:
10275 np->type = NFROM;
10276 pungetc();
10277 break;
10278 }
Eric Andersencb57d552001-06-28 07:25:16 +000010279 }
Eric Andersenc470f442003-07-28 09:56:35 +000010280 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010281 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010282 redirnode = np;
10283 goto parseredir_return;
10284}
Eric Andersencb57d552001-06-28 07:25:16 +000010285
Eric Andersencb57d552001-06-28 07:25:16 +000010286/*
10287 * Parse a substitution. At this point, we have read the dollar sign
10288 * and nothing else.
10289 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010290
10291/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10292 * (assuming ascii char codes, as the original implementation did) */
10293#define is_special(c) \
10294 ((((unsigned int)c) - 33 < 32) \
10295 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010296parsesub: {
10297 int subtype;
10298 int typeloc;
10299 int flags;
10300 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010301 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010302
Eric Andersenc470f442003-07-28 09:56:35 +000010303 c = pgetc();
10304 if (
10305 c <= PEOA_OR_PEOF ||
10306 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10307 ) {
10308 USTPUTC('$', out);
10309 pungetc();
10310 } else if (c == '(') { /* $(command) or $((arith)) */
10311 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010312#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010313 PARSEARITH();
10314#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010315 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010316#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010317 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010318 pungetc();
10319 PARSEBACKQNEW();
10320 }
10321 } else {
10322 USTPUTC(CTLVAR, out);
10323 typeloc = out - (char *)stackblock();
10324 USTPUTC(VSNORMAL, out);
10325 subtype = VSNORMAL;
10326 if (c == '{') {
10327 c = pgetc();
10328 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010329 c = pgetc();
10330 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010331 c = '#';
10332 else
10333 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010334 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010335 subtype = 0;
10336 }
10337 if (c > PEOA_OR_PEOF && is_name(c)) {
10338 do {
10339 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010340 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010341 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010342 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010343 do {
10344 STPUTC(c, out);
10345 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010346 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010347 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010348 USTPUTC(c, out);
10349 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010350 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010351 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010352
Eric Andersenc470f442003-07-28 09:56:35 +000010353 STPUTC('=', out);
10354 flags = 0;
10355 if (subtype == 0) {
10356 switch (c) {
10357 case ':':
10358 flags = VSNUL;
10359 c = pgetc();
10360 /*FALLTHROUGH*/
10361 default:
10362 p = strchr(types, c);
10363 if (p == NULL)
10364 goto badsub;
10365 subtype = p - types + VSNORMAL;
10366 break;
10367 case '%':
10368 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010369 {
10370 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010371 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010372 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010373 c = pgetc();
10374 if (c == cc)
10375 subtype++;
10376 else
10377 pungetc();
10378 break;
10379 }
10380 }
Eric Andersenc470f442003-07-28 09:56:35 +000010381 } else {
10382 pungetc();
10383 }
10384 if (dblquote || arinest)
10385 flags |= VSQUOTE;
10386 *((char *)stackblock() + typeloc) = subtype | flags;
10387 if (subtype != VSNORMAL) {
10388 varnest++;
10389 if (dblquote || arinest) {
10390 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 }
10392 }
10393 }
Eric Andersenc470f442003-07-28 09:56:35 +000010394 goto parsesub_return;
10395}
Eric Andersencb57d552001-06-28 07:25:16 +000010396
Eric Andersencb57d552001-06-28 07:25:16 +000010397/*
10398 * Called to parse command substitutions. Newstyle is set if the command
10399 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10400 * list of commands (passed by reference), and savelen is the number of
10401 * characters on the top of the stack which must be preserved.
10402 */
Eric Andersenc470f442003-07-28 09:56:35 +000010403parsebackq: {
10404 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010405 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010406 union node *n;
10407 char *volatile str;
10408 struct jmploc jmploc;
10409 struct jmploc *volatile savehandler;
10410 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010411 smallint saveprompt = 0;
10412
Eric Andersencb57d552001-06-28 07:25:16 +000010413#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010414 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010415#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010416 savepbq = parsebackquote;
10417 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010418 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010419 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010420 exception_handler = savehandler;
10421 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010422 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010423 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010424 str = NULL;
10425 savelen = out - (char *)stackblock();
10426 if (savelen > 0) {
10427 str = ckmalloc(savelen);
10428 memcpy(str, stackblock(), savelen);
10429 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010430 savehandler = exception_handler;
10431 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010432 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010433 if (oldstyle) {
10434 /* We must read until the closing backquote, giving special
10435 treatment to some slashes, and then push the string and
10436 reread it as input, interpreting it normally. */
10437 char *pout;
10438 int pc;
10439 size_t psavelen;
10440 char *pstr;
10441
10442
10443 STARTSTACKSTR(pout);
10444 for (;;) {
10445 if (needprompt) {
10446 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010447 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010448 pc = pgetc();
10449 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010450 case '`':
10451 goto done;
10452
10453 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010454 pc = pgetc();
10455 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010456 plinno++;
10457 if (doprompt)
10458 setprompt(2);
10459 /*
10460 * If eating a newline, avoid putting
10461 * the newline into the new character
10462 * stream (via the STPUTC after the
10463 * switch).
10464 */
10465 continue;
10466 }
10467 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010468 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010469 STPUTC('\\', pout);
10470 if (pc > PEOA_OR_PEOF) {
10471 break;
10472 }
10473 /* fall through */
10474
10475 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010476#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010477 case PEOA:
10478#endif
10479 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010480 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010481
10482 case '\n':
10483 plinno++;
10484 needprompt = doprompt;
10485 break;
10486
10487 default:
10488 break;
10489 }
10490 STPUTC(pc, pout);
10491 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010492 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010493 STPUTC('\0', pout);
10494 psavelen = pout - (char *)stackblock();
10495 if (psavelen > 0) {
10496 pstr = grabstackstr(pout);
10497 setinputstring(pstr);
10498 }
10499 }
10500 nlpp = &bqlist;
10501 while (*nlpp)
10502 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010503 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010504 (*nlpp)->next = NULL;
10505 parsebackquote = oldstyle;
10506
10507 if (oldstyle) {
10508 saveprompt = doprompt;
10509 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010510 }
10511
Eric Andersenc470f442003-07-28 09:56:35 +000010512 n = list(2);
10513
10514 if (oldstyle)
10515 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010516 else if (readtoken() != TRP)
10517 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010518
10519 (*nlpp)->n = n;
10520 if (oldstyle) {
10521 /*
10522 * Start reading from old file again, ignoring any pushed back
10523 * tokens left from the backquote parsing
10524 */
10525 popfile();
10526 tokpushback = 0;
10527 }
10528 while (stackblocksize() <= savelen)
10529 growstackblock();
10530 STARTSTACKSTR(out);
10531 if (str) {
10532 memcpy(out, str, savelen);
10533 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010534 INT_OFF;
10535 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010536 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010537 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010538 }
10539 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010540 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010541 if (arinest || dblquote)
10542 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10543 else
10544 USTPUTC(CTLBACKQ, out);
10545 if (oldstyle)
10546 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010547 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010548}
10549
Denis Vlasenko131ae172007-02-18 13:00:19 +000010550#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010551/*
10552 * Parse an arithmetic expansion (indicate start of one and set state)
10553 */
Eric Andersenc470f442003-07-28 09:56:35 +000010554parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010555 if (++arinest == 1) {
10556 prevsyntax = syntax;
10557 syntax = ARISYNTAX;
10558 USTPUTC(CTLARI, out);
10559 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010560 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010561 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010562 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010563 } else {
10564 /*
10565 * we collapse embedded arithmetic expansion to
10566 * parenthesis, which should be equivalent
10567 */
10568 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010569 }
Eric Andersenc470f442003-07-28 09:56:35 +000010570 goto parsearith_return;
10571}
10572#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010573
Eric Andersenc470f442003-07-28 09:56:35 +000010574} /* end of readtoken */
10575
Eric Andersencb57d552001-06-28 07:25:16 +000010576/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010577 * Read the next input token.
10578 * If the token is a word, we set backquotelist to the list of cmds in
10579 * backquotes. We set quoteflag to true if any part of the word was
10580 * quoted.
10581 * If the token is TREDIR, then we set redirnode to a structure containing
10582 * the redirection.
10583 * In all cases, the variable startlinno is set to the number of the line
10584 * on which the token starts.
10585 *
10586 * [Change comment: here documents and internal procedures]
10587 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10588 * word parsing code into a separate routine. In this case, readtoken
10589 * doesn't need to have any internal procedures, but parseword does.
10590 * We could also make parseoperator in essence the main routine, and
10591 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010592 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010593#define NEW_xxreadtoken
10594#ifdef NEW_xxreadtoken
10595/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010596static const char xxreadtoken_chars[7] ALIGN1 = {
10597 '\n', '(', ')', '&', '|', ';', 0
10598};
Eric Andersencb57d552001-06-28 07:25:16 +000010599
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010600static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010601 TNL, TLP, TRP, /* only single occurrence allowed */
10602 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10603 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010604 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010605};
10606
10607#define xxreadtoken_doubles \
10608 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10609#define xxreadtoken_singles \
10610 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10611
10612static int
10613xxreadtoken(void)
10614{
10615 int c;
10616
10617 if (tokpushback) {
10618 tokpushback = 0;
10619 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010620 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010621 if (needprompt) {
10622 setprompt(2);
10623 }
10624 startlinno = plinno;
10625 for (;;) { /* until token or start of word found */
10626 c = pgetc_macro();
10627
10628 if ((c != ' ') && (c != '\t')
10629#if ENABLE_ASH_ALIAS
10630 && (c != PEOA)
10631#endif
10632 ) {
10633 if (c == '#') {
10634 while ((c = pgetc()) != '\n' && c != PEOF);
10635 pungetc();
10636 } else if (c == '\\') {
10637 if (pgetc() != '\n') {
10638 pungetc();
10639 goto READTOKEN1;
10640 }
10641 startlinno = ++plinno;
10642 if (doprompt)
10643 setprompt(2);
10644 } else {
10645 const char *p
10646 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10647
10648 if (c != PEOF) {
10649 if (c == '\n') {
10650 plinno++;
10651 needprompt = doprompt;
10652 }
10653
10654 p = strchr(xxreadtoken_chars, c);
10655 if (p == NULL) {
10656 READTOKEN1:
10657 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10658 }
10659
10660 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10661 if (pgetc() == *p) { /* double occurrence? */
10662 p += xxreadtoken_doubles + 1;
10663 } else {
10664 pungetc();
10665 }
10666 }
10667 }
10668 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10669 }
10670 }
10671 } /* for */
10672}
10673#else
10674#define RETURN(token) return lasttoken = token
10675static int
10676xxreadtoken(void)
10677{
10678 int c;
10679
10680 if (tokpushback) {
10681 tokpushback = 0;
10682 return lasttoken;
10683 }
10684 if (needprompt) {
10685 setprompt(2);
10686 }
10687 startlinno = plinno;
10688 for (;;) { /* until token or start of word found */
10689 c = pgetc_macro();
10690 switch (c) {
10691 case ' ': case '\t':
10692#if ENABLE_ASH_ALIAS
10693 case PEOA:
10694#endif
10695 continue;
10696 case '#':
10697 while ((c = pgetc()) != '\n' && c != PEOF);
10698 pungetc();
10699 continue;
10700 case '\\':
10701 if (pgetc() == '\n') {
10702 startlinno = ++plinno;
10703 if (doprompt)
10704 setprompt(2);
10705 continue;
10706 }
10707 pungetc();
10708 goto breakloop;
10709 case '\n':
10710 plinno++;
10711 needprompt = doprompt;
10712 RETURN(TNL);
10713 case PEOF:
10714 RETURN(TEOF);
10715 case '&':
10716 if (pgetc() == '&')
10717 RETURN(TAND);
10718 pungetc();
10719 RETURN(TBACKGND);
10720 case '|':
10721 if (pgetc() == '|')
10722 RETURN(TOR);
10723 pungetc();
10724 RETURN(TPIPE);
10725 case ';':
10726 if (pgetc() == ';')
10727 RETURN(TENDCASE);
10728 pungetc();
10729 RETURN(TSEMI);
10730 case '(':
10731 RETURN(TLP);
10732 case ')':
10733 RETURN(TRP);
10734 default:
10735 goto breakloop;
10736 }
10737 }
10738 breakloop:
10739 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10740#undef RETURN
10741}
10742#endif /* NEW_xxreadtoken */
10743
10744static int
10745readtoken(void)
10746{
10747 int t;
10748#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010749 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010750#endif
10751
10752#if ENABLE_ASH_ALIAS
10753 top:
10754#endif
10755
10756 t = xxreadtoken();
10757
10758 /*
10759 * eat newlines
10760 */
10761 if (checkkwd & CHKNL) {
10762 while (t == TNL) {
10763 parseheredoc();
10764 t = xxreadtoken();
10765 }
10766 }
10767
10768 if (t != TWORD || quoteflag) {
10769 goto out;
10770 }
10771
10772 /*
10773 * check for keywords
10774 */
10775 if (checkkwd & CHKKWD) {
10776 const char *const *pp;
10777
10778 pp = findkwd(wordtext);
10779 if (pp) {
10780 lasttoken = t = pp - tokname_array;
10781 TRACE(("keyword %s recognized\n", tokname(t)));
10782 goto out;
10783 }
10784 }
10785
10786 if (checkkwd & CHKALIAS) {
10787#if ENABLE_ASH_ALIAS
10788 struct alias *ap;
10789 ap = lookupalias(wordtext, 1);
10790 if (ap != NULL) {
10791 if (*ap->val) {
10792 pushstring(ap->val, ap);
10793 }
10794 goto top;
10795 }
10796#endif
10797 }
10798 out:
10799 checkkwd = 0;
10800#if DEBUG
10801 if (!alreadyseen)
10802 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10803 else
10804 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10805#endif
10806 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010807}
10808
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010809static char
10810peektoken(void)
10811{
10812 int t;
10813
10814 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010815 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010816 return tokname_array[t][0];
10817}
Eric Andersencb57d552001-06-28 07:25:16 +000010818
10819/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010820 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10821 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010822 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010823static union node *
10824parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010825{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010827
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010828 tokpushback = 0;
10829 doprompt = interact;
10830 if (doprompt)
10831 setprompt(doprompt);
10832 needprompt = 0;
10833 t = readtoken();
10834 if (t == TEOF)
10835 return NEOF;
10836 if (t == TNL)
10837 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010838 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010839 return list(1);
10840}
10841
10842/*
10843 * Input any here documents.
10844 */
10845static void
10846parseheredoc(void)
10847{
10848 struct heredoc *here;
10849 union node *n;
10850
10851 here = heredoclist;
10852 heredoclist = 0;
10853
10854 while (here) {
10855 if (needprompt) {
10856 setprompt(2);
10857 }
10858 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10859 here->eofmark, here->striptabs);
10860 n = stalloc(sizeof(struct narg));
10861 n->narg.type = NARG;
10862 n->narg.next = NULL;
10863 n->narg.text = wordtext;
10864 n->narg.backquote = backquotelist;
10865 here->here->nhere.doc = n;
10866 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010867 }
Eric Andersencb57d552001-06-28 07:25:16 +000010868}
10869
10870
10871/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010872 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010873 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010874#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010875static const char *
10876expandstr(const char *ps)
10877{
10878 union node n;
10879
10880 /* XXX Fix (char *) cast. */
10881 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010882 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010883 popfile();
10884
10885 n.narg.type = NARG;
10886 n.narg.next = NULL;
10887 n.narg.text = wordtext;
10888 n.narg.backquote = backquotelist;
10889
10890 expandarg(&n, NULL, 0);
10891 return stackblock();
10892}
10893#endif
10894
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010895/*
10896 * Execute a command or commands contained in a string.
10897 */
10898static int
10899evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010900{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010901 union node *n;
10902 struct stackmark smark;
10903 int skip;
10904
10905 setinputstring(s);
10906 setstackmark(&smark);
10907
10908 skip = 0;
10909 while ((n = parsecmd(0)) != NEOF) {
10910 evaltree(n, 0);
10911 popstackmark(&smark);
10912 skip = evalskip;
10913 if (skip)
10914 break;
10915 }
10916 popfile();
10917
10918 skip &= mask;
10919 evalskip = skip;
10920 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010921}
10922
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010923/*
10924 * The eval command.
10925 */
10926static int
10927evalcmd(int argc, char **argv)
10928{
10929 char *p;
10930 char *concat;
10931 char **ap;
10932
10933 if (argc > 1) {
10934 p = argv[1];
10935 if (argc > 2) {
10936 STARTSTACKSTR(concat);
10937 ap = argv + 2;
10938 for (;;) {
10939 concat = stack_putstr(p, concat);
10940 p = *ap++;
10941 if (p == NULL)
10942 break;
10943 STPUTC(' ', concat);
10944 }
10945 STPUTC('\0', concat);
10946 p = grabstackstr(concat);
10947 }
10948 evalstring(p, ~SKIPEVAL);
10949
10950 }
10951 return exitstatus;
10952}
10953
10954/*
10955 * Read and execute commands. "Top" is nonzero for the top level command
10956 * loop; it turns on prompting if the shell is interactive.
10957 */
10958static int
10959cmdloop(int top)
10960{
10961 union node *n;
10962 struct stackmark smark;
10963 int inter;
10964 int numeof = 0;
10965
10966 TRACE(("cmdloop(%d) called\n", top));
10967 for (;;) {
10968 int skip;
10969
10970 setstackmark(&smark);
10971#if JOBS
10972 if (jobctl)
10973 showjobs(stderr, SHOW_CHANGED);
10974#endif
10975 inter = 0;
10976 if (iflag && top) {
10977 inter++;
10978#if ENABLE_ASH_MAIL
10979 chkmail();
10980#endif
10981 }
10982 n = parsecmd(inter);
10983 /* showtree(n); DEBUG */
10984 if (n == NEOF) {
10985 if (!top || numeof >= 50)
10986 break;
10987 if (!stoppedjobs()) {
10988 if (!Iflag)
10989 break;
10990 out2str("\nUse \"exit\" to leave shell.\n");
10991 }
10992 numeof++;
10993 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000010994 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
10995 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010996 numeof = 0;
10997 evaltree(n, 0);
10998 }
10999 popstackmark(&smark);
11000 skip = evalskip;
11001
11002 if (skip) {
11003 evalskip = 0;
11004 return skip & SKIPEVAL;
11005 }
11006 }
11007 return 0;
11008}
11009
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011010/*
11011 * Take commands from a file. To be compatible we should do a path
11012 * search for the file, which is necessary to find sub-commands.
11013 */
11014static char *
11015find_dot_file(char *name)
11016{
11017 char *fullname;
11018 const char *path = pathval();
11019 struct stat statb;
11020
11021 /* don't try this for absolute or relative paths */
11022 if (strchr(name, '/'))
11023 return name;
11024
11025 while ((fullname = padvance(&path, name)) != NULL) {
11026 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11027 /*
11028 * Don't bother freeing here, since it will
11029 * be freed by the caller.
11030 */
11031 return fullname;
11032 }
11033 stunalloc(fullname);
11034 }
11035
11036 /* not found in the PATH */
11037 ash_msg_and_raise_error("%s: not found", name);
11038 /* NOTREACHED */
11039}
11040
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011041static int
11042dotcmd(int argc, char **argv)
11043{
11044 struct strlist *sp;
11045 volatile struct shparam saveparam;
11046 int status = 0;
11047
11048 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011049 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011050
11051 if (argc >= 2) { /* That's what SVR2 does */
11052 char *fullname;
11053
11054 fullname = find_dot_file(argv[1]);
11055
11056 if (argc > 2) {
11057 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011058 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011059 shellparam.nparam = argc - 2;
11060 shellparam.p = argv + 2;
11061 };
11062
11063 setinputfile(fullname, INPUT_PUSH_FILE);
11064 commandname = fullname;
11065 cmdloop(0);
11066 popfile();
11067
11068 if (argc > 2) {
11069 freeparam(&shellparam);
11070 shellparam = saveparam;
11071 };
11072 status = exitstatus;
11073 }
11074 return status;
11075}
11076
11077static int
11078exitcmd(int argc, char **argv)
11079{
11080 if (stoppedjobs())
11081 return 0;
11082 if (argc > 1)
11083 exitstatus = number(argv[1]);
11084 raise_exception(EXEXIT);
11085 /* NOTREACHED */
11086}
11087
11088#if ENABLE_ASH_BUILTIN_ECHO
11089static int
11090echocmd(int argc, char **argv)
11091{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011092 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011093}
11094#endif
11095
11096#if ENABLE_ASH_BUILTIN_TEST
11097static int
11098testcmd(int argc, char **argv)
11099{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011100 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011101}
11102#endif
11103
11104/*
11105 * Read a file containing shell functions.
11106 */
11107static void
11108readcmdfile(char *name)
11109{
11110 setinputfile(name, INPUT_PUSH_FILE);
11111 cmdloop(0);
11112 popfile();
11113}
11114
11115
Denis Vlasenkocc571512007-02-23 21:10:35 +000011116/* ============ find_command inplementation */
11117
11118/*
11119 * Resolve a command name. If you change this routine, you may have to
11120 * change the shellexec routine as well.
11121 */
11122static void
11123find_command(char *name, struct cmdentry *entry, int act, const char *path)
11124{
11125 struct tblentry *cmdp;
11126 int idx;
11127 int prev;
11128 char *fullname;
11129 struct stat statb;
11130 int e;
11131 int updatetbl;
11132 struct builtincmd *bcmd;
11133
11134 /* If name contains a slash, don't use PATH or hash table */
11135 if (strchr(name, '/') != NULL) {
11136 entry->u.index = -1;
11137 if (act & DO_ABS) {
11138 while (stat(name, &statb) < 0) {
11139#ifdef SYSV
11140 if (errno == EINTR)
11141 continue;
11142#endif
11143 entry->cmdtype = CMDUNKNOWN;
11144 return;
11145 }
11146 }
11147 entry->cmdtype = CMDNORMAL;
11148 return;
11149 }
11150
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011151/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011152
11153 updatetbl = (path == pathval());
11154 if (!updatetbl) {
11155 act |= DO_ALTPATH;
11156 if (strstr(path, "%builtin") != NULL)
11157 act |= DO_ALTBLTIN;
11158 }
11159
11160 /* If name is in the table, check answer will be ok */
11161 cmdp = cmdlookup(name, 0);
11162 if (cmdp != NULL) {
11163 int bit;
11164
11165 switch (cmdp->cmdtype) {
11166 default:
11167#if DEBUG
11168 abort();
11169#endif
11170 case CMDNORMAL:
11171 bit = DO_ALTPATH;
11172 break;
11173 case CMDFUNCTION:
11174 bit = DO_NOFUNC;
11175 break;
11176 case CMDBUILTIN:
11177 bit = DO_ALTBLTIN;
11178 break;
11179 }
11180 if (act & bit) {
11181 updatetbl = 0;
11182 cmdp = NULL;
11183 } else if (cmdp->rehash == 0)
11184 /* if not invalidated by cd, we're done */
11185 goto success;
11186 }
11187
11188 /* If %builtin not in path, check for builtin next */
11189 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011190 if (bcmd) {
11191 if (IS_BUILTIN_REGULAR(bcmd))
11192 goto builtin_success;
11193 if (act & DO_ALTPATH) {
11194 if (!(act & DO_ALTBLTIN))
11195 goto builtin_success;
11196 } else if (builtinloc <= 0) {
11197 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011198 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011199 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011200
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011201#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011202 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011203 entry->cmdtype = CMDNORMAL;
11204 entry->u.index = -1;
11205 return;
11206 }
11207#endif
11208
Denis Vlasenkocc571512007-02-23 21:10:35 +000011209 /* We have to search path. */
11210 prev = -1; /* where to start */
11211 if (cmdp && cmdp->rehash) { /* doing a rehash */
11212 if (cmdp->cmdtype == CMDBUILTIN)
11213 prev = builtinloc;
11214 else
11215 prev = cmdp->param.index;
11216 }
11217
11218 e = ENOENT;
11219 idx = -1;
11220 loop:
11221 while ((fullname = padvance(&path, name)) != NULL) {
11222 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011223 /* NB: code below will still use fullname
11224 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011225 idx++;
11226 if (pathopt) {
11227 if (prefix(pathopt, "builtin")) {
11228 if (bcmd)
11229 goto builtin_success;
11230 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011231 } else if (!(act & DO_NOFUNC)
11232 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011233 /* handled below */
11234 } else {
11235 /* ignore unimplemented options */
11236 continue;
11237 }
11238 }
11239 /* if rehash, don't redo absolute path names */
11240 if (fullname[0] == '/' && idx <= prev) {
11241 if (idx < prev)
11242 continue;
11243 TRACE(("searchexec \"%s\": no change\n", name));
11244 goto success;
11245 }
11246 while (stat(fullname, &statb) < 0) {
11247#ifdef SYSV
11248 if (errno == EINTR)
11249 continue;
11250#endif
11251 if (errno != ENOENT && errno != ENOTDIR)
11252 e = errno;
11253 goto loop;
11254 }
11255 e = EACCES; /* if we fail, this will be the error */
11256 if (!S_ISREG(statb.st_mode))
11257 continue;
11258 if (pathopt) { /* this is a %func directory */
11259 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011260 /* NB: stalloc will return space pointed by fullname
11261 * (because we don't have any intervening allocations
11262 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011263 readcmdfile(fullname);
11264 cmdp = cmdlookup(name, 0);
11265 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11266 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11267 stunalloc(fullname);
11268 goto success;
11269 }
11270 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11271 if (!updatetbl) {
11272 entry->cmdtype = CMDNORMAL;
11273 entry->u.index = idx;
11274 return;
11275 }
11276 INT_OFF;
11277 cmdp = cmdlookup(name, 1);
11278 cmdp->cmdtype = CMDNORMAL;
11279 cmdp->param.index = idx;
11280 INT_ON;
11281 goto success;
11282 }
11283
11284 /* We failed. If there was an entry for this command, delete it */
11285 if (cmdp && updatetbl)
11286 delete_cmd_entry();
11287 if (act & DO_ERR)
11288 ash_msg("%s: %s", name, errmsg(e, "not found"));
11289 entry->cmdtype = CMDUNKNOWN;
11290 return;
11291
11292 builtin_success:
11293 if (!updatetbl) {
11294 entry->cmdtype = CMDBUILTIN;
11295 entry->u.cmd = bcmd;
11296 return;
11297 }
11298 INT_OFF;
11299 cmdp = cmdlookup(name, 1);
11300 cmdp->cmdtype = CMDBUILTIN;
11301 cmdp->param.cmd = bcmd;
11302 INT_ON;
11303 success:
11304 cmdp->rehash = 0;
11305 entry->cmdtype = cmdp->cmdtype;
11306 entry->u = cmdp->param;
11307}
11308
11309
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011310/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011311
Eric Andersencb57d552001-06-28 07:25:16 +000011312/*
Eric Andersencb57d552001-06-28 07:25:16 +000011313 * The trap builtin.
11314 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011315static int
Eric Andersenc470f442003-07-28 09:56:35 +000011316trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011317{
11318 char *action;
11319 char **ap;
11320 int signo;
11321
Eric Andersenc470f442003-07-28 09:56:35 +000011322 nextopt(nullstr);
11323 ap = argptr;
11324 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011325 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011326 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011327 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011328
Rob Landleyc9c1a412006-07-12 19:17:55 +000011329 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011330 out1fmt("trap -- %s %s\n",
11331 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011332 }
11333 }
11334 return 0;
11335 }
Eric Andersenc470f442003-07-28 09:56:35 +000011336 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011337 action = NULL;
11338 else
11339 action = *ap++;
11340 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011341 signo = get_signum(*ap);
11342 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011343 ash_msg_and_raise_error("%s: bad trap", *ap);
11344 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011345 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011346 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011347 action = NULL;
11348 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011349 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011350 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011351 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011352 trap[signo] = action;
11353 if (signo != 0)
11354 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011355 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011356 ap++;
11357 }
11358 return 0;
11359}
11360
Eric Andersenc470f442003-07-28 09:56:35 +000011361
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011362/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011363
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011364#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011365/*
11366 * Lists available builtins
11367 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011368static int
11369helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011370{
11371 int col, i;
11372
11373 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011374 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011375 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011376 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011377 if (col > 60) {
11378 out1fmt("\n");
11379 col = 0;
11380 }
11381 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011382#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011383 {
11384 const char *a = applet_names;
11385 while (*a) {
11386 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11387 if (col > 60) {
11388 out1fmt("\n");
11389 col = 0;
11390 }
11391 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011392 }
11393 }
11394#endif
11395 out1fmt("\n\n");
11396 return EXIT_SUCCESS;
11397}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011398#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011399
Eric Andersencb57d552001-06-28 07:25:16 +000011400/*
Eric Andersencb57d552001-06-28 07:25:16 +000011401 * The export and readonly commands.
11402 */
Eric Andersenc470f442003-07-28 09:56:35 +000011403static int
11404exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011405{
11406 struct var *vp;
11407 char *name;
11408 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011409 char **aptr;
11410 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011411
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011412 if (nextopt("p") != 'p') {
11413 aptr = argptr;
11414 name = *aptr;
11415 if (name) {
11416 do {
11417 p = strchr(name, '=');
11418 if (p != NULL) {
11419 p++;
11420 } else {
11421 vp = *findvar(hashvar(name), name);
11422 if (vp) {
11423 vp->flags |= flag;
11424 continue;
11425 }
Eric Andersencb57d552001-06-28 07:25:16 +000011426 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011427 setvar(name, p, flag);
11428 } while ((name = *++aptr) != NULL);
11429 return 0;
11430 }
Eric Andersencb57d552001-06-28 07:25:16 +000011431 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011432 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011433 return 0;
11434}
11435
Eric Andersencb57d552001-06-28 07:25:16 +000011436/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011437 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011438 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011439static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011440unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011441{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011442 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011444 cmdp = cmdlookup(name, 0);
11445 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11446 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011447}
11448
Eric Andersencb57d552001-06-28 07:25:16 +000011449/*
Eric Andersencb57d552001-06-28 07:25:16 +000011450 * The unset builtin command. We unset the function before we unset the
11451 * variable to allow a function to be unset when there is a readonly variable
11452 * with the same name.
11453 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011454static int
Eric Andersenc470f442003-07-28 09:56:35 +000011455unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011456{
11457 char **ap;
11458 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011459 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011460 int ret = 0;
11461
11462 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011463 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011464 }
Eric Andersencb57d552001-06-28 07:25:16 +000011465
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011466 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011467 if (flag != 'f') {
11468 i = unsetvar(*ap);
11469 ret |= i;
11470 if (!(i & 2))
11471 continue;
11472 }
11473 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011474 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011475 }
Eric Andersenc470f442003-07-28 09:56:35 +000011476 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011477}
11478
11479
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011480/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011481
Eric Andersenc470f442003-07-28 09:56:35 +000011482#include <sys/times.h>
11483
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011484static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011485 ' ', offsetof(struct tms, tms_utime),
11486 '\n', offsetof(struct tms, tms_stime),
11487 ' ', offsetof(struct tms, tms_cutime),
11488 '\n', offsetof(struct tms, tms_cstime),
11489 0
11490};
Eric Andersencb57d552001-06-28 07:25:16 +000011491
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011492static int
11493timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011494{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011495 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011496 const unsigned char *p;
11497 struct tms buf;
11498
11499 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011500 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011501
11502 p = timescmd_str;
11503 do {
11504 t = *(clock_t *)(((char *) &buf) + p[1]);
11505 s = t / clk_tck;
11506 out1fmt("%ldm%ld.%.3lds%c",
11507 s/60, s%60,
11508 ((t - s * clk_tck) * 1000) / clk_tck,
11509 p[0]);
11510 } while (*(p += 2));
11511
Eric Andersencb57d552001-06-28 07:25:16 +000011512 return 0;
11513}
11514
Denis Vlasenko131ae172007-02-18 13:00:19 +000011515#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011516static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011517dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011518{
Eric Andersened9ecf72004-06-22 08:29:45 +000011519 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011520 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011521
Denis Vlasenkob012b102007-02-19 22:43:01 +000011522 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011523 result = arith(s, &errcode);
11524 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011525 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011526 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011527 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011529 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011530 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011531 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011532 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011533 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011534
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011535 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011536}
Eric Andersenc470f442003-07-28 09:56:35 +000011537
Eric Andersenc470f442003-07-28 09:56:35 +000011538/*
Eric Andersen90898442003-08-06 11:20:52 +000011539 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11540 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11541 *
11542 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011543 */
11544static int
Eric Andersen90898442003-08-06 11:20:52 +000011545letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011546{
Eric Andersenc470f442003-07-28 09:56:35 +000011547 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011548 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011549
Eric Andersen90898442003-08-06 11:20:52 +000011550 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011551 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011552 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011553 for (ap = argv + 1; *ap; ap++) {
11554 i = dash_arith(*ap);
11555 }
Eric Andersenc470f442003-07-28 09:56:35 +000011556
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011557 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011558}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011559#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011560
Eric Andersenc470f442003-07-28 09:56:35 +000011561
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011562/* ============ miscbltin.c
11563 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011564 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011565 */
11566
11567#undef rflag
11568
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011569#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011570typedef enum __rlimit_resource rlim_t;
11571#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011572
Eric Andersenc470f442003-07-28 09:56:35 +000011573/*
11574 * The read builtin. The -e option causes backslashes to escape the
11575 * following character.
11576 *
11577 * This uses unbuffered input, which may be avoidable in some cases.
11578 */
Eric Andersenc470f442003-07-28 09:56:35 +000011579static int
11580readcmd(int argc, char **argv)
11581{
11582 char **ap;
11583 int backslash;
11584 char c;
11585 int rflag;
11586 char *prompt;
11587 const char *ifs;
11588 char *p;
11589 int startword;
11590 int status;
11591 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011592#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011593 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011594 int nchars = 0;
11595 int silent = 0;
11596 struct termios tty, old_tty;
11597#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011598#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011599 fd_set set;
11600 struct timeval ts;
11601
11602 ts.tv_sec = ts.tv_usec = 0;
11603#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011604
11605 rflag = 0;
11606 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011607#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011608 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011609#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011610 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011611#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011612 while ((i = nextopt("p:rt:")) != '\0')
11613#else
11614 while ((i = nextopt("p:r")) != '\0')
11615#endif
11616 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011617 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011618 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011619 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011620 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011621#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011622 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011623 nchars = bb_strtou(optionarg, NULL, 10);
11624 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011625 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011626 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011627 break;
11628 case 's':
11629 silent = 1;
11630 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011631#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011633 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011634 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011635 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011636 /* EINVAL means number is ok, but not terminated by NUL */
11637 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011638 char *p2;
11639 if (*++p) {
11640 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011641 ts.tv_usec = bb_strtou(p, &p2, 10);
11642 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011643 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011644 scale = p2 - p;
11645 /* normalize to usec */
11646 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011647 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011648 while (scale++ < 6)
11649 ts.tv_usec *= 10;
11650 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011651 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011652 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011653 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011654 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011655 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011656 }
Paul Fox02eb9342005-09-07 16:56:02 +000011657 break;
11658#endif
11659 case 'r':
11660 rflag = 1;
11661 break;
11662 default:
11663 break;
11664 }
Eric Andersenc470f442003-07-28 09:56:35 +000011665 }
11666 if (prompt && isatty(0)) {
11667 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011668 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011669 ap = argptr;
11670 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011671 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011672 ifs = bltinlookup("IFS");
11673 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011674 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011675#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011676 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011677 if (tcgetattr(0, &tty) != 0) {
11678 /* Not a tty */
11679 n_flag = 0;
11680 silent = 0;
11681 } else {
11682 old_tty = tty;
11683 if (n_flag) {
11684 tty.c_lflag &= ~ICANON;
11685 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11686 }
11687 if (silent) {
11688 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11689 }
11690 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011691 }
Paul Fox02eb9342005-09-07 16:56:02 +000011692 }
11693#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011694#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011695 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011696 FD_ZERO(&set);
11697 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011698
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011699 /* poll-based wait produces bigger code, using select */
11700 i = select(1, &set, NULL, NULL, &ts);
11701 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011702#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011703 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011704 tcsetattr(0, TCSANOW, &old_tty);
11705#endif
11706 return 1;
11707 }
11708 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011709#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011710 status = 0;
11711 startword = 1;
11712 backslash = 0;
11713 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011714 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011715 if (read(0, &c, 1) != 1) {
11716 status = 1;
11717 break;
11718 }
11719 if (c == '\0')
11720 continue;
11721 if (backslash) {
11722 backslash = 0;
11723 if (c != '\n')
11724 goto put;
11725 continue;
11726 }
11727 if (!rflag && c == '\\') {
11728 backslash++;
11729 continue;
11730 }
11731 if (c == '\n')
11732 break;
11733 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11734 continue;
11735 }
11736 startword = 0;
11737 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11738 STACKSTRNUL(p);
11739 setvar(*ap, stackblock(), 0);
11740 ap++;
11741 startword = 1;
11742 STARTSTACKSTR(p);
11743 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011744 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011745 STPUTC(c, p);
11746 }
11747 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011748/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011749#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011750 while (!n_flag || --nchars);
11751#else
11752 while (1);
11753#endif
11754
11755#if ENABLE_ASH_READ_NCHARS
11756 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011757 tcsetattr(0, TCSANOW, &old_tty);
11758#endif
11759
Eric Andersenc470f442003-07-28 09:56:35 +000011760 STACKSTRNUL(p);
11761 /* Remove trailing blanks */
11762 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11763 *p = '\0';
11764 setvar(*ap, stackblock(), 0);
11765 while (*++ap != NULL)
11766 setvar(*ap, nullstr, 0);
11767 return status;
11768}
11769
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011770static int
11771umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011772{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011773 static const char permuser[3] ALIGN1 = "ugo";
11774 static const char permmode[3] ALIGN1 = "rwx";
11775 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011776 S_IRUSR, S_IWUSR, S_IXUSR,
11777 S_IRGRP, S_IWGRP, S_IXGRP,
11778 S_IROTH, S_IWOTH, S_IXOTH
11779 };
11780
11781 char *ap;
11782 mode_t mask;
11783 int i;
11784 int symbolic_mode = 0;
11785
11786 while (nextopt("S") != '\0') {
11787 symbolic_mode = 1;
11788 }
11789
Denis Vlasenkob012b102007-02-19 22:43:01 +000011790 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011791 mask = umask(0);
11792 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011793 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011794
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011795 ap = *argptr;
11796 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011797 if (symbolic_mode) {
11798 char buf[18];
11799 char *p = buf;
11800
11801 for (i = 0; i < 3; i++) {
11802 int j;
11803
11804 *p++ = permuser[i];
11805 *p++ = '=';
11806 for (j = 0; j < 3; j++) {
11807 if ((mask & permmask[3 * i + j]) == 0) {
11808 *p++ = permmode[j];
11809 }
11810 }
11811 *p++ = ',';
11812 }
11813 *--p = 0;
11814 puts(buf);
11815 } else {
11816 out1fmt("%.4o\n", mask);
11817 }
11818 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011819 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011820 mask = 0;
11821 do {
11822 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011823 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011824 mask = (mask << 3) + (*ap - '0');
11825 } while (*++ap != '\0');
11826 umask(mask);
11827 } else {
11828 mask = ~mask & 0777;
11829 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011830 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011831 }
11832 umask(~mask & 0777);
11833 }
11834 }
11835 return 0;
11836}
11837
11838/*
11839 * ulimit builtin
11840 *
11841 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11842 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11843 * ash by J.T. Conklin.
11844 *
11845 * Public domain.
11846 */
11847
11848struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011849 uint8_t cmd; /* RLIMIT_xxx fit into it */
11850 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011851 char option;
11852};
11853
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011854static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011855#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011856 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011857#endif
11858#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011859 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011860#endif
11861#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011862 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011863#endif
11864#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011865 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011866#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011867#ifdef RLIMIT_CORE
11868 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011869#endif
11870#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011871 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011872#endif
11873#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011874 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011875#endif
11876#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011877 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011878#endif
11879#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011880 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011881#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011882#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011883 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011884#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011885#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011886 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011887#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011888};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011889static const char limits_name[] =
11890#ifdef RLIMIT_CPU
11891 "time(seconds)" "\0"
11892#endif
11893#ifdef RLIMIT_FSIZE
11894 "file(blocks)" "\0"
11895#endif
11896#ifdef RLIMIT_DATA
11897 "data(kb)" "\0"
11898#endif
11899#ifdef RLIMIT_STACK
11900 "stack(kb)" "\0"
11901#endif
11902#ifdef RLIMIT_CORE
11903 "coredump(blocks)" "\0"
11904#endif
11905#ifdef RLIMIT_RSS
11906 "memory(kb)" "\0"
11907#endif
11908#ifdef RLIMIT_MEMLOCK
11909 "locked memory(kb)" "\0"
11910#endif
11911#ifdef RLIMIT_NPROC
11912 "process" "\0"
11913#endif
11914#ifdef RLIMIT_NOFILE
11915 "nofiles" "\0"
11916#endif
11917#ifdef RLIMIT_AS
11918 "vmemory(kb)" "\0"
11919#endif
11920#ifdef RLIMIT_LOCKS
11921 "locks" "\0"
11922#endif
11923;
Eric Andersenc470f442003-07-28 09:56:35 +000011924
Glenn L McGrath76620622004-01-13 10:19:37 +000011925enum limtype { SOFT = 0x1, HARD = 0x2 };
11926
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011927static void
11928printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011929 const struct limits *l)
11930{
11931 rlim_t val;
11932
11933 val = limit->rlim_max;
11934 if (how & SOFT)
11935 val = limit->rlim_cur;
11936
11937 if (val == RLIM_INFINITY)
11938 out1fmt("unlimited\n");
11939 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011940 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011941 out1fmt("%lld\n", (long long) val);
11942 }
11943}
11944
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011945static int
Eric Andersenc470f442003-07-28 09:56:35 +000011946ulimitcmd(int argc, char **argv)
11947{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011948 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011949 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011950 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011951 const struct limits *l;
11952 int set, all = 0;
11953 int optc, what;
11954 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011955
11956 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011957 while ((optc = nextopt("HSa"
11958#ifdef RLIMIT_CPU
11959 "t"
11960#endif
11961#ifdef RLIMIT_FSIZE
11962 "f"
11963#endif
11964#ifdef RLIMIT_DATA
11965 "d"
11966#endif
11967#ifdef RLIMIT_STACK
11968 "s"
11969#endif
11970#ifdef RLIMIT_CORE
11971 "c"
11972#endif
11973#ifdef RLIMIT_RSS
11974 "m"
11975#endif
11976#ifdef RLIMIT_MEMLOCK
11977 "l"
11978#endif
11979#ifdef RLIMIT_NPROC
11980 "p"
11981#endif
11982#ifdef RLIMIT_NOFILE
11983 "n"
11984#endif
11985#ifdef RLIMIT_AS
11986 "v"
11987#endif
11988#ifdef RLIMIT_LOCKS
11989 "w"
11990#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011991 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011992 switch (optc) {
11993 case 'H':
11994 how = HARD;
11995 break;
11996 case 'S':
11997 how = SOFT;
11998 break;
11999 case 'a':
12000 all = 1;
12001 break;
12002 default:
12003 what = optc;
12004 }
12005
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012006 for (l = limits_tbl; l->option != what; l++)
12007 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012008
12009 set = *argptr ? 1 : 0;
12010 if (set) {
12011 char *p = *argptr;
12012
12013 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012014 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012015 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012016 val = RLIM_INFINITY;
12017 else {
12018 val = (rlim_t) 0;
12019
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012020 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012021 val = (val * 10) + (long)(c - '0');
12022 if (val < (rlim_t) 0)
12023 break;
12024 }
12025 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012026 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012027 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012028 }
12029 }
12030 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012031 const char *lname = limits_name;
12032 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012033 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012034 out1fmt("%-20s ", lname);
12035 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012036 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012037 }
12038 return 0;
12039 }
12040
12041 getrlimit(l->cmd, &limit);
12042 if (set) {
12043 if (how & HARD)
12044 limit.rlim_max = val;
12045 if (how & SOFT)
12046 limit.rlim_cur = val;
12047 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012048 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012049 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012050 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012051 }
12052 return 0;
12053}
12054
Eric Andersen90898442003-08-06 11:20:52 +000012055
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012056/* ============ Math support */
12057
Denis Vlasenko131ae172007-02-18 13:00:19 +000012058#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012059
12060/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12061
12062 Permission is hereby granted, free of charge, to any person obtaining
12063 a copy of this software and associated documentation files (the
12064 "Software"), to deal in the Software without restriction, including
12065 without limitation the rights to use, copy, modify, merge, publish,
12066 distribute, sublicense, and/or sell copies of the Software, and to
12067 permit persons to whom the Software is furnished to do so, subject to
12068 the following conditions:
12069
12070 The above copyright notice and this permission notice shall be
12071 included in all copies or substantial portions of the Software.
12072
12073 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12074 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12075 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12076 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12077 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12078 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12079 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12080*/
12081
12082/* This is my infix parser/evaluator. It is optimized for size, intended
12083 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012084 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012085 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012086 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012087 * be that which POSIX specifies for shells. */
12088
12089/* The code uses a simple two-stack algorithm. See
12090 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012091 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012092 * this is based (this code differs in that it applies operators immediately
12093 * to the stack instead of adding them to a queue to end up with an
12094 * expression). */
12095
12096/* To use the routine, call it with an expression string and error return
12097 * pointer */
12098
12099/*
12100 * Aug 24, 2001 Manuel Novoa III
12101 *
12102 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12103 *
12104 * 1) In arith_apply():
12105 * a) Cached values of *numptr and &(numptr[-1]).
12106 * b) Removed redundant test for zero denominator.
12107 *
12108 * 2) In arith():
12109 * a) Eliminated redundant code for processing operator tokens by moving
12110 * to a table-based implementation. Also folded handling of parens
12111 * into the table.
12112 * b) Combined all 3 loops which called arith_apply to reduce generated
12113 * code size at the cost of speed.
12114 *
12115 * 3) The following expressions were treated as valid by the original code:
12116 * 1() , 0! , 1 ( *3 ) .
12117 * These bugs have been fixed by internally enclosing the expression in
12118 * parens and then checking that all binary ops and right parens are
12119 * preceded by a valid expression (NUM_TOKEN).
12120 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012121 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012122 * ctype's isspace() if it is used by another busybox applet or if additional
12123 * whitespace chars should be considered. Look below the "#include"s for a
12124 * precompiler test.
12125 */
12126
12127/*
12128 * Aug 26, 2001 Manuel Novoa III
12129 *
12130 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12131 *
12132 * Merge in Aaron's comments previously posted to the busybox list,
12133 * modified slightly to take account of my changes to the code.
12134 *
12135 */
12136
12137/*
12138 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12139 *
12140 * - allow access to variable,
12141 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12142 * - realize assign syntax (VAR=expr, +=, *= etc)
12143 * - realize exponentiation (** operator)
12144 * - realize comma separated - expr, expr
12145 * - realise ++expr --expr expr++ expr--
12146 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012147 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012148 * - was restored loses XOR operator
12149 * - remove one goto label, added three ;-)
12150 * - protect $((num num)) as true zero expr (Manuel`s error)
12151 * - always use special isspace(), see comment from bash ;-)
12152 */
12153
Eric Andersen90898442003-08-06 11:20:52 +000012154#define arith_isspace(arithval) \
12155 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12156
Eric Andersen90898442003-08-06 11:20:52 +000012157typedef unsigned char operator;
12158
12159/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012160 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012161 * precedence. The ID portion is so that multiple operators can have the
12162 * same precedence, ensuring that the leftmost one is evaluated first.
12163 * Consider * and /. */
12164
12165#define tok_decl(prec,id) (((id)<<5)|(prec))
12166#define PREC(op) ((op) & 0x1F)
12167
12168#define TOK_LPAREN tok_decl(0,0)
12169
12170#define TOK_COMMA tok_decl(1,0)
12171
12172#define TOK_ASSIGN tok_decl(2,0)
12173#define TOK_AND_ASSIGN tok_decl(2,1)
12174#define TOK_OR_ASSIGN tok_decl(2,2)
12175#define TOK_XOR_ASSIGN tok_decl(2,3)
12176#define TOK_PLUS_ASSIGN tok_decl(2,4)
12177#define TOK_MINUS_ASSIGN tok_decl(2,5)
12178#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12179#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12180
12181#define TOK_MUL_ASSIGN tok_decl(3,0)
12182#define TOK_DIV_ASSIGN tok_decl(3,1)
12183#define TOK_REM_ASSIGN tok_decl(3,2)
12184
12185/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012186#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012187
12188/* conditional is right associativity too */
12189#define TOK_CONDITIONAL tok_decl(4,0)
12190#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12191
12192#define TOK_OR tok_decl(5,0)
12193
12194#define TOK_AND tok_decl(6,0)
12195
12196#define TOK_BOR tok_decl(7,0)
12197
12198#define TOK_BXOR tok_decl(8,0)
12199
12200#define TOK_BAND tok_decl(9,0)
12201
12202#define TOK_EQ tok_decl(10,0)
12203#define TOK_NE tok_decl(10,1)
12204
12205#define TOK_LT tok_decl(11,0)
12206#define TOK_GT tok_decl(11,1)
12207#define TOK_GE tok_decl(11,2)
12208#define TOK_LE tok_decl(11,3)
12209
12210#define TOK_LSHIFT tok_decl(12,0)
12211#define TOK_RSHIFT tok_decl(12,1)
12212
12213#define TOK_ADD tok_decl(13,0)
12214#define TOK_SUB tok_decl(13,1)
12215
12216#define TOK_MUL tok_decl(14,0)
12217#define TOK_DIV tok_decl(14,1)
12218#define TOK_REM tok_decl(14,2)
12219
12220/* exponent is right associativity */
12221#define TOK_EXPONENT tok_decl(15,1)
12222
12223/* For now unary operators. */
12224#define UNARYPREC 16
12225#define TOK_BNOT tok_decl(UNARYPREC,0)
12226#define TOK_NOT tok_decl(UNARYPREC,1)
12227
12228#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12229#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12230
12231#define PREC_PRE (UNARYPREC+2)
12232
12233#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12234#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12235
12236#define PREC_POST (UNARYPREC+3)
12237
12238#define TOK_POST_INC tok_decl(PREC_POST, 0)
12239#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12240
12241#define SPEC_PREC (UNARYPREC+4)
12242
12243#define TOK_NUM tok_decl(SPEC_PREC, 0)
12244#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12245
12246#define NUMPTR (*numstackptr)
12247
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012248static int
12249tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012250{
12251 operator prec = PREC(op);
12252
12253 convert_prec_is_assing(prec);
12254 return (prec == PREC(TOK_ASSIGN) ||
12255 prec == PREC_PRE || prec == PREC_POST);
12256}
12257
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012258static int
12259is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012260{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012261 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12262 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012263}
12264
Eric Andersen90898442003-08-06 11:20:52 +000012265typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012266 arith_t val;
12267 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012268 char contidional_second_val_initialized;
12269 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012270 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012271} v_n_t;
12272
Eric Andersen90898442003-08-06 11:20:52 +000012273typedef struct CHK_VAR_RECURSIVE_LOOPED {
12274 const char *var;
12275 struct CHK_VAR_RECURSIVE_LOOPED *next;
12276} chk_var_recursive_looped_t;
12277
12278static chk_var_recursive_looped_t *prev_chk_var_recursive;
12279
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012280static int
12281arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012282{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012283 if (t->var) {
12284 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012286 if (p) {
12287 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012288
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012289 /* recursive try as expression */
12290 chk_var_recursive_looped_t *cur;
12291 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012292
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12294 if (strcmp(cur->var, t->var) == 0) {
12295 /* expression recursion loop detected */
12296 return -5;
12297 }
12298 }
12299 /* save current lookuped var name */
12300 cur = prev_chk_var_recursive;
12301 cur_save.var = t->var;
12302 cur_save.next = cur;
12303 prev_chk_var_recursive = &cur_save;
12304
12305 t->val = arith (p, &errcode);
12306 /* restore previous ptr after recursiving */
12307 prev_chk_var_recursive = cur;
12308 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012309 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 /* allow undefined var as 0 */
12311 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012312 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012313 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012314}
12315
12316/* "applying" a token means performing it on the top elements on the integer
12317 * stack. For a unary operator it will only change the top element, but a
12318 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012319static int
12320arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012321{
Eric Andersen90898442003-08-06 11:20:52 +000012322 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012323 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012324 int ret_arith_lookup_val;
12325
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012326 /* There is no operator that can work without arguments */
12327 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012328 numptr_m1 = NUMPTR - 1;
12329
12330 /* check operand is var with noninteger value */
12331 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012332 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012333 return ret_arith_lookup_val;
12334
12335 rez = numptr_m1->val;
12336 if (op == TOK_UMINUS)
12337 rez *= -1;
12338 else if (op == TOK_NOT)
12339 rez = !rez;
12340 else if (op == TOK_BNOT)
12341 rez = ~rez;
12342 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12343 rez++;
12344 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12345 rez--;
12346 else if (op != TOK_UPLUS) {
12347 /* Binary operators */
12348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 /* check and binary operators need two arguments */
12350 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012351
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012352 /* ... and they pop one */
12353 --NUMPTR;
12354 numptr_val = rez;
12355 if (op == TOK_CONDITIONAL) {
12356 if (! numptr_m1->contidional_second_val_initialized) {
12357 /* protect $((expr1 ? expr2)) without ": expr" */
12358 goto err;
12359 }
12360 rez = numptr_m1->contidional_second_val;
12361 } else if (numptr_m1->contidional_second_val_initialized) {
12362 /* protect $((expr1 : expr2)) without "expr ? " */
12363 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012364 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012365 numptr_m1 = NUMPTR - 1;
12366 if (op != TOK_ASSIGN) {
12367 /* check operand is var with noninteger value for not '=' */
12368 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12369 if (ret_arith_lookup_val)
12370 return ret_arith_lookup_val;
12371 }
12372 if (op == TOK_CONDITIONAL) {
12373 numptr_m1->contidional_second_val = rez;
12374 }
12375 rez = numptr_m1->val;
12376 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012377 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012378 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012379 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012381 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012382 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012383 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012384 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012385 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012387 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012388 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012389 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012390 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012391 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012392 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012393 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012394 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012395 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012397 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012399 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012400 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012401 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012403 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012404 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012405 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012407 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012408 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012409 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012410 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012411 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012412 /* protect $((expr : expr)) without "expr ? " */
12413 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012414 }
12415 numptr_m1->contidional_second_val_initialized = op;
12416 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012417 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012418 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012419 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012420 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012421 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012422 return -3; /* exponent less than 0 */
12423 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012424 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012425
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012426 if (numptr_val)
12427 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012428 c *= rez;
12429 rez = c;
12430 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012431 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012432 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012433 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012434 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012435 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012436 rez %= numptr_val;
12437 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012439 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012440
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012442 /* Hmm, 1=2 ? */
12443 goto err;
12444 }
12445 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012446#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012447 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012448#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012449 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012450#endif
Eric Andersen90898442003-08-06 11:20:52 +000012451 setvar(numptr_m1->var, buf, 0);
12452 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012454 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012456 rez++;
12457 }
12458 numptr_m1->val = rez;
12459 /* protect geting var value, is number now */
12460 numptr_m1->var = NULL;
12461 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012462 err:
12463 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012464}
12465
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012466/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012467static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012468 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12469 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12470 '<','<', 0, TOK_LSHIFT,
12471 '>','>', 0, TOK_RSHIFT,
12472 '|','|', 0, TOK_OR,
12473 '&','&', 0, TOK_AND,
12474 '!','=', 0, TOK_NE,
12475 '<','=', 0, TOK_LE,
12476 '>','=', 0, TOK_GE,
12477 '=','=', 0, TOK_EQ,
12478 '|','=', 0, TOK_OR_ASSIGN,
12479 '&','=', 0, TOK_AND_ASSIGN,
12480 '*','=', 0, TOK_MUL_ASSIGN,
12481 '/','=', 0, TOK_DIV_ASSIGN,
12482 '%','=', 0, TOK_REM_ASSIGN,
12483 '+','=', 0, TOK_PLUS_ASSIGN,
12484 '-','=', 0, TOK_MINUS_ASSIGN,
12485 '-','-', 0, TOK_POST_DEC,
12486 '^','=', 0, TOK_XOR_ASSIGN,
12487 '+','+', 0, TOK_POST_INC,
12488 '*','*', 0, TOK_EXPONENT,
12489 '!', 0, TOK_NOT,
12490 '<', 0, TOK_LT,
12491 '>', 0, TOK_GT,
12492 '=', 0, TOK_ASSIGN,
12493 '|', 0, TOK_BOR,
12494 '&', 0, TOK_BAND,
12495 '*', 0, TOK_MUL,
12496 '/', 0, TOK_DIV,
12497 '%', 0, TOK_REM,
12498 '+', 0, TOK_ADD,
12499 '-', 0, TOK_SUB,
12500 '^', 0, TOK_BXOR,
12501 /* uniq */
12502 '~', 0, TOK_BNOT,
12503 ',', 0, TOK_COMMA,
12504 '?', 0, TOK_CONDITIONAL,
12505 ':', 0, TOK_CONDITIONAL_SEP,
12506 ')', 0, TOK_RPAREN,
12507 '(', 0, TOK_LPAREN,
12508 0
12509};
12510/* ptr to ")" */
12511#define endexpression &op_tokens[sizeof(op_tokens)-7]
12512
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012513static arith_t
12514arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012515{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012516 char arithval; /* Current character under analysis */
12517 operator lasttok, op;
12518 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012519
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012520 const char *p = endexpression;
12521 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012522
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012524
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 /* Stack of integers */
12526 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12527 * in any given correct or incorrect expression is left as an exercise to
12528 * the reader. */
12529 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12530 *numstackptr = numstack;
12531 /* Stack of operator tokens */
12532 operator *stack = alloca((datasizes) * sizeof(operator)),
12533 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012534
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012535 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12536 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012537
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012538 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012539 arithval = *expr;
12540 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012541 if (p == endexpression) {
12542 /* Null expression. */
12543 return 0;
12544 }
12545
12546 /* This is only reached after all tokens have been extracted from the
12547 * input stream. If there are still tokens on the operator stack, they
12548 * are to be applied in order. At the end, there should be a final
12549 * result on the integer stack */
12550
12551 if (expr != endexpression + 1) {
12552 /* If we haven't done so already, */
12553 /* append a closing right paren */
12554 expr = endexpression;
12555 /* and let the loop process it. */
12556 continue;
12557 }
12558 /* At this point, we're done with the expression. */
12559 if (numstackptr != numstack+1) {
12560 /* ... but if there isn't, it's bad */
12561 err:
12562 return (*perrcode = -1);
12563 }
12564 if (numstack->var) {
12565 /* expression is $((var)) only, lookup now */
12566 errcode = arith_lookup_val(numstack);
12567 }
12568 ret:
12569 *perrcode = errcode;
12570 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012571 }
12572
Eric Andersen90898442003-08-06 11:20:52 +000012573 /* Continue processing the expression. */
12574 if (arith_isspace(arithval)) {
12575 /* Skip whitespace */
12576 goto prologue;
12577 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012578 p = endofname(expr);
12579 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012580 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012581
12582 numstackptr->var = alloca(var_name_size);
12583 safe_strncpy(numstackptr->var, expr, var_name_size);
12584 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012585 num:
Eric Andersen90898442003-08-06 11:20:52 +000012586 numstackptr->contidional_second_val_initialized = 0;
12587 numstackptr++;
12588 lasttok = TOK_NUM;
12589 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012590 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012591 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012592 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012593#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012594 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012595#else
12596 numstackptr->val = strtol(expr, (char **) &expr, 0);
12597#endif
Eric Andersen90898442003-08-06 11:20:52 +000012598 goto num;
12599 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012600 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012601 const char *o;
12602
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012603 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012604 /* strange operator not found */
12605 goto err;
12606 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012607 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012608 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012609 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012610 /* found */
12611 expr = o - 1;
12612 break;
12613 }
12614 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012615 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012616 p++;
12617 /* skip zero delim */
12618 p++;
12619 }
12620 op = p[1];
12621
12622 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012623 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12624 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012625
12626 /* Plus and minus are binary (not unary) _only_ if the last
12627 * token was as number, or a right paren (which pretends to be
12628 * a number, since it evaluates to one). Think about it.
12629 * It makes sense. */
12630 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012631 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012632 case TOK_ADD:
12633 op = TOK_UPLUS;
12634 break;
12635 case TOK_SUB:
12636 op = TOK_UMINUS;
12637 break;
12638 case TOK_POST_INC:
12639 op = TOK_PRE_INC;
12640 break;
12641 case TOK_POST_DEC:
12642 op = TOK_PRE_DEC;
12643 break;
Eric Andersen90898442003-08-06 11:20:52 +000012644 }
12645 }
12646 /* We don't want a unary operator to cause recursive descent on the
12647 * stack, because there can be many in a row and it could cause an
12648 * operator to be evaluated before its argument is pushed onto the
12649 * integer stack. */
12650 /* But for binary operators, "apply" everything on the operator
12651 * stack until we find an operator with a lesser priority than the
12652 * one we have just extracted. */
12653 /* Left paren is given the lowest priority so it will never be
12654 * "applied" in this way.
12655 * if associativity is right and priority eq, applied also skip
12656 */
12657 prec = PREC(op);
12658 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12659 /* not left paren or unary */
12660 if (lasttok != TOK_NUM) {
12661 /* binary op must be preceded by a num */
12662 goto err;
12663 }
12664 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012665 if (op == TOK_RPAREN) {
12666 /* The algorithm employed here is simple: while we don't
12667 * hit an open paren nor the bottom of the stack, pop
12668 * tokens and apply them */
12669 if (stackptr[-1] == TOK_LPAREN) {
12670 --stackptr;
12671 /* Any operator directly after a */
12672 lasttok = TOK_NUM;
12673 /* close paren should consider itself binary */
12674 goto prologue;
12675 }
12676 } else {
12677 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012678
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012679 convert_prec_is_assing(prec);
12680 convert_prec_is_assing(prev_prec);
12681 if (prev_prec < prec)
12682 break;
12683 /* check right assoc */
12684 if (prev_prec == prec && is_right_associativity(prec))
12685 break;
12686 }
12687 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12688 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012689 }
12690 if (op == TOK_RPAREN) {
12691 goto err;
12692 }
12693 }
12694
12695 /* Push this operator to the stack and remember it. */
12696 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012697 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012698 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012699 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012700}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012701#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012702
12703
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012704/* ============ main() and helpers */
12705
12706/*
12707 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012708 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012709static void exitshell(void) ATTRIBUTE_NORETURN;
12710static void
12711exitshell(void)
12712{
12713 struct jmploc loc;
12714 char *p;
12715 int status;
12716
12717 status = exitstatus;
12718 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12719 if (setjmp(loc.loc)) {
12720 if (exception == EXEXIT)
12721/* dash bug: it just does _exit(exitstatus) here
12722 * but we have to do setjobctl(0) first!
12723 * (bug is still not fixed in dash-0.5.3 - if you run dash
12724 * under Midnight Commander, on exit from dash MC is backgrounded) */
12725 status = exitstatus;
12726 goto out;
12727 }
12728 exception_handler = &loc;
12729 p = trap[0];
12730 if (p) {
12731 trap[0] = NULL;
12732 evalstring(p, 0);
12733 }
12734 flush_stdout_stderr();
12735 out:
12736 setjobctl(0);
12737 _exit(status);
12738 /* NOTREACHED */
12739}
12740
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012741static void
12742init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012743{
12744 /* from input.c: */
12745 basepf.nextc = basepf.buf = basebuf;
12746
12747 /* from trap.c: */
12748 signal(SIGCHLD, SIG_DFL);
12749
12750 /* from var.c: */
12751 {
12752 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012753 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012754 const char *p;
12755 struct stat st1, st2;
12756
12757 initvar();
12758 for (envp = environ; envp && *envp; envp++) {
12759 if (strchr(*envp, '=')) {
12760 setvareq(*envp, VEXPORT|VTEXTFIXED);
12761 }
12762 }
12763
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012764 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012765 setvar("PPID", ppid, 0);
12766
12767 p = lookupvar("PWD");
12768 if (p)
12769 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12770 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12771 p = '\0';
12772 setpwd(p, 0);
12773 }
12774}
12775
12776/*
12777 * Process the shell command line arguments.
12778 */
12779static void
12780procargs(int argc, char **argv)
12781{
12782 int i;
12783 const char *xminusc;
12784 char **xargv;
12785
12786 xargv = argv;
12787 arg0 = xargv[0];
12788 if (argc > 0)
12789 xargv++;
12790 for (i = 0; i < NOPTS; i++)
12791 optlist[i] = 2;
12792 argptr = xargv;
12793 options(1);
12794 xargv = argptr;
12795 xminusc = minusc;
12796 if (*xargv == NULL) {
12797 if (xminusc)
12798 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12799 sflag = 1;
12800 }
12801 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12802 iflag = 1;
12803 if (mflag == 2)
12804 mflag = iflag;
12805 for (i = 0; i < NOPTS; i++)
12806 if (optlist[i] == 2)
12807 optlist[i] = 0;
12808#if DEBUG == 2
12809 debug = 1;
12810#endif
12811 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12812 if (xminusc) {
12813 minusc = *xargv++;
12814 if (*xargv)
12815 goto setarg0;
12816 } else if (!sflag) {
12817 setinputfile(*xargv, 0);
12818 setarg0:
12819 arg0 = *xargv++;
12820 commandname = arg0;
12821 }
12822
12823 shellparam.p = xargv;
12824#if ENABLE_ASH_GETOPTS
12825 shellparam.optind = 1;
12826 shellparam.optoff = -1;
12827#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012828 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012829 while (*xargv) {
12830 shellparam.nparam++;
12831 xargv++;
12832 }
12833 optschanged();
12834}
12835
12836/*
12837 * Read /etc/profile or .profile.
12838 */
12839static void
12840read_profile(const char *name)
12841{
12842 int skip;
12843
12844 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12845 return;
12846 skip = cmdloop(0);
12847 popfile();
12848 if (skip)
12849 exitshell();
12850}
12851
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012852/*
12853 * This routine is called when an error or an interrupt occurs in an
12854 * interactive shell and control is returned to the main command loop.
12855 */
12856static void
12857reset(void)
12858{
12859 /* from eval.c: */
12860 evalskip = 0;
12861 loopnest = 0;
12862 /* from input.c: */
12863 parselleft = parsenleft = 0; /* clear input buffer */
12864 popallfiles();
12865 /* from parser.c: */
12866 tokpushback = 0;
12867 checkkwd = 0;
12868 /* from redir.c: */
12869 clearredir(0);
12870}
12871
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872#if PROFILE
12873static short profile_buf[16384];
12874extern int etext();
12875#endif
12876
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012877/*
12878 * Main routine. We initialize things, parse the arguments, execute
12879 * profiles if we're a login shell, and then call cmdloop to execute
12880 * commands. The setjmp call sets up the location to jump to when an
12881 * exception occurs. When an exception occurs the variable "state"
12882 * is used to figure out how far we had gotten.
12883 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012884int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012885int ash_main(int argc, char **argv)
12886{
12887 char *shinit;
12888 volatile int state;
12889 struct jmploc jmploc;
12890 struct stackmark smark;
12891
Denis Vlasenko01631112007-12-16 17:20:38 +000012892 /* Initialize global data */
12893 INIT_G_misc();
12894 INIT_G_memstack();
12895 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012896#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012897 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012898#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012899 INIT_G_cmdtable();
12900
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012901#if PROFILE
12902 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12903#endif
12904
12905#if ENABLE_FEATURE_EDITING
12906 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12907#endif
12908 state = 0;
12909 if (setjmp(jmploc.loc)) {
12910 int e;
12911 int s;
12912
12913 reset();
12914
12915 e = exception;
12916 if (e == EXERROR)
12917 exitstatus = 2;
12918 s = state;
12919 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12920 exitshell();
12921
12922 if (e == EXINT) {
12923 outcslow('\n', stderr);
12924 }
12925 popstackmark(&smark);
12926 FORCE_INT_ON; /* enable interrupts */
12927 if (s == 1)
12928 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012929 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012930 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012931 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012933 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012934 }
12935 exception_handler = &jmploc;
12936#if DEBUG
12937 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012938 trace_puts("Shell args: ");
12939 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012940#endif
12941 rootpid = getpid();
12942
12943#if ENABLE_ASH_RANDOM_SUPPORT
12944 rseed = rootpid + time(NULL);
12945#endif
12946 init();
12947 setstackmark(&smark);
12948 procargs(argc, argv);
12949#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12950 if (iflag) {
12951 const char *hp = lookupvar("HISTFILE");
12952
12953 if (hp == NULL) {
12954 hp = lookupvar("HOME");
12955 if (hp != NULL) {
12956 char *defhp = concat_path_file(hp, ".ash_history");
12957 setvar("HISTFILE", defhp, 0);
12958 free(defhp);
12959 }
12960 }
12961 }
12962#endif
12963 if (argv[0] && argv[0][0] == '-')
12964 isloginsh = 1;
12965 if (isloginsh) {
12966 state = 1;
12967 read_profile("/etc/profile");
12968 state1:
12969 state = 2;
12970 read_profile(".profile");
12971 }
12972 state2:
12973 state = 3;
12974 if (
12975#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012976 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012977#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012978 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012979 ) {
12980 shinit = lookupvar("ENV");
12981 if (shinit != NULL && *shinit != '\0') {
12982 read_profile(shinit);
12983 }
12984 }
12985 state3:
12986 state = 4;
12987 if (minusc)
12988 evalstring(minusc, 0);
12989
12990 if (sflag || minusc == NULL) {
12991#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12992 if ( iflag ) {
12993 const char *hp = lookupvar("HISTFILE");
12994
12995 if (hp != NULL)
12996 line_input_state->hist_file = hp;
12997 }
12998#endif
12999 state4: /* XXX ??? - why isn't this before the "if" statement */
13000 cmdloop(1);
13001 }
13002#if PROFILE
13003 monitor(0);
13004#endif
13005#ifdef GPROF
13006 {
13007 extern void _mcleanup(void);
13008 _mcleanup();
13009 }
13010#endif
13011 exitshell();
13012 /* NOTREACHED */
13013}
13014
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013015#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013016const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013017int main(int argc, char **argv)
13018{
13019 return ash_main(argc, argv);
13020}
13021#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013022
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023
Eric Andersendf82f612001-06-28 07:46:40 +000013024/*-
13025 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013026 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013027 *
13028 * This code is derived from software contributed to Berkeley by
13029 * Kenneth Almquist.
13030 *
13031 * Redistribution and use in source and binary forms, with or without
13032 * modification, are permitted provided that the following conditions
13033 * are met:
13034 * 1. Redistributions of source code must retain the above copyright
13035 * notice, this list of conditions and the following disclaimer.
13036 * 2. Redistributions in binary form must reproduce the above copyright
13037 * notice, this list of conditions and the following disclaimer in the
13038 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013039 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013040 * may be used to endorse or promote products derived from this software
13041 * without specific prior written permission.
13042 *
13043 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13044 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13045 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13046 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13047 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13048 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13049 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13050 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13051 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13052 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13053 * SUCH DAMAGE.
13054 */