blob: 61217204357938994cdc88ff701b61c22a8a10bb [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 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009079static int
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;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009088 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009089 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009090 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009091 ash_msg("illegal option -o %s", name);
9092 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009093 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009094 out1str("Current option settings\n");
9095 for (i = 0; i < NOPTS; i++)
9096 out1fmt("%-16s%s\n", optnames(i),
9097 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009098 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009099}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009100static void
9101setoption(int flag, int val)
9102{
9103 int i;
9104
9105 for (i = 0; i < NOPTS; i++) {
9106 if (optletters(i) == flag) {
9107 optlist[i] = val;
9108 return;
9109 }
9110 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009111 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009112 /* NOTREACHED */
9113}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009114static int
Eric Andersenc470f442003-07-28 09:56:35 +00009115options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009116{
9117 char *p;
9118 int val;
9119 int c;
9120
9121 if (cmdline)
9122 minusc = NULL;
9123 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009124 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009125 if (c != '-' && c != '+')
9126 break;
9127 argptr++;
9128 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009129 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009130 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009131 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009132 if (!cmdline) {
9133 /* "-" means turn off -x and -v */
9134 if (p[0] == '\0')
9135 xflag = vflag = 0;
9136 /* "--" means reset params */
9137 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009138 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009139 }
Eric Andersenc470f442003-07-28 09:56:35 +00009140 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009141 }
Eric Andersencb57d552001-06-28 07:25:16 +00009142 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009143 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009144 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009145 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009146 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009147 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009148 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009149 if (minus_o(*argptr, val)) {
9150 /* it already printed err message */
9151 return 1; /* error */
9152 }
Eric Andersencb57d552001-06-28 07:25:16 +00009153 if (*argptr)
9154 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009155 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9156 isloginsh = 1;
9157 /* bash does not accept +-login, we also won't */
9158 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009159 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009160 isloginsh = 1;
9161 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009162 } else {
9163 setoption(c, val);
9164 }
9165 }
9166 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009167 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009168}
9169
Eric Andersencb57d552001-06-28 07:25:16 +00009170/*
Eric Andersencb57d552001-06-28 07:25:16 +00009171 * The shift builtin command.
9172 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009173static int
Eric Andersenc470f442003-07-28 09:56:35 +00009174shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009175{
9176 int n;
9177 char **ap1, **ap2;
9178
9179 n = 1;
9180 if (argc > 1)
9181 n = number(argv[1]);
9182 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009183 ash_msg_and_raise_error("can't shift that many");
9184 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009185 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009186 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009187 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009188 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009189 }
9190 ap2 = shellparam.p;
9191 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009192#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009193 shellparam.optind = 1;
9194 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009195#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009196 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 return 0;
9198}
9199
Eric Andersencb57d552001-06-28 07:25:16 +00009200/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009201 * POSIX requires that 'set' (but not export or readonly) output the
9202 * variables in lexicographic order - by the locale's collating order (sigh).
9203 * Maybe we could keep them in an ordered balanced binary tree
9204 * instead of hashed lists.
9205 * For now just roll 'em through qsort for printing...
9206 */
9207static int
9208showvars(const char *sep_prefix, int on, int off)
9209{
9210 const char *sep;
9211 char **ep, **epend;
9212
9213 ep = listvars(on, off, &epend);
9214 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9215
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009216 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009217
9218 for (; ep < epend; ep++) {
9219 const char *p;
9220 const char *q;
9221
9222 p = strchrnul(*ep, '=');
9223 q = nullstr;
9224 if (*p)
9225 q = single_quote(++p);
9226 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9227 }
9228 return 0;
9229}
9230
9231/*
Eric Andersencb57d552001-06-28 07:25:16 +00009232 * The set command builtin.
9233 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009234static int
Eric Andersenc470f442003-07-28 09:56:35 +00009235setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009236{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009237 int retval;
9238
Eric Andersencb57d552001-06-28 07:25:16 +00009239 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009240 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009241 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009242 retval = 1;
9243 if (!options(0)) { /* if no parse error... */
9244 retval = 0;
9245 optschanged();
9246 if (*argptr != NULL) {
9247 setparam(argptr);
9248 }
Eric Andersencb57d552001-06-28 07:25:16 +00009249 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009250 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009251 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009252}
9253
Denis Vlasenko131ae172007-02-18 13:00:19 +00009254#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009255/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009256static void
9257change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009258{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009259 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009260 /* "get", generate */
9261 char buf[16];
9262
9263 rseed = rseed * 1103515245 + 12345;
9264 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9265 /* set without recursion */
9266 setvar(vrandom.text, buf, VNOFUNC);
9267 vrandom.flags &= ~VNOFUNC;
9268 } else {
9269 /* set/reset */
9270 rseed = strtoul(value, (char **)NULL, 10);
9271 }
Eric Andersenef02f822004-03-11 13:34:24 +00009272}
Eric Andersen16767e22004-03-16 05:14:10 +00009273#endif
9274
Denis Vlasenko131ae172007-02-18 13:00:19 +00009275#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009276static int
Eric Andersenc470f442003-07-28 09:56:35 +00009277getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009278{
9279 char *p, *q;
9280 char c = '?';
9281 int done = 0;
9282 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009283 char s[12];
9284 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009286 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009287 return 1;
9288 optnext = optfirst + *param_optind - 1;
9289
9290 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009291 p = NULL;
9292 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009293 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009294 if (p == NULL || *p == '\0') {
9295 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009296 p = *optnext;
9297 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009298 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009299 p = NULL;
9300 done = 1;
9301 goto out;
9302 }
9303 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009304 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009305 goto atend;
9306 }
9307
9308 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009309 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009310 if (*q == '\0') {
9311 if (optstr[0] == ':') {
9312 s[0] = c;
9313 s[1] = '\0';
9314 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009315 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009316 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009317 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009318 }
9319 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009320 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009321 }
9322 if (*++q == ':')
9323 q++;
9324 }
9325
9326 if (*++q == ':') {
9327 if (*p == '\0' && (p = *optnext) == NULL) {
9328 if (optstr[0] == ':') {
9329 s[0] = c;
9330 s[1] = '\0';
9331 err |= setvarsafe("OPTARG", s, 0);
9332 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009333 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009334 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009335 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009336 c = '?';
9337 }
Eric Andersenc470f442003-07-28 09:56:35 +00009338 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009339 }
9340
9341 if (p == *optnext)
9342 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009343 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009344 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009345 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009346 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009347 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009348 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009349 *param_optind = optnext - optfirst + 1;
9350 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009351 err |= setvarsafe("OPTIND", s, VNOFUNC);
9352 s[0] = c;
9353 s[1] = '\0';
9354 err |= setvarsafe(optvar, s, 0);
9355 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009356 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009357 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009358 flush_stdout_stderr();
9359 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009360 }
9361 return done;
9362}
Eric Andersenc470f442003-07-28 09:56:35 +00009363
9364/*
9365 * The getopts builtin. Shellparam.optnext points to the next argument
9366 * to be processed. Shellparam.optptr points to the next character to
9367 * be processed in the current argument. If shellparam.optnext is NULL,
9368 * then it's the first time getopts has been called.
9369 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009370static int
Eric Andersenc470f442003-07-28 09:56:35 +00009371getoptscmd(int argc, char **argv)
9372{
9373 char **optbase;
9374
9375 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009376 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009377 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009378 optbase = shellparam.p;
9379 if (shellparam.optind > shellparam.nparam + 1) {
9380 shellparam.optind = 1;
9381 shellparam.optoff = -1;
9382 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009383 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009384 optbase = &argv[3];
9385 if (shellparam.optind > argc - 2) {
9386 shellparam.optind = 1;
9387 shellparam.optoff = -1;
9388 }
9389 }
9390
9391 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009392 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009393}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009394#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009395
Eric Andersencb57d552001-06-28 07:25:16 +00009396
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009397/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009398
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009399/*
9400 * NEOF is returned by parsecmd when it encounters an end of file. It
9401 * must be distinct from NULL, so we use the address of a variable that
9402 * happens to be handy.
9403 */
9404static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009405#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009406static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009407static int lasttoken; /* last token read */
9408static char *wordtext; /* text of last word returned by readtoken */
9409static struct nodelist *backquotelist;
9410static union node *redirnode;
9411static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009412static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009413
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009414static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9415static void
9416raise_error_syntax(const char *msg)
9417{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009418 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009419 /* NOTREACHED */
9420}
9421
9422/*
9423 * Called when an unexpected token is read during the parse. The argument
9424 * is the token that is expected, or -1 if more than one type of token can
9425 * occur at this point.
9426 */
9427static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9428static void
9429raise_error_unexpected_syntax(int token)
9430{
9431 char msg[64];
9432 int l;
9433
9434 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9435 if (token >= 0)
9436 sprintf(msg + l, " (expecting %s)", tokname(token));
9437 raise_error_syntax(msg);
9438 /* NOTREACHED */
9439}
Eric Andersencb57d552001-06-28 07:25:16 +00009440
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009441#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009442
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009443struct heredoc {
9444 struct heredoc *next; /* next here document in list */
9445 union node *here; /* redirection node */
9446 char *eofmark; /* string indicating end of input */
9447 int striptabs; /* if set, strip leading tabs */
9448};
Eric Andersencb57d552001-06-28 07:25:16 +00009449
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009450static struct heredoc *heredoclist; /* list of here documents to read */
9451
9452/* parsing is heavily cross-recursive, need these forward decls */
9453static union node *andor(void);
9454static union node *pipeline(void);
9455static union node *parse_command(void);
9456static void parseheredoc(void);
9457static char peektoken(void);
9458static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009459
Eric Andersenc470f442003-07-28 09:56:35 +00009460static union node *
9461list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009462{
9463 union node *n1, *n2, *n3;
9464 int tok;
9465
Eric Andersenc470f442003-07-28 09:56:35 +00009466 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9467 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009468 return NULL;
9469 n1 = NULL;
9470 for (;;) {
9471 n2 = andor();
9472 tok = readtoken();
9473 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009474 if (n2->type == NPIPE) {
9475 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009476 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009477 if (n2->type != NREDIR) {
9478 n3 = stalloc(sizeof(struct nredir));
9479 n3->nredir.n = n2;
9480 n3->nredir.redirect = NULL;
9481 n2 = n3;
9482 }
9483 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009484 }
9485 }
9486 if (n1 == NULL) {
9487 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009489 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009490 n3->type = NSEMI;
9491 n3->nbinary.ch1 = n1;
9492 n3->nbinary.ch2 = n2;
9493 n1 = n3;
9494 }
9495 switch (tok) {
9496 case TBACKGND:
9497 case TSEMI:
9498 tok = readtoken();
9499 /* fall through */
9500 case TNL:
9501 if (tok == TNL) {
9502 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009503 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009504 return n1;
9505 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009506 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009507 }
Eric Andersenc470f442003-07-28 09:56:35 +00009508 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009509 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009510 return n1;
9511 break;
9512 case TEOF:
9513 if (heredoclist)
9514 parseheredoc();
9515 else
Eric Andersenc470f442003-07-28 09:56:35 +00009516 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009517 return n1;
9518 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009519 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009520 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009521 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009522 return n1;
9523 }
9524 }
9525}
9526
Eric Andersenc470f442003-07-28 09:56:35 +00009527static union node *
9528andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009529{
Eric Andersencb57d552001-06-28 07:25:16 +00009530 union node *n1, *n2, *n3;
9531 int t;
9532
Eric Andersencb57d552001-06-28 07:25:16 +00009533 n1 = pipeline();
9534 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009535 t = readtoken();
9536 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009537 t = NAND;
9538 } else if (t == TOR) {
9539 t = NOR;
9540 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009541 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009542 return n1;
9543 }
Eric Andersenc470f442003-07-28 09:56:35 +00009544 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009545 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009546 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009547 n3->type = t;
9548 n3->nbinary.ch1 = n1;
9549 n3->nbinary.ch2 = n2;
9550 n1 = n3;
9551 }
9552}
9553
Eric Andersenc470f442003-07-28 09:56:35 +00009554static union node *
9555pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009556{
Eric Andersencb57d552001-06-28 07:25:16 +00009557 union node *n1, *n2, *pipenode;
9558 struct nodelist *lp, *prev;
9559 int negate;
9560
9561 negate = 0;
9562 TRACE(("pipeline: entered\n"));
9563 if (readtoken() == TNOT) {
9564 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009565 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009566 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009567 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009568 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009569 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009570 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009571 pipenode->type = NPIPE;
9572 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009573 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009574 pipenode->npipe.cmdlist = lp;
9575 lp->n = n1;
9576 do {
9577 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009578 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009579 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009580 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009581 prev->next = lp;
9582 } while (readtoken() == TPIPE);
9583 lp->next = NULL;
9584 n1 = pipenode;
9585 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009586 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009587 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009588 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009589 n2->type = NNOT;
9590 n2->nnot.com = n1;
9591 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009592 }
9593 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009594}
9595
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009596static union node *
9597makename(void)
9598{
9599 union node *n;
9600
9601 n = stalloc(sizeof(struct narg));
9602 n->type = NARG;
9603 n->narg.next = NULL;
9604 n->narg.text = wordtext;
9605 n->narg.backquote = backquotelist;
9606 return n;
9607}
9608
9609static void
9610fixredir(union node *n, const char *text, int err)
9611{
9612 TRACE(("Fix redir %s %d\n", text, err));
9613 if (!err)
9614 n->ndup.vname = NULL;
9615
9616 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009617 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009618 else if (LONE_DASH(text))
9619 n->ndup.dupfd = -1;
9620 else {
9621 if (err)
9622 raise_error_syntax("Bad fd number");
9623 n->ndup.vname = makename();
9624 }
9625}
9626
9627/*
9628 * Returns true if the text contains nothing to expand (no dollar signs
9629 * or backquotes).
9630 */
9631static int
9632noexpand(char *text)
9633{
9634 char *p;
9635 char c;
9636
9637 p = text;
9638 while ((c = *p++) != '\0') {
9639 if (c == CTLQUOTEMARK)
9640 continue;
9641 if (c == CTLESC)
9642 p++;
9643 else if (SIT(c, BASESYNTAX) == CCTL)
9644 return 0;
9645 }
9646 return 1;
9647}
9648
9649static void
9650parsefname(void)
9651{
9652 union node *n = redirnode;
9653
9654 if (readtoken() != TWORD)
9655 raise_error_unexpected_syntax(-1);
9656 if (n->type == NHERE) {
9657 struct heredoc *here = heredoc;
9658 struct heredoc *p;
9659 int i;
9660
9661 if (quoteflag == 0)
9662 n->type = NXHERE;
9663 TRACE(("Here document %d\n", n->type));
9664 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9665 raise_error_syntax("Illegal eof marker for << redirection");
9666 rmescapes(wordtext);
9667 here->eofmark = wordtext;
9668 here->next = NULL;
9669 if (heredoclist == NULL)
9670 heredoclist = here;
9671 else {
9672 for (p = heredoclist; p->next; p = p->next);
9673 p->next = here;
9674 }
9675 } else if (n->type == NTOFD || n->type == NFROMFD) {
9676 fixredir(n, wordtext, 0);
9677 } else {
9678 n->nfile.fname = makename();
9679 }
9680}
Eric Andersencb57d552001-06-28 07:25:16 +00009681
Eric Andersenc470f442003-07-28 09:56:35 +00009682static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009683simplecmd(void)
9684{
9685 union node *args, **app;
9686 union node *n = NULL;
9687 union node *vars, **vpp;
9688 union node **rpp, *redir;
9689 int savecheckkwd;
9690
9691 args = NULL;
9692 app = &args;
9693 vars = NULL;
9694 vpp = &vars;
9695 redir = NULL;
9696 rpp = &redir;
9697
9698 savecheckkwd = CHKALIAS;
9699 for (;;) {
9700 checkkwd = savecheckkwd;
9701 switch (readtoken()) {
9702 case TWORD:
9703 n = stalloc(sizeof(struct narg));
9704 n->type = NARG;
9705 n->narg.text = wordtext;
9706 n->narg.backquote = backquotelist;
9707 if (savecheckkwd && isassignment(wordtext)) {
9708 *vpp = n;
9709 vpp = &n->narg.next;
9710 } else {
9711 *app = n;
9712 app = &n->narg.next;
9713 savecheckkwd = 0;
9714 }
9715 break;
9716 case TREDIR:
9717 *rpp = n = redirnode;
9718 rpp = &n->nfile.next;
9719 parsefname(); /* read name of redirection file */
9720 break;
9721 case TLP:
9722 if (args && app == &args->narg.next
9723 && !vars && !redir
9724 ) {
9725 struct builtincmd *bcmd;
9726 const char *name;
9727
9728 /* We have a function */
9729 if (readtoken() != TRP)
9730 raise_error_unexpected_syntax(TRP);
9731 name = n->narg.text;
9732 if (!goodname(name)
9733 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9734 ) {
9735 raise_error_syntax("Bad function name");
9736 }
9737 n->type = NDEFUN;
9738 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9739 n->narg.next = parse_command();
9740 return n;
9741 }
9742 /* fall through */
9743 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009744 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009745 goto out;
9746 }
9747 }
9748 out:
9749 *app = NULL;
9750 *vpp = NULL;
9751 *rpp = NULL;
9752 n = stalloc(sizeof(struct ncmd));
9753 n->type = NCMD;
9754 n->ncmd.args = args;
9755 n->ncmd.assign = vars;
9756 n->ncmd.redirect = redir;
9757 return n;
9758}
9759
9760static union node *
9761parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009762{
Eric Andersencb57d552001-06-28 07:25:16 +00009763 union node *n1, *n2;
9764 union node *ap, **app;
9765 union node *cp, **cpp;
9766 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009767 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009768 int t;
9769
9770 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009771 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009772
Eric Andersencb57d552001-06-28 07:25:16 +00009773 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009774 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009775 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009776 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009777 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009778 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009779 n1->type = NIF;
9780 n1->nif.test = list(0);
9781 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009782 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009783 n1->nif.ifpart = list(0);
9784 n2 = n1;
9785 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009786 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009787 n2 = n2->nif.elsepart;
9788 n2->type = NIF;
9789 n2->nif.test = list(0);
9790 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009791 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009792 n2->nif.ifpart = list(0);
9793 }
9794 if (lasttoken == TELSE)
9795 n2->nif.elsepart = list(0);
9796 else {
9797 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009798 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009799 }
Eric Andersenc470f442003-07-28 09:56:35 +00009800 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009801 break;
9802 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009803 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009804 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009805 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009806 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009807 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009808 got = readtoken();
9809 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009810 TRACE(("expecting DO got %s %s\n", tokname(got),
9811 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009812 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009813 }
9814 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009815 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009816 break;
9817 }
9818 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009819 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009820 raise_error_syntax("Bad for loop variable");
9821 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009822 n1->type = NFOR;
9823 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009824 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009825 if (readtoken() == TIN) {
9826 app = &ap;
9827 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009828 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009829 n2->type = NARG;
9830 n2->narg.text = wordtext;
9831 n2->narg.backquote = backquotelist;
9832 *app = n2;
9833 app = &n2->narg.next;
9834 }
9835 *app = NULL;
9836 n1->nfor.args = ap;
9837 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009838 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009839 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009840 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009841 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009842 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009843 n2->narg.backquote = NULL;
9844 n2->narg.next = NULL;
9845 n1->nfor.args = n2;
9846 /*
9847 * Newline or semicolon here is optional (but note
9848 * that the original Bourne shell only allowed NL).
9849 */
9850 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009851 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009852 }
Eric Andersenc470f442003-07-28 09:56:35 +00009853 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009854 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009855 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009856 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009857 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009858 break;
9859 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009860 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009861 n1->type = NCASE;
9862 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009863 raise_error_unexpected_syntax(TWORD);
9864 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009865 n2->type = NARG;
9866 n2->narg.text = wordtext;
9867 n2->narg.backquote = backquotelist;
9868 n2->narg.next = NULL;
9869 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009870 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009871 } while (readtoken() == TNL);
9872 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009873 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009874 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009875 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009876 checkkwd = CHKNL | CHKKWD;
9877 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009878 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009879 if (lasttoken == TLP)
9880 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009881 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009882 cp->type = NCLIST;
9883 app = &cp->nclist.pattern;
9884 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009885 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009886 ap->type = NARG;
9887 ap->narg.text = wordtext;
9888 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009889 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009890 break;
9891 app = &ap->narg.next;
9892 readtoken();
9893 }
9894 ap->narg.next = NULL;
9895 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009896 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009897 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009898
Eric Andersenc470f442003-07-28 09:56:35 +00009899 cpp = &cp->nclist.next;
9900
9901 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009902 t = readtoken();
9903 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009904 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009905 raise_error_unexpected_syntax(TENDCASE);
9906 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 }
Eric Andersenc470f442003-07-28 09:56:35 +00009908 }
Eric Andersencb57d552001-06-28 07:25:16 +00009909 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009910 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009912 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009913 n1->type = NSUBSHELL;
9914 n1->nredir.n = list(0);
9915 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009916 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009917 break;
9918 case TBEGIN:
9919 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009920 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009921 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009922 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009923 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009924 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009925 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009926 }
9927
Eric Andersenc470f442003-07-28 09:56:35 +00009928 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009929 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009930
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009931 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009932 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009933 checkkwd = CHKKWD | CHKALIAS;
9934 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009935 while (readtoken() == TREDIR) {
9936 *rpp = n2 = redirnode;
9937 rpp = &n2->nfile.next;
9938 parsefname();
9939 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009940 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009941 *rpp = NULL;
9942 if (redir) {
9943 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009944 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009945 n2->type = NREDIR;
9946 n2->nredir.n = n1;
9947 n1 = n2;
9948 }
9949 n1->nredir.redirect = redir;
9950 }
Eric Andersencb57d552001-06-28 07:25:16 +00009951 return n1;
9952}
9953
Eric Andersencb57d552001-06-28 07:25:16 +00009954/*
9955 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9956 * is not NULL, read a here document. In the latter case, eofmark is the
9957 * word which marks the end of the document and striptabs is true if
9958 * leading tabs should be stripped from the document. The argument firstc
9959 * is the first character of the input token or document.
9960 *
9961 * Because C does not have internal subroutines, I have simulated them
9962 * using goto's to implement the subroutine linkage. The following macros
9963 * will run code that appears at the end of readtoken1.
9964 */
9965
Eric Andersen2870d962001-07-02 17:27:21 +00009966#define CHECKEND() {goto checkend; checkend_return:;}
9967#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9968#define PARSESUB() {goto parsesub; parsesub_return:;}
9969#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9970#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9971#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009972
9973static int
Eric Andersenc470f442003-07-28 09:56:35 +00009974readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009975{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009976 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +00009977 int c = firstc;
9978 char *out;
9979 int len;
9980 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009981 struct nodelist *bqlist;
9982 smallint quotef;
9983 smallint dblquote;
9984 smallint oldstyle;
9985 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00009986#if ENABLE_ASH_EXPAND_PRMT
9987 smallint pssyntax; /* we are expanding a prompt string */
9988#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009989 int varnest; /* levels of variables expansion */
9990 int arinest; /* levels of arithmetic expansion */
9991 int parenlevel; /* levels of parens in arithmetic */
9992 int dqvarnest; /* levels of variables expansion within double quotes */
9993
Eric Andersencb57d552001-06-28 07:25:16 +00009994#if __GNUC__
9995 /* Avoid longjmp clobbering */
9996 (void) &out;
9997 (void) &quotef;
9998 (void) &dblquote;
9999 (void) &varnest;
10000 (void) &arinest;
10001 (void) &parenlevel;
10002 (void) &dqvarnest;
10003 (void) &oldstyle;
10004 (void) &prevsyntax;
10005 (void) &syntax;
10006#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010007 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010008 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010009 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010010 oldstyle = 0;
10011 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010012#if ENABLE_ASH_EXPAND_PRMT
10013 pssyntax = (syntax == PSSYNTAX);
10014 if (pssyntax)
10015 syntax = DQSYNTAX;
10016#endif
10017 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010018 varnest = 0;
10019 arinest = 0;
10020 parenlevel = 0;
10021 dqvarnest = 0;
10022
10023 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010024 loop: { /* for each line, until end of word */
10025 CHECKEND(); /* set c to PEOF if at end of here document */
10026 for (;;) { /* until end of line or end of word */
10027 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010028 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010029 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010030 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010031 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010032 USTPUTC(c, out);
10033 plinno++;
10034 if (doprompt)
10035 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010036 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010037 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010038 case CWORD:
10039 USTPUTC(c, out);
10040 break;
10041 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010042 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010043 USTPUTC(CTLESC, out);
10044 USTPUTC(c, out);
10045 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010046 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010047 c = pgetc2();
10048 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010049 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010050 USTPUTC('\\', out);
10051 pungetc();
10052 } else if (c == '\n') {
10053 if (doprompt)
10054 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010055 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010056#if ENABLE_ASH_EXPAND_PRMT
10057 if (c == '$' && pssyntax) {
10058 USTPUTC(CTLESC, out);
10059 USTPUTC('\\', out);
10060 }
10061#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010062 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010063 c != '\\' && c != '`' &&
10064 c != '$' && (
10065 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010066 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010067 ) {
10068 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010069 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010070 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010071 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010072 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010073 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010074 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010075 }
10076 break;
10077 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010078 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010079 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010080 if (eofmark == NULL) {
10081 USTPUTC(CTLQUOTEMARK, out);
10082 }
Eric Andersencb57d552001-06-28 07:25:16 +000010083 break;
10084 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010085 syntax = DQSYNTAX;
10086 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010087 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010088 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010089 if (eofmark != NULL && arinest == 0
10090 && varnest == 0
10091 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010092 USTPUTC(c, out);
10093 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010094 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010095 syntax = BASESYNTAX;
10096 dblquote = 0;
10097 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010098 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010099 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010100 }
10101 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010102 case CVAR: /* '$' */
10103 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010104 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010105 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010106 if (varnest > 0) {
10107 varnest--;
10108 if (dqvarnest > 0) {
10109 dqvarnest--;
10110 }
10111 USTPUTC(CTLENDVAR, out);
10112 } else {
10113 USTPUTC(c, out);
10114 }
10115 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010116#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010117 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010118 parenlevel++;
10119 USTPUTC(c, out);
10120 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010121 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010122 if (parenlevel > 0) {
10123 USTPUTC(c, out);
10124 --parenlevel;
10125 } else {
10126 if (pgetc() == ')') {
10127 if (--arinest == 0) {
10128 USTPUTC(CTLENDARI, out);
10129 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010130 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010131 } else
10132 USTPUTC(')', out);
10133 } else {
10134 /*
10135 * unbalanced parens
10136 * (don't 2nd guess - no error)
10137 */
10138 pungetc();
10139 USTPUTC(')', out);
10140 }
10141 }
10142 break;
10143#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010144 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010145 PARSEBACKQOLD();
10146 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010147 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010148 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010149 case CIGN:
10150 break;
10151 default:
10152 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010153 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010154#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010155 if (c != PEOA)
10156#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010157 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010158
Eric Andersencb57d552001-06-28 07:25:16 +000010159 }
10160 c = pgetc_macro();
10161 }
10162 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010163 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010164#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010165 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010166 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010167#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010168 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010169 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010170 if (varnest != 0) {
10171 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010172 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010173 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010174 }
10175 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010176 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010177 out = stackblock();
10178 if (eofmark == NULL) {
10179 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010180 && quotef == 0
10181 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010182 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010183 PARSEREDIR();
10184 return lasttoken = TREDIR;
10185 } else {
10186 pungetc();
10187 }
10188 }
10189 quoteflag = quotef;
10190 backquotelist = bqlist;
10191 grabstackblock(len);
10192 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010193 lasttoken = TWORD;
10194 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010195/* end of readtoken routine */
10196
Eric Andersencb57d552001-06-28 07:25:16 +000010197/*
10198 * Check to see whether we are at the end of the here document. When this
10199 * is called, c is set to the first character of the next input line. If
10200 * we are at the end of the here document, this routine sets the c to PEOF.
10201 */
Eric Andersenc470f442003-07-28 09:56:35 +000010202checkend: {
10203 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010204#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010205 if (c == PEOA) {
10206 c = pgetc2();
10207 }
10208#endif
10209 if (striptabs) {
10210 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010211 c = pgetc2();
10212 }
Eric Andersenc470f442003-07-28 09:56:35 +000010213 }
10214 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010215 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010216 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010217
Eric Andersenc470f442003-07-28 09:56:35 +000010218 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010219 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010220 if (*p == '\n' && *q == '\0') {
10221 c = PEOF;
10222 plinno++;
10223 needprompt = doprompt;
10224 } else {
10225 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010226 }
10227 }
10228 }
10229 }
Eric Andersenc470f442003-07-28 09:56:35 +000010230 goto checkend_return;
10231}
Eric Andersencb57d552001-06-28 07:25:16 +000010232
Eric Andersencb57d552001-06-28 07:25:16 +000010233/*
10234 * Parse a redirection operator. The variable "out" points to a string
10235 * specifying the fd to be redirected. The variable "c" contains the
10236 * first character of the redirection operator.
10237 */
Eric Andersenc470f442003-07-28 09:56:35 +000010238parseredir: {
10239 char fd = *out;
10240 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010241
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010242 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010243 if (c == '>') {
10244 np->nfile.fd = 1;
10245 c = pgetc();
10246 if (c == '>')
10247 np->type = NAPPEND;
10248 else if (c == '|')
10249 np->type = NCLOBBER;
10250 else if (c == '&')
10251 np->type = NTOFD;
10252 else {
10253 np->type = NTO;
10254 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010255 }
Eric Andersenc470f442003-07-28 09:56:35 +000010256 } else { /* c == '<' */
10257 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010258 c = pgetc();
10259 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010260 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010261 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010262 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010263 np->nfile.fd = 0;
10264 }
10265 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010266 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010267 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010268 c = pgetc();
10269 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010270 heredoc->striptabs = 1;
10271 } else {
10272 heredoc->striptabs = 0;
10273 pungetc();
10274 }
10275 break;
10276
10277 case '&':
10278 np->type = NFROMFD;
10279 break;
10280
10281 case '>':
10282 np->type = NFROMTO;
10283 break;
10284
10285 default:
10286 np->type = NFROM;
10287 pungetc();
10288 break;
10289 }
Eric Andersencb57d552001-06-28 07:25:16 +000010290 }
Eric Andersenc470f442003-07-28 09:56:35 +000010291 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010292 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010293 redirnode = np;
10294 goto parseredir_return;
10295}
Eric Andersencb57d552001-06-28 07:25:16 +000010296
Eric Andersencb57d552001-06-28 07:25:16 +000010297/*
10298 * Parse a substitution. At this point, we have read the dollar sign
10299 * and nothing else.
10300 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010301
10302/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10303 * (assuming ascii char codes, as the original implementation did) */
10304#define is_special(c) \
10305 ((((unsigned int)c) - 33 < 32) \
10306 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010307parsesub: {
10308 int subtype;
10309 int typeloc;
10310 int flags;
10311 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010312 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010313
Eric Andersenc470f442003-07-28 09:56:35 +000010314 c = pgetc();
10315 if (
10316 c <= PEOA_OR_PEOF ||
10317 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10318 ) {
10319 USTPUTC('$', out);
10320 pungetc();
10321 } else if (c == '(') { /* $(command) or $((arith)) */
10322 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010323#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010324 PARSEARITH();
10325#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010326 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010327#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010328 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010329 pungetc();
10330 PARSEBACKQNEW();
10331 }
10332 } else {
10333 USTPUTC(CTLVAR, out);
10334 typeloc = out - (char *)stackblock();
10335 USTPUTC(VSNORMAL, out);
10336 subtype = VSNORMAL;
10337 if (c == '{') {
10338 c = pgetc();
10339 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010340 c = pgetc();
10341 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010342 c = '#';
10343 else
10344 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010345 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010346 subtype = 0;
10347 }
10348 if (c > PEOA_OR_PEOF && is_name(c)) {
10349 do {
10350 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010351 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010352 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010354 do {
10355 STPUTC(c, out);
10356 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010357 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010358 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010359 USTPUTC(c, out);
10360 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010361 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010362 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010363
Eric Andersenc470f442003-07-28 09:56:35 +000010364 STPUTC('=', out);
10365 flags = 0;
10366 if (subtype == 0) {
10367 switch (c) {
10368 case ':':
10369 flags = VSNUL;
10370 c = pgetc();
10371 /*FALLTHROUGH*/
10372 default:
10373 p = strchr(types, c);
10374 if (p == NULL)
10375 goto badsub;
10376 subtype = p - types + VSNORMAL;
10377 break;
10378 case '%':
10379 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010380 {
10381 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010382 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010383 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010384 c = pgetc();
10385 if (c == cc)
10386 subtype++;
10387 else
10388 pungetc();
10389 break;
10390 }
10391 }
Eric Andersenc470f442003-07-28 09:56:35 +000010392 } else {
10393 pungetc();
10394 }
10395 if (dblquote || arinest)
10396 flags |= VSQUOTE;
10397 *((char *)stackblock() + typeloc) = subtype | flags;
10398 if (subtype != VSNORMAL) {
10399 varnest++;
10400 if (dblquote || arinest) {
10401 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010402 }
10403 }
10404 }
Eric Andersenc470f442003-07-28 09:56:35 +000010405 goto parsesub_return;
10406}
Eric Andersencb57d552001-06-28 07:25:16 +000010407
Eric Andersencb57d552001-06-28 07:25:16 +000010408/*
10409 * Called to parse command substitutions. Newstyle is set if the command
10410 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10411 * list of commands (passed by reference), and savelen is the number of
10412 * characters on the top of the stack which must be preserved.
10413 */
Eric Andersenc470f442003-07-28 09:56:35 +000010414parsebackq: {
10415 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010416 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010417 union node *n;
10418 char *volatile str;
10419 struct jmploc jmploc;
10420 struct jmploc *volatile savehandler;
10421 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010422 smallint saveprompt = 0;
10423
Eric Andersencb57d552001-06-28 07:25:16 +000010424#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010425 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010426#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010427 savepbq = parsebackquote;
10428 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010429 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010430 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010431 exception_handler = savehandler;
10432 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010433 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010434 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010435 str = NULL;
10436 savelen = out - (char *)stackblock();
10437 if (savelen > 0) {
10438 str = ckmalloc(savelen);
10439 memcpy(str, stackblock(), savelen);
10440 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010441 savehandler = exception_handler;
10442 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010443 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010444 if (oldstyle) {
10445 /* We must read until the closing backquote, giving special
10446 treatment to some slashes, and then push the string and
10447 reread it as input, interpreting it normally. */
10448 char *pout;
10449 int pc;
10450 size_t psavelen;
10451 char *pstr;
10452
10453
10454 STARTSTACKSTR(pout);
10455 for (;;) {
10456 if (needprompt) {
10457 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010458 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010459 pc = pgetc();
10460 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010461 case '`':
10462 goto done;
10463
10464 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010465 pc = pgetc();
10466 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010467 plinno++;
10468 if (doprompt)
10469 setprompt(2);
10470 /*
10471 * If eating a newline, avoid putting
10472 * the newline into the new character
10473 * stream (via the STPUTC after the
10474 * switch).
10475 */
10476 continue;
10477 }
10478 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010479 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010480 STPUTC('\\', pout);
10481 if (pc > PEOA_OR_PEOF) {
10482 break;
10483 }
10484 /* fall through */
10485
10486 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010487#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010488 case PEOA:
10489#endif
10490 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010492
10493 case '\n':
10494 plinno++;
10495 needprompt = doprompt;
10496 break;
10497
10498 default:
10499 break;
10500 }
10501 STPUTC(pc, pout);
10502 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010503 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010504 STPUTC('\0', pout);
10505 psavelen = pout - (char *)stackblock();
10506 if (psavelen > 0) {
10507 pstr = grabstackstr(pout);
10508 setinputstring(pstr);
10509 }
10510 }
10511 nlpp = &bqlist;
10512 while (*nlpp)
10513 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010514 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010515 (*nlpp)->next = NULL;
10516 parsebackquote = oldstyle;
10517
10518 if (oldstyle) {
10519 saveprompt = doprompt;
10520 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010521 }
10522
Eric Andersenc470f442003-07-28 09:56:35 +000010523 n = list(2);
10524
10525 if (oldstyle)
10526 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010527 else if (readtoken() != TRP)
10528 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010529
10530 (*nlpp)->n = n;
10531 if (oldstyle) {
10532 /*
10533 * Start reading from old file again, ignoring any pushed back
10534 * tokens left from the backquote parsing
10535 */
10536 popfile();
10537 tokpushback = 0;
10538 }
10539 while (stackblocksize() <= savelen)
10540 growstackblock();
10541 STARTSTACKSTR(out);
10542 if (str) {
10543 memcpy(out, str, savelen);
10544 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010545 INT_OFF;
10546 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010547 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010548 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010549 }
10550 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010551 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010552 if (arinest || dblquote)
10553 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10554 else
10555 USTPUTC(CTLBACKQ, out);
10556 if (oldstyle)
10557 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010558 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010559}
10560
Denis Vlasenko131ae172007-02-18 13:00:19 +000010561#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010562/*
10563 * Parse an arithmetic expansion (indicate start of one and set state)
10564 */
Eric Andersenc470f442003-07-28 09:56:35 +000010565parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010566 if (++arinest == 1) {
10567 prevsyntax = syntax;
10568 syntax = ARISYNTAX;
10569 USTPUTC(CTLARI, out);
10570 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010571 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010572 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010573 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010574 } else {
10575 /*
10576 * we collapse embedded arithmetic expansion to
10577 * parenthesis, which should be equivalent
10578 */
10579 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010580 }
Eric Andersenc470f442003-07-28 09:56:35 +000010581 goto parsearith_return;
10582}
10583#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010584
Eric Andersenc470f442003-07-28 09:56:35 +000010585} /* end of readtoken */
10586
Eric Andersencb57d552001-06-28 07:25:16 +000010587/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010588 * Read the next input token.
10589 * If the token is a word, we set backquotelist to the list of cmds in
10590 * backquotes. We set quoteflag to true if any part of the word was
10591 * quoted.
10592 * If the token is TREDIR, then we set redirnode to a structure containing
10593 * the redirection.
10594 * In all cases, the variable startlinno is set to the number of the line
10595 * on which the token starts.
10596 *
10597 * [Change comment: here documents and internal procedures]
10598 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10599 * word parsing code into a separate routine. In this case, readtoken
10600 * doesn't need to have any internal procedures, but parseword does.
10601 * We could also make parseoperator in essence the main routine, and
10602 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010603 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010604#define NEW_xxreadtoken
10605#ifdef NEW_xxreadtoken
10606/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010607static const char xxreadtoken_chars[7] ALIGN1 = {
10608 '\n', '(', ')', '&', '|', ';', 0
10609};
Eric Andersencb57d552001-06-28 07:25:16 +000010610
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010611static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010612 TNL, TLP, TRP, /* only single occurrence allowed */
10613 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10614 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010615 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010616};
10617
10618#define xxreadtoken_doubles \
10619 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10620#define xxreadtoken_singles \
10621 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10622
10623static int
10624xxreadtoken(void)
10625{
10626 int c;
10627
10628 if (tokpushback) {
10629 tokpushback = 0;
10630 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010631 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010632 if (needprompt) {
10633 setprompt(2);
10634 }
10635 startlinno = plinno;
10636 for (;;) { /* until token or start of word found */
10637 c = pgetc_macro();
10638
10639 if ((c != ' ') && (c != '\t')
10640#if ENABLE_ASH_ALIAS
10641 && (c != PEOA)
10642#endif
10643 ) {
10644 if (c == '#') {
10645 while ((c = pgetc()) != '\n' && c != PEOF);
10646 pungetc();
10647 } else if (c == '\\') {
10648 if (pgetc() != '\n') {
10649 pungetc();
10650 goto READTOKEN1;
10651 }
10652 startlinno = ++plinno;
10653 if (doprompt)
10654 setprompt(2);
10655 } else {
10656 const char *p
10657 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10658
10659 if (c != PEOF) {
10660 if (c == '\n') {
10661 plinno++;
10662 needprompt = doprompt;
10663 }
10664
10665 p = strchr(xxreadtoken_chars, c);
10666 if (p == NULL) {
10667 READTOKEN1:
10668 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10669 }
10670
10671 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10672 if (pgetc() == *p) { /* double occurrence? */
10673 p += xxreadtoken_doubles + 1;
10674 } else {
10675 pungetc();
10676 }
10677 }
10678 }
10679 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10680 }
10681 }
10682 } /* for */
10683}
10684#else
10685#define RETURN(token) return lasttoken = token
10686static int
10687xxreadtoken(void)
10688{
10689 int c;
10690
10691 if (tokpushback) {
10692 tokpushback = 0;
10693 return lasttoken;
10694 }
10695 if (needprompt) {
10696 setprompt(2);
10697 }
10698 startlinno = plinno;
10699 for (;;) { /* until token or start of word found */
10700 c = pgetc_macro();
10701 switch (c) {
10702 case ' ': case '\t':
10703#if ENABLE_ASH_ALIAS
10704 case PEOA:
10705#endif
10706 continue;
10707 case '#':
10708 while ((c = pgetc()) != '\n' && c != PEOF);
10709 pungetc();
10710 continue;
10711 case '\\':
10712 if (pgetc() == '\n') {
10713 startlinno = ++plinno;
10714 if (doprompt)
10715 setprompt(2);
10716 continue;
10717 }
10718 pungetc();
10719 goto breakloop;
10720 case '\n':
10721 plinno++;
10722 needprompt = doprompt;
10723 RETURN(TNL);
10724 case PEOF:
10725 RETURN(TEOF);
10726 case '&':
10727 if (pgetc() == '&')
10728 RETURN(TAND);
10729 pungetc();
10730 RETURN(TBACKGND);
10731 case '|':
10732 if (pgetc() == '|')
10733 RETURN(TOR);
10734 pungetc();
10735 RETURN(TPIPE);
10736 case ';':
10737 if (pgetc() == ';')
10738 RETURN(TENDCASE);
10739 pungetc();
10740 RETURN(TSEMI);
10741 case '(':
10742 RETURN(TLP);
10743 case ')':
10744 RETURN(TRP);
10745 default:
10746 goto breakloop;
10747 }
10748 }
10749 breakloop:
10750 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10751#undef RETURN
10752}
10753#endif /* NEW_xxreadtoken */
10754
10755static int
10756readtoken(void)
10757{
10758 int t;
10759#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010760 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010761#endif
10762
10763#if ENABLE_ASH_ALIAS
10764 top:
10765#endif
10766
10767 t = xxreadtoken();
10768
10769 /*
10770 * eat newlines
10771 */
10772 if (checkkwd & CHKNL) {
10773 while (t == TNL) {
10774 parseheredoc();
10775 t = xxreadtoken();
10776 }
10777 }
10778
10779 if (t != TWORD || quoteflag) {
10780 goto out;
10781 }
10782
10783 /*
10784 * check for keywords
10785 */
10786 if (checkkwd & CHKKWD) {
10787 const char *const *pp;
10788
10789 pp = findkwd(wordtext);
10790 if (pp) {
10791 lasttoken = t = pp - tokname_array;
10792 TRACE(("keyword %s recognized\n", tokname(t)));
10793 goto out;
10794 }
10795 }
10796
10797 if (checkkwd & CHKALIAS) {
10798#if ENABLE_ASH_ALIAS
10799 struct alias *ap;
10800 ap = lookupalias(wordtext, 1);
10801 if (ap != NULL) {
10802 if (*ap->val) {
10803 pushstring(ap->val, ap);
10804 }
10805 goto top;
10806 }
10807#endif
10808 }
10809 out:
10810 checkkwd = 0;
10811#if DEBUG
10812 if (!alreadyseen)
10813 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10814 else
10815 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10816#endif
10817 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010818}
10819
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010820static char
10821peektoken(void)
10822{
10823 int t;
10824
10825 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010826 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010827 return tokname_array[t][0];
10828}
Eric Andersencb57d552001-06-28 07:25:16 +000010829
10830/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010831 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10832 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010833 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010834static union node *
10835parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010836{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010837 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010838
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010839 tokpushback = 0;
10840 doprompt = interact;
10841 if (doprompt)
10842 setprompt(doprompt);
10843 needprompt = 0;
10844 t = readtoken();
10845 if (t == TEOF)
10846 return NEOF;
10847 if (t == TNL)
10848 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010849 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010850 return list(1);
10851}
10852
10853/*
10854 * Input any here documents.
10855 */
10856static void
10857parseheredoc(void)
10858{
10859 struct heredoc *here;
10860 union node *n;
10861
10862 here = heredoclist;
10863 heredoclist = 0;
10864
10865 while (here) {
10866 if (needprompt) {
10867 setprompt(2);
10868 }
10869 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10870 here->eofmark, here->striptabs);
10871 n = stalloc(sizeof(struct narg));
10872 n->narg.type = NARG;
10873 n->narg.next = NULL;
10874 n->narg.text = wordtext;
10875 n->narg.backquote = backquotelist;
10876 here->here->nhere.doc = n;
10877 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010878 }
Eric Andersencb57d552001-06-28 07:25:16 +000010879}
10880
10881
10882/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010883 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010884 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010885#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010886static const char *
10887expandstr(const char *ps)
10888{
10889 union node n;
10890
10891 /* XXX Fix (char *) cast. */
10892 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010893 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010894 popfile();
10895
10896 n.narg.type = NARG;
10897 n.narg.next = NULL;
10898 n.narg.text = wordtext;
10899 n.narg.backquote = backquotelist;
10900
10901 expandarg(&n, NULL, 0);
10902 return stackblock();
10903}
10904#endif
10905
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010906/*
10907 * Execute a command or commands contained in a string.
10908 */
10909static int
10910evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010911{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010912 union node *n;
10913 struct stackmark smark;
10914 int skip;
10915
10916 setinputstring(s);
10917 setstackmark(&smark);
10918
10919 skip = 0;
10920 while ((n = parsecmd(0)) != NEOF) {
10921 evaltree(n, 0);
10922 popstackmark(&smark);
10923 skip = evalskip;
10924 if (skip)
10925 break;
10926 }
10927 popfile();
10928
10929 skip &= mask;
10930 evalskip = skip;
10931 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010932}
10933
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010934/*
10935 * The eval command.
10936 */
10937static int
10938evalcmd(int argc, char **argv)
10939{
10940 char *p;
10941 char *concat;
10942 char **ap;
10943
10944 if (argc > 1) {
10945 p = argv[1];
10946 if (argc > 2) {
10947 STARTSTACKSTR(concat);
10948 ap = argv + 2;
10949 for (;;) {
10950 concat = stack_putstr(p, concat);
10951 p = *ap++;
10952 if (p == NULL)
10953 break;
10954 STPUTC(' ', concat);
10955 }
10956 STPUTC('\0', concat);
10957 p = grabstackstr(concat);
10958 }
10959 evalstring(p, ~SKIPEVAL);
10960
10961 }
10962 return exitstatus;
10963}
10964
10965/*
10966 * Read and execute commands. "Top" is nonzero for the top level command
10967 * loop; it turns on prompting if the shell is interactive.
10968 */
10969static int
10970cmdloop(int top)
10971{
10972 union node *n;
10973 struct stackmark smark;
10974 int inter;
10975 int numeof = 0;
10976
10977 TRACE(("cmdloop(%d) called\n", top));
10978 for (;;) {
10979 int skip;
10980
10981 setstackmark(&smark);
10982#if JOBS
10983 if (jobctl)
10984 showjobs(stderr, SHOW_CHANGED);
10985#endif
10986 inter = 0;
10987 if (iflag && top) {
10988 inter++;
10989#if ENABLE_ASH_MAIL
10990 chkmail();
10991#endif
10992 }
10993 n = parsecmd(inter);
10994 /* showtree(n); DEBUG */
10995 if (n == NEOF) {
10996 if (!top || numeof >= 50)
10997 break;
10998 if (!stoppedjobs()) {
10999 if (!Iflag)
11000 break;
11001 out2str("\nUse \"exit\" to leave shell.\n");
11002 }
11003 numeof++;
11004 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011005 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11006 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011007 numeof = 0;
11008 evaltree(n, 0);
11009 }
11010 popstackmark(&smark);
11011 skip = evalskip;
11012
11013 if (skip) {
11014 evalskip = 0;
11015 return skip & SKIPEVAL;
11016 }
11017 }
11018 return 0;
11019}
11020
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011021/*
11022 * Take commands from a file. To be compatible we should do a path
11023 * search for the file, which is necessary to find sub-commands.
11024 */
11025static char *
11026find_dot_file(char *name)
11027{
11028 char *fullname;
11029 const char *path = pathval();
11030 struct stat statb;
11031
11032 /* don't try this for absolute or relative paths */
11033 if (strchr(name, '/'))
11034 return name;
11035
11036 while ((fullname = padvance(&path, name)) != NULL) {
11037 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11038 /*
11039 * Don't bother freeing here, since it will
11040 * be freed by the caller.
11041 */
11042 return fullname;
11043 }
11044 stunalloc(fullname);
11045 }
11046
11047 /* not found in the PATH */
11048 ash_msg_and_raise_error("%s: not found", name);
11049 /* NOTREACHED */
11050}
11051
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011052static int
11053dotcmd(int argc, char **argv)
11054{
11055 struct strlist *sp;
11056 volatile struct shparam saveparam;
11057 int status = 0;
11058
11059 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011060 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011061
11062 if (argc >= 2) { /* That's what SVR2 does */
11063 char *fullname;
11064
11065 fullname = find_dot_file(argv[1]);
11066
11067 if (argc > 2) {
11068 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011069 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011070 shellparam.nparam = argc - 2;
11071 shellparam.p = argv + 2;
11072 };
11073
11074 setinputfile(fullname, INPUT_PUSH_FILE);
11075 commandname = fullname;
11076 cmdloop(0);
11077 popfile();
11078
11079 if (argc > 2) {
11080 freeparam(&shellparam);
11081 shellparam = saveparam;
11082 };
11083 status = exitstatus;
11084 }
11085 return status;
11086}
11087
11088static int
11089exitcmd(int argc, char **argv)
11090{
11091 if (stoppedjobs())
11092 return 0;
11093 if (argc > 1)
11094 exitstatus = number(argv[1]);
11095 raise_exception(EXEXIT);
11096 /* NOTREACHED */
11097}
11098
11099#if ENABLE_ASH_BUILTIN_ECHO
11100static int
11101echocmd(int argc, char **argv)
11102{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011103 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011104}
11105#endif
11106
11107#if ENABLE_ASH_BUILTIN_TEST
11108static int
11109testcmd(int argc, char **argv)
11110{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011111 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011112}
11113#endif
11114
11115/*
11116 * Read a file containing shell functions.
11117 */
11118static void
11119readcmdfile(char *name)
11120{
11121 setinputfile(name, INPUT_PUSH_FILE);
11122 cmdloop(0);
11123 popfile();
11124}
11125
11126
Denis Vlasenkocc571512007-02-23 21:10:35 +000011127/* ============ find_command inplementation */
11128
11129/*
11130 * Resolve a command name. If you change this routine, you may have to
11131 * change the shellexec routine as well.
11132 */
11133static void
11134find_command(char *name, struct cmdentry *entry, int act, const char *path)
11135{
11136 struct tblentry *cmdp;
11137 int idx;
11138 int prev;
11139 char *fullname;
11140 struct stat statb;
11141 int e;
11142 int updatetbl;
11143 struct builtincmd *bcmd;
11144
11145 /* If name contains a slash, don't use PATH or hash table */
11146 if (strchr(name, '/') != NULL) {
11147 entry->u.index = -1;
11148 if (act & DO_ABS) {
11149 while (stat(name, &statb) < 0) {
11150#ifdef SYSV
11151 if (errno == EINTR)
11152 continue;
11153#endif
11154 entry->cmdtype = CMDUNKNOWN;
11155 return;
11156 }
11157 }
11158 entry->cmdtype = CMDNORMAL;
11159 return;
11160 }
11161
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011162/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011163
11164 updatetbl = (path == pathval());
11165 if (!updatetbl) {
11166 act |= DO_ALTPATH;
11167 if (strstr(path, "%builtin") != NULL)
11168 act |= DO_ALTBLTIN;
11169 }
11170
11171 /* If name is in the table, check answer will be ok */
11172 cmdp = cmdlookup(name, 0);
11173 if (cmdp != NULL) {
11174 int bit;
11175
11176 switch (cmdp->cmdtype) {
11177 default:
11178#if DEBUG
11179 abort();
11180#endif
11181 case CMDNORMAL:
11182 bit = DO_ALTPATH;
11183 break;
11184 case CMDFUNCTION:
11185 bit = DO_NOFUNC;
11186 break;
11187 case CMDBUILTIN:
11188 bit = DO_ALTBLTIN;
11189 break;
11190 }
11191 if (act & bit) {
11192 updatetbl = 0;
11193 cmdp = NULL;
11194 } else if (cmdp->rehash == 0)
11195 /* if not invalidated by cd, we're done */
11196 goto success;
11197 }
11198
11199 /* If %builtin not in path, check for builtin next */
11200 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011201 if (bcmd) {
11202 if (IS_BUILTIN_REGULAR(bcmd))
11203 goto builtin_success;
11204 if (act & DO_ALTPATH) {
11205 if (!(act & DO_ALTBLTIN))
11206 goto builtin_success;
11207 } else if (builtinloc <= 0) {
11208 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011209 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011210 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011211
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011212#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011213 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011214 entry->cmdtype = CMDNORMAL;
11215 entry->u.index = -1;
11216 return;
11217 }
11218#endif
11219
Denis Vlasenkocc571512007-02-23 21:10:35 +000011220 /* We have to search path. */
11221 prev = -1; /* where to start */
11222 if (cmdp && cmdp->rehash) { /* doing a rehash */
11223 if (cmdp->cmdtype == CMDBUILTIN)
11224 prev = builtinloc;
11225 else
11226 prev = cmdp->param.index;
11227 }
11228
11229 e = ENOENT;
11230 idx = -1;
11231 loop:
11232 while ((fullname = padvance(&path, name)) != NULL) {
11233 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011234 /* NB: code below will still use fullname
11235 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011236 idx++;
11237 if (pathopt) {
11238 if (prefix(pathopt, "builtin")) {
11239 if (bcmd)
11240 goto builtin_success;
11241 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011242 } else if (!(act & DO_NOFUNC)
11243 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011244 /* handled below */
11245 } else {
11246 /* ignore unimplemented options */
11247 continue;
11248 }
11249 }
11250 /* if rehash, don't redo absolute path names */
11251 if (fullname[0] == '/' && idx <= prev) {
11252 if (idx < prev)
11253 continue;
11254 TRACE(("searchexec \"%s\": no change\n", name));
11255 goto success;
11256 }
11257 while (stat(fullname, &statb) < 0) {
11258#ifdef SYSV
11259 if (errno == EINTR)
11260 continue;
11261#endif
11262 if (errno != ENOENT && errno != ENOTDIR)
11263 e = errno;
11264 goto loop;
11265 }
11266 e = EACCES; /* if we fail, this will be the error */
11267 if (!S_ISREG(statb.st_mode))
11268 continue;
11269 if (pathopt) { /* this is a %func directory */
11270 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011271 /* NB: stalloc will return space pointed by fullname
11272 * (because we don't have any intervening allocations
11273 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011274 readcmdfile(fullname);
11275 cmdp = cmdlookup(name, 0);
11276 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11277 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11278 stunalloc(fullname);
11279 goto success;
11280 }
11281 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11282 if (!updatetbl) {
11283 entry->cmdtype = CMDNORMAL;
11284 entry->u.index = idx;
11285 return;
11286 }
11287 INT_OFF;
11288 cmdp = cmdlookup(name, 1);
11289 cmdp->cmdtype = CMDNORMAL;
11290 cmdp->param.index = idx;
11291 INT_ON;
11292 goto success;
11293 }
11294
11295 /* We failed. If there was an entry for this command, delete it */
11296 if (cmdp && updatetbl)
11297 delete_cmd_entry();
11298 if (act & DO_ERR)
11299 ash_msg("%s: %s", name, errmsg(e, "not found"));
11300 entry->cmdtype = CMDUNKNOWN;
11301 return;
11302
11303 builtin_success:
11304 if (!updatetbl) {
11305 entry->cmdtype = CMDBUILTIN;
11306 entry->u.cmd = bcmd;
11307 return;
11308 }
11309 INT_OFF;
11310 cmdp = cmdlookup(name, 1);
11311 cmdp->cmdtype = CMDBUILTIN;
11312 cmdp->param.cmd = bcmd;
11313 INT_ON;
11314 success:
11315 cmdp->rehash = 0;
11316 entry->cmdtype = cmdp->cmdtype;
11317 entry->u = cmdp->param;
11318}
11319
11320
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011321/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011322
Eric Andersencb57d552001-06-28 07:25:16 +000011323/*
Eric Andersencb57d552001-06-28 07:25:16 +000011324 * The trap builtin.
11325 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011326static int
Eric Andersenc470f442003-07-28 09:56:35 +000011327trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011328{
11329 char *action;
11330 char **ap;
11331 int signo;
11332
Eric Andersenc470f442003-07-28 09:56:35 +000011333 nextopt(nullstr);
11334 ap = argptr;
11335 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011336 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011337 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011338 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011339
Rob Landleyc9c1a412006-07-12 19:17:55 +000011340 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011341 out1fmt("trap -- %s %s\n",
11342 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011343 }
11344 }
11345 return 0;
11346 }
Eric Andersenc470f442003-07-28 09:56:35 +000011347 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011348 action = NULL;
11349 else
11350 action = *ap++;
11351 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011352 signo = get_signum(*ap);
11353 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011354 ash_msg_and_raise_error("%s: bad trap", *ap);
11355 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011356 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011357 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011358 action = NULL;
11359 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011360 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011361 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011362 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011363 trap[signo] = action;
11364 if (signo != 0)
11365 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011366 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011367 ap++;
11368 }
11369 return 0;
11370}
11371
Eric Andersenc470f442003-07-28 09:56:35 +000011372
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011373/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011374
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011375#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011376/*
11377 * Lists available builtins
11378 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011379static int
11380helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011381{
11382 int col, i;
11383
11384 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011385 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011386 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011387 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011388 if (col > 60) {
11389 out1fmt("\n");
11390 col = 0;
11391 }
11392 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011393#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011394 {
11395 const char *a = applet_names;
11396 while (*a) {
11397 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11398 if (col > 60) {
11399 out1fmt("\n");
11400 col = 0;
11401 }
11402 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011403 }
11404 }
11405#endif
11406 out1fmt("\n\n");
11407 return EXIT_SUCCESS;
11408}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011409#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011410
Eric Andersencb57d552001-06-28 07:25:16 +000011411/*
Eric Andersencb57d552001-06-28 07:25:16 +000011412 * The export and readonly commands.
11413 */
Eric Andersenc470f442003-07-28 09:56:35 +000011414static int
11415exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011416{
11417 struct var *vp;
11418 char *name;
11419 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011420 char **aptr;
11421 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011422
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011423 if (nextopt("p") != 'p') {
11424 aptr = argptr;
11425 name = *aptr;
11426 if (name) {
11427 do {
11428 p = strchr(name, '=');
11429 if (p != NULL) {
11430 p++;
11431 } else {
11432 vp = *findvar(hashvar(name), name);
11433 if (vp) {
11434 vp->flags |= flag;
11435 continue;
11436 }
Eric Andersencb57d552001-06-28 07:25:16 +000011437 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011438 setvar(name, p, flag);
11439 } while ((name = *++aptr) != NULL);
11440 return 0;
11441 }
Eric Andersencb57d552001-06-28 07:25:16 +000011442 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011443 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011444 return 0;
11445}
11446
Eric Andersencb57d552001-06-28 07:25:16 +000011447/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011448 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011449 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011450static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011451unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011452{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011453 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011454
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011455 cmdp = cmdlookup(name, 0);
11456 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11457 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011458}
11459
Eric Andersencb57d552001-06-28 07:25:16 +000011460/*
Eric Andersencb57d552001-06-28 07:25:16 +000011461 * The unset builtin command. We unset the function before we unset the
11462 * variable to allow a function to be unset when there is a readonly variable
11463 * with the same name.
11464 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011465static int
Eric Andersenc470f442003-07-28 09:56:35 +000011466unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011467{
11468 char **ap;
11469 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011470 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011471 int ret = 0;
11472
11473 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011474 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011475 }
Eric Andersencb57d552001-06-28 07:25:16 +000011476
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011477 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011478 if (flag != 'f') {
11479 i = unsetvar(*ap);
11480 ret |= i;
11481 if (!(i & 2))
11482 continue;
11483 }
11484 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011485 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011486 }
Eric Andersenc470f442003-07-28 09:56:35 +000011487 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011488}
11489
11490
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011491/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011492
Eric Andersenc470f442003-07-28 09:56:35 +000011493#include <sys/times.h>
11494
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011495static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011496 ' ', offsetof(struct tms, tms_utime),
11497 '\n', offsetof(struct tms, tms_stime),
11498 ' ', offsetof(struct tms, tms_cutime),
11499 '\n', offsetof(struct tms, tms_cstime),
11500 0
11501};
Eric Andersencb57d552001-06-28 07:25:16 +000011502
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011503static int
11504timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011505{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011506 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011507 const unsigned char *p;
11508 struct tms buf;
11509
11510 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011511 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011512
11513 p = timescmd_str;
11514 do {
11515 t = *(clock_t *)(((char *) &buf) + p[1]);
11516 s = t / clk_tck;
11517 out1fmt("%ldm%ld.%.3lds%c",
11518 s/60, s%60,
11519 ((t - s * clk_tck) * 1000) / clk_tck,
11520 p[0]);
11521 } while (*(p += 2));
11522
Eric Andersencb57d552001-06-28 07:25:16 +000011523 return 0;
11524}
11525
Denis Vlasenko131ae172007-02-18 13:00:19 +000011526#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011527static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011528dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011529{
Eric Andersened9ecf72004-06-22 08:29:45 +000011530 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011531 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011532
Denis Vlasenkob012b102007-02-19 22:43:01 +000011533 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011534 result = arith(s, &errcode);
11535 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011536 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011537 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011538 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011539 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011540 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011541 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011542 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011543 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011544 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011545
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011546 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011547}
Eric Andersenc470f442003-07-28 09:56:35 +000011548
Eric Andersenc470f442003-07-28 09:56:35 +000011549/*
Eric Andersen90898442003-08-06 11:20:52 +000011550 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11551 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11552 *
11553 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011554 */
11555static int
Eric Andersen90898442003-08-06 11:20:52 +000011556letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011557{
Eric Andersenc470f442003-07-28 09:56:35 +000011558 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011559 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011560
Eric Andersen90898442003-08-06 11:20:52 +000011561 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011562 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011563 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011564 for (ap = argv + 1; *ap; ap++) {
11565 i = dash_arith(*ap);
11566 }
Eric Andersenc470f442003-07-28 09:56:35 +000011567
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011568 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011569}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011570#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011571
Eric Andersenc470f442003-07-28 09:56:35 +000011572
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011573/* ============ miscbltin.c
11574 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011575 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011576 */
11577
11578#undef rflag
11579
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011580#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011581typedef enum __rlimit_resource rlim_t;
11582#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011583
Eric Andersenc470f442003-07-28 09:56:35 +000011584/*
11585 * The read builtin. The -e option causes backslashes to escape the
11586 * following character.
11587 *
11588 * This uses unbuffered input, which may be avoidable in some cases.
11589 */
Eric Andersenc470f442003-07-28 09:56:35 +000011590static int
11591readcmd(int argc, char **argv)
11592{
11593 char **ap;
11594 int backslash;
11595 char c;
11596 int rflag;
11597 char *prompt;
11598 const char *ifs;
11599 char *p;
11600 int startword;
11601 int status;
11602 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011603#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011604 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011605 int nchars = 0;
11606 int silent = 0;
11607 struct termios tty, old_tty;
11608#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011609#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011610 fd_set set;
11611 struct timeval ts;
11612
11613 ts.tv_sec = ts.tv_usec = 0;
11614#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011615
11616 rflag = 0;
11617 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011618#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011619 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011620#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011621 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011622#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011623 while ((i = nextopt("p:rt:")) != '\0')
11624#else
11625 while ((i = nextopt("p:r")) != '\0')
11626#endif
11627 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011628 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011629 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011630 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011631 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011633 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011634 nchars = bb_strtou(optionarg, NULL, 10);
11635 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011636 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011637 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011638 break;
11639 case 's':
11640 silent = 1;
11641 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011642#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011643#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011644 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011645 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011646 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011647 /* EINVAL means number is ok, but not terminated by NUL */
11648 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011649 char *p2;
11650 if (*++p) {
11651 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011652 ts.tv_usec = bb_strtou(p, &p2, 10);
11653 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011654 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011655 scale = p2 - p;
11656 /* normalize to usec */
11657 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011658 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011659 while (scale++ < 6)
11660 ts.tv_usec *= 10;
11661 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011662 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011663 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011664 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011665 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011666 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011667 }
Paul Fox02eb9342005-09-07 16:56:02 +000011668 break;
11669#endif
11670 case 'r':
11671 rflag = 1;
11672 break;
11673 default:
11674 break;
11675 }
Eric Andersenc470f442003-07-28 09:56:35 +000011676 }
11677 if (prompt && isatty(0)) {
11678 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011679 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011680 ap = argptr;
11681 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011682 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011683 ifs = bltinlookup("IFS");
11684 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011685 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011686#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011687 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011688 if (tcgetattr(0, &tty) != 0) {
11689 /* Not a tty */
11690 n_flag = 0;
11691 silent = 0;
11692 } else {
11693 old_tty = tty;
11694 if (n_flag) {
11695 tty.c_lflag &= ~ICANON;
11696 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11697 }
11698 if (silent) {
11699 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11700 }
11701 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011702 }
Paul Fox02eb9342005-09-07 16:56:02 +000011703 }
11704#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011705#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011706 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011707 FD_ZERO(&set);
11708 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011709
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011710 /* poll-based wait produces bigger code, using select */
11711 i = select(1, &set, NULL, NULL, &ts);
11712 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011713#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011714 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011715 tcsetattr(0, TCSANOW, &old_tty);
11716#endif
11717 return 1;
11718 }
11719 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011720#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011721 status = 0;
11722 startword = 1;
11723 backslash = 0;
11724 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011725 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011726 if (read(0, &c, 1) != 1) {
11727 status = 1;
11728 break;
11729 }
11730 if (c == '\0')
11731 continue;
11732 if (backslash) {
11733 backslash = 0;
11734 if (c != '\n')
11735 goto put;
11736 continue;
11737 }
11738 if (!rflag && c == '\\') {
11739 backslash++;
11740 continue;
11741 }
11742 if (c == '\n')
11743 break;
11744 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11745 continue;
11746 }
11747 startword = 0;
11748 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11749 STACKSTRNUL(p);
11750 setvar(*ap, stackblock(), 0);
11751 ap++;
11752 startword = 1;
11753 STARTSTACKSTR(p);
11754 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011755 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011756 STPUTC(c, p);
11757 }
11758 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011759/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011760#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011761 while (!n_flag || --nchars);
11762#else
11763 while (1);
11764#endif
11765
11766#if ENABLE_ASH_READ_NCHARS
11767 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011768 tcsetattr(0, TCSANOW, &old_tty);
11769#endif
11770
Eric Andersenc470f442003-07-28 09:56:35 +000011771 STACKSTRNUL(p);
11772 /* Remove trailing blanks */
11773 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11774 *p = '\0';
11775 setvar(*ap, stackblock(), 0);
11776 while (*++ap != NULL)
11777 setvar(*ap, nullstr, 0);
11778 return status;
11779}
11780
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011781static int
11782umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011783{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011784 static const char permuser[3] ALIGN1 = "ugo";
11785 static const char permmode[3] ALIGN1 = "rwx";
11786 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011787 S_IRUSR, S_IWUSR, S_IXUSR,
11788 S_IRGRP, S_IWGRP, S_IXGRP,
11789 S_IROTH, S_IWOTH, S_IXOTH
11790 };
11791
11792 char *ap;
11793 mode_t mask;
11794 int i;
11795 int symbolic_mode = 0;
11796
11797 while (nextopt("S") != '\0') {
11798 symbolic_mode = 1;
11799 }
11800
Denis Vlasenkob012b102007-02-19 22:43:01 +000011801 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011802 mask = umask(0);
11803 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011804 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011805
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011806 ap = *argptr;
11807 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011808 if (symbolic_mode) {
11809 char buf[18];
11810 char *p = buf;
11811
11812 for (i = 0; i < 3; i++) {
11813 int j;
11814
11815 *p++ = permuser[i];
11816 *p++ = '=';
11817 for (j = 0; j < 3; j++) {
11818 if ((mask & permmask[3 * i + j]) == 0) {
11819 *p++ = permmode[j];
11820 }
11821 }
11822 *p++ = ',';
11823 }
11824 *--p = 0;
11825 puts(buf);
11826 } else {
11827 out1fmt("%.4o\n", mask);
11828 }
11829 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011830 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011831 mask = 0;
11832 do {
11833 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011834 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011835 mask = (mask << 3) + (*ap - '0');
11836 } while (*++ap != '\0');
11837 umask(mask);
11838 } else {
11839 mask = ~mask & 0777;
11840 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011841 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011842 }
11843 umask(~mask & 0777);
11844 }
11845 }
11846 return 0;
11847}
11848
11849/*
11850 * ulimit builtin
11851 *
11852 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11853 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11854 * ash by J.T. Conklin.
11855 *
11856 * Public domain.
11857 */
11858
11859struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011860 uint8_t cmd; /* RLIMIT_xxx fit into it */
11861 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011862 char option;
11863};
11864
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011865static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011866#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011867 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011868#endif
11869#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011870 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011871#endif
11872#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011873 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011874#endif
11875#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011876 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011877#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011878#ifdef RLIMIT_CORE
11879 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011880#endif
11881#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011882 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011883#endif
11884#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011885 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011886#endif
11887#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011888 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011889#endif
11890#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011891 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011892#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011893#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011894 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011895#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011896#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011897 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011898#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011899};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011900static const char limits_name[] =
11901#ifdef RLIMIT_CPU
11902 "time(seconds)" "\0"
11903#endif
11904#ifdef RLIMIT_FSIZE
11905 "file(blocks)" "\0"
11906#endif
11907#ifdef RLIMIT_DATA
11908 "data(kb)" "\0"
11909#endif
11910#ifdef RLIMIT_STACK
11911 "stack(kb)" "\0"
11912#endif
11913#ifdef RLIMIT_CORE
11914 "coredump(blocks)" "\0"
11915#endif
11916#ifdef RLIMIT_RSS
11917 "memory(kb)" "\0"
11918#endif
11919#ifdef RLIMIT_MEMLOCK
11920 "locked memory(kb)" "\0"
11921#endif
11922#ifdef RLIMIT_NPROC
11923 "process" "\0"
11924#endif
11925#ifdef RLIMIT_NOFILE
11926 "nofiles" "\0"
11927#endif
11928#ifdef RLIMIT_AS
11929 "vmemory(kb)" "\0"
11930#endif
11931#ifdef RLIMIT_LOCKS
11932 "locks" "\0"
11933#endif
11934;
Eric Andersenc470f442003-07-28 09:56:35 +000011935
Glenn L McGrath76620622004-01-13 10:19:37 +000011936enum limtype { SOFT = 0x1, HARD = 0x2 };
11937
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011938static void
11939printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011940 const struct limits *l)
11941{
11942 rlim_t val;
11943
11944 val = limit->rlim_max;
11945 if (how & SOFT)
11946 val = limit->rlim_cur;
11947
11948 if (val == RLIM_INFINITY)
11949 out1fmt("unlimited\n");
11950 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011951 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000011952 out1fmt("%lld\n", (long long) val);
11953 }
11954}
11955
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011956static int
Eric Andersenc470f442003-07-28 09:56:35 +000011957ulimitcmd(int argc, char **argv)
11958{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011959 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011960 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011961 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011962 const struct limits *l;
11963 int set, all = 0;
11964 int optc, what;
11965 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011966
11967 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011968 while ((optc = nextopt("HSa"
11969#ifdef RLIMIT_CPU
11970 "t"
11971#endif
11972#ifdef RLIMIT_FSIZE
11973 "f"
11974#endif
11975#ifdef RLIMIT_DATA
11976 "d"
11977#endif
11978#ifdef RLIMIT_STACK
11979 "s"
11980#endif
11981#ifdef RLIMIT_CORE
11982 "c"
11983#endif
11984#ifdef RLIMIT_RSS
11985 "m"
11986#endif
11987#ifdef RLIMIT_MEMLOCK
11988 "l"
11989#endif
11990#ifdef RLIMIT_NPROC
11991 "p"
11992#endif
11993#ifdef RLIMIT_NOFILE
11994 "n"
11995#endif
11996#ifdef RLIMIT_AS
11997 "v"
11998#endif
11999#ifdef RLIMIT_LOCKS
12000 "w"
12001#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012002 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012003 switch (optc) {
12004 case 'H':
12005 how = HARD;
12006 break;
12007 case 'S':
12008 how = SOFT;
12009 break;
12010 case 'a':
12011 all = 1;
12012 break;
12013 default:
12014 what = optc;
12015 }
12016
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012017 for (l = limits_tbl; l->option != what; l++)
12018 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012019
12020 set = *argptr ? 1 : 0;
12021 if (set) {
12022 char *p = *argptr;
12023
12024 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012025 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012026 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012027 val = RLIM_INFINITY;
12028 else {
12029 val = (rlim_t) 0;
12030
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012031 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012032 val = (val * 10) + (long)(c - '0');
12033 if (val < (rlim_t) 0)
12034 break;
12035 }
12036 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012037 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012038 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012039 }
12040 }
12041 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012042 const char *lname = limits_name;
12043 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012044 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012045 out1fmt("%-20s ", lname);
12046 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012047 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012048 }
12049 return 0;
12050 }
12051
12052 getrlimit(l->cmd, &limit);
12053 if (set) {
12054 if (how & HARD)
12055 limit.rlim_max = val;
12056 if (how & SOFT)
12057 limit.rlim_cur = val;
12058 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012059 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012060 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012061 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012062 }
12063 return 0;
12064}
12065
Eric Andersen90898442003-08-06 11:20:52 +000012066
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012067/* ============ Math support */
12068
Denis Vlasenko131ae172007-02-18 13:00:19 +000012069#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012070
12071/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12072
12073 Permission is hereby granted, free of charge, to any person obtaining
12074 a copy of this software and associated documentation files (the
12075 "Software"), to deal in the Software without restriction, including
12076 without limitation the rights to use, copy, modify, merge, publish,
12077 distribute, sublicense, and/or sell copies of the Software, and to
12078 permit persons to whom the Software is furnished to do so, subject to
12079 the following conditions:
12080
12081 The above copyright notice and this permission notice shall be
12082 included in all copies or substantial portions of the Software.
12083
12084 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12085 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12086 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12087 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12088 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12089 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12090 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12091*/
12092
12093/* This is my infix parser/evaluator. It is optimized for size, intended
12094 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012095 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012096 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012097 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012098 * be that which POSIX specifies for shells. */
12099
12100/* The code uses a simple two-stack algorithm. See
12101 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012102 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012103 * this is based (this code differs in that it applies operators immediately
12104 * to the stack instead of adding them to a queue to end up with an
12105 * expression). */
12106
12107/* To use the routine, call it with an expression string and error return
12108 * pointer */
12109
12110/*
12111 * Aug 24, 2001 Manuel Novoa III
12112 *
12113 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12114 *
12115 * 1) In arith_apply():
12116 * a) Cached values of *numptr and &(numptr[-1]).
12117 * b) Removed redundant test for zero denominator.
12118 *
12119 * 2) In arith():
12120 * a) Eliminated redundant code for processing operator tokens by moving
12121 * to a table-based implementation. Also folded handling of parens
12122 * into the table.
12123 * b) Combined all 3 loops which called arith_apply to reduce generated
12124 * code size at the cost of speed.
12125 *
12126 * 3) The following expressions were treated as valid by the original code:
12127 * 1() , 0! , 1 ( *3 ) .
12128 * These bugs have been fixed by internally enclosing the expression in
12129 * parens and then checking that all binary ops and right parens are
12130 * preceded by a valid expression (NUM_TOKEN).
12131 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012132 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012133 * ctype's isspace() if it is used by another busybox applet or if additional
12134 * whitespace chars should be considered. Look below the "#include"s for a
12135 * precompiler test.
12136 */
12137
12138/*
12139 * Aug 26, 2001 Manuel Novoa III
12140 *
12141 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12142 *
12143 * Merge in Aaron's comments previously posted to the busybox list,
12144 * modified slightly to take account of my changes to the code.
12145 *
12146 */
12147
12148/*
12149 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12150 *
12151 * - allow access to variable,
12152 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12153 * - realize assign syntax (VAR=expr, +=, *= etc)
12154 * - realize exponentiation (** operator)
12155 * - realize comma separated - expr, expr
12156 * - realise ++expr --expr expr++ expr--
12157 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012158 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012159 * - was restored loses XOR operator
12160 * - remove one goto label, added three ;-)
12161 * - protect $((num num)) as true zero expr (Manuel`s error)
12162 * - always use special isspace(), see comment from bash ;-)
12163 */
12164
Eric Andersen90898442003-08-06 11:20:52 +000012165#define arith_isspace(arithval) \
12166 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12167
Eric Andersen90898442003-08-06 11:20:52 +000012168typedef unsigned char operator;
12169
12170/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012171 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012172 * precedence. The ID portion is so that multiple operators can have the
12173 * same precedence, ensuring that the leftmost one is evaluated first.
12174 * Consider * and /. */
12175
12176#define tok_decl(prec,id) (((id)<<5)|(prec))
12177#define PREC(op) ((op) & 0x1F)
12178
12179#define TOK_LPAREN tok_decl(0,0)
12180
12181#define TOK_COMMA tok_decl(1,0)
12182
12183#define TOK_ASSIGN tok_decl(2,0)
12184#define TOK_AND_ASSIGN tok_decl(2,1)
12185#define TOK_OR_ASSIGN tok_decl(2,2)
12186#define TOK_XOR_ASSIGN tok_decl(2,3)
12187#define TOK_PLUS_ASSIGN tok_decl(2,4)
12188#define TOK_MINUS_ASSIGN tok_decl(2,5)
12189#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12190#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12191
12192#define TOK_MUL_ASSIGN tok_decl(3,0)
12193#define TOK_DIV_ASSIGN tok_decl(3,1)
12194#define TOK_REM_ASSIGN tok_decl(3,2)
12195
12196/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012197#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012198
12199/* conditional is right associativity too */
12200#define TOK_CONDITIONAL tok_decl(4,0)
12201#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12202
12203#define TOK_OR tok_decl(5,0)
12204
12205#define TOK_AND tok_decl(6,0)
12206
12207#define TOK_BOR tok_decl(7,0)
12208
12209#define TOK_BXOR tok_decl(8,0)
12210
12211#define TOK_BAND tok_decl(9,0)
12212
12213#define TOK_EQ tok_decl(10,0)
12214#define TOK_NE tok_decl(10,1)
12215
12216#define TOK_LT tok_decl(11,0)
12217#define TOK_GT tok_decl(11,1)
12218#define TOK_GE tok_decl(11,2)
12219#define TOK_LE tok_decl(11,3)
12220
12221#define TOK_LSHIFT tok_decl(12,0)
12222#define TOK_RSHIFT tok_decl(12,1)
12223
12224#define TOK_ADD tok_decl(13,0)
12225#define TOK_SUB tok_decl(13,1)
12226
12227#define TOK_MUL tok_decl(14,0)
12228#define TOK_DIV tok_decl(14,1)
12229#define TOK_REM tok_decl(14,2)
12230
12231/* exponent is right associativity */
12232#define TOK_EXPONENT tok_decl(15,1)
12233
12234/* For now unary operators. */
12235#define UNARYPREC 16
12236#define TOK_BNOT tok_decl(UNARYPREC,0)
12237#define TOK_NOT tok_decl(UNARYPREC,1)
12238
12239#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12240#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12241
12242#define PREC_PRE (UNARYPREC+2)
12243
12244#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12245#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12246
12247#define PREC_POST (UNARYPREC+3)
12248
12249#define TOK_POST_INC tok_decl(PREC_POST, 0)
12250#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12251
12252#define SPEC_PREC (UNARYPREC+4)
12253
12254#define TOK_NUM tok_decl(SPEC_PREC, 0)
12255#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12256
12257#define NUMPTR (*numstackptr)
12258
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012259static int
12260tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012261{
12262 operator prec = PREC(op);
12263
12264 convert_prec_is_assing(prec);
12265 return (prec == PREC(TOK_ASSIGN) ||
12266 prec == PREC_PRE || prec == PREC_POST);
12267}
12268
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012269static int
12270is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012271{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012272 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12273 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012274}
12275
Eric Andersen90898442003-08-06 11:20:52 +000012276typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012277 arith_t val;
12278 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012279 char contidional_second_val_initialized;
12280 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012281 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012282} v_n_t;
12283
Eric Andersen90898442003-08-06 11:20:52 +000012284typedef struct CHK_VAR_RECURSIVE_LOOPED {
12285 const char *var;
12286 struct CHK_VAR_RECURSIVE_LOOPED *next;
12287} chk_var_recursive_looped_t;
12288
12289static chk_var_recursive_looped_t *prev_chk_var_recursive;
12290
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012291static int
12292arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012293{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012294 if (t->var) {
12295 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012296
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012297 if (p) {
12298 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012299
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012300 /* recursive try as expression */
12301 chk_var_recursive_looped_t *cur;
12302 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012303
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012304 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12305 if (strcmp(cur->var, t->var) == 0) {
12306 /* expression recursion loop detected */
12307 return -5;
12308 }
12309 }
12310 /* save current lookuped var name */
12311 cur = prev_chk_var_recursive;
12312 cur_save.var = t->var;
12313 cur_save.next = cur;
12314 prev_chk_var_recursive = &cur_save;
12315
12316 t->val = arith (p, &errcode);
12317 /* restore previous ptr after recursiving */
12318 prev_chk_var_recursive = cur;
12319 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012320 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321 /* allow undefined var as 0 */
12322 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012323 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012324 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012325}
12326
12327/* "applying" a token means performing it on the top elements on the integer
12328 * stack. For a unary operator it will only change the top element, but a
12329 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012330static int
12331arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012332{
Eric Andersen90898442003-08-06 11:20:52 +000012333 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012334 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012335 int ret_arith_lookup_val;
12336
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012337 /* There is no operator that can work without arguments */
12338 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012339 numptr_m1 = NUMPTR - 1;
12340
12341 /* check operand is var with noninteger value */
12342 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012343 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012344 return ret_arith_lookup_val;
12345
12346 rez = numptr_m1->val;
12347 if (op == TOK_UMINUS)
12348 rez *= -1;
12349 else if (op == TOK_NOT)
12350 rez = !rez;
12351 else if (op == TOK_BNOT)
12352 rez = ~rez;
12353 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12354 rez++;
12355 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12356 rez--;
12357 else if (op != TOK_UPLUS) {
12358 /* Binary operators */
12359
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012360 /* check and binary operators need two arguments */
12361 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012362
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012363 /* ... and they pop one */
12364 --NUMPTR;
12365 numptr_val = rez;
12366 if (op == TOK_CONDITIONAL) {
12367 if (! numptr_m1->contidional_second_val_initialized) {
12368 /* protect $((expr1 ? expr2)) without ": expr" */
12369 goto err;
12370 }
12371 rez = numptr_m1->contidional_second_val;
12372 } else if (numptr_m1->contidional_second_val_initialized) {
12373 /* protect $((expr1 : expr2)) without "expr ? " */
12374 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012375 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 numptr_m1 = NUMPTR - 1;
12377 if (op != TOK_ASSIGN) {
12378 /* check operand is var with noninteger value for not '=' */
12379 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12380 if (ret_arith_lookup_val)
12381 return ret_arith_lookup_val;
12382 }
12383 if (op == TOK_CONDITIONAL) {
12384 numptr_m1->contidional_second_val = rez;
12385 }
12386 rez = numptr_m1->val;
12387 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012388 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012389 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012390 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012391 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012392 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012393 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012394 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012395 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012396 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012397 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012398 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012399 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012400 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012401 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012402 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012403 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012404 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012405 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012406 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012407 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012408 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012409 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012410 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012411 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012412 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012413 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012414 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012415 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012416 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012417 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012418 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012419 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012420 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012421 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012422 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012423 /* protect $((expr : expr)) without "expr ? " */
12424 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012425 }
12426 numptr_m1->contidional_second_val_initialized = op;
12427 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012428 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012429 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012430 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012431 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012432 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012433 return -3; /* exponent less than 0 */
12434 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012435 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012436
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012437 if (numptr_val)
12438 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012439 c *= rez;
12440 rez = c;
12441 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012442 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012443 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012444 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012445 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012446 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012447 rez %= numptr_val;
12448 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012449 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012450 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012451
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012452 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012453 /* Hmm, 1=2 ? */
12454 goto err;
12455 }
12456 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012457#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012458 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012459#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012460 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012461#endif
Eric Andersen90898442003-08-06 11:20:52 +000012462 setvar(numptr_m1->var, buf, 0);
12463 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012464 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012465 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012466 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012467 rez++;
12468 }
12469 numptr_m1->val = rez;
12470 /* protect geting var value, is number now */
12471 numptr_m1->var = NULL;
12472 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012473 err:
12474 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012475}
12476
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012477/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012478static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012479 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12480 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12481 '<','<', 0, TOK_LSHIFT,
12482 '>','>', 0, TOK_RSHIFT,
12483 '|','|', 0, TOK_OR,
12484 '&','&', 0, TOK_AND,
12485 '!','=', 0, TOK_NE,
12486 '<','=', 0, TOK_LE,
12487 '>','=', 0, TOK_GE,
12488 '=','=', 0, TOK_EQ,
12489 '|','=', 0, TOK_OR_ASSIGN,
12490 '&','=', 0, TOK_AND_ASSIGN,
12491 '*','=', 0, TOK_MUL_ASSIGN,
12492 '/','=', 0, TOK_DIV_ASSIGN,
12493 '%','=', 0, TOK_REM_ASSIGN,
12494 '+','=', 0, TOK_PLUS_ASSIGN,
12495 '-','=', 0, TOK_MINUS_ASSIGN,
12496 '-','-', 0, TOK_POST_DEC,
12497 '^','=', 0, TOK_XOR_ASSIGN,
12498 '+','+', 0, TOK_POST_INC,
12499 '*','*', 0, TOK_EXPONENT,
12500 '!', 0, TOK_NOT,
12501 '<', 0, TOK_LT,
12502 '>', 0, TOK_GT,
12503 '=', 0, TOK_ASSIGN,
12504 '|', 0, TOK_BOR,
12505 '&', 0, TOK_BAND,
12506 '*', 0, TOK_MUL,
12507 '/', 0, TOK_DIV,
12508 '%', 0, TOK_REM,
12509 '+', 0, TOK_ADD,
12510 '-', 0, TOK_SUB,
12511 '^', 0, TOK_BXOR,
12512 /* uniq */
12513 '~', 0, TOK_BNOT,
12514 ',', 0, TOK_COMMA,
12515 '?', 0, TOK_CONDITIONAL,
12516 ':', 0, TOK_CONDITIONAL_SEP,
12517 ')', 0, TOK_RPAREN,
12518 '(', 0, TOK_LPAREN,
12519 0
12520};
12521/* ptr to ")" */
12522#define endexpression &op_tokens[sizeof(op_tokens)-7]
12523
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012524static arith_t
12525arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012526{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012527 char arithval; /* Current character under analysis */
12528 operator lasttok, op;
12529 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012530
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 const char *p = endexpression;
12532 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012533
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012534 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012535
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012536 /* Stack of integers */
12537 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12538 * in any given correct or incorrect expression is left as an exercise to
12539 * the reader. */
12540 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12541 *numstackptr = numstack;
12542 /* Stack of operator tokens */
12543 operator *stack = alloca((datasizes) * sizeof(operator)),
12544 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012545
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012546 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12547 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012548
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012549 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012550 arithval = *expr;
12551 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012552 if (p == endexpression) {
12553 /* Null expression. */
12554 return 0;
12555 }
12556
12557 /* This is only reached after all tokens have been extracted from the
12558 * input stream. If there are still tokens on the operator stack, they
12559 * are to be applied in order. At the end, there should be a final
12560 * result on the integer stack */
12561
12562 if (expr != endexpression + 1) {
12563 /* If we haven't done so already, */
12564 /* append a closing right paren */
12565 expr = endexpression;
12566 /* and let the loop process it. */
12567 continue;
12568 }
12569 /* At this point, we're done with the expression. */
12570 if (numstackptr != numstack+1) {
12571 /* ... but if there isn't, it's bad */
12572 err:
12573 return (*perrcode = -1);
12574 }
12575 if (numstack->var) {
12576 /* expression is $((var)) only, lookup now */
12577 errcode = arith_lookup_val(numstack);
12578 }
12579 ret:
12580 *perrcode = errcode;
12581 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012582 }
12583
Eric Andersen90898442003-08-06 11:20:52 +000012584 /* Continue processing the expression. */
12585 if (arith_isspace(arithval)) {
12586 /* Skip whitespace */
12587 goto prologue;
12588 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012589 p = endofname(expr);
12590 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012591 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012592
12593 numstackptr->var = alloca(var_name_size);
12594 safe_strncpy(numstackptr->var, expr, var_name_size);
12595 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012596 num:
Eric Andersen90898442003-08-06 11:20:52 +000012597 numstackptr->contidional_second_val_initialized = 0;
12598 numstackptr++;
12599 lasttok = TOK_NUM;
12600 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012601 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012602 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012603 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012604#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012605 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012606#else
12607 numstackptr->val = strtol(expr, (char **) &expr, 0);
12608#endif
Eric Andersen90898442003-08-06 11:20:52 +000012609 goto num;
12610 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012611 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012612 const char *o;
12613
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012614 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012615 /* strange operator not found */
12616 goto err;
12617 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012618 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012619 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012620 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012621 /* found */
12622 expr = o - 1;
12623 break;
12624 }
12625 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012626 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012627 p++;
12628 /* skip zero delim */
12629 p++;
12630 }
12631 op = p[1];
12632
12633 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012634 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12635 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012636
12637 /* Plus and minus are binary (not unary) _only_ if the last
12638 * token was as number, or a right paren (which pretends to be
12639 * a number, since it evaluates to one). Think about it.
12640 * It makes sense. */
12641 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012642 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012643 case TOK_ADD:
12644 op = TOK_UPLUS;
12645 break;
12646 case TOK_SUB:
12647 op = TOK_UMINUS;
12648 break;
12649 case TOK_POST_INC:
12650 op = TOK_PRE_INC;
12651 break;
12652 case TOK_POST_DEC:
12653 op = TOK_PRE_DEC;
12654 break;
Eric Andersen90898442003-08-06 11:20:52 +000012655 }
12656 }
12657 /* We don't want a unary operator to cause recursive descent on the
12658 * stack, because there can be many in a row and it could cause an
12659 * operator to be evaluated before its argument is pushed onto the
12660 * integer stack. */
12661 /* But for binary operators, "apply" everything on the operator
12662 * stack until we find an operator with a lesser priority than the
12663 * one we have just extracted. */
12664 /* Left paren is given the lowest priority so it will never be
12665 * "applied" in this way.
12666 * if associativity is right and priority eq, applied also skip
12667 */
12668 prec = PREC(op);
12669 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12670 /* not left paren or unary */
12671 if (lasttok != TOK_NUM) {
12672 /* binary op must be preceded by a num */
12673 goto err;
12674 }
12675 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012676 if (op == TOK_RPAREN) {
12677 /* The algorithm employed here is simple: while we don't
12678 * hit an open paren nor the bottom of the stack, pop
12679 * tokens and apply them */
12680 if (stackptr[-1] == TOK_LPAREN) {
12681 --stackptr;
12682 /* Any operator directly after a */
12683 lasttok = TOK_NUM;
12684 /* close paren should consider itself binary */
12685 goto prologue;
12686 }
12687 } else {
12688 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012689
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012690 convert_prec_is_assing(prec);
12691 convert_prec_is_assing(prev_prec);
12692 if (prev_prec < prec)
12693 break;
12694 /* check right assoc */
12695 if (prev_prec == prec && is_right_associativity(prec))
12696 break;
12697 }
12698 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12699 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012700 }
12701 if (op == TOK_RPAREN) {
12702 goto err;
12703 }
12704 }
12705
12706 /* Push this operator to the stack and remember it. */
12707 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012708 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012709 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012710 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012711}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012712#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012713
12714
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012715/* ============ main() and helpers */
12716
12717/*
12718 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012719 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012720static void exitshell(void) ATTRIBUTE_NORETURN;
12721static void
12722exitshell(void)
12723{
12724 struct jmploc loc;
12725 char *p;
12726 int status;
12727
12728 status = exitstatus;
12729 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12730 if (setjmp(loc.loc)) {
12731 if (exception == EXEXIT)
12732/* dash bug: it just does _exit(exitstatus) here
12733 * but we have to do setjobctl(0) first!
12734 * (bug is still not fixed in dash-0.5.3 - if you run dash
12735 * under Midnight Commander, on exit from dash MC is backgrounded) */
12736 status = exitstatus;
12737 goto out;
12738 }
12739 exception_handler = &loc;
12740 p = trap[0];
12741 if (p) {
12742 trap[0] = NULL;
12743 evalstring(p, 0);
12744 }
12745 flush_stdout_stderr();
12746 out:
12747 setjobctl(0);
12748 _exit(status);
12749 /* NOTREACHED */
12750}
12751
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012752static void
12753init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012754{
12755 /* from input.c: */
12756 basepf.nextc = basepf.buf = basebuf;
12757
12758 /* from trap.c: */
12759 signal(SIGCHLD, SIG_DFL);
12760
12761 /* from var.c: */
12762 {
12763 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012764 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012765 const char *p;
12766 struct stat st1, st2;
12767
12768 initvar();
12769 for (envp = environ; envp && *envp; envp++) {
12770 if (strchr(*envp, '=')) {
12771 setvareq(*envp, VEXPORT|VTEXTFIXED);
12772 }
12773 }
12774
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012775 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012776 setvar("PPID", ppid, 0);
12777
12778 p = lookupvar("PWD");
12779 if (p)
12780 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12781 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12782 p = '\0';
12783 setpwd(p, 0);
12784 }
12785}
12786
12787/*
12788 * Process the shell command line arguments.
12789 */
12790static void
12791procargs(int argc, char **argv)
12792{
12793 int i;
12794 const char *xminusc;
12795 char **xargv;
12796
12797 xargv = argv;
12798 arg0 = xargv[0];
12799 if (argc > 0)
12800 xargv++;
12801 for (i = 0; i < NOPTS; i++)
12802 optlist[i] = 2;
12803 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012804 if (options(1)) {
12805 /* it already printed err message */
12806 raise_exception(EXERROR);
12807 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012808 xargv = argptr;
12809 xminusc = minusc;
12810 if (*xargv == NULL) {
12811 if (xminusc)
12812 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12813 sflag = 1;
12814 }
12815 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12816 iflag = 1;
12817 if (mflag == 2)
12818 mflag = iflag;
12819 for (i = 0; i < NOPTS; i++)
12820 if (optlist[i] == 2)
12821 optlist[i] = 0;
12822#if DEBUG == 2
12823 debug = 1;
12824#endif
12825 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12826 if (xminusc) {
12827 minusc = *xargv++;
12828 if (*xargv)
12829 goto setarg0;
12830 } else if (!sflag) {
12831 setinputfile(*xargv, 0);
12832 setarg0:
12833 arg0 = *xargv++;
12834 commandname = arg0;
12835 }
12836
12837 shellparam.p = xargv;
12838#if ENABLE_ASH_GETOPTS
12839 shellparam.optind = 1;
12840 shellparam.optoff = -1;
12841#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012842 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012843 while (*xargv) {
12844 shellparam.nparam++;
12845 xargv++;
12846 }
12847 optschanged();
12848}
12849
12850/*
12851 * Read /etc/profile or .profile.
12852 */
12853static void
12854read_profile(const char *name)
12855{
12856 int skip;
12857
12858 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12859 return;
12860 skip = cmdloop(0);
12861 popfile();
12862 if (skip)
12863 exitshell();
12864}
12865
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012866/*
12867 * This routine is called when an error or an interrupt occurs in an
12868 * interactive shell and control is returned to the main command loop.
12869 */
12870static void
12871reset(void)
12872{
12873 /* from eval.c: */
12874 evalskip = 0;
12875 loopnest = 0;
12876 /* from input.c: */
12877 parselleft = parsenleft = 0; /* clear input buffer */
12878 popallfiles();
12879 /* from parser.c: */
12880 tokpushback = 0;
12881 checkkwd = 0;
12882 /* from redir.c: */
12883 clearredir(0);
12884}
12885
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012886#if PROFILE
12887static short profile_buf[16384];
12888extern int etext();
12889#endif
12890
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012891/*
12892 * Main routine. We initialize things, parse the arguments, execute
12893 * profiles if we're a login shell, and then call cmdloop to execute
12894 * commands. The setjmp call sets up the location to jump to when an
12895 * exception occurs. When an exception occurs the variable "state"
12896 * is used to figure out how far we had gotten.
12897 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012898int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012899int ash_main(int argc, char **argv)
12900{
12901 char *shinit;
12902 volatile int state;
12903 struct jmploc jmploc;
12904 struct stackmark smark;
12905
Denis Vlasenko01631112007-12-16 17:20:38 +000012906 /* Initialize global data */
12907 INIT_G_misc();
12908 INIT_G_memstack();
12909 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012910#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012911 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012912#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012913 INIT_G_cmdtable();
12914
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012915#if PROFILE
12916 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12917#endif
12918
12919#if ENABLE_FEATURE_EDITING
12920 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12921#endif
12922 state = 0;
12923 if (setjmp(jmploc.loc)) {
12924 int e;
12925 int s;
12926
12927 reset();
12928
12929 e = exception;
12930 if (e == EXERROR)
12931 exitstatus = 2;
12932 s = state;
12933 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12934 exitshell();
12935
12936 if (e == EXINT) {
12937 outcslow('\n', stderr);
12938 }
12939 popstackmark(&smark);
12940 FORCE_INT_ON; /* enable interrupts */
12941 if (s == 1)
12942 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012943 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012944 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012945 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012946 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012947 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012948 }
12949 exception_handler = &jmploc;
12950#if DEBUG
12951 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012952 trace_puts("Shell args: ");
12953 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012954#endif
12955 rootpid = getpid();
12956
12957#if ENABLE_ASH_RANDOM_SUPPORT
12958 rseed = rootpid + time(NULL);
12959#endif
12960 init();
12961 setstackmark(&smark);
12962 procargs(argc, argv);
12963#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12964 if (iflag) {
12965 const char *hp = lookupvar("HISTFILE");
12966
12967 if (hp == NULL) {
12968 hp = lookupvar("HOME");
12969 if (hp != NULL) {
12970 char *defhp = concat_path_file(hp, ".ash_history");
12971 setvar("HISTFILE", defhp, 0);
12972 free(defhp);
12973 }
12974 }
12975 }
12976#endif
12977 if (argv[0] && argv[0][0] == '-')
12978 isloginsh = 1;
12979 if (isloginsh) {
12980 state = 1;
12981 read_profile("/etc/profile");
12982 state1:
12983 state = 2;
12984 read_profile(".profile");
12985 }
12986 state2:
12987 state = 3;
12988 if (
12989#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012990 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012991#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012992 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012993 ) {
12994 shinit = lookupvar("ENV");
12995 if (shinit != NULL && *shinit != '\0') {
12996 read_profile(shinit);
12997 }
12998 }
12999 state3:
13000 state = 4;
13001 if (minusc)
13002 evalstring(minusc, 0);
13003
13004 if (sflag || minusc == NULL) {
13005#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13006 if ( iflag ) {
13007 const char *hp = lookupvar("HISTFILE");
13008
13009 if (hp != NULL)
13010 line_input_state->hist_file = hp;
13011 }
13012#endif
13013 state4: /* XXX ??? - why isn't this before the "if" statement */
13014 cmdloop(1);
13015 }
13016#if PROFILE
13017 monitor(0);
13018#endif
13019#ifdef GPROF
13020 {
13021 extern void _mcleanup(void);
13022 _mcleanup();
13023 }
13024#endif
13025 exitshell();
13026 /* NOTREACHED */
13027}
13028
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013029#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013030const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013031int main(int argc, char **argv)
13032{
13033 return ash_main(argc, argv);
13034}
13035#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013036
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013037
Eric Andersendf82f612001-06-28 07:46:40 +000013038/*-
13039 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013040 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013041 *
13042 * This code is derived from software contributed to Berkeley by
13043 * Kenneth Almquist.
13044 *
13045 * Redistribution and use in source and binary forms, with or without
13046 * modification, are permitted provided that the following conditions
13047 * are met:
13048 * 1. Redistributions of source code must retain the above copyright
13049 * notice, this list of conditions and the following disclaimer.
13050 * 2. Redistributions in binary form must reproduce the above copyright
13051 * notice, this list of conditions and the following disclaimer in the
13052 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013053 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013054 * may be used to endorse or promote products derived from this software
13055 * without specific prior written permission.
13056 *
13057 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13058 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13059 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13060 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13061 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13062 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13063 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13064 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13065 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13066 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13067 * SUCH DAMAGE.
13068 */