blob: 58bfc5278a0a5d4bc1aa0aef33a6c29bf09aad1f [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000045#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000046
47#define IFS_BROKEN
48
49#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000050
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000052#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#define _GNU_SOURCE
54#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000055#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000056
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000057#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000061#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000062#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko01631112007-12-16 17:20:38 +000070/* ============ Hash table sizes. Configurable. */
71
72#define VTABSIZE 39
73#define ATABSIZE 39
74#define CMDTABLESIZE 31 /* should be prime */
75
76
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000077/* ============ Misc helpers */
78
79#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
80
81/* C99 say: "char" declaration may be signed or unsigned default */
82#define signed_char2int(sc) ((int)((signed char)sc))
83
84
Denis Vlasenkob012b102007-02-19 22:43:01 +000085/* ============ Shell options */
86
87static const char *const optletters_optnames[] = {
88 "e" "errexit",
89 "f" "noglob",
90 "I" "ignoreeof",
91 "i" "interactive",
92 "m" "monitor",
93 "n" "noexec",
94 "s" "stdin",
95 "x" "xtrace",
96 "v" "verbose",
97 "C" "noclobber",
98 "a" "allexport",
99 "b" "notify",
100 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000101 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000102#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 ,"\0" "nolog"
104 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000105#endif
106};
107
108#define optletters(n) optletters_optnames[(n)][0]
109#define optnames(n) (&optletters_optnames[(n)][1])
110
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000111enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000112
Eric Andersenc470f442003-07-28 09:56:35 +0000113
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000115
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000116static const char homestr[] ALIGN1 = "HOME";
117static const char snlfmt[] ALIGN1 = "%s\n";
118static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000119
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000120/*
Eric Andersenc470f442003-07-28 09:56:35 +0000121 * We enclose jmp_buf in a structure so that we can declare pointers to
122 * jump locations. The global variable handler contains the location to
123 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000124 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000125 * exception handlers, the user should save the value of handler on entry
126 * to an inner scope, set handler to point to a jmploc structure for the
127 * inner scope, and restore handler on exit from the scope.
128 */
Eric Andersenc470f442003-07-28 09:56:35 +0000129struct jmploc {
130 jmp_buf loc;
131};
Denis Vlasenko01631112007-12-16 17:20:38 +0000132
133struct globals_misc {
134 /* pid of main shell */
135 int rootpid;
136 /* shell level: 0 for the main shell, 1 for its children, and so on */
137 int shlvl;
138#define rootshell (!shlvl)
139 char *minusc; /* argument to -c option */
140
141 char *curdir; // = nullstr; /* current working directory */
142 char *physdir; // = nullstr; /* physical working directory */
143
144 char *arg0; /* value of $0 */
145
146 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000147
148// disabled by vda: cannot understand how it was supposed to work -
149// cannot fix bugs. That's why you have to explain your non-trivial designs!
150// /* do we generate EXSIG events */
151// int exsig; /* counter */
152 volatile int suppressint; /* counter */
153 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
154 /* last pending signal */
155 volatile /*sig_atomic_t*/ smallint pendingsig;
156 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000157 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000158#define EXINT 0 /* SIGINT received */
159#define EXERROR 1 /* a generic error */
160#define EXSHELLPROC 2 /* execute a shell procedure */
161#define EXEXEC 3 /* command execution failed */
162#define EXEXIT 4 /* exit the shell */
163#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000164
Denis Vlasenko01631112007-12-16 17:20:38 +0000165 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000166 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000167
168 char optlist[NOPTS];
169#define eflag optlist[0]
170#define fflag optlist[1]
171#define Iflag optlist[2]
172#define iflag optlist[3]
173#define mflag optlist[4]
174#define nflag optlist[5]
175#define sflag optlist[6]
176#define xflag optlist[7]
177#define vflag optlist[8]
178#define Cflag optlist[9]
179#define aflag optlist[10]
180#define bflag optlist[11]
181#define uflag optlist[12]
182#define viflag optlist[13]
183#if DEBUG
184#define nolog optlist[14]
185#define debug optlist[15]
186#endif
187
188 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000189 /*
190 * Sigmode records the current value of the signal handlers for the various
191 * modes. A value of zero means that the current handler is not known.
192 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
193 */
194 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000195#define S_DFL 1 /* default signal handling (SIG_DFL) */
196#define S_CATCH 2 /* signal is caught */
197#define S_IGN 3 /* signal is ignored (SIG_IGN) */
198#define S_HARD_IGN 4 /* signal is ignored permenantly */
199#define S_RESET 5 /* temporary - to reset a hard ignored sig */
200
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /* indicates specified signal received */
202 char gotsig[NSIG - 1];
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000203 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000204
205 /* Rarely referenced stuff */
206#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000207 /* Random number generators */
208 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
209 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000210#endif
211 pid_t backgndpid; /* pid of last background process */
212 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000213};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000214extern struct globals_misc *const ash_ptr_to_globals_misc;
215#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000216#define rootpid (G_misc.rootpid )
217#define shlvl (G_misc.shlvl )
218#define minusc (G_misc.minusc )
219#define curdir (G_misc.curdir )
220#define physdir (G_misc.physdir )
221#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000222#define exception_handler (G_misc.exception_handler)
223#define exception (G_misc.exception )
224#define suppressint (G_misc.suppressint )
225#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000226//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000227#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000228#define isloginsh (G_misc.isloginsh )
229#define nullstr (G_misc.nullstr )
230#define optlist (G_misc.optlist )
231#define sigmode (G_misc.sigmode )
232#define gotsig (G_misc.gotsig )
233#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000234#define random_galois_LFSR (G_misc.random_galois_LFSR)
235#define random_LCG (G_misc.random_LCG )
236#define backgndpid (G_misc.backgndpid )
237#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000238#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000239 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
240 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000241 curdir = nullstr; \
242 physdir = nullstr; \
243} while (0)
244
245
246/* ============ Interrupts / exceptions */
247
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000248/*
Eric Andersen2870d962001-07-02 17:27:21 +0000249 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000250 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000251 * much more efficient and portable. (But hacking the kernel is so much
252 * more fun than worrying about efficiency and portability. :-))
253 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000254#define INT_OFF do { \
255 suppressint++; \
256 xbarrier(); \
257} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000258
259/*
260 * Called to raise an exception. Since C doesn't include exceptions, we
261 * just do a longjmp to the exception handler. The type of exception is
262 * stored in the global variable "exception".
263 */
264static void raise_exception(int) ATTRIBUTE_NORETURN;
265static void
266raise_exception(int e)
267{
268#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000269 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000270 abort();
271#endif
272 INT_OFF;
273 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000274 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000275}
276
277/*
278 * Called from trap.c when a SIGINT is received. (If the user specifies
279 * that SIGINT is to be trapped or ignored using the trap builtin, then
280 * this routine is not called.) Suppressint is nonzero when interrupts
281 * are held using the INT_OFF macro. (The test for iflag is just
282 * defensive programming.)
283 */
284static void raise_interrupt(void) ATTRIBUTE_NORETURN;
285static void
286raise_interrupt(void)
287{
288 int i;
289
290 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000291 /* Signal is not automatically unmasked after it is raised,
292 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000293 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000294 /* pendingsig = 0; - now done in onsig() */
295
Denis Vlasenkob012b102007-02-19 22:43:01 +0000296 i = EXSIG;
297 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
298 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000299 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000300 signal(SIGINT, SIG_DFL);
301 raise(SIGINT);
302 }
303 i = EXINT;
304 }
305 raise_exception(i);
306 /* NOTREACHED */
307}
308
309#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000310static void
311int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000312{
313 if (--suppressint == 0 && intpending) {
314 raise_interrupt();
315 }
316}
317#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000318static void
319force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000320{
321 suppressint = 0;
322 if (intpending)
323 raise_interrupt();
324}
325#define FORCE_INT_ON force_int_on()
326#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000327#define INT_ON do { \
328 xbarrier(); \
329 if (--suppressint == 0 && intpending) \
330 raise_interrupt(); \
331} while (0)
332#define FORCE_INT_ON do { \
333 xbarrier(); \
334 suppressint = 0; \
335 if (intpending) \
336 raise_interrupt(); \
337} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000338#endif /* ASH_OPTIMIZE_FOR_SIZE */
339
340#define SAVE_INT(v) ((v) = suppressint)
341
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000342#define RESTORE_INT(v) do { \
343 xbarrier(); \
344 suppressint = (v); \
345 if (suppressint == 0 && intpending) \
346 raise_interrupt(); \
347} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000348
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000349/*
350 * Ignore a signal. Only one usage site - in forkchild()
351 */
352static void
353ignoresig(int signo)
354{
355 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
356 signal(signo, SIG_IGN);
357 }
358 sigmode[signo - 1] = S_HARD_IGN;
359}
360
361/*
362 * Signal handler. Only one usage site - in setsignal()
363 */
364static void
365onsig(int signo)
366{
367 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000368 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000369
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000370 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000371 if (!suppressint) {
372 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000373 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000374 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000375 intpending = 1;
376 }
377}
378
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000379
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000380/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000381
Eric Andersenc470f442003-07-28 09:56:35 +0000382static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000383outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000384{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000385 INT_OFF;
386 fputs(p, file);
387 INT_ON;
388}
389
390static void
391flush_stdout_stderr(void)
392{
393 INT_OFF;
394 fflush(stdout);
395 fflush(stderr);
396 INT_ON;
397}
398
399static void
400flush_stderr(void)
401{
402 INT_OFF;
403 fflush(stderr);
404 INT_ON;
405}
406
407static void
408outcslow(int c, FILE *dest)
409{
410 INT_OFF;
411 putc(c, dest);
412 fflush(dest);
413 INT_ON;
414}
415
416static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
417static int
418out1fmt(const char *fmt, ...)
419{
420 va_list ap;
421 int r;
422
423 INT_OFF;
424 va_start(ap, fmt);
425 r = vprintf(fmt, ap);
426 va_end(ap);
427 INT_ON;
428 return r;
429}
430
431static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
432static int
433fmtstr(char *outbuf, size_t length, const char *fmt, ...)
434{
435 va_list ap;
436 int ret;
437
438 va_start(ap, fmt);
439 INT_OFF;
440 ret = vsnprintf(outbuf, length, fmt, ap);
441 va_end(ap);
442 INT_ON;
443 return ret;
444}
445
446static void
447out1str(const char *p)
448{
449 outstr(p, stdout);
450}
451
452static void
453out2str(const char *p)
454{
455 outstr(p, stderr);
456 flush_stderr();
457}
458
459
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000460/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000461
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000462/* control characters in argument strings */
463#define CTLESC '\201' /* escape next character */
464#define CTLVAR '\202' /* variable defn */
465#define CTLENDVAR '\203'
466#define CTLBACKQ '\204'
467#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
468/* CTLBACKQ | CTLQUOTE == '\205' */
469#define CTLARI '\206' /* arithmetic expression */
470#define CTLENDARI '\207'
471#define CTLQUOTEMARK '\210'
472
473/* variable substitution byte (follows CTLVAR) */
474#define VSTYPE 0x0f /* type of variable substitution */
475#define VSNUL 0x10 /* colon--treat the empty string as unset */
476#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
477
478/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000479#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
480#define VSMINUS 0x2 /* ${var-text} */
481#define VSPLUS 0x3 /* ${var+text} */
482#define VSQUESTION 0x4 /* ${var?message} */
483#define VSASSIGN 0x5 /* ${var=text} */
484#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
485#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
486#define VSTRIMLEFT 0x8 /* ${var#pattern} */
487#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
488#define VSLENGTH 0xa /* ${#var} */
489#if ENABLE_ASH_BASH_COMPAT
490#define VSSUBSTR 0xc /* ${var:position:length} */
491#define VSREPLACE 0xd /* ${var/pattern/replacement} */
492#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
493#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000494
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000495static const char dolatstr[] ALIGN1 = {
496 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
497};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000498
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000499#define NCMD 0
500#define NPIPE 1
501#define NREDIR 2
502#define NBACKGND 3
503#define NSUBSHELL 4
504#define NAND 5
505#define NOR 6
506#define NSEMI 7
507#define NIF 8
508#define NWHILE 9
509#define NUNTIL 10
510#define NFOR 11
511#define NCASE 12
512#define NCLIST 13
513#define NDEFUN 14
514#define NARG 15
515#define NTO 16
516#define NCLOBBER 17
517#define NFROM 18
518#define NFROMTO 19
519#define NAPPEND 20
520#define NTOFD 21
521#define NFROMFD 22
522#define NHERE 23
523#define NXHERE 24
524#define NNOT 25
525
526union node;
527
528struct ncmd {
529 int type;
530 union node *assign;
531 union node *args;
532 union node *redirect;
533};
534
535struct npipe {
536 int type;
537 int backgnd;
538 struct nodelist *cmdlist;
539};
540
541struct nredir {
542 int type;
543 union node *n;
544 union node *redirect;
545};
546
547struct nbinary {
548 int type;
549 union node *ch1;
550 union node *ch2;
551};
552
553struct nif {
554 int type;
555 union node *test;
556 union node *ifpart;
557 union node *elsepart;
558};
559
560struct nfor {
561 int type;
562 union node *args;
563 union node *body;
564 char *var;
565};
566
567struct ncase {
568 int type;
569 union node *expr;
570 union node *cases;
571};
572
573struct nclist {
574 int type;
575 union node *next;
576 union node *pattern;
577 union node *body;
578};
579
580struct narg {
581 int type;
582 union node *next;
583 char *text;
584 struct nodelist *backquote;
585};
586
587struct nfile {
588 int type;
589 union node *next;
590 int fd;
591 union node *fname;
592 char *expfname;
593};
594
595struct ndup {
596 int type;
597 union node *next;
598 int fd;
599 int dupfd;
600 union node *vname;
601};
602
603struct nhere {
604 int type;
605 union node *next;
606 int fd;
607 union node *doc;
608};
609
610struct nnot {
611 int type;
612 union node *com;
613};
614
615union node {
616 int type;
617 struct ncmd ncmd;
618 struct npipe npipe;
619 struct nredir nredir;
620 struct nbinary nbinary;
621 struct nif nif;
622 struct nfor nfor;
623 struct ncase ncase;
624 struct nclist nclist;
625 struct narg narg;
626 struct nfile nfile;
627 struct ndup ndup;
628 struct nhere nhere;
629 struct nnot nnot;
630};
631
632struct nodelist {
633 struct nodelist *next;
634 union node *n;
635};
636
637struct funcnode {
638 int count;
639 union node n;
640};
641
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000642/*
643 * Free a parse tree.
644 */
645static void
646freefunc(struct funcnode *f)
647{
648 if (f && --f->count < 0)
649 free(f);
650}
651
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000652
653/* ============ Debugging output */
654
655#if DEBUG
656
657static FILE *tracefile;
658
659static void
660trace_printf(const char *fmt, ...)
661{
662 va_list va;
663
664 if (debug != 1)
665 return;
666 va_start(va, fmt);
667 vfprintf(tracefile, fmt, va);
668 va_end(va);
669}
670
671static void
672trace_vprintf(const char *fmt, va_list va)
673{
674 if (debug != 1)
675 return;
676 vfprintf(tracefile, fmt, va);
677}
678
679static void
680trace_puts(const char *s)
681{
682 if (debug != 1)
683 return;
684 fputs(s, tracefile);
685}
686
687static void
688trace_puts_quoted(char *s)
689{
690 char *p;
691 char c;
692
693 if (debug != 1)
694 return;
695 putc('"', tracefile);
696 for (p = s; *p; p++) {
697 switch (*p) {
698 case '\n': c = 'n'; goto backslash;
699 case '\t': c = 't'; goto backslash;
700 case '\r': c = 'r'; goto backslash;
701 case '"': c = '"'; goto backslash;
702 case '\\': c = '\\'; goto backslash;
703 case CTLESC: c = 'e'; goto backslash;
704 case CTLVAR: c = 'v'; goto backslash;
705 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
706 case CTLBACKQ: c = 'q'; goto backslash;
707 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
708 backslash:
709 putc('\\', tracefile);
710 putc(c, tracefile);
711 break;
712 default:
713 if (*p >= ' ' && *p <= '~')
714 putc(*p, tracefile);
715 else {
716 putc('\\', tracefile);
717 putc(*p >> 6 & 03, tracefile);
718 putc(*p >> 3 & 07, tracefile);
719 putc(*p & 07, tracefile);
720 }
721 break;
722 }
723 }
724 putc('"', tracefile);
725}
726
727static void
728trace_puts_args(char **ap)
729{
730 if (debug != 1)
731 return;
732 if (!*ap)
733 return;
734 while (1) {
735 trace_puts_quoted(*ap);
736 if (!*++ap) {
737 putc('\n', tracefile);
738 break;
739 }
740 putc(' ', tracefile);
741 }
742}
743
744static void
745opentrace(void)
746{
747 char s[100];
748#ifdef O_APPEND
749 int flags;
750#endif
751
752 if (debug != 1) {
753 if (tracefile)
754 fflush(tracefile);
755 /* leave open because libedit might be using it */
756 return;
757 }
758 strcpy(s, "./trace");
759 if (tracefile) {
760 if (!freopen(s, "a", tracefile)) {
761 fprintf(stderr, "Can't re-open %s\n", s);
762 debug = 0;
763 return;
764 }
765 } else {
766 tracefile = fopen(s, "a");
767 if (tracefile == NULL) {
768 fprintf(stderr, "Can't open %s\n", s);
769 debug = 0;
770 return;
771 }
772 }
773#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000774 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000775 if (flags >= 0)
776 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
777#endif
778 setlinebuf(tracefile);
779 fputs("\nTracing started.\n", tracefile);
780}
781
782static void
783indent(int amount, char *pfx, FILE *fp)
784{
785 int i;
786
787 for (i = 0; i < amount; i++) {
788 if (pfx && i == amount - 1)
789 fputs(pfx, fp);
790 putc('\t', fp);
791 }
792}
793
794/* little circular references here... */
795static void shtree(union node *n, int ind, char *pfx, FILE *fp);
796
797static void
798sharg(union node *arg, FILE *fp)
799{
800 char *p;
801 struct nodelist *bqlist;
802 int subtype;
803
804 if (arg->type != NARG) {
805 out1fmt("<node type %d>\n", arg->type);
806 abort();
807 }
808 bqlist = arg->narg.backquote;
809 for (p = arg->narg.text; *p; p++) {
810 switch (*p) {
811 case CTLESC:
812 putc(*++p, fp);
813 break;
814 case CTLVAR:
815 putc('$', fp);
816 putc('{', fp);
817 subtype = *++p;
818 if (subtype == VSLENGTH)
819 putc('#', fp);
820
821 while (*p != '=')
822 putc(*p++, fp);
823
824 if (subtype & VSNUL)
825 putc(':', fp);
826
827 switch (subtype & VSTYPE) {
828 case VSNORMAL:
829 putc('}', fp);
830 break;
831 case VSMINUS:
832 putc('-', fp);
833 break;
834 case VSPLUS:
835 putc('+', fp);
836 break;
837 case VSQUESTION:
838 putc('?', fp);
839 break;
840 case VSASSIGN:
841 putc('=', fp);
842 break;
843 case VSTRIMLEFT:
844 putc('#', fp);
845 break;
846 case VSTRIMLEFTMAX:
847 putc('#', fp);
848 putc('#', fp);
849 break;
850 case VSTRIMRIGHT:
851 putc('%', fp);
852 break;
853 case VSTRIMRIGHTMAX:
854 putc('%', fp);
855 putc('%', fp);
856 break;
857 case VSLENGTH:
858 break;
859 default:
860 out1fmt("<subtype %d>", subtype);
861 }
862 break;
863 case CTLENDVAR:
864 putc('}', fp);
865 break;
866 case CTLBACKQ:
867 case CTLBACKQ|CTLQUOTE:
868 putc('$', fp);
869 putc('(', fp);
870 shtree(bqlist->n, -1, NULL, fp);
871 putc(')', fp);
872 break;
873 default:
874 putc(*p, fp);
875 break;
876 }
877 }
878}
879
880static void
881shcmd(union node *cmd, FILE *fp)
882{
883 union node *np;
884 int first;
885 const char *s;
886 int dftfd;
887
888 first = 1;
889 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000890 if (!first)
891 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000892 sharg(np, fp);
893 first = 0;
894 }
895 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000896 if (!first)
897 putc(' ', fp);
898 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000899 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000900 case NTO: s = ">>"+1; dftfd = 1; break;
901 case NCLOBBER: s = ">|"; dftfd = 1; break;
902 case NAPPEND: s = ">>"; dftfd = 1; break;
903 case NTOFD: s = ">&"; dftfd = 1; break;
904 case NFROM: s = "<"; break;
905 case NFROMFD: s = "<&"; break;
906 case NFROMTO: s = "<>"; break;
907 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000908 }
909 if (np->nfile.fd != dftfd)
910 fprintf(fp, "%d", np->nfile.fd);
911 fputs(s, fp);
912 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
913 fprintf(fp, "%d", np->ndup.dupfd);
914 } else {
915 sharg(np->nfile.fname, fp);
916 }
917 first = 0;
918 }
919}
920
921static void
922shtree(union node *n, int ind, char *pfx, FILE *fp)
923{
924 struct nodelist *lp;
925 const char *s;
926
927 if (n == NULL)
928 return;
929
930 indent(ind, pfx, fp);
931 switch (n->type) {
932 case NSEMI:
933 s = "; ";
934 goto binop;
935 case NAND:
936 s = " && ";
937 goto binop;
938 case NOR:
939 s = " || ";
940 binop:
941 shtree(n->nbinary.ch1, ind, NULL, fp);
942 /* if (ind < 0) */
943 fputs(s, fp);
944 shtree(n->nbinary.ch2, ind, NULL, fp);
945 break;
946 case NCMD:
947 shcmd(n, fp);
948 if (ind >= 0)
949 putc('\n', fp);
950 break;
951 case NPIPE:
952 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
953 shcmd(lp->n, fp);
954 if (lp->next)
955 fputs(" | ", fp);
956 }
957 if (n->npipe.backgnd)
958 fputs(" &", fp);
959 if (ind >= 0)
960 putc('\n', fp);
961 break;
962 default:
963 fprintf(fp, "<node type %d>", n->type);
964 if (ind >= 0)
965 putc('\n', fp);
966 break;
967 }
968}
969
970static void
971showtree(union node *n)
972{
973 trace_puts("showtree called\n");
974 shtree(n, 1, NULL, stdout);
975}
976
977#define TRACE(param) trace_printf param
978#define TRACEV(param) trace_vprintf param
979
980#else
981
982#define TRACE(param)
983#define TRACEV(param)
984
985#endif /* DEBUG */
986
987
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000988/* ============ Parser data */
989
990/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000991 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
992 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000993struct strlist {
994 struct strlist *next;
995 char *text;
996};
997
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000998struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000999
Denis Vlasenkob012b102007-02-19 22:43:01 +00001000struct strpush {
1001 struct strpush *prev; /* preceding string on stack */
1002 char *prevstring;
1003 int prevnleft;
1004#if ENABLE_ASH_ALIAS
1005 struct alias *ap; /* if push was associated with an alias */
1006#endif
1007 char *string; /* remember the string since it may change */
1008};
1009
1010struct parsefile {
1011 struct parsefile *prev; /* preceding file on stack */
1012 int linno; /* current line */
1013 int fd; /* file descriptor (or -1 if string) */
1014 int nleft; /* number of chars left in this line */
1015 int lleft; /* number of chars left in this buffer */
1016 char *nextc; /* next char in buffer */
1017 char *buf; /* input buffer */
1018 struct strpush *strpush; /* for pushing strings at this level */
1019 struct strpush basestrpush; /* so pushing one is fast */
1020};
1021
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001022static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001023static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001024static int startlinno; /* line # where last token started */
1025static char *commandname; /* currently executing command */
1026static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001027static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001028
1029
1030/* ============ Message printing */
1031
1032static void
1033ash_vmsg(const char *msg, va_list ap)
1034{
1035 fprintf(stderr, "%s: ", arg0);
1036 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001037 if (strcmp(arg0, commandname))
1038 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001039 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001040 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001041 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001042 vfprintf(stderr, msg, ap);
1043 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001044}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001045
1046/*
1047 * Exverror is called to raise the error exception. If the second argument
1048 * is not NULL then error prints an error message using printf style
1049 * formatting. It then raises the error exception.
1050 */
1051static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1052static void
1053ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001054{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001055#if DEBUG
1056 if (msg) {
1057 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1058 TRACEV((msg, ap));
1059 TRACE(("\") pid=%d\n", getpid()));
1060 } else
1061 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1062 if (msg)
1063#endif
1064 ash_vmsg(msg, ap);
1065
1066 flush_stdout_stderr();
1067 raise_exception(cond);
1068 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001069}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001070
1071static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1072static void
1073ash_msg_and_raise_error(const char *msg, ...)
1074{
1075 va_list ap;
1076
1077 va_start(ap, msg);
1078 ash_vmsg_and_raise(EXERROR, msg, ap);
1079 /* NOTREACHED */
1080 va_end(ap);
1081}
1082
1083static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1084static void
1085ash_msg_and_raise(int cond, const char *msg, ...)
1086{
1087 va_list ap;
1088
1089 va_start(ap, msg);
1090 ash_vmsg_and_raise(cond, msg, ap);
1091 /* NOTREACHED */
1092 va_end(ap);
1093}
1094
1095/*
1096 * error/warning routines for external builtins
1097 */
1098static void
1099ash_msg(const char *fmt, ...)
1100{
1101 va_list ap;
1102
1103 va_start(ap, fmt);
1104 ash_vmsg(fmt, ap);
1105 va_end(ap);
1106}
1107
1108/*
1109 * Return a string describing an error. The returned string may be a
1110 * pointer to a static buffer that will be overwritten on the next call.
1111 * Action describes the operation that got the error.
1112 */
1113static const char *
1114errmsg(int e, const char *em)
1115{
1116 if (e == ENOENT || e == ENOTDIR) {
1117 return em;
1118 }
1119 return strerror(e);
1120}
1121
1122
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001123/* ============ Memory allocation */
1124
1125/*
1126 * It appears that grabstackstr() will barf with such alignments
1127 * because stalloc() will return a string allocated in a new stackblock.
1128 */
1129#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1130enum {
1131 /* Most machines require the value returned from malloc to be aligned
1132 * in some way. The following macro will get this right
1133 * on many machines. */
1134 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1135 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001136 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001137};
1138
1139struct stack_block {
1140 struct stack_block *prev;
1141 char space[MINSIZE];
1142};
1143
1144struct stackmark {
1145 struct stack_block *stackp;
1146 char *stacknxt;
1147 size_t stacknleft;
1148 struct stackmark *marknext;
1149};
1150
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001151
Denis Vlasenko01631112007-12-16 17:20:38 +00001152struct globals_memstack {
1153 struct stack_block *g_stackp; // = &stackbase;
1154 struct stackmark *markp;
1155 char *g_stacknxt; // = stackbase.space;
1156 char *sstrend; // = stackbase.space + MINSIZE;
1157 size_t g_stacknleft; // = MINSIZE;
1158 int herefd; // = -1;
1159 struct stack_block stackbase;
1160};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001161extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1162#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001163#define g_stackp (G_memstack.g_stackp )
1164#define markp (G_memstack.markp )
1165#define g_stacknxt (G_memstack.g_stacknxt )
1166#define sstrend (G_memstack.sstrend )
1167#define g_stacknleft (G_memstack.g_stacknleft)
1168#define herefd (G_memstack.herefd )
1169#define stackbase (G_memstack.stackbase )
1170#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001171 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1172 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001173 g_stackp = &stackbase; \
1174 g_stacknxt = stackbase.space; \
1175 g_stacknleft = MINSIZE; \
1176 sstrend = stackbase.space + MINSIZE; \
1177 herefd = -1; \
1178} while (0)
1179
1180#define stackblock() ((void *)g_stacknxt)
1181#define stackblocksize() g_stacknleft
1182
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001183
1184static void *
1185ckrealloc(void * p, size_t nbytes)
1186{
1187 p = realloc(p, nbytes);
1188 if (!p)
1189 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1190 return p;
1191}
1192
1193static void *
1194ckmalloc(size_t nbytes)
1195{
1196 return ckrealloc(NULL, nbytes);
1197}
1198
Denis Vlasenko597906c2008-02-20 16:38:54 +00001199static void *
1200ckzalloc(size_t nbytes)
1201{
1202 return memset(ckmalloc(nbytes), 0, nbytes);
1203}
1204
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001205/*
1206 * Make a copy of a string in safe storage.
1207 */
1208static char *
1209ckstrdup(const char *s)
1210{
1211 char *p = strdup(s);
1212 if (!p)
1213 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1214 return p;
1215}
1216
1217/*
1218 * Parse trees for commands are allocated in lifo order, so we use a stack
1219 * to make this more efficient, and also to avoid all sorts of exception
1220 * handling code to handle interrupts in the middle of a parse.
1221 *
1222 * The size 504 was chosen because the Ultrix malloc handles that size
1223 * well.
1224 */
1225static void *
1226stalloc(size_t nbytes)
1227{
1228 char *p;
1229 size_t aligned;
1230
1231 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001232 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001233 size_t len;
1234 size_t blocksize;
1235 struct stack_block *sp;
1236
1237 blocksize = aligned;
1238 if (blocksize < MINSIZE)
1239 blocksize = MINSIZE;
1240 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1241 if (len < blocksize)
1242 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1243 INT_OFF;
1244 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001245 sp->prev = g_stackp;
1246 g_stacknxt = sp->space;
1247 g_stacknleft = blocksize;
1248 sstrend = g_stacknxt + blocksize;
1249 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001250 INT_ON;
1251 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001252 p = g_stacknxt;
1253 g_stacknxt += aligned;
1254 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001255 return p;
1256}
1257
Denis Vlasenko597906c2008-02-20 16:38:54 +00001258static void *
1259stzalloc(size_t nbytes)
1260{
1261 return memset(stalloc(nbytes), 0, nbytes);
1262}
1263
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001264static void
1265stunalloc(void *p)
1266{
1267#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001268 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001269 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001270 abort();
1271 }
1272#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001273 g_stacknleft += g_stacknxt - (char *)p;
1274 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001275}
1276
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001277/*
1278 * Like strdup but works with the ash stack.
1279 */
1280static char *
1281ststrdup(const char *p)
1282{
1283 size_t len = strlen(p) + 1;
1284 return memcpy(stalloc(len), p, len);
1285}
1286
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001287static void
1288setstackmark(struct stackmark *mark)
1289{
Denis Vlasenko01631112007-12-16 17:20:38 +00001290 mark->stackp = g_stackp;
1291 mark->stacknxt = g_stacknxt;
1292 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001293 mark->marknext = markp;
1294 markp = mark;
1295}
1296
1297static void
1298popstackmark(struct stackmark *mark)
1299{
1300 struct stack_block *sp;
1301
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001302 if (!mark->stackp)
1303 return;
1304
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001305 INT_OFF;
1306 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001307 while (g_stackp != mark->stackp) {
1308 sp = g_stackp;
1309 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001310 free(sp);
1311 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001312 g_stacknxt = mark->stacknxt;
1313 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001314 sstrend = mark->stacknxt + mark->stacknleft;
1315 INT_ON;
1316}
1317
1318/*
1319 * When the parser reads in a string, it wants to stick the string on the
1320 * stack and only adjust the stack pointer when it knows how big the
1321 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1322 * of space on top of the stack and stackblocklen returns the length of
1323 * this block. Growstackblock will grow this space by at least one byte,
1324 * possibly moving it (like realloc). Grabstackblock actually allocates the
1325 * part of the block that has been used.
1326 */
1327static void
1328growstackblock(void)
1329{
1330 size_t newlen;
1331
Denis Vlasenko01631112007-12-16 17:20:38 +00001332 newlen = g_stacknleft * 2;
1333 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001334 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1335 if (newlen < 128)
1336 newlen += 128;
1337
Denis Vlasenko01631112007-12-16 17:20:38 +00001338 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001339 struct stack_block *oldstackp;
1340 struct stackmark *xmark;
1341 struct stack_block *sp;
1342 struct stack_block *prevstackp;
1343 size_t grosslen;
1344
1345 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001346 oldstackp = g_stackp;
1347 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348 prevstackp = sp->prev;
1349 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1350 sp = ckrealloc(sp, grosslen);
1351 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001352 g_stackp = sp;
1353 g_stacknxt = sp->space;
1354 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001355 sstrend = sp->space + newlen;
1356
1357 /*
1358 * Stack marks pointing to the start of the old block
1359 * must be relocated to point to the new block
1360 */
1361 xmark = markp;
1362 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001363 xmark->stackp = g_stackp;
1364 xmark->stacknxt = g_stacknxt;
1365 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001366 xmark = xmark->marknext;
1367 }
1368 INT_ON;
1369 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001370 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001371 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001372 char *p = stalloc(newlen);
1373
1374 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001375 g_stacknxt = memcpy(p, oldspace, oldlen);
1376 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001377 }
1378}
1379
1380static void
1381grabstackblock(size_t len)
1382{
1383 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001384 g_stacknxt += len;
1385 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001386}
1387
1388/*
1389 * The following routines are somewhat easier to use than the above.
1390 * The user declares a variable of type STACKSTR, which may be declared
1391 * to be a register. The macro STARTSTACKSTR initializes things. Then
1392 * the user uses the macro STPUTC to add characters to the string. In
1393 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1394 * grown as necessary. When the user is done, she can just leave the
1395 * string there and refer to it using stackblock(). Or she can allocate
1396 * the space for it using grabstackstr(). If it is necessary to allow
1397 * someone else to use the stack temporarily and then continue to grow
1398 * the string, the user should use grabstack to allocate the space, and
1399 * then call ungrabstr(p) to return to the previous mode of operation.
1400 *
1401 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1402 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1403 * is space for at least one character.
1404 */
1405static void *
1406growstackstr(void)
1407{
1408 size_t len = stackblocksize();
1409 if (herefd >= 0 && len >= 1024) {
1410 full_write(herefd, stackblock(), len);
1411 return stackblock();
1412 }
1413 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001414 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001415}
1416
1417/*
1418 * Called from CHECKSTRSPACE.
1419 */
1420static char *
1421makestrspace(size_t newlen, char *p)
1422{
Denis Vlasenko01631112007-12-16 17:20:38 +00001423 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001424 size_t size = stackblocksize();
1425
1426 for (;;) {
1427 size_t nleft;
1428
1429 size = stackblocksize();
1430 nleft = size - len;
1431 if (nleft >= newlen)
1432 break;
1433 growstackblock();
1434 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001435 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001436}
1437
1438static char *
1439stack_nputstr(const char *s, size_t n, char *p)
1440{
1441 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001442 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001443 return p;
1444}
1445
1446static char *
1447stack_putstr(const char *s, char *p)
1448{
1449 return stack_nputstr(s, strlen(s), p);
1450}
1451
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001452static char *
1453_STPUTC(int c, char *p)
1454{
1455 if (p == sstrend)
1456 p = growstackstr();
1457 *p++ = c;
1458 return p;
1459}
1460
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001461#define STARTSTACKSTR(p) ((p) = stackblock())
1462#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001463#define CHECKSTRSPACE(n, p) do { \
1464 char *q = (p); \
1465 size_t l = (n); \
1466 size_t m = sstrend - q; \
1467 if (l > m) \
1468 (p) = makestrspace(l, q); \
1469} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001470#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001471#define STACKSTRNUL(p) do { \
1472 if ((p) == sstrend) \
1473 (p) = growstackstr(); \
1474 *(p) = '\0'; \
1475} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001476#define STUNPUTC(p) (--(p))
1477#define STTOPC(p) ((p)[-1])
1478#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001479
1480#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001481#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001482#define stackstrend() ((void *)sstrend)
1483
1484
1485/* ============ String helpers */
1486
1487/*
1488 * prefix -- see if pfx is a prefix of string.
1489 */
1490static char *
1491prefix(const char *string, const char *pfx)
1492{
1493 while (*pfx) {
1494 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001495 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001496 }
1497 return (char *) string;
1498}
1499
1500/*
1501 * Check for a valid number. This should be elsewhere.
1502 */
1503static int
1504is_number(const char *p)
1505{
1506 do {
1507 if (!isdigit(*p))
1508 return 0;
1509 } while (*++p != '\0');
1510 return 1;
1511}
1512
1513/*
1514 * Convert a string of digits to an integer, printing an error message on
1515 * failure.
1516 */
1517static int
1518number(const char *s)
1519{
1520 if (!is_number(s))
1521 ash_msg_and_raise_error(illnum, s);
1522 return atoi(s);
1523}
1524
1525/*
1526 * Produce a possibly single quoted string suitable as input to the shell.
1527 * The return string is allocated on the stack.
1528 */
1529static char *
1530single_quote(const char *s)
1531{
1532 char *p;
1533
1534 STARTSTACKSTR(p);
1535
1536 do {
1537 char *q;
1538 size_t len;
1539
1540 len = strchrnul(s, '\'') - s;
1541
1542 q = p = makestrspace(len + 3, p);
1543
1544 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001545 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001546 *q++ = '\'';
1547 s += len;
1548
1549 STADJUST(q - p, p);
1550
1551 len = strspn(s, "'");
1552 if (!len)
1553 break;
1554
1555 q = p = makestrspace(len + 3, p);
1556
1557 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001558 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001559 *q++ = '"';
1560 s += len;
1561
1562 STADJUST(q - p, p);
1563 } while (*s);
1564
1565 USTPUTC(0, p);
1566
1567 return stackblock();
1568}
1569
1570
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001571/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001572
1573static char **argptr; /* argument list for builtin commands */
1574static char *optionarg; /* set by nextopt (like getopt) */
1575static char *optptr; /* used by nextopt */
1576
1577/*
1578 * XXX - should get rid of. have all builtins use getopt(3). the
1579 * library getopt must have the BSD extension static variable "optreset"
1580 * otherwise it can't be used within the shell safely.
1581 *
1582 * Standard option processing (a la getopt) for builtin routines. The
1583 * only argument that is passed to nextopt is the option string; the
1584 * other arguments are unnecessary. It return the character, or '\0' on
1585 * end of input.
1586 */
1587static int
1588nextopt(const char *optstring)
1589{
1590 char *p;
1591 const char *q;
1592 char c;
1593
1594 p = optptr;
1595 if (p == NULL || *p == '\0') {
1596 p = *argptr;
1597 if (p == NULL || *p != '-' || *++p == '\0')
1598 return '\0';
1599 argptr++;
1600 if (LONE_DASH(p)) /* check for "--" */
1601 return '\0';
1602 }
1603 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001604 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001605 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001606 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001607 if (*++q == ':')
1608 q++;
1609 }
1610 if (*++q == ':') {
1611 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001612 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001613 optionarg = p;
1614 p = NULL;
1615 }
1616 optptr = p;
1617 return c;
1618}
1619
1620
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001621/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001622
Denis Vlasenko01631112007-12-16 17:20:38 +00001623/*
1624 * The parsefile structure pointed to by the global variable parsefile
1625 * contains information about the current file being read.
1626 */
1627struct redirtab {
1628 struct redirtab *next;
1629 int renamed[10];
1630 int nullredirs;
1631};
1632
1633struct shparam {
1634 int nparam; /* # of positional parameters (without $0) */
1635#if ENABLE_ASH_GETOPTS
1636 int optind; /* next parameter to be processed by getopts */
1637 int optoff; /* used by getopts */
1638#endif
1639 unsigned char malloced; /* if parameter list dynamically allocated */
1640 char **p; /* parameter list */
1641};
1642
1643/*
1644 * Free the list of positional parameters.
1645 */
1646static void
1647freeparam(volatile struct shparam *param)
1648{
1649 char **ap;
1650
1651 if (param->malloced) {
1652 for (ap = param->p; *ap; ap++)
1653 free(*ap);
1654 free(param->p);
1655 }
1656}
1657
1658#if ENABLE_ASH_GETOPTS
1659static void getoptsreset(const char *value);
1660#endif
1661
1662struct var {
1663 struct var *next; /* next entry in hash list */
1664 int flags; /* flags are defined above */
1665 const char *text; /* name=value */
1666 void (*func)(const char *); /* function to be called when */
1667 /* the variable gets set/unset */
1668};
1669
1670struct localvar {
1671 struct localvar *next; /* next local variable in list */
1672 struct var *vp; /* the variable that was made local */
1673 int flags; /* saved flags */
1674 const char *text; /* saved text */
1675};
1676
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001677/* flags */
1678#define VEXPORT 0x01 /* variable is exported */
1679#define VREADONLY 0x02 /* variable cannot be modified */
1680#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1681#define VTEXTFIXED 0x08 /* text is statically allocated */
1682#define VSTACK 0x10 /* text is allocated on the stack */
1683#define VUNSET 0x20 /* the variable is not set */
1684#define VNOFUNC 0x40 /* don't call the callback function */
1685#define VNOSET 0x80 /* do not set variable - just readonly test */
1686#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001687#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001688# define VDYNAMIC 0x200 /* dynamic variable */
1689#else
1690# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001691#endif
1692
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001694static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001695#define defifs (defifsvar + 4)
1696#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001697static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001698#endif
1699
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001700
Denis Vlasenko01631112007-12-16 17:20:38 +00001701/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001702#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001703static void
1704change_lc_all(const char *value)
1705{
1706 if (value && *value != '\0')
1707 setlocale(LC_ALL, value);
1708}
1709static void
1710change_lc_ctype(const char *value)
1711{
1712 if (value && *value != '\0')
1713 setlocale(LC_CTYPE, value);
1714}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001715#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001716#if ENABLE_ASH_MAIL
1717static void chkmail(void);
1718static void changemail(const char *);
1719#endif
1720static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001721#if ENABLE_ASH_RANDOM_SUPPORT
1722static void change_random(const char *);
1723#endif
1724
Denis Vlasenko01631112007-12-16 17:20:38 +00001725static const struct {
1726 int flags;
1727 const char *text;
1728 void (*func)(const char *);
1729} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001730#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001731 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001733 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001736 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1737 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001739 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1740 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1741 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1742 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001744 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745#endif
1746#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001747 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
1749#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1751 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752#endif
1753#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
1756};
1757
Denis Vlasenko01631112007-12-16 17:20:38 +00001758
1759struct globals_var {
1760 struct shparam shellparam; /* $@ current positional parameters */
1761 struct redirtab *redirlist;
1762 int g_nullredirs;
1763 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1764 struct var *vartab[VTABSIZE];
1765 struct var varinit[ARRAY_SIZE(varinit_data)];
1766};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001767extern struct globals_var *const ash_ptr_to_globals_var;
1768#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001769#define shellparam (G_var.shellparam )
1770#define redirlist (G_var.redirlist )
1771#define g_nullredirs (G_var.g_nullredirs )
1772#define preverrout_fd (G_var.preverrout_fd)
1773#define vartab (G_var.vartab )
1774#define varinit (G_var.varinit )
1775#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001776 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001777 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1778 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001779 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1780 varinit[i].flags = varinit_data[i].flags; \
1781 varinit[i].text = varinit_data[i].text; \
1782 varinit[i].func = varinit_data[i].func; \
1783 } \
1784} while (0)
1785
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001786#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001787#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001788# define vmail (&vifs)[1]
1789# define vmpath (&vmail)[1]
1790# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001792# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001794#define vps1 (&vpath)[1]
1795#define vps2 (&vps1)[1]
1796#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001798# define voptind (&vps4)[1]
1799# if ENABLE_ASH_RANDOM_SUPPORT
1800# define vrandom (&voptind)[1]
1801# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001802#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001803# if ENABLE_ASH_RANDOM_SUPPORT
1804# define vrandom (&vps4)[1]
1805# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001806#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001807
1808/*
1809 * The following macros access the values of the above variables.
1810 * They have to skip over the name. They return the null string
1811 * for unset variables.
1812 */
1813#define ifsval() (vifs.text + 4)
1814#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001815#if ENABLE_ASH_MAIL
1816# define mailval() (vmail.text + 5)
1817# define mpathval() (vmpath.text + 9)
1818# define mpathset() ((vmpath.flags & VUNSET) == 0)
1819#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001820#define pathval() (vpath.text + 5)
1821#define ps1val() (vps1.text + 4)
1822#define ps2val() (vps2.text + 4)
1823#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001824#if ENABLE_ASH_GETOPTS
1825# define optindval() (voptind.text + 7)
1826#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001827
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001828
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001829#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1830#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1831
Denis Vlasenko01631112007-12-16 17:20:38 +00001832#if ENABLE_ASH_GETOPTS
1833static void
1834getoptsreset(const char *value)
1835{
1836 shellparam.optind = number(value);
1837 shellparam.optoff = -1;
1838}
1839#endif
1840
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001841/*
1842 * Return of a legal variable name (a letter or underscore followed by zero or
1843 * more letters, underscores, and digits).
1844 */
1845static char *
1846endofname(const char *name)
1847{
1848 char *p;
1849
1850 p = (char *) name;
1851 if (!is_name(*p))
1852 return p;
1853 while (*++p) {
1854 if (!is_in_name(*p))
1855 break;
1856 }
1857 return p;
1858}
1859
1860/*
1861 * Compares two strings up to the first = or '\0'. The first
1862 * string must be terminated by '='; the second may be terminated by
1863 * either '=' or '\0'.
1864 */
1865static int
1866varcmp(const char *p, const char *q)
1867{
1868 int c, d;
1869
1870 while ((c = *p) == (d = *q)) {
1871 if (!c || c == '=')
1872 goto out;
1873 p++;
1874 q++;
1875 }
1876 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001877 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001878 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001879 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001880 out:
1881 return c - d;
1882}
1883
1884static int
1885varequal(const char *a, const char *b)
1886{
1887 return !varcmp(a, b);
1888}
1889
1890/*
1891 * Find the appropriate entry in the hash table from the name.
1892 */
1893static struct var **
1894hashvar(const char *p)
1895{
1896 unsigned hashval;
1897
1898 hashval = ((unsigned char) *p) << 4;
1899 while (*p && *p != '=')
1900 hashval += (unsigned char) *p++;
1901 return &vartab[hashval % VTABSIZE];
1902}
1903
1904static int
1905vpcmp(const void *a, const void *b)
1906{
1907 return varcmp(*(const char **)a, *(const char **)b);
1908}
1909
1910/*
1911 * This routine initializes the builtin variables.
1912 */
1913static void
1914initvar(void)
1915{
1916 struct var *vp;
1917 struct var *end;
1918 struct var **vpp;
1919
1920 /*
1921 * PS1 depends on uid
1922 */
1923#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1924 vps1.text = "PS1=\\w \\$ ";
1925#else
1926 if (!geteuid())
1927 vps1.text = "PS1=# ";
1928#endif
1929 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001930 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001931 do {
1932 vpp = hashvar(vp->text);
1933 vp->next = *vpp;
1934 *vpp = vp;
1935 } while (++vp < end);
1936}
1937
1938static struct var **
1939findvar(struct var **vpp, const char *name)
1940{
1941 for (; *vpp; vpp = &(*vpp)->next) {
1942 if (varequal((*vpp)->text, name)) {
1943 break;
1944 }
1945 }
1946 return vpp;
1947}
1948
1949/*
1950 * Find the value of a variable. Returns NULL if not set.
1951 */
1952static char *
1953lookupvar(const char *name)
1954{
1955 struct var *v;
1956
1957 v = *findvar(hashvar(name), name);
1958 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001959#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960 /*
1961 * Dynamic variables are implemented roughly the same way they are
1962 * in bash. Namely, they're "special" so long as they aren't unset.
1963 * As soon as they're unset, they're no longer dynamic, and dynamic
1964 * lookup will no longer happen at that point. -- PFM.
1965 */
1966 if ((v->flags & VDYNAMIC))
1967 (*v->func)(NULL);
1968#endif
1969 if (!(v->flags & VUNSET))
1970 return strchrnul(v->text, '=') + 1;
1971 }
1972 return NULL;
1973}
1974
1975/*
1976 * Search the environment of a builtin command.
1977 */
1978static char *
1979bltinlookup(const char *name)
1980{
1981 struct strlist *sp;
1982
1983 for (sp = cmdenviron; sp; sp = sp->next) {
1984 if (varequal(sp->text, name))
1985 return strchrnul(sp->text, '=') + 1;
1986 }
1987 return lookupvar(name);
1988}
1989
1990/*
1991 * Same as setvar except that the variable and value are passed in
1992 * the first argument as name=value. Since the first argument will
1993 * be actually stored in the table, it should not be a string that
1994 * will go away.
1995 * Called with interrupts off.
1996 */
1997static void
1998setvareq(char *s, int flags)
1999{
2000 struct var *vp, **vpp;
2001
2002 vpp = hashvar(s);
2003 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2004 vp = *findvar(vpp, s);
2005 if (vp) {
2006 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2007 const char *n;
2008
2009 if (flags & VNOSAVE)
2010 free(s);
2011 n = vp->text;
2012 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2013 }
2014
2015 if (flags & VNOSET)
2016 return;
2017
2018 if (vp->func && (flags & VNOFUNC) == 0)
2019 (*vp->func)(strchrnul(s, '=') + 1);
2020
2021 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2022 free((char*)vp->text);
2023
2024 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2025 } else {
2026 if (flags & VNOSET)
2027 return;
2028 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002029 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002030 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002031 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002032 *vpp = vp;
2033 }
2034 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2035 s = ckstrdup(s);
2036 vp->text = s;
2037 vp->flags = flags;
2038}
2039
2040/*
2041 * Set the value of a variable. The flags argument is ored with the
2042 * flags of the variable. If val is NULL, the variable is unset.
2043 */
2044static void
2045setvar(const char *name, const char *val, int flags)
2046{
2047 char *p, *q;
2048 size_t namelen;
2049 char *nameeq;
2050 size_t vallen;
2051
2052 q = endofname(name);
2053 p = strchrnul(q, '=');
2054 namelen = p - name;
2055 if (!namelen || p != q)
2056 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2057 vallen = 0;
2058 if (val == NULL) {
2059 flags |= VUNSET;
2060 } else {
2061 vallen = strlen(val);
2062 }
2063 INT_OFF;
2064 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002065 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002066 if (val) {
2067 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002068 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002069 }
2070 *p = '\0';
2071 setvareq(nameeq, flags | VNOSAVE);
2072 INT_ON;
2073}
2074
2075#if ENABLE_ASH_GETOPTS
2076/*
2077 * Safe version of setvar, returns 1 on success 0 on failure.
2078 */
2079static int
2080setvarsafe(const char *name, const char *val, int flags)
2081{
2082 int err;
2083 volatile int saveint;
2084 struct jmploc *volatile savehandler = exception_handler;
2085 struct jmploc jmploc;
2086
2087 SAVE_INT(saveint);
2088 if (setjmp(jmploc.loc))
2089 err = 1;
2090 else {
2091 exception_handler = &jmploc;
2092 setvar(name, val, flags);
2093 err = 0;
2094 }
2095 exception_handler = savehandler;
2096 RESTORE_INT(saveint);
2097 return err;
2098}
2099#endif
2100
2101/*
2102 * Unset the specified variable.
2103 */
2104static int
2105unsetvar(const char *s)
2106{
2107 struct var **vpp;
2108 struct var *vp;
2109 int retval;
2110
2111 vpp = findvar(hashvar(s), s);
2112 vp = *vpp;
2113 retval = 2;
2114 if (vp) {
2115 int flags = vp->flags;
2116
2117 retval = 1;
2118 if (flags & VREADONLY)
2119 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002120#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121 vp->flags &= ~VDYNAMIC;
2122#endif
2123 if (flags & VUNSET)
2124 goto ok;
2125 if ((flags & VSTRFIXED) == 0) {
2126 INT_OFF;
2127 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2128 free((char*)vp->text);
2129 *vpp = vp->next;
2130 free(vp);
2131 INT_ON;
2132 } else {
2133 setvar(s, 0, 0);
2134 vp->flags &= ~VEXPORT;
2135 }
2136 ok:
2137 retval = 0;
2138 }
2139 out:
2140 return retval;
2141}
2142
2143/*
2144 * Process a linked list of variable assignments.
2145 */
2146static void
2147listsetvar(struct strlist *list_set_var, int flags)
2148{
2149 struct strlist *lp = list_set_var;
2150
2151 if (!lp)
2152 return;
2153 INT_OFF;
2154 do {
2155 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002156 lp = lp->next;
2157 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002158 INT_ON;
2159}
2160
2161/*
2162 * Generate a list of variables satisfying the given conditions.
2163 */
2164static char **
2165listvars(int on, int off, char ***end)
2166{
2167 struct var **vpp;
2168 struct var *vp;
2169 char **ep;
2170 int mask;
2171
2172 STARTSTACKSTR(ep);
2173 vpp = vartab;
2174 mask = on | off;
2175 do {
2176 for (vp = *vpp; vp; vp = vp->next) {
2177 if ((vp->flags & mask) == on) {
2178 if (ep == stackstrend())
2179 ep = growstackstr();
2180 *ep++ = (char *) vp->text;
2181 }
2182 }
2183 } while (++vpp < vartab + VTABSIZE);
2184 if (ep == stackstrend())
2185 ep = growstackstr();
2186 if (end)
2187 *end = ep;
2188 *ep++ = NULL;
2189 return grabstackstr(ep);
2190}
2191
2192
2193/* ============ Path search helper
2194 *
2195 * The variable path (passed by reference) should be set to the start
2196 * of the path before the first call; padvance will update
2197 * this value as it proceeds. Successive calls to padvance will return
2198 * the possible path expansions in sequence. If an option (indicated by
2199 * a percent sign) appears in the path entry then the global variable
2200 * pathopt will be set to point to it; otherwise pathopt will be set to
2201 * NULL.
2202 */
2203static const char *pathopt; /* set by padvance */
2204
2205static char *
2206padvance(const char **path, const char *name)
2207{
2208 const char *p;
2209 char *q;
2210 const char *start;
2211 size_t len;
2212
2213 if (*path == NULL)
2214 return NULL;
2215 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002216 for (p = start; *p && *p != ':' && *p != '%'; p++)
2217 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002218 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2219 while (stackblocksize() < len)
2220 growstackblock();
2221 q = stackblock();
2222 if (p != start) {
2223 memcpy(q, start, p - start);
2224 q += p - start;
2225 *q++ = '/';
2226 }
2227 strcpy(q, name);
2228 pathopt = NULL;
2229 if (*p == '%') {
2230 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002231 while (*p && *p != ':')
2232 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002233 }
2234 if (*p == ':')
2235 *path = p + 1;
2236 else
2237 *path = NULL;
2238 return stalloc(len);
2239}
2240
2241
2242/* ============ Prompt */
2243
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002244static smallint doprompt; /* if set, prompt the user */
2245static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002246
2247#if ENABLE_FEATURE_EDITING
2248static line_input_t *line_input_state;
2249static const char *cmdedit_prompt;
2250static void
2251putprompt(const char *s)
2252{
2253 if (ENABLE_ASH_EXPAND_PRMT) {
2254 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002255 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256 return;
2257 }
2258 cmdedit_prompt = s;
2259}
2260#else
2261static void
2262putprompt(const char *s)
2263{
2264 out2str(s);
2265}
2266#endif
2267
2268#if ENABLE_ASH_EXPAND_PRMT
2269/* expandstr() needs parsing machinery, so it is far away ahead... */
2270static const char *expandstr(const char *ps);
2271#else
2272#define expandstr(s) s
2273#endif
2274
2275static void
2276setprompt(int whichprompt)
2277{
2278 const char *prompt;
2279#if ENABLE_ASH_EXPAND_PRMT
2280 struct stackmark smark;
2281#endif
2282
2283 needprompt = 0;
2284
2285 switch (whichprompt) {
2286 case 1:
2287 prompt = ps1val();
2288 break;
2289 case 2:
2290 prompt = ps2val();
2291 break;
2292 default: /* 0 */
2293 prompt = nullstr;
2294 }
2295#if ENABLE_ASH_EXPAND_PRMT
2296 setstackmark(&smark);
2297 stalloc(stackblocksize());
2298#endif
2299 putprompt(expandstr(prompt));
2300#if ENABLE_ASH_EXPAND_PRMT
2301 popstackmark(&smark);
2302#endif
2303}
2304
2305
2306/* ============ The cd and pwd commands */
2307
2308#define CD_PHYSICAL 1
2309#define CD_PRINT 2
2310
2311static int docd(const char *, int);
2312
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002313static int
2314cdopt(void)
2315{
2316 int flags = 0;
2317 int i, j;
2318
2319 j = 'L';
2320 while ((i = nextopt("LP"))) {
2321 if (i != j) {
2322 flags ^= CD_PHYSICAL;
2323 j = i;
2324 }
2325 }
2326
2327 return flags;
2328}
2329
2330/*
2331 * Update curdir (the name of the current directory) in response to a
2332 * cd command.
2333 */
2334static const char *
2335updatepwd(const char *dir)
2336{
2337 char *new;
2338 char *p;
2339 char *cdcomppath;
2340 const char *lim;
2341
2342 cdcomppath = ststrdup(dir);
2343 STARTSTACKSTR(new);
2344 if (*dir != '/') {
2345 if (curdir == nullstr)
2346 return 0;
2347 new = stack_putstr(curdir, new);
2348 }
2349 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002350 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002351 if (*dir != '/') {
2352 if (new[-1] != '/')
2353 USTPUTC('/', new);
2354 if (new > lim && *lim == '/')
2355 lim++;
2356 } else {
2357 USTPUTC('/', new);
2358 cdcomppath++;
2359 if (dir[1] == '/' && dir[2] != '/') {
2360 USTPUTC('/', new);
2361 cdcomppath++;
2362 lim++;
2363 }
2364 }
2365 p = strtok(cdcomppath, "/");
2366 while (p) {
2367 switch (*p) {
2368 case '.':
2369 if (p[1] == '.' && p[2] == '\0') {
2370 while (new > lim) {
2371 STUNPUTC(new);
2372 if (new[-1] == '/')
2373 break;
2374 }
2375 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002376 }
2377 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378 break;
2379 /* fall through */
2380 default:
2381 new = stack_putstr(p, new);
2382 USTPUTC('/', new);
2383 }
2384 p = strtok(0, "/");
2385 }
2386 if (new > lim)
2387 STUNPUTC(new);
2388 *new = 0;
2389 return stackblock();
2390}
2391
2392/*
2393 * Find out what the current directory is. If we already know the current
2394 * directory, this routine returns immediately.
2395 */
2396static char *
2397getpwd(void)
2398{
Denis Vlasenko01631112007-12-16 17:20:38 +00002399 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400 return dir ? dir : nullstr;
2401}
2402
2403static void
2404setpwd(const char *val, int setold)
2405{
2406 char *oldcur, *dir;
2407
2408 oldcur = dir = curdir;
2409
2410 if (setold) {
2411 setvar("OLDPWD", oldcur, VEXPORT);
2412 }
2413 INT_OFF;
2414 if (physdir != nullstr) {
2415 if (physdir != oldcur)
2416 free(physdir);
2417 physdir = nullstr;
2418 }
2419 if (oldcur == val || !val) {
2420 char *s = getpwd();
2421 physdir = s;
2422 if (!val)
2423 dir = s;
2424 } else
2425 dir = ckstrdup(val);
2426 if (oldcur != dir && oldcur != nullstr) {
2427 free(oldcur);
2428 }
2429 curdir = dir;
2430 INT_ON;
2431 setvar("PWD", dir, VEXPORT);
2432}
2433
2434static void hashcd(void);
2435
2436/*
2437 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2438 * know that the current directory has changed.
2439 */
2440static int
2441docd(const char *dest, int flags)
2442{
2443 const char *dir = 0;
2444 int err;
2445
2446 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2447
2448 INT_OFF;
2449 if (!(flags & CD_PHYSICAL)) {
2450 dir = updatepwd(dest);
2451 if (dir)
2452 dest = dir;
2453 }
2454 err = chdir(dest);
2455 if (err)
2456 goto out;
2457 setpwd(dir, 1);
2458 hashcd();
2459 out:
2460 INT_ON;
2461 return err;
2462}
2463
2464static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002465cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002466{
2467 const char *dest;
2468 const char *path;
2469 const char *p;
2470 char c;
2471 struct stat statb;
2472 int flags;
2473
2474 flags = cdopt();
2475 dest = *argptr;
2476 if (!dest)
2477 dest = bltinlookup(homestr);
2478 else if (LONE_DASH(dest)) {
2479 dest = bltinlookup("OLDPWD");
2480 flags |= CD_PRINT;
2481 }
2482 if (!dest)
2483 dest = nullstr;
2484 if (*dest == '/')
2485 goto step7;
2486 if (*dest == '.') {
2487 c = dest[1];
2488 dotdot:
2489 switch (c) {
2490 case '\0':
2491 case '/':
2492 goto step6;
2493 case '.':
2494 c = dest[2];
2495 if (c != '.')
2496 goto dotdot;
2497 }
2498 }
2499 if (!*dest)
2500 dest = ".";
2501 path = bltinlookup("CDPATH");
2502 if (!path) {
2503 step6:
2504 step7:
2505 p = dest;
2506 goto docd;
2507 }
2508 do {
2509 c = *path;
2510 p = padvance(&path, dest);
2511 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2512 if (c && c != ':')
2513 flags |= CD_PRINT;
2514 docd:
2515 if (!docd(p, flags))
2516 goto out;
2517 break;
2518 }
2519 } while (path);
2520 ash_msg_and_raise_error("can't cd to %s", dest);
2521 /* NOTREACHED */
2522 out:
2523 if (flags & CD_PRINT)
2524 out1fmt(snlfmt, curdir);
2525 return 0;
2526}
2527
2528static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002529pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002530{
2531 int flags;
2532 const char *dir = curdir;
2533
2534 flags = cdopt();
2535 if (flags) {
2536 if (physdir == nullstr)
2537 setpwd(dir, 0);
2538 dir = physdir;
2539 }
2540 out1fmt(snlfmt, dir);
2541 return 0;
2542}
2543
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002544
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002545/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002546
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002547#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002548#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002549
Eric Andersenc470f442003-07-28 09:56:35 +00002550/* Syntax classes */
2551#define CWORD 0 /* character is nothing special */
2552#define CNL 1 /* newline character */
2553#define CBACK 2 /* a backslash character */
2554#define CSQUOTE 3 /* single quote */
2555#define CDQUOTE 4 /* double quote */
2556#define CENDQUOTE 5 /* a terminating quote */
2557#define CBQUOTE 6 /* backwards single quote */
2558#define CVAR 7 /* a dollar sign */
2559#define CENDVAR 8 /* a '}' character */
2560#define CLP 9 /* a left paren in arithmetic */
2561#define CRP 10 /* a right paren in arithmetic */
2562#define CENDFILE 11 /* end of file */
2563#define CCTL 12 /* like CWORD, except it must be escaped */
2564#define CSPCL 13 /* these terminate a word */
2565#define CIGN 14 /* character should be ignored */
2566
Denis Vlasenko131ae172007-02-18 13:00:19 +00002567#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002568#define SYNBASE 130
2569#define PEOF -130
2570#define PEOA -129
2571#define PEOA_OR_PEOF PEOA
2572#else
2573#define SYNBASE 129
2574#define PEOF -129
2575#define PEOA_OR_PEOF PEOF
2576#endif
2577
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002578/* number syntax index */
2579#define BASESYNTAX 0 /* not in quotes */
2580#define DQSYNTAX 1 /* in double quotes */
2581#define SQSYNTAX 2 /* in single quotes */
2582#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002583#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002584
Denis Vlasenko131ae172007-02-18 13:00:19 +00002585#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002586#define USE_SIT_FUNCTION
2587#endif
2588
Denis Vlasenko131ae172007-02-18 13:00:19 +00002589#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002590static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002591#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002592 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002593#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002594 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2595 { CNL, CNL, CNL, CNL }, /* 2, \n */
2596 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2597 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2598 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2599 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2600 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2601 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2602 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2603 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2604 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002605#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2607 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2608 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002609#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002610};
Eric Andersenc470f442003-07-28 09:56:35 +00002611#else
2612static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002613#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002614 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002615#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002616 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2617 { CNL, CNL, CNL }, /* 2, \n */
2618 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2619 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2620 { CVAR, CVAR, CWORD }, /* 5, $ */
2621 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2622 { CSPCL, CWORD, CWORD }, /* 7, ( */
2623 { CSPCL, CWORD, CWORD }, /* 8, ) */
2624 { CBACK, CBACK, CCTL }, /* 9, \ */
2625 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2626 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002627#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002628 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2629 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2630 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002631#endif
2632};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002633#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002634
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002635#ifdef USE_SIT_FUNCTION
2636
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002637static int
2638SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002639{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002640 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002641#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002642 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002643 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2644 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2645 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2646 11, 3 /* "}~" */
2647 };
2648#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002649 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002650 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2651 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2652 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2653 10, 2 /* "}~" */
2654 };
2655#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656 const char *s;
2657 int indx;
2658
Eric Andersenc470f442003-07-28 09:56:35 +00002659 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002660 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002661#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002662 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002663 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002664 else
2665#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002666#define U_C(c) ((unsigned char)(c))
2667
2668 if ((unsigned char)c >= (unsigned char)(CTLESC)
2669 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2670 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002671 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002672 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002673 s = strchrnul(spec_symbls, c);
2674 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002676 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002677 }
2678 return S_I_T[indx][syntax];
2679}
2680
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002681#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002682
Denis Vlasenko131ae172007-02-18 13:00:19 +00002683#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002684#define CSPCL_CIGN_CIGN_CIGN 0
2685#define CSPCL_CWORD_CWORD_CWORD 1
2686#define CNL_CNL_CNL_CNL 2
2687#define CWORD_CCTL_CCTL_CWORD 3
2688#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2689#define CVAR_CVAR_CWORD_CVAR 5
2690#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2691#define CSPCL_CWORD_CWORD_CLP 7
2692#define CSPCL_CWORD_CWORD_CRP 8
2693#define CBACK_CBACK_CCTL_CBACK 9
2694#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2695#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2696#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2697#define CWORD_CWORD_CWORD_CWORD 13
2698#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002699#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002700#define CSPCL_CWORD_CWORD_CWORD 0
2701#define CNL_CNL_CNL_CNL 1
2702#define CWORD_CCTL_CCTL_CWORD 2
2703#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2704#define CVAR_CVAR_CWORD_CVAR 4
2705#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2706#define CSPCL_CWORD_CWORD_CLP 6
2707#define CSPCL_CWORD_CWORD_CRP 7
2708#define CBACK_CBACK_CCTL_CBACK 8
2709#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2710#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2711#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2712#define CWORD_CWORD_CWORD_CWORD 12
2713#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002714#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002715
2716static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002717 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002718 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002719#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002720 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2721#endif
2722 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2724 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2725 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2726 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2727 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2728 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2729 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2730 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002731 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2860 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2861 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2883 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002884 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002885 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2887 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002889 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002890 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2891 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2892 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2893 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2895 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2896 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2897 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2898 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2909 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2911 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2912 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2913 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2914 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2942 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2943 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2944 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2947 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2975 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2976 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2977 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002978};
2979
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002980#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2981
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002982#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002983
Eric Andersen2870d962001-07-02 17:27:21 +00002984
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002985/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002986
Denis Vlasenko131ae172007-02-18 13:00:19 +00002987#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002988
2989#define ALIASINUSE 1
2990#define ALIASDEAD 2
2991
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002992struct alias {
2993 struct alias *next;
2994 char *name;
2995 char *val;
2996 int flag;
2997};
2998
Denis Vlasenko01631112007-12-16 17:20:38 +00002999
3000static struct alias **atab; // [ATABSIZE];
3001#define INIT_G_alias() do { \
3002 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3003} while (0)
3004
Eric Andersen2870d962001-07-02 17:27:21 +00003005
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003006static struct alias **
3007__lookupalias(const char *name) {
3008 unsigned int hashval;
3009 struct alias **app;
3010 const char *p;
3011 unsigned int ch;
3012
3013 p = name;
3014
3015 ch = (unsigned char)*p;
3016 hashval = ch << 4;
3017 while (ch) {
3018 hashval += ch;
3019 ch = (unsigned char)*++p;
3020 }
3021 app = &atab[hashval % ATABSIZE];
3022
3023 for (; *app; app = &(*app)->next) {
3024 if (strcmp(name, (*app)->name) == 0) {
3025 break;
3026 }
3027 }
3028
3029 return app;
3030}
3031
3032static struct alias *
3033lookupalias(const char *name, int check)
3034{
3035 struct alias *ap = *__lookupalias(name);
3036
3037 if (check && ap && (ap->flag & ALIASINUSE))
3038 return NULL;
3039 return ap;
3040}
3041
3042static struct alias *
3043freealias(struct alias *ap)
3044{
3045 struct alias *next;
3046
3047 if (ap->flag & ALIASINUSE) {
3048 ap->flag |= ALIASDEAD;
3049 return ap;
3050 }
3051
3052 next = ap->next;
3053 free(ap->name);
3054 free(ap->val);
3055 free(ap);
3056 return next;
3057}
Eric Andersencb57d552001-06-28 07:25:16 +00003058
Eric Andersenc470f442003-07-28 09:56:35 +00003059static void
3060setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003061{
3062 struct alias *ap, **app;
3063
3064 app = __lookupalias(name);
3065 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003066 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003067 if (ap) {
3068 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003069 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003070 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003071 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003072 ap->flag &= ~ALIASDEAD;
3073 } else {
3074 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003075 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003076 ap->name = ckstrdup(name);
3077 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003078 /*ap->flag = 0; - ckzalloc did it */
3079 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003080 *app = ap;
3081 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003082 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003083}
3084
Eric Andersenc470f442003-07-28 09:56:35 +00003085static int
3086unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003087{
Eric Andersencb57d552001-06-28 07:25:16 +00003088 struct alias **app;
3089
3090 app = __lookupalias(name);
3091
3092 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003093 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003094 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003095 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003096 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003097 }
3098
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003099 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003100}
3101
Eric Andersenc470f442003-07-28 09:56:35 +00003102static void
3103rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003104{
Eric Andersencb57d552001-06-28 07:25:16 +00003105 struct alias *ap, **app;
3106 int i;
3107
Denis Vlasenkob012b102007-02-19 22:43:01 +00003108 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003109 for (i = 0; i < ATABSIZE; i++) {
3110 app = &atab[i];
3111 for (ap = *app; ap; ap = *app) {
3112 *app = freealias(*app);
3113 if (ap == *app) {
3114 app = &ap->next;
3115 }
3116 }
3117 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003119}
3120
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003121static void
3122printalias(const struct alias *ap)
3123{
3124 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3125}
3126
Eric Andersencb57d552001-06-28 07:25:16 +00003127/*
3128 * TODO - sort output
3129 */
Eric Andersenc470f442003-07-28 09:56:35 +00003130static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003131aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003132{
3133 char *n, *v;
3134 int ret = 0;
3135 struct alias *ap;
3136
Denis Vlasenko68404f12008-03-17 09:00:54 +00003137 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003138 int i;
3139
Denis Vlasenko68404f12008-03-17 09:00:54 +00003140 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003141 for (ap = atab[i]; ap; ap = ap->next) {
3142 printalias(ap);
3143 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003144 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003145 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003146 }
3147 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003148 v = strchr(n+1, '=');
3149 if (v == NULL) { /* n+1: funny ksh stuff */
3150 ap = *__lookupalias(n);
3151 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003152 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003153 ret = 1;
3154 } else
3155 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003156 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003157 *v++ = '\0';
3158 setalias(n, v);
3159 }
3160 }
3161
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003162 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003163}
3164
Eric Andersenc470f442003-07-28 09:56:35 +00003165static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003166unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003167{
3168 int i;
3169
3170 while ((i = nextopt("a")) != '\0') {
3171 if (i == 'a') {
3172 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003173 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003174 }
3175 }
3176 for (i = 0; *argptr; argptr++) {
3177 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003178 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003179 i = 1;
3180 }
3181 }
3182
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003183 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003184}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003185
Denis Vlasenko131ae172007-02-18 13:00:19 +00003186#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003187
Eric Andersenc470f442003-07-28 09:56:35 +00003188
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003189/* ============ jobs.c */
3190
3191/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3192#define FORK_FG 0
3193#define FORK_BG 1
3194#define FORK_NOJOB 2
3195
3196/* mode flags for showjob(s) */
3197#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3198#define SHOW_PID 0x04 /* include process pid */
3199#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3200
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003201/*
3202 * A job structure contains information about a job. A job is either a
3203 * single process or a set of processes contained in a pipeline. In the
3204 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3205 * array of pids.
3206 */
3207
3208struct procstat {
3209 pid_t pid; /* process id */
3210 int status; /* last process status from wait() */
3211 char *cmd; /* text of command being run */
3212};
3213
3214struct job {
3215 struct procstat ps0; /* status of process */
3216 struct procstat *ps; /* status or processes when more than one */
3217#if JOBS
3218 int stopstatus; /* status of a stopped job */
3219#endif
3220 uint32_t
3221 nprocs: 16, /* number of processes */
3222 state: 8,
3223#define JOBRUNNING 0 /* at least one proc running */
3224#define JOBSTOPPED 1 /* all procs are stopped */
3225#define JOBDONE 2 /* all procs are completed */
3226#if JOBS
3227 sigint: 1, /* job was killed by SIGINT */
3228 jobctl: 1, /* job running under job control */
3229#endif
3230 waited: 1, /* true if this entry has been waited for */
3231 used: 1, /* true if this entry is in used */
3232 changed: 1; /* true if status has changed */
3233 struct job *prev_job; /* previous job */
3234};
3235
Denis Vlasenko68404f12008-03-17 09:00:54 +00003236static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003237#if !JOBS
3238#define forkshell(job, node, mode) forkshell(job, mode)
3239#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003240static int forkshell(struct job *, union node *, int);
3241static int waitforjob(struct job *);
3242
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003243#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003244enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003245#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003246#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003247static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003248static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003249#endif
3250
3251/*
3252 * Set the signal handler for the specified signal. The routine figures
3253 * out what it should be set to.
3254 */
3255static void
3256setsignal(int signo)
3257{
3258 int action;
3259 char *t, tsig;
3260 struct sigaction act;
3261
3262 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003263 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003264 if (t == NULL)
3265 action = S_DFL;
3266 else if (*t != '\0')
3267 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003268 if (rootshell && action == S_DFL) {
3269 switch (signo) {
3270 case SIGINT:
3271 if (iflag || minusc || sflag == 0)
3272 action = S_CATCH;
3273 break;
3274 case SIGQUIT:
3275#if DEBUG
3276 if (debug)
3277 break;
3278#endif
3279 /* FALLTHROUGH */
3280 case SIGTERM:
3281 if (iflag)
3282 action = S_IGN;
3283 break;
3284#if JOBS
3285 case SIGTSTP:
3286 case SIGTTOU:
3287 if (mflag)
3288 action = S_IGN;
3289 break;
3290#endif
3291 }
3292 }
3293
3294 t = &sigmode[signo - 1];
3295 tsig = *t;
3296 if (tsig == 0) {
3297 /*
3298 * current setting unknown
3299 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003300 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003301 /*
3302 * Pretend it worked; maybe we should give a warning
3303 * here, but other shells don't. We don't alter
3304 * sigmode, so that we retry every time.
3305 */
3306 return;
3307 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003308 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003310 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003311 if (mflag
3312 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3313 ) {
3314 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003315 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003316 }
3317 }
3318 if (tsig == S_HARD_IGN || tsig == action)
3319 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003320 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321 switch (action) {
3322 case S_CATCH:
3323 act.sa_handler = onsig;
3324 break;
3325 case S_IGN:
3326 act.sa_handler = SIG_IGN;
3327 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003328 }
3329 *t = action;
3330 act.sa_flags = 0;
3331 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003332 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003333}
3334
3335/* mode flags for set_curjob */
3336#define CUR_DELETE 2
3337#define CUR_RUNNING 1
3338#define CUR_STOPPED 0
3339
3340/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003341#define DOWAIT_NONBLOCK WNOHANG
3342#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003343
3344#if JOBS
3345/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003346static int initialpgrp; //references:2
3347static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348#endif
3349/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003350static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003351/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003352static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003354static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003355/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003356static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357
3358static void
3359set_curjob(struct job *jp, unsigned mode)
3360{
3361 struct job *jp1;
3362 struct job **jpp, **curp;
3363
3364 /* first remove from list */
3365 jpp = curp = &curjob;
3366 do {
3367 jp1 = *jpp;
3368 if (jp1 == jp)
3369 break;
3370 jpp = &jp1->prev_job;
3371 } while (1);
3372 *jpp = jp1->prev_job;
3373
3374 /* Then re-insert in correct position */
3375 jpp = curp;
3376 switch (mode) {
3377 default:
3378#if DEBUG
3379 abort();
3380#endif
3381 case CUR_DELETE:
3382 /* job being deleted */
3383 break;
3384 case CUR_RUNNING:
3385 /* newly created job or backgrounded job,
3386 put after all stopped jobs. */
3387 do {
3388 jp1 = *jpp;
3389#if JOBS
3390 if (!jp1 || jp1->state != JOBSTOPPED)
3391#endif
3392 break;
3393 jpp = &jp1->prev_job;
3394 } while (1);
3395 /* FALLTHROUGH */
3396#if JOBS
3397 case CUR_STOPPED:
3398#endif
3399 /* newly stopped job - becomes curjob */
3400 jp->prev_job = *jpp;
3401 *jpp = jp;
3402 break;
3403 }
3404}
3405
3406#if JOBS || DEBUG
3407static int
3408jobno(const struct job *jp)
3409{
3410 return jp - jobtab + 1;
3411}
3412#endif
3413
3414/*
3415 * Convert a job name to a job structure.
3416 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003417#if !JOBS
3418#define getjob(name, getctl) getjob(name)
3419#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420static struct job *
3421getjob(const char *name, int getctl)
3422{
3423 struct job *jp;
3424 struct job *found;
3425 const char *err_msg = "No such job: %s";
3426 unsigned num;
3427 int c;
3428 const char *p;
3429 char *(*match)(const char *, const char *);
3430
3431 jp = curjob;
3432 p = name;
3433 if (!p)
3434 goto currentjob;
3435
3436 if (*p != '%')
3437 goto err;
3438
3439 c = *++p;
3440 if (!c)
3441 goto currentjob;
3442
3443 if (!p[1]) {
3444 if (c == '+' || c == '%') {
3445 currentjob:
3446 err_msg = "No current job";
3447 goto check;
3448 }
3449 if (c == '-') {
3450 if (jp)
3451 jp = jp->prev_job;
3452 err_msg = "No previous job";
3453 check:
3454 if (!jp)
3455 goto err;
3456 goto gotit;
3457 }
3458 }
3459
3460 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003461// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003462 num = atoi(p);
3463 if (num < njobs) {
3464 jp = jobtab + num - 1;
3465 if (jp->used)
3466 goto gotit;
3467 goto err;
3468 }
3469 }
3470
3471 match = prefix;
3472 if (*p == '?') {
3473 match = strstr;
3474 p++;
3475 }
3476
3477 found = 0;
3478 while (1) {
3479 if (!jp)
3480 goto err;
3481 if (match(jp->ps[0].cmd, p)) {
3482 if (found)
3483 goto err;
3484 found = jp;
3485 err_msg = "%s: ambiguous";
3486 }
3487 jp = jp->prev_job;
3488 }
3489
3490 gotit:
3491#if JOBS
3492 err_msg = "job %s not created under job control";
3493 if (getctl && jp->jobctl == 0)
3494 goto err;
3495#endif
3496 return jp;
3497 err:
3498 ash_msg_and_raise_error(err_msg, name);
3499}
3500
3501/*
3502 * Mark a job structure as unused.
3503 */
3504static void
3505freejob(struct job *jp)
3506{
3507 struct procstat *ps;
3508 int i;
3509
3510 INT_OFF;
3511 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3512 if (ps->cmd != nullstr)
3513 free(ps->cmd);
3514 }
3515 if (jp->ps != &jp->ps0)
3516 free(jp->ps);
3517 jp->used = 0;
3518 set_curjob(jp, CUR_DELETE);
3519 INT_ON;
3520}
3521
3522#if JOBS
3523static void
3524xtcsetpgrp(int fd, pid_t pgrp)
3525{
3526 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003527 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003528}
3529
3530/*
3531 * Turn job control on and off.
3532 *
3533 * Note: This code assumes that the third arg to ioctl is a character
3534 * pointer, which is true on Berkeley systems but not System V. Since
3535 * System V doesn't have job control yet, this isn't a problem now.
3536 *
3537 * Called with interrupts off.
3538 */
3539static void
3540setjobctl(int on)
3541{
3542 int fd;
3543 int pgrp;
3544
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003545 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003546 return;
3547 if (on) {
3548 int ofd;
3549 ofd = fd = open(_PATH_TTY, O_RDWR);
3550 if (fd < 0) {
3551 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3552 * That sometimes helps to acquire controlling tty.
3553 * Obviously, a workaround for bugs when someone
3554 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003555 fd = 2;
3556 while (!isatty(fd))
3557 if (--fd < 0)
3558 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003559 }
3560 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003561 if (ofd >= 0)
3562 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003563 if (fd < 0)
3564 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003565 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003566 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003567 do { /* while we are in the background */
3568 pgrp = tcgetpgrp(fd);
3569 if (pgrp < 0) {
3570 out:
3571 ash_msg("can't access tty; job control turned off");
3572 mflag = on = 0;
3573 goto close;
3574 }
3575 if (pgrp == getpgrp())
3576 break;
3577 killpg(0, SIGTTIN);
3578 } while (1);
3579 initialpgrp = pgrp;
3580
3581 setsignal(SIGTSTP);
3582 setsignal(SIGTTOU);
3583 setsignal(SIGTTIN);
3584 pgrp = rootpid;
3585 setpgid(0, pgrp);
3586 xtcsetpgrp(fd, pgrp);
3587 } else {
3588 /* turning job control off */
3589 fd = ttyfd;
3590 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003591 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003592 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003593 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003594 setpgid(0, pgrp);
3595 setsignal(SIGTSTP);
3596 setsignal(SIGTTOU);
3597 setsignal(SIGTTIN);
3598 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003599 if (fd >= 0)
3600 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003601 fd = -1;
3602 }
3603 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003604 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003605}
3606
3607static int
3608killcmd(int argc, char **argv)
3609{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003610 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003611 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003612 do {
3613 if (argv[i][0] == '%') {
3614 struct job *jp = getjob(argv[i], 0);
3615 unsigned pid = jp->ps[0].pid;
3616 /* Enough space for ' -NNN<nul>' */
3617 argv[i] = alloca(sizeof(int)*3 + 3);
3618 /* kill_main has matching code to expect
3619 * leading space. Needed to not confuse
3620 * negative pids with "kill -SIGNAL_NO" syntax */
3621 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003622 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003623 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003624 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003625 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003626}
3627
3628static void
3629showpipe(struct job *jp, FILE *out)
3630{
3631 struct procstat *sp;
3632 struct procstat *spend;
3633
3634 spend = jp->ps + jp->nprocs;
3635 for (sp = jp->ps + 1; sp < spend; sp++)
3636 fprintf(out, " | %s", sp->cmd);
3637 outcslow('\n', out);
3638 flush_stdout_stderr();
3639}
3640
3641
3642static int
3643restartjob(struct job *jp, int mode)
3644{
3645 struct procstat *ps;
3646 int i;
3647 int status;
3648 pid_t pgid;
3649
3650 INT_OFF;
3651 if (jp->state == JOBDONE)
3652 goto out;
3653 jp->state = JOBRUNNING;
3654 pgid = jp->ps->pid;
3655 if (mode == FORK_FG)
3656 xtcsetpgrp(ttyfd, pgid);
3657 killpg(pgid, SIGCONT);
3658 ps = jp->ps;
3659 i = jp->nprocs;
3660 do {
3661 if (WIFSTOPPED(ps->status)) {
3662 ps->status = -1;
3663 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003664 ps++;
3665 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003666 out:
3667 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3668 INT_ON;
3669 return status;
3670}
3671
3672static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003673fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003674{
3675 struct job *jp;
3676 FILE *out;
3677 int mode;
3678 int retval;
3679
3680 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3681 nextopt(nullstr);
3682 argv = argptr;
3683 out = stdout;
3684 do {
3685 jp = getjob(*argv, 1);
3686 if (mode == FORK_BG) {
3687 set_curjob(jp, CUR_RUNNING);
3688 fprintf(out, "[%d] ", jobno(jp));
3689 }
3690 outstr(jp->ps->cmd, out);
3691 showpipe(jp, out);
3692 retval = restartjob(jp, mode);
3693 } while (*argv && *++argv);
3694 return retval;
3695}
3696#endif
3697
3698static int
3699sprint_status(char *s, int status, int sigonly)
3700{
3701 int col;
3702 int st;
3703
3704 col = 0;
3705 if (!WIFEXITED(status)) {
3706#if JOBS
3707 if (WIFSTOPPED(status))
3708 st = WSTOPSIG(status);
3709 else
3710#endif
3711 st = WTERMSIG(status);
3712 if (sigonly) {
3713 if (st == SIGINT || st == SIGPIPE)
3714 goto out;
3715#if JOBS
3716 if (WIFSTOPPED(status))
3717 goto out;
3718#endif
3719 }
3720 st &= 0x7f;
3721 col = fmtstr(s, 32, strsignal(st));
3722 if (WCOREDUMP(status)) {
3723 col += fmtstr(s + col, 16, " (core dumped)");
3724 }
3725 } else if (!sigonly) {
3726 st = WEXITSTATUS(status);
3727 if (st)
3728 col = fmtstr(s, 16, "Done(%d)", st);
3729 else
3730 col = fmtstr(s, 16, "Done");
3731 }
3732 out:
3733 return col;
3734}
3735
3736/*
3737 * Do a wait system call. If job control is compiled in, we accept
3738 * stopped processes. If block is zero, we return a value of zero
3739 * rather than blocking.
3740 *
3741 * System V doesn't have a non-blocking wait system call. It does
3742 * have a SIGCLD signal that is sent to a process when one of it's
3743 * children dies. The obvious way to use SIGCLD would be to install
3744 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3745 * was received, and have waitproc bump another counter when it got
3746 * the status of a process. Waitproc would then know that a wait
3747 * system call would not block if the two counters were different.
3748 * This approach doesn't work because if a process has children that
3749 * have not been waited for, System V will send it a SIGCLD when it
3750 * installs a signal handler for SIGCLD. What this means is that when
3751 * a child exits, the shell will be sent SIGCLD signals continuously
3752 * until is runs out of stack space, unless it does a wait call before
3753 * restoring the signal handler. The code below takes advantage of
3754 * this (mis)feature by installing a signal handler for SIGCLD and
3755 * then checking to see whether it was called. If there are any
3756 * children to be waited for, it will be.
3757 *
3758 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3759 * waits at all. In this case, the user will not be informed when
3760 * a background process until the next time she runs a real program
3761 * (as opposed to running a builtin command or just typing return),
3762 * and the jobs command may give out of date information.
3763 */
3764static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003765waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003767#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003768 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003769 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003771 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3772 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003773}
3774
3775/*
3776 * Wait for a process to terminate.
3777 */
3778static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003779dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780{
3781 int pid;
3782 int status;
3783 struct job *jp;
3784 struct job *thisjob;
3785 int state;
3786
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003787 TRACE(("dowait(%d) called\n", wait_flags));
3788 pid = waitproc(wait_flags, &status);
3789 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003790 if (pid <= 0) {
3791 /* If we were doing blocking wait and (probably) got EINTR,
3792 * check for pending sigs received while waiting.
3793 * (NB: can be moved into callers if needed) */
3794 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3795 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003797 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798 INT_OFF;
3799 thisjob = NULL;
3800 for (jp = curjob; jp; jp = jp->prev_job) {
3801 struct procstat *sp;
3802 struct procstat *spend;
3803 if (jp->state == JOBDONE)
3804 continue;
3805 state = JOBDONE;
3806 spend = jp->ps + jp->nprocs;
3807 sp = jp->ps;
3808 do {
3809 if (sp->pid == pid) {
3810 TRACE(("Job %d: changing status of proc %d "
3811 "from 0x%x to 0x%x\n",
3812 jobno(jp), pid, sp->status, status));
3813 sp->status = status;
3814 thisjob = jp;
3815 }
3816 if (sp->status == -1)
3817 state = JOBRUNNING;
3818#if JOBS
3819 if (state == JOBRUNNING)
3820 continue;
3821 if (WIFSTOPPED(sp->status)) {
3822 jp->stopstatus = sp->status;
3823 state = JOBSTOPPED;
3824 }
3825#endif
3826 } while (++sp < spend);
3827 if (thisjob)
3828 goto gotjob;
3829 }
3830#if JOBS
3831 if (!WIFSTOPPED(status))
3832#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003833 jobless--;
3834 goto out;
3835
3836 gotjob:
3837 if (state != JOBRUNNING) {
3838 thisjob->changed = 1;
3839
3840 if (thisjob->state != state) {
3841 TRACE(("Job %d: changing state from %d to %d\n",
3842 jobno(thisjob), thisjob->state, state));
3843 thisjob->state = state;
3844#if JOBS
3845 if (state == JOBSTOPPED) {
3846 set_curjob(thisjob, CUR_STOPPED);
3847 }
3848#endif
3849 }
3850 }
3851
3852 out:
3853 INT_ON;
3854
3855 if (thisjob && thisjob == job) {
3856 char s[48 + 1];
3857 int len;
3858
3859 len = sprint_status(s, status, 1);
3860 if (len) {
3861 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003862 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003863 out2str(s);
3864 }
3865 }
3866 return pid;
3867}
3868
3869#if JOBS
3870static void
3871showjob(FILE *out, struct job *jp, int mode)
3872{
3873 struct procstat *ps;
3874 struct procstat *psend;
3875 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003876 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003877 char s[80];
3878
3879 ps = jp->ps;
3880
3881 if (mode & SHOW_PGID) {
3882 /* just output process (group) id of pipeline */
3883 fprintf(out, "%d\n", ps->pid);
3884 return;
3885 }
3886
3887 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003888 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889
3890 if (jp == curjob)
3891 s[col - 2] = '+';
3892 else if (curjob && jp == curjob->prev_job)
3893 s[col - 2] = '-';
3894
3895 if (mode & SHOW_PID)
3896 col += fmtstr(s + col, 16, "%d ", ps->pid);
3897
3898 psend = ps + jp->nprocs;
3899
3900 if (jp->state == JOBRUNNING) {
3901 strcpy(s + col, "Running");
3902 col += sizeof("Running") - 1;
3903 } else {
3904 int status = psend[-1].status;
3905 if (jp->state == JOBSTOPPED)
3906 status = jp->stopstatus;
3907 col += sprint_status(s + col, status, 0);
3908 }
3909
3910 goto start;
3911
3912 do {
3913 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003914 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003915 start:
3916 fprintf(out, "%s%*c%s",
3917 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3918 );
3919 if (!(mode & SHOW_PID)) {
3920 showpipe(jp, out);
3921 break;
3922 }
3923 if (++ps == psend) {
3924 outcslow('\n', out);
3925 break;
3926 }
3927 } while (1);
3928
3929 jp->changed = 0;
3930
3931 if (jp->state == JOBDONE) {
3932 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3933 freejob(jp);
3934 }
3935}
3936
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003937/*
3938 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3939 * statuses have changed since the last call to showjobs.
3940 */
3941static void
3942showjobs(FILE *out, int mode)
3943{
3944 struct job *jp;
3945
3946 TRACE(("showjobs(%x) called\n", mode));
3947
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003948 /* If not even one job changed, there is nothing to do */
3949 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003950 continue;
3951
3952 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003953 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003954 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003955 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003956 }
3957}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003958
3959static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003960jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003961{
3962 int mode, m;
3963
3964 mode = 0;
3965 while ((m = nextopt("lp"))) {
3966 if (m == 'l')
3967 mode = SHOW_PID;
3968 else
3969 mode = SHOW_PGID;
3970 }
3971
3972 argv = argptr;
3973 if (*argv) {
3974 do
3975 showjob(stdout, getjob(*argv,0), mode);
3976 while (*++argv);
3977 } else
3978 showjobs(stdout, mode);
3979
3980 return 0;
3981}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003982#endif /* JOBS */
3983
3984static int
3985getstatus(struct job *job)
3986{
3987 int status;
3988 int retval;
3989
3990 status = job->ps[job->nprocs - 1].status;
3991 retval = WEXITSTATUS(status);
3992 if (!WIFEXITED(status)) {
3993#if JOBS
3994 retval = WSTOPSIG(status);
3995 if (!WIFSTOPPED(status))
3996#endif
3997 {
3998 /* XXX: limits number of signals */
3999 retval = WTERMSIG(status);
4000#if JOBS
4001 if (retval == SIGINT)
4002 job->sigint = 1;
4003#endif
4004 }
4005 retval += 128;
4006 }
4007 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4008 jobno(job), job->nprocs, status, retval));
4009 return retval;
4010}
4011
4012static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004013waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004014{
4015 struct job *job;
4016 int retval;
4017 struct job *jp;
4018
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004019// exsig++;
4020// xbarrier();
4021 if (pendingsig)
4022 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004023
4024 nextopt(nullstr);
4025 retval = 0;
4026
4027 argv = argptr;
4028 if (!*argv) {
4029 /* wait for all jobs */
4030 for (;;) {
4031 jp = curjob;
4032 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004033 if (!jp) /* no running procs */
4034 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035 if (jp->state == JOBRUNNING)
4036 break;
4037 jp->waited = 1;
4038 jp = jp->prev_job;
4039 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004040 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041 }
4042 }
4043
4044 retval = 127;
4045 do {
4046 if (**argv != '%') {
4047 pid_t pid = number(*argv);
4048 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004049 while (1) {
4050 if (!job)
4051 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 if (job->ps[job->nprocs - 1].pid == pid)
4053 break;
4054 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004055 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004056 } else
4057 job = getjob(*argv, 0);
4058 /* loop until process terminated or stopped */
4059 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004060 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 job->waited = 1;
4062 retval = getstatus(job);
4063 repeat:
4064 ;
4065 } while (*++argv);
4066
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004067 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 return retval;
4069}
4070
4071static struct job *
4072growjobtab(void)
4073{
4074 size_t len;
4075 ptrdiff_t offset;
4076 struct job *jp, *jq;
4077
4078 len = njobs * sizeof(*jp);
4079 jq = jobtab;
4080 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4081
4082 offset = (char *)jp - (char *)jq;
4083 if (offset) {
4084 /* Relocate pointers */
4085 size_t l = len;
4086
4087 jq = (struct job *)((char *)jq + l);
4088 while (l) {
4089 l -= sizeof(*jp);
4090 jq--;
4091#define joff(p) ((struct job *)((char *)(p) + l))
4092#define jmove(p) (p) = (void *)((char *)(p) + offset)
4093 if (joff(jp)->ps == &jq->ps0)
4094 jmove(joff(jp)->ps);
4095 if (joff(jp)->prev_job)
4096 jmove(joff(jp)->prev_job);
4097 }
4098 if (curjob)
4099 jmove(curjob);
4100#undef joff
4101#undef jmove
4102 }
4103
4104 njobs += 4;
4105 jobtab = jp;
4106 jp = (struct job *)((char *)jp + len);
4107 jq = jp + 3;
4108 do {
4109 jq->used = 0;
4110 } while (--jq >= jp);
4111 return jp;
4112}
4113
4114/*
4115 * Return a new job structure.
4116 * Called with interrupts off.
4117 */
4118static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004119makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004120{
4121 int i;
4122 struct job *jp;
4123
4124 for (i = njobs, jp = jobtab; ; jp++) {
4125 if (--i < 0) {
4126 jp = growjobtab();
4127 break;
4128 }
4129 if (jp->used == 0)
4130 break;
4131 if (jp->state != JOBDONE || !jp->waited)
4132 continue;
4133#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004134 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004135 continue;
4136#endif
4137 freejob(jp);
4138 break;
4139 }
4140 memset(jp, 0, sizeof(*jp));
4141#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004142 /* jp->jobctl is a bitfield.
4143 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004144 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004145 jp->jobctl = 1;
4146#endif
4147 jp->prev_job = curjob;
4148 curjob = jp;
4149 jp->used = 1;
4150 jp->ps = &jp->ps0;
4151 if (nprocs > 1) {
4152 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4153 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004154 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155 jobno(jp)));
4156 return jp;
4157}
4158
4159#if JOBS
4160/*
4161 * Return a string identifying a command (to be printed by the
4162 * jobs command).
4163 */
4164static char *cmdnextc;
4165
4166static void
4167cmdputs(const char *s)
4168{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004169 static const char vstype[VSTYPE + 1][3] = {
4170 "", "}", "-", "+", "?", "=",
4171 "%", "%%", "#", "##"
4172 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4173 };
4174
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 const char *p, *str;
4176 char c, cc[2] = " ";
4177 char *nextc;
4178 int subtype = 0;
4179 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004180
4181 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4182 p = s;
4183 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004184 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004185 switch (c) {
4186 case CTLESC:
4187 c = *p++;
4188 break;
4189 case CTLVAR:
4190 subtype = *p++;
4191 if ((subtype & VSTYPE) == VSLENGTH)
4192 str = "${#";
4193 else
4194 str = "${";
4195 if (!(subtype & VSQUOTE) == !(quoted & 1))
4196 goto dostr;
4197 quoted ^= 1;
4198 c = '"';
4199 break;
4200 case CTLENDVAR:
4201 str = "\"}" + !(quoted & 1);
4202 quoted >>= 1;
4203 subtype = 0;
4204 goto dostr;
4205 case CTLBACKQ:
4206 str = "$(...)";
4207 goto dostr;
4208 case CTLBACKQ+CTLQUOTE:
4209 str = "\"$(...)\"";
4210 goto dostr;
4211#if ENABLE_ASH_MATH_SUPPORT
4212 case CTLARI:
4213 str = "$((";
4214 goto dostr;
4215 case CTLENDARI:
4216 str = "))";
4217 goto dostr;
4218#endif
4219 case CTLQUOTEMARK:
4220 quoted ^= 1;
4221 c = '"';
4222 break;
4223 case '=':
4224 if (subtype == 0)
4225 break;
4226 if ((subtype & VSTYPE) != VSNORMAL)
4227 quoted <<= 1;
4228 str = vstype[subtype & VSTYPE];
4229 if (subtype & VSNUL)
4230 c = ':';
4231 else
4232 goto checkstr;
4233 break;
4234 case '\'':
4235 case '\\':
4236 case '"':
4237 case '$':
4238 /* These can only happen inside quotes */
4239 cc[0] = c;
4240 str = cc;
4241 c = '\\';
4242 break;
4243 default:
4244 break;
4245 }
4246 USTPUTC(c, nextc);
4247 checkstr:
4248 if (!str)
4249 continue;
4250 dostr:
4251 while ((c = *str++)) {
4252 USTPUTC(c, nextc);
4253 }
4254 }
4255 if (quoted & 1) {
4256 USTPUTC('"', nextc);
4257 }
4258 *nextc = 0;
4259 cmdnextc = nextc;
4260}
4261
4262/* cmdtxt() and cmdlist() call each other */
4263static void cmdtxt(union node *n);
4264
4265static void
4266cmdlist(union node *np, int sep)
4267{
4268 for (; np; np = np->narg.next) {
4269 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004270 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004271 cmdtxt(np);
4272 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004273 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004274 }
4275}
4276
4277static void
4278cmdtxt(union node *n)
4279{
4280 union node *np;
4281 struct nodelist *lp;
4282 const char *p;
4283 char s[2];
4284
4285 if (!n)
4286 return;
4287 switch (n->type) {
4288 default:
4289#if DEBUG
4290 abort();
4291#endif
4292 case NPIPE:
4293 lp = n->npipe.cmdlist;
4294 for (;;) {
4295 cmdtxt(lp->n);
4296 lp = lp->next;
4297 if (!lp)
4298 break;
4299 cmdputs(" | ");
4300 }
4301 break;
4302 case NSEMI:
4303 p = "; ";
4304 goto binop;
4305 case NAND:
4306 p = " && ";
4307 goto binop;
4308 case NOR:
4309 p = " || ";
4310 binop:
4311 cmdtxt(n->nbinary.ch1);
4312 cmdputs(p);
4313 n = n->nbinary.ch2;
4314 goto donode;
4315 case NREDIR:
4316 case NBACKGND:
4317 n = n->nredir.n;
4318 goto donode;
4319 case NNOT:
4320 cmdputs("!");
4321 n = n->nnot.com;
4322 donode:
4323 cmdtxt(n);
4324 break;
4325 case NIF:
4326 cmdputs("if ");
4327 cmdtxt(n->nif.test);
4328 cmdputs("; then ");
4329 n = n->nif.ifpart;
4330 if (n->nif.elsepart) {
4331 cmdtxt(n);
4332 cmdputs("; else ");
4333 n = n->nif.elsepart;
4334 }
4335 p = "; fi";
4336 goto dotail;
4337 case NSUBSHELL:
4338 cmdputs("(");
4339 n = n->nredir.n;
4340 p = ")";
4341 goto dotail;
4342 case NWHILE:
4343 p = "while ";
4344 goto until;
4345 case NUNTIL:
4346 p = "until ";
4347 until:
4348 cmdputs(p);
4349 cmdtxt(n->nbinary.ch1);
4350 n = n->nbinary.ch2;
4351 p = "; done";
4352 dodo:
4353 cmdputs("; do ");
4354 dotail:
4355 cmdtxt(n);
4356 goto dotail2;
4357 case NFOR:
4358 cmdputs("for ");
4359 cmdputs(n->nfor.var);
4360 cmdputs(" in ");
4361 cmdlist(n->nfor.args, 1);
4362 n = n->nfor.body;
4363 p = "; done";
4364 goto dodo;
4365 case NDEFUN:
4366 cmdputs(n->narg.text);
4367 p = "() { ... }";
4368 goto dotail2;
4369 case NCMD:
4370 cmdlist(n->ncmd.args, 1);
4371 cmdlist(n->ncmd.redirect, 0);
4372 break;
4373 case NARG:
4374 p = n->narg.text;
4375 dotail2:
4376 cmdputs(p);
4377 break;
4378 case NHERE:
4379 case NXHERE:
4380 p = "<<...";
4381 goto dotail2;
4382 case NCASE:
4383 cmdputs("case ");
4384 cmdputs(n->ncase.expr->narg.text);
4385 cmdputs(" in ");
4386 for (np = n->ncase.cases; np; np = np->nclist.next) {
4387 cmdtxt(np->nclist.pattern);
4388 cmdputs(") ");
4389 cmdtxt(np->nclist.body);
4390 cmdputs(";; ");
4391 }
4392 p = "esac";
4393 goto dotail2;
4394 case NTO:
4395 p = ">";
4396 goto redir;
4397 case NCLOBBER:
4398 p = ">|";
4399 goto redir;
4400 case NAPPEND:
4401 p = ">>";
4402 goto redir;
4403 case NTOFD:
4404 p = ">&";
4405 goto redir;
4406 case NFROM:
4407 p = "<";
4408 goto redir;
4409 case NFROMFD:
4410 p = "<&";
4411 goto redir;
4412 case NFROMTO:
4413 p = "<>";
4414 redir:
4415 s[0] = n->nfile.fd + '0';
4416 s[1] = '\0';
4417 cmdputs(s);
4418 cmdputs(p);
4419 if (n->type == NTOFD || n->type == NFROMFD) {
4420 s[0] = n->ndup.dupfd + '0';
4421 p = s;
4422 goto dotail2;
4423 }
4424 n = n->nfile.fname;
4425 goto donode;
4426 }
4427}
4428
4429static char *
4430commandtext(union node *n)
4431{
4432 char *name;
4433
4434 STARTSTACKSTR(cmdnextc);
4435 cmdtxt(n);
4436 name = stackblock();
4437 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4438 name, cmdnextc, cmdnextc));
4439 return ckstrdup(name);
4440}
4441#endif /* JOBS */
4442
4443/*
4444 * Fork off a subshell. If we are doing job control, give the subshell its
4445 * own process group. Jp is a job structure that the job is to be added to.
4446 * N is the command that will be evaluated by the child. Both jp and n may
4447 * be NULL. The mode parameter can be one of the following:
4448 * FORK_FG - Fork off a foreground process.
4449 * FORK_BG - Fork off a background process.
4450 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4451 * process group even if job control is on.
4452 *
4453 * When job control is turned off, background processes have their standard
4454 * input redirected to /dev/null (except for the second and later processes
4455 * in a pipeline).
4456 *
4457 * Called with interrupts off.
4458 */
4459/*
4460 * Clear traps on a fork.
4461 */
4462static void
4463clear_traps(void)
4464{
4465 char **tp;
4466
4467 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004468 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469 INT_OFF;
4470 free(*tp);
4471 *tp = NULL;
4472 if (tp != &trap[0])
4473 setsignal(tp - trap);
4474 INT_ON;
4475 }
4476 }
4477}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004478
4479/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004480static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004481
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004482/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004483static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004484forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485{
4486 int oldlvl;
4487
4488 TRACE(("Child shell %d\n", getpid()));
4489 oldlvl = shlvl;
4490 shlvl++;
4491
4492 closescript();
4493 clear_traps();
4494#if JOBS
4495 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004496 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004497 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4498 pid_t pgrp;
4499
4500 if (jp->nprocs == 0)
4501 pgrp = getpid();
4502 else
4503 pgrp = jp->ps[0].pid;
4504 /* This can fail because we are doing it in the parent also */
4505 (void)setpgid(0, pgrp);
4506 if (mode == FORK_FG)
4507 xtcsetpgrp(ttyfd, pgrp);
4508 setsignal(SIGTSTP);
4509 setsignal(SIGTTOU);
4510 } else
4511#endif
4512 if (mode == FORK_BG) {
4513 ignoresig(SIGINT);
4514 ignoresig(SIGQUIT);
4515 if (jp->nprocs == 0) {
4516 close(0);
4517 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004518 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004519 }
4520 }
4521 if (!oldlvl && iflag) {
4522 setsignal(SIGINT);
4523 setsignal(SIGQUIT);
4524 setsignal(SIGTERM);
4525 }
4526 for (jp = curjob; jp; jp = jp->prev_job)
4527 freejob(jp);
4528 jobless = 0;
4529}
4530
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004531/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004532#if !JOBS
4533#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4534#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535static void
4536forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4537{
4538 TRACE(("In parent shell: child = %d\n", pid));
4539 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004540 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4541 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004542 jobless++;
4543 return;
4544 }
4545#if JOBS
4546 if (mode != FORK_NOJOB && jp->jobctl) {
4547 int pgrp;
4548
4549 if (jp->nprocs == 0)
4550 pgrp = pid;
4551 else
4552 pgrp = jp->ps[0].pid;
4553 /* This can fail because we are doing it in the child also */
4554 setpgid(pid, pgrp);
4555 }
4556#endif
4557 if (mode == FORK_BG) {
4558 backgndpid = pid; /* set $! */
4559 set_curjob(jp, CUR_RUNNING);
4560 }
4561 if (jp) {
4562 struct procstat *ps = &jp->ps[jp->nprocs++];
4563 ps->pid = pid;
4564 ps->status = -1;
4565 ps->cmd = nullstr;
4566#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004567 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568 ps->cmd = commandtext(n);
4569#endif
4570 }
4571}
4572
4573static int
4574forkshell(struct job *jp, union node *n, int mode)
4575{
4576 int pid;
4577
4578 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4579 pid = fork();
4580 if (pid < 0) {
4581 TRACE(("Fork failed, errno=%d", errno));
4582 if (jp)
4583 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004584 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004585 }
4586 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004587 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004588 else
4589 forkparent(jp, n, mode, pid);
4590 return pid;
4591}
4592
4593/*
4594 * Wait for job to finish.
4595 *
4596 * Under job control we have the problem that while a child process is
4597 * running interrupts generated by the user are sent to the child but not
4598 * to the shell. This means that an infinite loop started by an inter-
4599 * active user may be hard to kill. With job control turned off, an
4600 * interactive user may place an interactive program inside a loop. If
4601 * the interactive program catches interrupts, the user doesn't want
4602 * these interrupts to also abort the loop. The approach we take here
4603 * is to have the shell ignore interrupt signals while waiting for a
4604 * foreground process to terminate, and then send itself an interrupt
4605 * signal if the child process was terminated by an interrupt signal.
4606 * Unfortunately, some programs want to do a bit of cleanup and then
4607 * exit on interrupt; unless these processes terminate themselves by
4608 * sending a signal to themselves (instead of calling exit) they will
4609 * confuse this approach.
4610 *
4611 * Called with interrupts off.
4612 */
4613static int
4614waitforjob(struct job *jp)
4615{
4616 int st;
4617
4618 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4619 while (jp->state == JOBRUNNING) {
4620 dowait(DOWAIT_BLOCK, jp);
4621 }
4622 st = getstatus(jp);
4623#if JOBS
4624 if (jp->jobctl) {
4625 xtcsetpgrp(ttyfd, rootpid);
4626 /*
4627 * This is truly gross.
4628 * If we're doing job control, then we did a TIOCSPGRP which
4629 * caused us (the shell) to no longer be in the controlling
4630 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4631 * intuit from the subprocess exit status whether a SIGINT
4632 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4633 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004634 if (jp->sigint) /* TODO: do the same with all signals */
4635 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004636 }
4637 if (jp->state == JOBDONE)
4638#endif
4639 freejob(jp);
4640 return st;
4641}
4642
4643/*
4644 * return 1 if there are stopped jobs, otherwise 0
4645 */
4646static int
4647stoppedjobs(void)
4648{
4649 struct job *jp;
4650 int retval;
4651
4652 retval = 0;
4653 if (job_warning)
4654 goto out;
4655 jp = curjob;
4656 if (jp && jp->state == JOBSTOPPED) {
4657 out2str("You have stopped jobs.\n");
4658 job_warning = 2;
4659 retval++;
4660 }
4661 out:
4662 return retval;
4663}
4664
4665
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004666/* ============ redir.c
4667 *
4668 * Code for dealing with input/output redirection.
4669 */
4670
4671#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004672#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004673#ifndef PIPE_BUF
4674# define PIPESIZE 4096 /* amount of buffering in a pipe */
4675#else
4676# define PIPESIZE PIPE_BUF
4677#endif
4678
4679/*
4680 * Open a file in noclobber mode.
4681 * The code was copied from bash.
4682 */
4683static int
4684noclobberopen(const char *fname)
4685{
4686 int r, fd;
4687 struct stat finfo, finfo2;
4688
4689 /*
4690 * If the file exists and is a regular file, return an error
4691 * immediately.
4692 */
4693 r = stat(fname, &finfo);
4694 if (r == 0 && S_ISREG(finfo.st_mode)) {
4695 errno = EEXIST;
4696 return -1;
4697 }
4698
4699 /*
4700 * If the file was not present (r != 0), make sure we open it
4701 * exclusively so that if it is created before we open it, our open
4702 * will fail. Make sure that we do not truncate an existing file.
4703 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4704 * file was not a regular file, we leave O_EXCL off.
4705 */
4706 if (r != 0)
4707 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4708 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4709
4710 /* If the open failed, return the file descriptor right away. */
4711 if (fd < 0)
4712 return fd;
4713
4714 /*
4715 * OK, the open succeeded, but the file may have been changed from a
4716 * non-regular file to a regular file between the stat and the open.
4717 * We are assuming that the O_EXCL open handles the case where FILENAME
4718 * did not exist and is symlinked to an existing file between the stat
4719 * and open.
4720 */
4721
4722 /*
4723 * If we can open it and fstat the file descriptor, and neither check
4724 * revealed that it was a regular file, and the file has not been
4725 * replaced, return the file descriptor.
4726 */
4727 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4728 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4729 return fd;
4730
4731 /* The file has been replaced. badness. */
4732 close(fd);
4733 errno = EEXIST;
4734 return -1;
4735}
4736
4737/*
4738 * Handle here documents. Normally we fork off a process to write the
4739 * data to a pipe. If the document is short, we can stuff the data in
4740 * the pipe without forking.
4741 */
4742/* openhere needs this forward reference */
4743static void expandhere(union node *arg, int fd);
4744static int
4745openhere(union node *redir)
4746{
4747 int pip[2];
4748 size_t len = 0;
4749
4750 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004751 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004752 if (redir->type == NHERE) {
4753 len = strlen(redir->nhere.doc->narg.text);
4754 if (len <= PIPESIZE) {
4755 full_write(pip[1], redir->nhere.doc->narg.text, len);
4756 goto out;
4757 }
4758 }
4759 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4760 close(pip[0]);
4761 signal(SIGINT, SIG_IGN);
4762 signal(SIGQUIT, SIG_IGN);
4763 signal(SIGHUP, SIG_IGN);
4764#ifdef SIGTSTP
4765 signal(SIGTSTP, SIG_IGN);
4766#endif
4767 signal(SIGPIPE, SIG_DFL);
4768 if (redir->type == NHERE)
4769 full_write(pip[1], redir->nhere.doc->narg.text, len);
4770 else
4771 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004772 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004773 }
4774 out:
4775 close(pip[1]);
4776 return pip[0];
4777}
4778
4779static int
4780openredirect(union node *redir)
4781{
4782 char *fname;
4783 int f;
4784
4785 switch (redir->nfile.type) {
4786 case NFROM:
4787 fname = redir->nfile.expfname;
4788 f = open(fname, O_RDONLY);
4789 if (f < 0)
4790 goto eopen;
4791 break;
4792 case NFROMTO:
4793 fname = redir->nfile.expfname;
4794 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4795 if (f < 0)
4796 goto ecreate;
4797 break;
4798 case NTO:
4799 /* Take care of noclobber mode. */
4800 if (Cflag) {
4801 fname = redir->nfile.expfname;
4802 f = noclobberopen(fname);
4803 if (f < 0)
4804 goto ecreate;
4805 break;
4806 }
4807 /* FALLTHROUGH */
4808 case NCLOBBER:
4809 fname = redir->nfile.expfname;
4810 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4811 if (f < 0)
4812 goto ecreate;
4813 break;
4814 case NAPPEND:
4815 fname = redir->nfile.expfname;
4816 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4817 if (f < 0)
4818 goto ecreate;
4819 break;
4820 default:
4821#if DEBUG
4822 abort();
4823#endif
4824 /* Fall through to eliminate warning. */
4825 case NTOFD:
4826 case NFROMFD:
4827 f = -1;
4828 break;
4829 case NHERE:
4830 case NXHERE:
4831 f = openhere(redir);
4832 break;
4833 }
4834
4835 return f;
4836 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004837 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004838 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004839 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004840}
4841
4842/*
4843 * Copy a file descriptor to be >= to. Returns -1
4844 * if the source file descriptor is closed, EMPTY if there are no unused
4845 * file descriptors left.
4846 */
4847static int
4848copyfd(int from, int to)
4849{
4850 int newfd;
4851
4852 newfd = fcntl(from, F_DUPFD, to);
4853 if (newfd < 0) {
4854 if (errno == EMFILE)
4855 return EMPTY;
4856 ash_msg_and_raise_error("%d: %m", from);
4857 }
4858 return newfd;
4859}
4860
4861static void
4862dupredirect(union node *redir, int f)
4863{
4864 int fd = redir->nfile.fd;
4865
4866 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4867 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4868 copyfd(redir->ndup.dupfd, fd);
4869 }
4870 return;
4871 }
4872
4873 if (f != fd) {
4874 copyfd(f, fd);
4875 close(f);
4876 }
4877}
4878
4879/*
4880 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4881 * old file descriptors are stashed away so that the redirection can be
4882 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4883 * standard output, and the standard error if it becomes a duplicate of
4884 * stdout, is saved in memory.
4885 */
4886/* flags passed to redirect */
4887#define REDIR_PUSH 01 /* save previous values of file descriptors */
4888#define REDIR_SAVEFD2 03 /* set preverrout */
4889static void
4890redirect(union node *redir, int flags)
4891{
4892 union node *n;
4893 struct redirtab *sv;
4894 int i;
4895 int fd;
4896 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004897
Denis Vlasenko01631112007-12-16 17:20:38 +00004898 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004899 if (!redir) {
4900 return;
4901 }
4902 sv = NULL;
4903 INT_OFF;
4904 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004905 sv = ckmalloc(sizeof(*sv));
4906 sv->next = redirlist;
4907 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004908 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004909 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004910 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004911 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004912 }
4913 n = redir;
4914 do {
4915 fd = n->nfile.fd;
4916 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4917 && n->ndup.dupfd == fd)
4918 continue; /* redirect from/to same file descriptor */
4919
4920 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004921 if (fd == newfd) {
4922 /* Descriptor wasn't open before redirect.
4923 * Mark it for close in the future */
4924 if (sv && sv->renamed[fd] == EMPTY)
4925 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004926 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004927 }
4928 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004929 i = fcntl(fd, F_DUPFD, 10);
4930
4931 if (i == -1) {
4932 i = errno;
4933 if (i != EBADF) {
4934 close(newfd);
4935 errno = i;
4936 ash_msg_and_raise_error("%d: %m", fd);
4937 /* NOTREACHED */
4938 }
4939 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004940 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004941 close(fd);
4942 }
4943 } else {
4944 close(fd);
4945 }
4946 dupredirect(n, newfd);
4947 } while ((n = n->nfile.next));
4948 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004949 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004950 preverrout_fd = sv->renamed[2];
4951}
4952
4953/*
4954 * Undo the effects of the last redirection.
4955 */
4956static void
4957popredir(int drop)
4958{
4959 struct redirtab *rp;
4960 int i;
4961
Denis Vlasenko01631112007-12-16 17:20:38 +00004962 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004963 return;
4964 INT_OFF;
4965 rp = redirlist;
4966 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004967 if (rp->renamed[i] == CLOSED) {
4968 if (!drop)
4969 close(i);
4970 continue;
4971 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004972 if (rp->renamed[i] != EMPTY) {
4973 if (!drop) {
4974 close(i);
4975 copyfd(rp->renamed[i], i);
4976 }
4977 close(rp->renamed[i]);
4978 }
4979 }
4980 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004981 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982 free(rp);
4983 INT_ON;
4984}
4985
4986/*
4987 * Undo all redirections. Called on error or interrupt.
4988 */
4989
4990/*
4991 * Discard all saved file descriptors.
4992 */
4993static void
4994clearredir(int drop)
4995{
4996 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004997 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004998 if (!redirlist)
4999 break;
5000 popredir(drop);
5001 }
5002}
5003
5004static int
5005redirectsafe(union node *redir, int flags)
5006{
5007 int err;
5008 volatile int saveint;
5009 struct jmploc *volatile savehandler = exception_handler;
5010 struct jmploc jmploc;
5011
5012 SAVE_INT(saveint);
5013 err = setjmp(jmploc.loc) * 2;
5014 if (!err) {
5015 exception_handler = &jmploc;
5016 redirect(redir, flags);
5017 }
5018 exception_handler = savehandler;
5019 if (err && exception != EXERROR)
5020 longjmp(exception_handler->loc, 1);
5021 RESTORE_INT(saveint);
5022 return err;
5023}
5024
5025
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005026/* ============ Routines to expand arguments to commands
5027 *
5028 * We have to deal with backquotes, shell variables, and file metacharacters.
5029 */
5030
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005031#if ENABLE_ASH_MATH_SUPPORT_64
5032typedef int64_t arith_t;
5033#define arith_t_type long long
5034#else
5035typedef long arith_t;
5036#define arith_t_type long
5037#endif
5038
5039#if ENABLE_ASH_MATH_SUPPORT
5040static arith_t dash_arith(const char *);
5041static arith_t arith(const char *expr, int *perrcode);
5042#endif
5043
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005044/*
5045 * expandarg flags
5046 */
5047#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5048#define EXP_TILDE 0x2 /* do normal tilde expansion */
5049#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5050#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5051#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5052#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5053#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5054#define EXP_WORD 0x80 /* expand word in parameter expansion */
5055#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5056/*
5057 * _rmescape() flags
5058 */
5059#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5060#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5061#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5062#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5063#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5064
5065/*
5066 * Structure specifying which parts of the string should be searched
5067 * for IFS characters.
5068 */
5069struct ifsregion {
5070 struct ifsregion *next; /* next region in list */
5071 int begoff; /* offset of start of region */
5072 int endoff; /* offset of end of region */
5073 int nulonly; /* search for nul bytes only */
5074};
5075
5076struct arglist {
5077 struct strlist *list;
5078 struct strlist **lastp;
5079};
5080
5081/* output of current string */
5082static char *expdest;
5083/* list of back quote expressions */
5084static struct nodelist *argbackq;
5085/* first struct in list of ifs regions */
5086static struct ifsregion ifsfirst;
5087/* last struct in list */
5088static struct ifsregion *ifslastp;
5089/* holds expanded arg list */
5090static struct arglist exparg;
5091
5092/*
5093 * Our own itoa().
5094 */
5095static int
5096cvtnum(arith_t num)
5097{
5098 int len;
5099
5100 expdest = makestrspace(32, expdest);
5101#if ENABLE_ASH_MATH_SUPPORT_64
5102 len = fmtstr(expdest, 32, "%lld", (long long) num);
5103#else
5104 len = fmtstr(expdest, 32, "%ld", num);
5105#endif
5106 STADJUST(len, expdest);
5107 return len;
5108}
5109
5110static size_t
5111esclen(const char *start, const char *p)
5112{
5113 size_t esc = 0;
5114
5115 while (p > start && *--p == CTLESC) {
5116 esc++;
5117 }
5118 return esc;
5119}
5120
5121/*
5122 * Remove any CTLESC characters from a string.
5123 */
5124static char *
5125_rmescapes(char *str, int flag)
5126{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005127 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005128
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005129 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005130 unsigned inquotes;
5131 int notescaped;
5132 int globbing;
5133
5134 p = strpbrk(str, qchars);
5135 if (!p) {
5136 return str;
5137 }
5138 q = p;
5139 r = str;
5140 if (flag & RMESCAPE_ALLOC) {
5141 size_t len = p - str;
5142 size_t fulllen = len + strlen(p) + 1;
5143
5144 if (flag & RMESCAPE_GROW) {
5145 r = makestrspace(fulllen, expdest);
5146 } else if (flag & RMESCAPE_HEAP) {
5147 r = ckmalloc(fulllen);
5148 } else {
5149 r = stalloc(fulllen);
5150 }
5151 q = r;
5152 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005153 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005154 }
5155 }
5156 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5157 globbing = flag & RMESCAPE_GLOB;
5158 notescaped = globbing;
5159 while (*p) {
5160 if (*p == CTLQUOTEMARK) {
5161 inquotes = ~inquotes;
5162 p++;
5163 notescaped = globbing;
5164 continue;
5165 }
5166 if (*p == '\\') {
5167 /* naked back slash */
5168 notescaped = 0;
5169 goto copy;
5170 }
5171 if (*p == CTLESC) {
5172 p++;
5173 if (notescaped && inquotes && *p != '/') {
5174 *q++ = '\\';
5175 }
5176 }
5177 notescaped = globbing;
5178 copy:
5179 *q++ = *p++;
5180 }
5181 *q = '\0';
5182 if (flag & RMESCAPE_GROW) {
5183 expdest = r;
5184 STADJUST(q - r + 1, expdest);
5185 }
5186 return r;
5187}
5188#define rmescapes(p) _rmescapes((p), 0)
5189
5190#define pmatch(a, b) !fnmatch((a), (b), 0)
5191
5192/*
5193 * Prepare a pattern for a expmeta (internal glob(3)) call.
5194 *
5195 * Returns an stalloced string.
5196 */
5197static char *
5198preglob(const char *pattern, int quoted, int flag)
5199{
5200 flag |= RMESCAPE_GLOB;
5201 if (quoted) {
5202 flag |= RMESCAPE_QUOTED;
5203 }
5204 return _rmescapes((char *)pattern, flag);
5205}
5206
5207/*
5208 * Put a string on the stack.
5209 */
5210static void
5211memtodest(const char *p, size_t len, int syntax, int quotes)
5212{
5213 char *q = expdest;
5214
5215 q = makestrspace(len * 2, q);
5216
5217 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005218 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005219 if (!c)
5220 continue;
5221 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5222 USTPUTC(CTLESC, q);
5223 USTPUTC(c, q);
5224 }
5225
5226 expdest = q;
5227}
5228
5229static void
5230strtodest(const char *p, int syntax, int quotes)
5231{
5232 memtodest(p, strlen(p), syntax, quotes);
5233}
5234
5235/*
5236 * Record the fact that we have to scan this region of the
5237 * string for IFS characters.
5238 */
5239static void
5240recordregion(int start, int end, int nulonly)
5241{
5242 struct ifsregion *ifsp;
5243
5244 if (ifslastp == NULL) {
5245 ifsp = &ifsfirst;
5246 } else {
5247 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005248 ifsp = ckzalloc(sizeof(*ifsp));
5249 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005250 ifslastp->next = ifsp;
5251 INT_ON;
5252 }
5253 ifslastp = ifsp;
5254 ifslastp->begoff = start;
5255 ifslastp->endoff = end;
5256 ifslastp->nulonly = nulonly;
5257}
5258
5259static void
5260removerecordregions(int endoff)
5261{
5262 if (ifslastp == NULL)
5263 return;
5264
5265 if (ifsfirst.endoff > endoff) {
5266 while (ifsfirst.next != NULL) {
5267 struct ifsregion *ifsp;
5268 INT_OFF;
5269 ifsp = ifsfirst.next->next;
5270 free(ifsfirst.next);
5271 ifsfirst.next = ifsp;
5272 INT_ON;
5273 }
5274 if (ifsfirst.begoff > endoff)
5275 ifslastp = NULL;
5276 else {
5277 ifslastp = &ifsfirst;
5278 ifsfirst.endoff = endoff;
5279 }
5280 return;
5281 }
5282
5283 ifslastp = &ifsfirst;
5284 while (ifslastp->next && ifslastp->next->begoff < endoff)
5285 ifslastp=ifslastp->next;
5286 while (ifslastp->next != NULL) {
5287 struct ifsregion *ifsp;
5288 INT_OFF;
5289 ifsp = ifslastp->next->next;
5290 free(ifslastp->next);
5291 ifslastp->next = ifsp;
5292 INT_ON;
5293 }
5294 if (ifslastp->endoff > endoff)
5295 ifslastp->endoff = endoff;
5296}
5297
5298static char *
5299exptilde(char *startp, char *p, int flag)
5300{
5301 char c;
5302 char *name;
5303 struct passwd *pw;
5304 const char *home;
5305 int quotes = flag & (EXP_FULL | EXP_CASE);
5306 int startloc;
5307
5308 name = p + 1;
5309
5310 while ((c = *++p) != '\0') {
5311 switch (c) {
5312 case CTLESC:
5313 return startp;
5314 case CTLQUOTEMARK:
5315 return startp;
5316 case ':':
5317 if (flag & EXP_VARTILDE)
5318 goto done;
5319 break;
5320 case '/':
5321 case CTLENDVAR:
5322 goto done;
5323 }
5324 }
5325 done:
5326 *p = '\0';
5327 if (*name == '\0') {
5328 home = lookupvar(homestr);
5329 } else {
5330 pw = getpwnam(name);
5331 if (pw == NULL)
5332 goto lose;
5333 home = pw->pw_dir;
5334 }
5335 if (!home || !*home)
5336 goto lose;
5337 *p = c;
5338 startloc = expdest - (char *)stackblock();
5339 strtodest(home, SQSYNTAX, quotes);
5340 recordregion(startloc, expdest - (char *)stackblock(), 0);
5341 return p;
5342 lose:
5343 *p = c;
5344 return startp;
5345}
5346
5347/*
5348 * Execute a command inside back quotes. If it's a builtin command, we
5349 * want to save its output in a block obtained from malloc. Otherwise
5350 * we fork off a subprocess and get the output of the command via a pipe.
5351 * Should be called with interrupts off.
5352 */
5353struct backcmd { /* result of evalbackcmd */
5354 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005355 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005356 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005357 struct job *jp; /* job structure for command */
5358};
5359
5360/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005361static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005362#define EV_EXIT 01 /* exit after evaluating tree */
5363static void evaltree(union node *, int);
5364
5365static void
5366evalbackcmd(union node *n, struct backcmd *result)
5367{
5368 int saveherefd;
5369
5370 result->fd = -1;
5371 result->buf = NULL;
5372 result->nleft = 0;
5373 result->jp = NULL;
5374 if (n == NULL) {
5375 goto out;
5376 }
5377
5378 saveherefd = herefd;
5379 herefd = -1;
5380
5381 {
5382 int pip[2];
5383 struct job *jp;
5384
5385 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005386 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005387 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005388 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5389 FORCE_INT_ON;
5390 close(pip[0]);
5391 if (pip[1] != 1) {
5392 close(1);
5393 copyfd(pip[1], 1);
5394 close(pip[1]);
5395 }
5396 eflag = 0;
5397 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5398 /* NOTREACHED */
5399 }
5400 close(pip[1]);
5401 result->fd = pip[0];
5402 result->jp = jp;
5403 }
5404 herefd = saveherefd;
5405 out:
5406 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5407 result->fd, result->buf, result->nleft, result->jp));
5408}
5409
5410/*
5411 * Expand stuff in backwards quotes.
5412 */
5413static void
5414expbackq(union node *cmd, int quoted, int quotes)
5415{
5416 struct backcmd in;
5417 int i;
5418 char buf[128];
5419 char *p;
5420 char *dest;
5421 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005422 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005423 struct stackmark smark;
5424
5425 INT_OFF;
5426 setstackmark(&smark);
5427 dest = expdest;
5428 startloc = dest - (char *)stackblock();
5429 grabstackstr(dest);
5430 evalbackcmd(cmd, &in);
5431 popstackmark(&smark);
5432
5433 p = in.buf;
5434 i = in.nleft;
5435 if (i == 0)
5436 goto read;
5437 for (;;) {
5438 memtodest(p, i, syntax, quotes);
5439 read:
5440 if (in.fd < 0)
5441 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005442 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005443 TRACE(("expbackq: read returns %d\n", i));
5444 if (i <= 0)
5445 break;
5446 p = buf;
5447 }
5448
Denis Vlasenko60818682007-09-28 22:07:23 +00005449 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005450 if (in.fd >= 0) {
5451 close(in.fd);
5452 back_exitstatus = waitforjob(in.jp);
5453 }
5454 INT_ON;
5455
5456 /* Eat all trailing newlines */
5457 dest = expdest;
5458 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5459 STUNPUTC(dest);
5460 expdest = dest;
5461
5462 if (quoted == 0)
5463 recordregion(startloc, dest - (char *)stackblock(), 0);
5464 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5465 (dest - (char *)stackblock()) - startloc,
5466 (dest - (char *)stackblock()) - startloc,
5467 stackblock() + startloc));
5468}
5469
5470#if ENABLE_ASH_MATH_SUPPORT
5471/*
5472 * Expand arithmetic expression. Backup to start of expression,
5473 * evaluate, place result in (backed up) result, adjust string position.
5474 */
5475static void
5476expari(int quotes)
5477{
5478 char *p, *start;
5479 int begoff;
5480 int flag;
5481 int len;
5482
5483 /* ifsfree(); */
5484
5485 /*
5486 * This routine is slightly over-complicated for
5487 * efficiency. Next we scan backwards looking for the
5488 * start of arithmetic.
5489 */
5490 start = stackblock();
5491 p = expdest - 1;
5492 *p = '\0';
5493 p--;
5494 do {
5495 int esc;
5496
5497 while (*p != CTLARI) {
5498 p--;
5499#if DEBUG
5500 if (p < start) {
5501 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5502 }
5503#endif
5504 }
5505
5506 esc = esclen(start, p);
5507 if (!(esc % 2)) {
5508 break;
5509 }
5510
5511 p -= esc + 1;
5512 } while (1);
5513
5514 begoff = p - start;
5515
5516 removerecordregions(begoff);
5517
5518 flag = p[1];
5519
5520 expdest = p;
5521
5522 if (quotes)
5523 rmescapes(p + 2);
5524
5525 len = cvtnum(dash_arith(p + 2));
5526
5527 if (flag != '"')
5528 recordregion(begoff, begoff + len, 0);
5529}
5530#endif
5531
5532/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005533static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005534
5535/*
5536 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5537 * characters to allow for further processing. Otherwise treat
5538 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005539 *
5540 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5541 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5542 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005543 */
5544static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005545argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005546{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005547 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005548 '=',
5549 ':',
5550 CTLQUOTEMARK,
5551 CTLENDVAR,
5552 CTLESC,
5553 CTLVAR,
5554 CTLBACKQ,
5555 CTLBACKQ | CTLQUOTE,
5556#if ENABLE_ASH_MATH_SUPPORT
5557 CTLENDARI,
5558#endif
5559 0
5560 };
5561 const char *reject = spclchars;
5562 int c;
5563 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5564 int breakall = flag & EXP_WORD;
5565 int inquotes;
5566 size_t length;
5567 int startloc;
5568
5569 if (!(flag & EXP_VARTILDE)) {
5570 reject += 2;
5571 } else if (flag & EXP_VARTILDE2) {
5572 reject++;
5573 }
5574 inquotes = 0;
5575 length = 0;
5576 if (flag & EXP_TILDE) {
5577 char *q;
5578
5579 flag &= ~EXP_TILDE;
5580 tilde:
5581 q = p;
5582 if (*q == CTLESC && (flag & EXP_QWORD))
5583 q++;
5584 if (*q == '~')
5585 p = exptilde(p, q, flag);
5586 }
5587 start:
5588 startloc = expdest - (char *)stackblock();
5589 for (;;) {
5590 length += strcspn(p + length, reject);
5591 c = p[length];
5592 if (c && (!(c & 0x80)
5593#if ENABLE_ASH_MATH_SUPPORT
5594 || c == CTLENDARI
5595#endif
5596 )) {
5597 /* c == '=' || c == ':' || c == CTLENDARI */
5598 length++;
5599 }
5600 if (length > 0) {
5601 int newloc;
5602 expdest = stack_nputstr(p, length, expdest);
5603 newloc = expdest - (char *)stackblock();
5604 if (breakall && !inquotes && newloc > startloc) {
5605 recordregion(startloc, newloc, 0);
5606 }
5607 startloc = newloc;
5608 }
5609 p += length + 1;
5610 length = 0;
5611
5612 switch (c) {
5613 case '\0':
5614 goto breakloop;
5615 case '=':
5616 if (flag & EXP_VARTILDE2) {
5617 p--;
5618 continue;
5619 }
5620 flag |= EXP_VARTILDE2;
5621 reject++;
5622 /* fall through */
5623 case ':':
5624 /*
5625 * sort of a hack - expand tildes in variable
5626 * assignments (after the first '=' and after ':'s).
5627 */
5628 if (*--p == '~') {
5629 goto tilde;
5630 }
5631 continue;
5632 }
5633
5634 switch (c) {
5635 case CTLENDVAR: /* ??? */
5636 goto breakloop;
5637 case CTLQUOTEMARK:
5638 /* "$@" syntax adherence hack */
5639 if (
5640 !inquotes &&
5641 !memcmp(p, dolatstr, 4) &&
5642 (p[4] == CTLQUOTEMARK || (
5643 p[4] == CTLENDVAR &&
5644 p[5] == CTLQUOTEMARK
5645 ))
5646 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005647 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648 goto start;
5649 }
5650 inquotes = !inquotes;
5651 addquote:
5652 if (quotes) {
5653 p--;
5654 length++;
5655 startloc++;
5656 }
5657 break;
5658 case CTLESC:
5659 startloc++;
5660 length++;
5661 goto addquote;
5662 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005663 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005664 goto start;
5665 case CTLBACKQ:
5666 c = 0;
5667 case CTLBACKQ|CTLQUOTE:
5668 expbackq(argbackq->n, c, quotes);
5669 argbackq = argbackq->next;
5670 goto start;
5671#if ENABLE_ASH_MATH_SUPPORT
5672 case CTLENDARI:
5673 p--;
5674 expari(quotes);
5675 goto start;
5676#endif
5677 }
5678 }
5679 breakloop:
5680 ;
5681}
5682
5683static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005684scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005685 int zero)
5686{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005687// This commented out code was added by James Simmons <jsimmons@infradead.org>
5688// as part of a larger change when he added support for ${var/a/b}.
5689// However, it broke # and % operators:
5690//
5691//var=ababcdcd
5692// ok bad
5693//echo ${var#ab} abcdcd abcdcd
5694//echo ${var##ab} abcdcd abcdcd
5695//echo ${var#a*b} abcdcd ababcdcd (!)
5696//echo ${var##a*b} cdcd cdcd
5697//echo ${var#?} babcdcd ababcdcd (!)
5698//echo ${var##?} babcdcd babcdcd
5699//echo ${var#*} ababcdcd babcdcd (!)
5700//echo ${var##*}
5701//echo ${var%cd} ababcd ababcd
5702//echo ${var%%cd} ababcd abab (!)
5703//echo ${var%c*d} ababcd ababcd
5704//echo ${var%%c*d} abab ababcdcd (!)
5705//echo ${var%?} ababcdc ababcdc
5706//echo ${var%%?} ababcdc ababcdcd (!)
5707//echo ${var%*} ababcdcd ababcdcd
5708//echo ${var%%*}
5709//
5710// Commenting it back out helped. Remove it completely if it really
5711// is not needed.
5712
5713 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005714 char c;
5715
5716 loc = startp;
5717 loc2 = rmesc;
5718 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005719 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005720 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005721
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722 c = *loc2;
5723 if (zero) {
5724 *loc2 = '\0';
5725 s = rmesc;
5726 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005727 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005728
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005729// // chop off end if its '*'
5730// full = strrchr(str, '*');
5731// if (full && full != str)
5732// match--;
5733//
5734// // If str starts with '*' replace with s.
5735// if ((*str == '*') && strlen(s) >= match) {
5736// full = xstrdup(s);
5737// strncpy(full+strlen(s)-match+1, str+1, match-1);
5738// } else
5739// full = xstrndup(str, match);
5740// match = strncmp(s, full, strlen(full));
5741// free(full);
5742//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005743 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005744 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005745 return loc;
5746 if (quotes && *loc == CTLESC)
5747 loc++;
5748 loc++;
5749 loc2++;
5750 } while (c);
5751 return 0;
5752}
5753
5754static char *
5755scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5756 int zero)
5757{
5758 int esc = 0;
5759 char *loc;
5760 char *loc2;
5761
5762 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5763 int match;
5764 char c = *loc2;
5765 const char *s = loc2;
5766 if (zero) {
5767 *loc2 = '\0';
5768 s = rmesc;
5769 }
5770 match = pmatch(str, s);
5771 *loc2 = c;
5772 if (match)
5773 return loc;
5774 loc--;
5775 if (quotes) {
5776 if (--esc < 0) {
5777 esc = esclen(startp, loc);
5778 }
5779 if (esc % 2) {
5780 esc--;
5781 loc--;
5782 }
5783 }
5784 }
5785 return 0;
5786}
5787
5788static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5789static void
5790varunset(const char *end, const char *var, const char *umsg, int varflags)
5791{
5792 const char *msg;
5793 const char *tail;
5794
5795 tail = nullstr;
5796 msg = "parameter not set";
5797 if (umsg) {
5798 if (*end == CTLENDVAR) {
5799 if (varflags & VSNUL)
5800 tail = " or null";
5801 } else
5802 msg = umsg;
5803 }
5804 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5805}
5806
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005807#if ENABLE_ASH_BASH_COMPAT
5808static char *
5809parse_sub_pattern(char *arg, int inquotes)
5810{
5811 char *idx, *repl = NULL;
5812 unsigned char c;
5813
Denis Vlasenko2659c632008-06-14 06:04:59 +00005814 idx = arg;
5815 while (1) {
5816 c = *arg;
5817 if (!c)
5818 break;
5819 if (c == '/') {
5820 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005821 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005822 repl = idx + 1;
5823 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005824 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005825 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005826 *idx++ = c;
5827 if (!inquotes && c == '\\' && arg[1] == '\\')
5828 arg++; /* skip both \\, not just first one */
5829 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005830 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005831 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005832
5833 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005834}
5835#endif /* ENABLE_ASH_BASH_COMPAT */
5836
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005838subevalvar(char *p, char *str, int strloc, int subtype,
5839 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005840{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005841 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005842 char *startp;
5843 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005845 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5846 USE_ASH_BASH_COMPAT(char null = '\0';)
5847 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5848 int saveherefd = herefd;
5849 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005851 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005852
5853 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005854 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5855 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856 STPUTC('\0', expdest);
5857 herefd = saveherefd;
5858 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005859 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860
5861 switch (subtype) {
5862 case VSASSIGN:
5863 setvar(str, startp, 0);
5864 amount = startp - expdest;
5865 STADJUST(amount, expdest);
5866 return startp;
5867
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005868#if ENABLE_ASH_BASH_COMPAT
5869 case VSSUBSTR:
5870 loc = str = stackblock() + strloc;
5871// TODO: number() instead? It does error checking...
5872 pos = atoi(loc);
5873 len = str - startp - 1;
5874
5875 /* *loc != '\0', guaranteed by parser */
5876 if (quotes) {
5877 char *ptr;
5878
5879 /* We must adjust the length by the number of escapes we find. */
5880 for (ptr = startp; ptr < (str - 1); ptr++) {
5881 if(*ptr == CTLESC) {
5882 len--;
5883 ptr++;
5884 }
5885 }
5886 }
5887 orig_len = len;
5888
5889 if (*loc++ == ':') {
5890// TODO: number() instead? It does error checking...
5891 len = atoi(loc);
5892 } else {
5893 len = orig_len;
5894 while (*loc && *loc != ':')
5895 loc++;
5896 if (*loc++ == ':')
5897// TODO: number() instead? It does error checking...
5898 len = atoi(loc);
5899 }
5900 if (pos >= orig_len) {
5901 pos = 0;
5902 len = 0;
5903 }
5904 if (len > (orig_len - pos))
5905 len = orig_len - pos;
5906
5907 for (str = startp; pos; str++, pos--) {
5908 if (quotes && *str == CTLESC)
5909 str++;
5910 }
5911 for (loc = startp; len; len--) {
5912 if (quotes && *str == CTLESC)
5913 *loc++ = *str++;
5914 *loc++ = *str++;
5915 }
5916 *loc = '\0';
5917 amount = loc - expdest;
5918 STADJUST(amount, expdest);
5919 return loc;
5920#endif
5921
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 case VSQUESTION:
5923 varunset(p, str, startp, varflags);
5924 /* NOTREACHED */
5925 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005926 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005927
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005928 /* We'll comeback here if we grow the stack while handling
5929 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5930 * stack will need rebasing, and we'll need to remove our work
5931 * areas each time
5932 */
5933 USE_ASH_BASH_COMPAT(restart:)
5934
5935 amount = expdest - ((char *)stackblock() + resetloc);
5936 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005937 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005938
5939 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005940 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005941 if (quotes) {
5942 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5943 if (rmesc != startp) {
5944 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005945 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946 }
5947 }
5948 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005949 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005951 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005952
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953#if ENABLE_ASH_BASH_COMPAT
5954 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5955 char *idx, *end, *restart_detect;
5956
5957 if(!repl) {
5958 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5959 if (!repl)
5960 repl = &null;
5961 }
5962
5963 /* If there's no pattern to match, return the expansion unmolested */
5964 if (*str == '\0')
5965 return 0;
5966
5967 len = 0;
5968 idx = startp;
5969 end = str - 1;
5970 while (idx < end) {
5971 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5972 if (!loc) {
5973 /* No match, advance */
5974 restart_detect = stackblock();
5975 STPUTC(*idx, expdest);
5976 if (quotes && *idx == CTLESC) {
5977 idx++;
5978 len++;
5979 STPUTC(*idx, expdest);
5980 }
5981 if (stackblock() != restart_detect)
5982 goto restart;
5983 idx++;
5984 len++;
5985 rmesc++;
5986 continue;
5987 }
5988
5989 if (subtype == VSREPLACEALL) {
5990 while (idx < loc) {
5991 if (quotes && *idx == CTLESC)
5992 idx++;
5993 idx++;
5994 rmesc++;
5995 }
5996 } else
5997 idx = loc;
5998
5999 for (loc = repl; *loc; loc++) {
6000 restart_detect = stackblock();
6001 STPUTC(*loc, expdest);
6002 if (stackblock() != restart_detect)
6003 goto restart;
6004 len++;
6005 }
6006
6007 if (subtype == VSREPLACE) {
6008 while (*idx) {
6009 restart_detect = stackblock();
6010 STPUTC(*idx, expdest);
6011 if (stackblock() != restart_detect)
6012 goto restart;
6013 len++;
6014 idx++;
6015 }
6016 break;
6017 }
6018 }
6019
6020 /* We've put the replaced text into a buffer at workloc, now
6021 * move it to the right place and adjust the stack.
6022 */
6023 startp = stackblock() + startloc;
6024 STPUTC('\0', expdest);
6025 memmove(startp, stackblock() + workloc, len);
6026 startp[len++] = '\0';
6027 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6028 STADJUST(-amount, expdest);
6029 return startp;
6030 }
6031#endif /* ENABLE_ASH_BASH_COMPAT */
6032
6033 subtype -= VSTRIMRIGHT;
6034#if DEBUG
6035 if (subtype < 0 || subtype > 7)
6036 abort();
6037#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006038 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6039 zero = subtype >> 1;
6040 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6041 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6042
6043 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6044 if (loc) {
6045 if (zero) {
6046 memmove(startp, loc, str - loc);
6047 loc = startp + (str - loc) - 1;
6048 }
6049 *loc = '\0';
6050 amount = loc - expdest;
6051 STADJUST(amount, expdest);
6052 }
6053 return loc;
6054}
6055
6056/*
6057 * Add the value of a specialized variable to the stack string.
6058 */
6059static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006060varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061{
6062 int num;
6063 char *p;
6064 int i;
6065 int sep = 0;
6066 int sepq = 0;
6067 ssize_t len = 0;
6068 char **ap;
6069 int syntax;
6070 int quoted = varflags & VSQUOTE;
6071 int subtype = varflags & VSTYPE;
6072 int quotes = flags & (EXP_FULL | EXP_CASE);
6073
6074 if (quoted && (flags & EXP_FULL))
6075 sep = 1 << CHAR_BIT;
6076
6077 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6078 switch (*name) {
6079 case '$':
6080 num = rootpid;
6081 goto numvar;
6082 case '?':
6083 num = exitstatus;
6084 goto numvar;
6085 case '#':
6086 num = shellparam.nparam;
6087 goto numvar;
6088 case '!':
6089 num = backgndpid;
6090 if (num == 0)
6091 return -1;
6092 numvar:
6093 len = cvtnum(num);
6094 break;
6095 case '-':
6096 p = makestrspace(NOPTS, expdest);
6097 for (i = NOPTS - 1; i >= 0; i--) {
6098 if (optlist[i]) {
6099 USTPUTC(optletters(i), p);
6100 len++;
6101 }
6102 }
6103 expdest = p;
6104 break;
6105 case '@':
6106 if (sep)
6107 goto param;
6108 /* fall through */
6109 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006110 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6112 sepq = 1;
6113 param:
6114 ap = shellparam.p;
6115 if (!ap)
6116 return -1;
6117 while ((p = *ap++)) {
6118 size_t partlen;
6119
6120 partlen = strlen(p);
6121 len += partlen;
6122
6123 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6124 memtodest(p, partlen, syntax, quotes);
6125
6126 if (*ap && sep) {
6127 char *q;
6128
6129 len++;
6130 if (subtype == VSPLUS || subtype == VSLENGTH) {
6131 continue;
6132 }
6133 q = expdest;
6134 if (sepq)
6135 STPUTC(CTLESC, q);
6136 STPUTC(sep, q);
6137 expdest = q;
6138 }
6139 }
6140 return len;
6141 case '0':
6142 case '1':
6143 case '2':
6144 case '3':
6145 case '4':
6146 case '5':
6147 case '6':
6148 case '7':
6149 case '8':
6150 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006151// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006152 num = atoi(name);
6153 if (num < 0 || num > shellparam.nparam)
6154 return -1;
6155 p = num ? shellparam.p[num - 1] : arg0;
6156 goto value;
6157 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006158 /* NB: name has form "VAR=..." */
6159
6160 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6161 * which should be considered before we check variables. */
6162 if (var_str_list) {
6163 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6164 p = NULL;
6165 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006166 char *str, *eq;
6167 str = var_str_list->text;
6168 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006169 if (!eq) /* stop at first non-assignment */
6170 break;
6171 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006172 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006173 && strncmp(str, name, name_len) == 0) {
6174 p = eq;
6175 /* goto value; - WRONG! */
6176 /* think "A=1 A=2 B=$A" */
6177 }
6178 var_str_list = var_str_list->next;
6179 } while (var_str_list);
6180 if (p)
6181 goto value;
6182 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 p = lookupvar(name);
6184 value:
6185 if (!p)
6186 return -1;
6187
6188 len = strlen(p);
6189 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6190 memtodest(p, len, syntax, quotes);
6191 return len;
6192 }
6193
6194 if (subtype == VSPLUS || subtype == VSLENGTH)
6195 STADJUST(-len, expdest);
6196 return len;
6197}
6198
6199/*
6200 * Expand a variable, and return a pointer to the next character in the
6201 * input string.
6202 */
6203static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006204evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006205{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006206 char varflags;
6207 char subtype;
6208 char quoted;
6209 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210 char *var;
6211 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006212 int startloc;
6213 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006214
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215 varflags = *p++;
6216 subtype = varflags & VSTYPE;
6217 quoted = varflags & VSQUOTE;
6218 var = p;
6219 easy = (!quoted || (*var == '@' && shellparam.nparam));
6220 startloc = expdest - (char *)stackblock();
6221 p = strchr(p, '=') + 1;
6222
6223 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006224 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006225 if (varflags & VSNUL)
6226 varlen--;
6227
6228 if (subtype == VSPLUS) {
6229 varlen = -1 - varlen;
6230 goto vsplus;
6231 }
6232
6233 if (subtype == VSMINUS) {
6234 vsplus:
6235 if (varlen < 0) {
6236 argstr(
6237 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006238 (quoted ? EXP_QWORD : EXP_WORD),
6239 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006240 );
6241 goto end;
6242 }
6243 if (easy)
6244 goto record;
6245 goto end;
6246 }
6247
6248 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6249 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006250 if (subevalvar(p, var, /* strloc: */ 0,
6251 subtype, startloc, varflags,
6252 /* quotes: */ 0,
6253 var_str_list)
6254 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006255 varflags &= ~VSNUL;
6256 /*
6257 * Remove any recorded regions beyond
6258 * start of variable
6259 */
6260 removerecordregions(startloc);
6261 goto again;
6262 }
6263 goto end;
6264 }
6265 if (easy)
6266 goto record;
6267 goto end;
6268 }
6269
6270 if (varlen < 0 && uflag)
6271 varunset(p, var, 0, 0);
6272
6273 if (subtype == VSLENGTH) {
6274 cvtnum(varlen > 0 ? varlen : 0);
6275 goto record;
6276 }
6277
6278 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006279 if (easy)
6280 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281 goto end;
6282 }
6283
6284#if DEBUG
6285 switch (subtype) {
6286 case VSTRIMLEFT:
6287 case VSTRIMLEFTMAX:
6288 case VSTRIMRIGHT:
6289 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006290#if ENABLE_ASH_BASH_COMPAT
6291 case VSSUBSTR:
6292 case VSREPLACE:
6293 case VSREPLACEALL:
6294#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006295 break;
6296 default:
6297 abort();
6298 }
6299#endif
6300
6301 if (varlen >= 0) {
6302 /*
6303 * Terminate the string and start recording the pattern
6304 * right after it
6305 */
6306 STPUTC('\0', expdest);
6307 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006308 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6309 startloc, varflags,
6310 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6311 var_str_list)
6312 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006313 int amount = expdest - (
6314 (char *)stackblock() + patloc - 1
6315 );
6316 STADJUST(-amount, expdest);
6317 }
6318 /* Remove any recorded regions beyond start of variable */
6319 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006320 record:
6321 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322 }
6323
6324 end:
6325 if (subtype != VSNORMAL) { /* skip to end of alternative */
6326 int nesting = 1;
6327 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006328 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329 if (c == CTLESC)
6330 p++;
6331 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6332 if (varlen >= 0)
6333 argbackq = argbackq->next;
6334 } else if (c == CTLVAR) {
6335 if ((*p++ & VSTYPE) != VSNORMAL)
6336 nesting++;
6337 } else if (c == CTLENDVAR) {
6338 if (--nesting == 0)
6339 break;
6340 }
6341 }
6342 }
6343 return p;
6344}
6345
6346/*
6347 * Break the argument string into pieces based upon IFS and add the
6348 * strings to the argument list. The regions of the string to be
6349 * searched for IFS characters have been stored by recordregion.
6350 */
6351static void
6352ifsbreakup(char *string, struct arglist *arglist)
6353{
6354 struct ifsregion *ifsp;
6355 struct strlist *sp;
6356 char *start;
6357 char *p;
6358 char *q;
6359 const char *ifs, *realifs;
6360 int ifsspc;
6361 int nulonly;
6362
6363 start = string;
6364 if (ifslastp != NULL) {
6365 ifsspc = 0;
6366 nulonly = 0;
6367 realifs = ifsset() ? ifsval() : defifs;
6368 ifsp = &ifsfirst;
6369 do {
6370 p = string + ifsp->begoff;
6371 nulonly = ifsp->nulonly;
6372 ifs = nulonly ? nullstr : realifs;
6373 ifsspc = 0;
6374 while (p < string + ifsp->endoff) {
6375 q = p;
6376 if (*p == CTLESC)
6377 p++;
6378 if (!strchr(ifs, *p)) {
6379 p++;
6380 continue;
6381 }
6382 if (!nulonly)
6383 ifsspc = (strchr(defifs, *p) != NULL);
6384 /* Ignore IFS whitespace at start */
6385 if (q == start && ifsspc) {
6386 p++;
6387 start = p;
6388 continue;
6389 }
6390 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006391 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006392 sp->text = start;
6393 *arglist->lastp = sp;
6394 arglist->lastp = &sp->next;
6395 p++;
6396 if (!nulonly) {
6397 for (;;) {
6398 if (p >= string + ifsp->endoff) {
6399 break;
6400 }
6401 q = p;
6402 if (*p == CTLESC)
6403 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006404 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405 p = q;
6406 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006407 }
6408 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006409 if (ifsspc) {
6410 p++;
6411 ifsspc = 0;
6412 } else {
6413 p = q;
6414 break;
6415 }
6416 } else
6417 p++;
6418 }
6419 }
6420 start = p;
6421 } /* while */
6422 ifsp = ifsp->next;
6423 } while (ifsp != NULL);
6424 if (nulonly)
6425 goto add;
6426 }
6427
6428 if (!*start)
6429 return;
6430
6431 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006432 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433 sp->text = start;
6434 *arglist->lastp = sp;
6435 arglist->lastp = &sp->next;
6436}
6437
6438static void
6439ifsfree(void)
6440{
6441 struct ifsregion *p;
6442
6443 INT_OFF;
6444 p = ifsfirst.next;
6445 do {
6446 struct ifsregion *ifsp;
6447 ifsp = p->next;
6448 free(p);
6449 p = ifsp;
6450 } while (p);
6451 ifslastp = NULL;
6452 ifsfirst.next = NULL;
6453 INT_ON;
6454}
6455
6456/*
6457 * Add a file name to the list.
6458 */
6459static void
6460addfname(const char *name)
6461{
6462 struct strlist *sp;
6463
Denis Vlasenko597906c2008-02-20 16:38:54 +00006464 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006465 sp->text = ststrdup(name);
6466 *exparg.lastp = sp;
6467 exparg.lastp = &sp->next;
6468}
6469
6470static char *expdir;
6471
6472/*
6473 * Do metacharacter (i.e. *, ?, [...]) expansion.
6474 */
6475static void
6476expmeta(char *enddir, char *name)
6477{
6478 char *p;
6479 const char *cp;
6480 char *start;
6481 char *endname;
6482 int metaflag;
6483 struct stat statb;
6484 DIR *dirp;
6485 struct dirent *dp;
6486 int atend;
6487 int matchdot;
6488
6489 metaflag = 0;
6490 start = name;
6491 for (p = name; *p; p++) {
6492 if (*p == '*' || *p == '?')
6493 metaflag = 1;
6494 else if (*p == '[') {
6495 char *q = p + 1;
6496 if (*q == '!')
6497 q++;
6498 for (;;) {
6499 if (*q == '\\')
6500 q++;
6501 if (*q == '/' || *q == '\0')
6502 break;
6503 if (*++q == ']') {
6504 metaflag = 1;
6505 break;
6506 }
6507 }
6508 } else if (*p == '\\')
6509 p++;
6510 else if (*p == '/') {
6511 if (metaflag)
6512 goto out;
6513 start = p + 1;
6514 }
6515 }
6516 out:
6517 if (metaflag == 0) { /* we've reached the end of the file name */
6518 if (enddir != expdir)
6519 metaflag++;
6520 p = name;
6521 do {
6522 if (*p == '\\')
6523 p++;
6524 *enddir++ = *p;
6525 } while (*p++);
6526 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6527 addfname(expdir);
6528 return;
6529 }
6530 endname = p;
6531 if (name < start) {
6532 p = name;
6533 do {
6534 if (*p == '\\')
6535 p++;
6536 *enddir++ = *p++;
6537 } while (p < start);
6538 }
6539 if (enddir == expdir) {
6540 cp = ".";
6541 } else if (enddir == expdir + 1 && *expdir == '/') {
6542 cp = "/";
6543 } else {
6544 cp = expdir;
6545 enddir[-1] = '\0';
6546 }
6547 dirp = opendir(cp);
6548 if (dirp == NULL)
6549 return;
6550 if (enddir != expdir)
6551 enddir[-1] = '/';
6552 if (*endname == 0) {
6553 atend = 1;
6554 } else {
6555 atend = 0;
6556 *endname++ = '\0';
6557 }
6558 matchdot = 0;
6559 p = start;
6560 if (*p == '\\')
6561 p++;
6562 if (*p == '.')
6563 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006564 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006565 if (dp->d_name[0] == '.' && ! matchdot)
6566 continue;
6567 if (pmatch(start, dp->d_name)) {
6568 if (atend) {
6569 strcpy(enddir, dp->d_name);
6570 addfname(expdir);
6571 } else {
6572 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6573 continue;
6574 p[-1] = '/';
6575 expmeta(p, endname);
6576 }
6577 }
6578 }
6579 closedir(dirp);
6580 if (! atend)
6581 endname[-1] = '/';
6582}
6583
6584static struct strlist *
6585msort(struct strlist *list, int len)
6586{
6587 struct strlist *p, *q = NULL;
6588 struct strlist **lpp;
6589 int half;
6590 int n;
6591
6592 if (len <= 1)
6593 return list;
6594 half = len >> 1;
6595 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006596 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 q = p;
6598 p = p->next;
6599 }
6600 q->next = NULL; /* terminate first half of list */
6601 q = msort(list, half); /* sort first half of list */
6602 p = msort(p, len - half); /* sort second half */
6603 lpp = &list;
6604 for (;;) {
6605#if ENABLE_LOCALE_SUPPORT
6606 if (strcoll(p->text, q->text) < 0)
6607#else
6608 if (strcmp(p->text, q->text) < 0)
6609#endif
6610 {
6611 *lpp = p;
6612 lpp = &p->next;
6613 p = *lpp;
6614 if (p == NULL) {
6615 *lpp = q;
6616 break;
6617 }
6618 } else {
6619 *lpp = q;
6620 lpp = &q->next;
6621 q = *lpp;
6622 if (q == NULL) {
6623 *lpp = p;
6624 break;
6625 }
6626 }
6627 }
6628 return list;
6629}
6630
6631/*
6632 * Sort the results of file name expansion. It calculates the number of
6633 * strings to sort and then calls msort (short for merge sort) to do the
6634 * work.
6635 */
6636static struct strlist *
6637expsort(struct strlist *str)
6638{
6639 int len;
6640 struct strlist *sp;
6641
6642 len = 0;
6643 for (sp = str; sp; sp = sp->next)
6644 len++;
6645 return msort(str, len);
6646}
6647
6648static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006649expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006650{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006651 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006652 '*', '?', '[', 0
6653 };
6654 /* TODO - EXP_REDIR */
6655
6656 while (str) {
6657 struct strlist **savelastp;
6658 struct strlist *sp;
6659 char *p;
6660
6661 if (fflag)
6662 goto nometa;
6663 if (!strpbrk(str->text, metachars))
6664 goto nometa;
6665 savelastp = exparg.lastp;
6666
6667 INT_OFF;
6668 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6669 {
6670 int i = strlen(str->text);
6671 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6672 }
6673
6674 expmeta(expdir, p);
6675 free(expdir);
6676 if (p != str->text)
6677 free(p);
6678 INT_ON;
6679 if (exparg.lastp == savelastp) {
6680 /*
6681 * no matches
6682 */
6683 nometa:
6684 *exparg.lastp = str;
6685 rmescapes(str->text);
6686 exparg.lastp = &str->next;
6687 } else {
6688 *exparg.lastp = NULL;
6689 *savelastp = sp = expsort(*savelastp);
6690 while (sp->next != NULL)
6691 sp = sp->next;
6692 exparg.lastp = &sp->next;
6693 }
6694 str = str->next;
6695 }
6696}
6697
6698/*
6699 * Perform variable substitution and command substitution on an argument,
6700 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6701 * perform splitting and file name expansion. When arglist is NULL, perform
6702 * here document expansion.
6703 */
6704static void
6705expandarg(union node *arg, struct arglist *arglist, int flag)
6706{
6707 struct strlist *sp;
6708 char *p;
6709
6710 argbackq = arg->narg.backquote;
6711 STARTSTACKSTR(expdest);
6712 ifsfirst.next = NULL;
6713 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006714 argstr(arg->narg.text, flag,
6715 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716 p = _STPUTC('\0', expdest);
6717 expdest = p - 1;
6718 if (arglist == NULL) {
6719 return; /* here document expanded */
6720 }
6721 p = grabstackstr(p);
6722 exparg.lastp = &exparg.list;
6723 /*
6724 * TODO - EXP_REDIR
6725 */
6726 if (flag & EXP_FULL) {
6727 ifsbreakup(p, &exparg);
6728 *exparg.lastp = NULL;
6729 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006730 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006731 } else {
6732 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6733 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006734 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006735 sp->text = p;
6736 *exparg.lastp = sp;
6737 exparg.lastp = &sp->next;
6738 }
6739 if (ifsfirst.next)
6740 ifsfree();
6741 *exparg.lastp = NULL;
6742 if (exparg.list) {
6743 *arglist->lastp = exparg.list;
6744 arglist->lastp = exparg.lastp;
6745 }
6746}
6747
6748/*
6749 * Expand shell variables and backquotes inside a here document.
6750 */
6751static void
6752expandhere(union node *arg, int fd)
6753{
6754 herefd = fd;
6755 expandarg(arg, (struct arglist *)NULL, 0);
6756 full_write(fd, stackblock(), expdest - (char *)stackblock());
6757}
6758
6759/*
6760 * Returns true if the pattern matches the string.
6761 */
6762static int
6763patmatch(char *pattern, const char *string)
6764{
6765 return pmatch(preglob(pattern, 0, 0), string);
6766}
6767
6768/*
6769 * See if a pattern matches in a case statement.
6770 */
6771static int
6772casematch(union node *pattern, char *val)
6773{
6774 struct stackmark smark;
6775 int result;
6776
6777 setstackmark(&smark);
6778 argbackq = pattern->narg.backquote;
6779 STARTSTACKSTR(expdest);
6780 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006781 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6782 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006783 STACKSTRNUL(expdest);
6784 result = patmatch(stackblock(), val);
6785 popstackmark(&smark);
6786 return result;
6787}
6788
6789
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006790/* ============ find_command */
6791
6792struct builtincmd {
6793 const char *name;
6794 int (*builtin)(int, char **);
6795 /* unsigned flags; */
6796};
6797#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006798/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006799 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006800#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006801#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006802
6803struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006804 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006805 union param {
6806 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006807 /* index >= 0 for commands without path (slashes) */
6808 /* (TODO: what exactly does the value mean? PATH position?) */
6809 /* index == -1 for commands with slashes */
6810 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006811 const struct builtincmd *cmd;
6812 struct funcnode *func;
6813 } u;
6814};
6815/* values of cmdtype */
6816#define CMDUNKNOWN -1 /* no entry in table for command */
6817#define CMDNORMAL 0 /* command is an executable program */
6818#define CMDFUNCTION 1 /* command is a shell function */
6819#define CMDBUILTIN 2 /* command is a shell builtin */
6820
6821/* action to find_command() */
6822#define DO_ERR 0x01 /* prints errors */
6823#define DO_ABS 0x02 /* checks absolute paths */
6824#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6825#define DO_ALTPATH 0x08 /* using alternate path */
6826#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6827
6828static void find_command(char *, struct cmdentry *, int, const char *);
6829
6830
6831/* ============ Hashing commands */
6832
6833/*
6834 * When commands are first encountered, they are entered in a hash table.
6835 * This ensures that a full path search will not have to be done for them
6836 * on each invocation.
6837 *
6838 * We should investigate converting to a linear search, even though that
6839 * would make the command name "hash" a misnomer.
6840 */
6841
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006842struct tblentry {
6843 struct tblentry *next; /* next entry in hash chain */
6844 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006845 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006846 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006847 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006848};
6849
Denis Vlasenko01631112007-12-16 17:20:38 +00006850static struct tblentry **cmdtable;
6851#define INIT_G_cmdtable() do { \
6852 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6853} while (0)
6854
6855static int builtinloc = -1; /* index in path of %builtin, or -1 */
6856
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006857
6858static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006859tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860{
6861 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006862
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006863#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006864 if (applet_no >= 0) {
6865 if (APPLET_IS_NOEXEC(applet_no))
6866 run_applet_no_and_exit(applet_no, argv);
6867 /* re-exec ourselves with the new arguments */
6868 execve(bb_busybox_exec_path, argv, envp);
6869 /* If they called chroot or otherwise made the binary no longer
6870 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006871 }
6872#endif
6873
6874 repeat:
6875#ifdef SYSV
6876 do {
6877 execve(cmd, argv, envp);
6878 } while (errno == EINTR);
6879#else
6880 execve(cmd, argv, envp);
6881#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006882 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006883 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006884 return;
6885 }
6886 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006887 char **ap;
6888 char **new;
6889
6890 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006891 continue;
6892 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006893 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006894 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006895 ap += 2;
6896 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006897 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006898 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006899 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006900 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901 goto repeat;
6902 }
6903}
6904
6905/*
6906 * Exec a program. Never returns. If you change this routine, you may
6907 * have to change the find_command routine as well.
6908 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006909static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6910static void
6911shellexec(char **argv, const char *path, int idx)
6912{
6913 char *cmdname;
6914 int e;
6915 char **envp;
6916 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006917#if ENABLE_FEATURE_SH_STANDALONE
6918 int applet_no = -1;
6919#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006920
6921 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006922 envp = listvars(VEXPORT, VUNSET, 0);
6923 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006924#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006925 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006926#endif
6927 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006928 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006929 e = errno;
6930 } else {
6931 e = ENOENT;
6932 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6933 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006934 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006935 if (errno != ENOENT && errno != ENOTDIR)
6936 e = errno;
6937 }
6938 stunalloc(cmdname);
6939 }
6940 }
6941
6942 /* Map to POSIX errors */
6943 switch (e) {
6944 case EACCES:
6945 exerrno = 126;
6946 break;
6947 case ENOENT:
6948 exerrno = 127;
6949 break;
6950 default:
6951 exerrno = 2;
6952 break;
6953 }
6954 exitstatus = exerrno;
6955 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006956 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6958 /* NOTREACHED */
6959}
6960
6961static void
6962printentry(struct tblentry *cmdp)
6963{
6964 int idx;
6965 const char *path;
6966 char *name;
6967
6968 idx = cmdp->param.index;
6969 path = pathval();
6970 do {
6971 name = padvance(&path, cmdp->cmdname);
6972 stunalloc(name);
6973 } while (--idx >= 0);
6974 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6975}
6976
6977/*
6978 * Clear out command entries. The argument specifies the first entry in
6979 * PATH which has changed.
6980 */
6981static void
6982clearcmdentry(int firstchange)
6983{
6984 struct tblentry **tblp;
6985 struct tblentry **pp;
6986 struct tblentry *cmdp;
6987
6988 INT_OFF;
6989 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6990 pp = tblp;
6991 while ((cmdp = *pp) != NULL) {
6992 if ((cmdp->cmdtype == CMDNORMAL &&
6993 cmdp->param.index >= firstchange)
6994 || (cmdp->cmdtype == CMDBUILTIN &&
6995 builtinloc >= firstchange)
6996 ) {
6997 *pp = cmdp->next;
6998 free(cmdp);
6999 } else {
7000 pp = &cmdp->next;
7001 }
7002 }
7003 }
7004 INT_ON;
7005}
7006
7007/*
7008 * Locate a command in the command hash table. If "add" is nonzero,
7009 * add the command to the table if it is not already present. The
7010 * variable "lastcmdentry" is set to point to the address of the link
7011 * pointing to the entry, so that delete_cmd_entry can delete the
7012 * entry.
7013 *
7014 * Interrupts must be off if called with add != 0.
7015 */
7016static struct tblentry **lastcmdentry;
7017
7018static struct tblentry *
7019cmdlookup(const char *name, int add)
7020{
7021 unsigned int hashval;
7022 const char *p;
7023 struct tblentry *cmdp;
7024 struct tblentry **pp;
7025
7026 p = name;
7027 hashval = (unsigned char)*p << 4;
7028 while (*p)
7029 hashval += (unsigned char)*p++;
7030 hashval &= 0x7FFF;
7031 pp = &cmdtable[hashval % CMDTABLESIZE];
7032 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7033 if (strcmp(cmdp->cmdname, name) == 0)
7034 break;
7035 pp = &cmdp->next;
7036 }
7037 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007038 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7039 + strlen(name)
7040 /* + 1 - already done because
7041 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007042 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043 cmdp->cmdtype = CMDUNKNOWN;
7044 strcpy(cmdp->cmdname, name);
7045 }
7046 lastcmdentry = pp;
7047 return cmdp;
7048}
7049
7050/*
7051 * Delete the command entry returned on the last lookup.
7052 */
7053static void
7054delete_cmd_entry(void)
7055{
7056 struct tblentry *cmdp;
7057
7058 INT_OFF;
7059 cmdp = *lastcmdentry;
7060 *lastcmdentry = cmdp->next;
7061 if (cmdp->cmdtype == CMDFUNCTION)
7062 freefunc(cmdp->param.func);
7063 free(cmdp);
7064 INT_ON;
7065}
7066
7067/*
7068 * Add a new command entry, replacing any existing command entry for
7069 * the same name - except special builtins.
7070 */
7071static void
7072addcmdentry(char *name, struct cmdentry *entry)
7073{
7074 struct tblentry *cmdp;
7075
7076 cmdp = cmdlookup(name, 1);
7077 if (cmdp->cmdtype == CMDFUNCTION) {
7078 freefunc(cmdp->param.func);
7079 }
7080 cmdp->cmdtype = entry->cmdtype;
7081 cmdp->param = entry->u;
7082 cmdp->rehash = 0;
7083}
7084
7085static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007086hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007087{
7088 struct tblentry **pp;
7089 struct tblentry *cmdp;
7090 int c;
7091 struct cmdentry entry;
7092 char *name;
7093
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007094 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007095 clearcmdentry(0);
7096 return 0;
7097 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007098
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007099 if (*argptr == NULL) {
7100 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7101 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7102 if (cmdp->cmdtype == CMDNORMAL)
7103 printentry(cmdp);
7104 }
7105 }
7106 return 0;
7107 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007108
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007109 c = 0;
7110 while ((name = *argptr) != NULL) {
7111 cmdp = cmdlookup(name, 0);
7112 if (cmdp != NULL
7113 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007114 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7115 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007116 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007117 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007118 find_command(name, &entry, DO_ERR, pathval());
7119 if (entry.cmdtype == CMDUNKNOWN)
7120 c = 1;
7121 argptr++;
7122 }
7123 return c;
7124}
7125
7126/*
7127 * Called when a cd is done. Marks all commands so the next time they
7128 * are executed they will be rehashed.
7129 */
7130static void
7131hashcd(void)
7132{
7133 struct tblentry **pp;
7134 struct tblentry *cmdp;
7135
7136 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7137 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007138 if (cmdp->cmdtype == CMDNORMAL
7139 || (cmdp->cmdtype == CMDBUILTIN
7140 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7141 && builtinloc > 0)
7142 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007143 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007144 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007145 }
7146 }
7147}
7148
7149/*
7150 * Fix command hash table when PATH changed.
7151 * Called before PATH is changed. The argument is the new value of PATH;
7152 * pathval() still returns the old value at this point.
7153 * Called with interrupts off.
7154 */
7155static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007156changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007157{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007158 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007159 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007160 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007161 int idx_bltin;
7162
7163 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007164 firstchange = 9999; /* assume no change */
7165 idx = 0;
7166 idx_bltin = -1;
7167 for (;;) {
7168 if (*old != *new) {
7169 firstchange = idx;
7170 if ((*old == '\0' && *new == ':')
7171 || (*old == ':' && *new == '\0'))
7172 firstchange++;
7173 old = new; /* ignore subsequent differences */
7174 }
7175 if (*new == '\0')
7176 break;
7177 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7178 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007179 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007180 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007181 new++, old++;
7182 }
7183 if (builtinloc < 0 && idx_bltin >= 0)
7184 builtinloc = idx_bltin; /* zap builtins */
7185 if (builtinloc >= 0 && idx_bltin < 0)
7186 firstchange = 0;
7187 clearcmdentry(firstchange);
7188 builtinloc = idx_bltin;
7189}
7190
7191#define TEOF 0
7192#define TNL 1
7193#define TREDIR 2
7194#define TWORD 3
7195#define TSEMI 4
7196#define TBACKGND 5
7197#define TAND 6
7198#define TOR 7
7199#define TPIPE 8
7200#define TLP 9
7201#define TRP 10
7202#define TENDCASE 11
7203#define TENDBQUOTE 12
7204#define TNOT 13
7205#define TCASE 14
7206#define TDO 15
7207#define TDONE 16
7208#define TELIF 17
7209#define TELSE 18
7210#define TESAC 19
7211#define TFI 20
7212#define TFOR 21
7213#define TIF 22
7214#define TIN 23
7215#define TTHEN 24
7216#define TUNTIL 25
7217#define TWHILE 26
7218#define TBEGIN 27
7219#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007220typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007221
7222/* first char is indicating which tokens mark the end of a list */
7223static const char *const tokname_array[] = {
7224 "\1end of file",
7225 "\0newline",
7226 "\0redirection",
7227 "\0word",
7228 "\0;",
7229 "\0&",
7230 "\0&&",
7231 "\0||",
7232 "\0|",
7233 "\0(",
7234 "\1)",
7235 "\1;;",
7236 "\1`",
7237#define KWDOFFSET 13
7238 /* the following are keywords */
7239 "\0!",
7240 "\0case",
7241 "\1do",
7242 "\1done",
7243 "\1elif",
7244 "\1else",
7245 "\1esac",
7246 "\1fi",
7247 "\0for",
7248 "\0if",
7249 "\0in",
7250 "\1then",
7251 "\0until",
7252 "\0while",
7253 "\0{",
7254 "\1}",
7255};
7256
7257static const char *
7258tokname(int tok)
7259{
7260 static char buf[16];
7261
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007262//try this:
7263//if (tok < TSEMI) return tokname_array[tok] + 1;
7264//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7265//return buf;
7266
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007267 if (tok >= TSEMI)
7268 buf[0] = '"';
7269 sprintf(buf + (tok >= TSEMI), "%s%c",
7270 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7271 return buf;
7272}
7273
7274/* Wrapper around strcmp for qsort/bsearch/... */
7275static int
7276pstrcmp(const void *a, const void *b)
7277{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007278 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007279}
7280
7281static const char *const *
7282findkwd(const char *s)
7283{
7284 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007285 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7286 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287}
7288
7289/*
7290 * Locate and print what a word is...
7291 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007292static int
7293describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294{
7295 struct cmdentry entry;
7296 struct tblentry *cmdp;
7297#if ENABLE_ASH_ALIAS
7298 const struct alias *ap;
7299#endif
7300 const char *path = pathval();
7301
7302 if (describe_command_verbose) {
7303 out1str(command);
7304 }
7305
7306 /* First look at the keywords */
7307 if (findkwd(command)) {
7308 out1str(describe_command_verbose ? " is a shell keyword" : command);
7309 goto out;
7310 }
7311
7312#if ENABLE_ASH_ALIAS
7313 /* Then look at the aliases */
7314 ap = lookupalias(command, 0);
7315 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007316 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007317 out1str("alias ");
7318 printalias(ap);
7319 return 0;
7320 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007321 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007322 goto out;
7323 }
7324#endif
7325 /* Then check if it is a tracked alias */
7326 cmdp = cmdlookup(command, 0);
7327 if (cmdp != NULL) {
7328 entry.cmdtype = cmdp->cmdtype;
7329 entry.u = cmdp->param;
7330 } else {
7331 /* Finally use brute force */
7332 find_command(command, &entry, DO_ABS, path);
7333 }
7334
7335 switch (entry.cmdtype) {
7336 case CMDNORMAL: {
7337 int j = entry.u.index;
7338 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007339 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340 p = command;
7341 } else {
7342 do {
7343 p = padvance(&path, command);
7344 stunalloc(p);
7345 } while (--j >= 0);
7346 }
7347 if (describe_command_verbose) {
7348 out1fmt(" is%s %s",
7349 (cmdp ? " a tracked alias for" : nullstr), p
7350 );
7351 } else {
7352 out1str(p);
7353 }
7354 break;
7355 }
7356
7357 case CMDFUNCTION:
7358 if (describe_command_verbose) {
7359 out1str(" is a shell function");
7360 } else {
7361 out1str(command);
7362 }
7363 break;
7364
7365 case CMDBUILTIN:
7366 if (describe_command_verbose) {
7367 out1fmt(" is a %sshell builtin",
7368 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7369 "special " : nullstr
7370 );
7371 } else {
7372 out1str(command);
7373 }
7374 break;
7375
7376 default:
7377 if (describe_command_verbose) {
7378 out1str(": not found\n");
7379 }
7380 return 127;
7381 }
7382 out:
7383 outstr("\n", stdout);
7384 return 0;
7385}
7386
7387static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007388typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007390 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007392 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393
Denis Vlasenko46846e22007-05-20 13:08:31 +00007394 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007395 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007396 i++;
7397 verbose = 0;
7398 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007399 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007400 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 }
7402 return err;
7403}
7404
7405#if ENABLE_ASH_CMDCMD
7406static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007407commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408{
7409 int c;
7410 enum {
7411 VERIFY_BRIEF = 1,
7412 VERIFY_VERBOSE = 2,
7413 } verify = 0;
7414
7415 while ((c = nextopt("pvV")) != '\0')
7416 if (c == 'V')
7417 verify |= VERIFY_VERBOSE;
7418 else if (c == 'v')
7419 verify |= VERIFY_BRIEF;
7420#if DEBUG
7421 else if (c != 'p')
7422 abort();
7423#endif
7424 if (verify)
7425 return describe_command(*argptr, verify - VERIFY_BRIEF);
7426
7427 return 0;
7428}
7429#endif
7430
7431
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007432/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007433
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007434static int funcblocksize; /* size of structures in function */
7435static int funcstringsize; /* size of strings in node */
7436static void *funcblock; /* block to allocate function from */
7437static char *funcstring; /* block to allocate strings from */
7438
Eric Andersencb57d552001-06-28 07:25:16 +00007439/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007440#define EV_EXIT 01 /* exit after evaluating tree */
7441#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7442#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007444static const short nodesize[26] = {
7445 SHELL_ALIGN(sizeof(struct ncmd)),
7446 SHELL_ALIGN(sizeof(struct npipe)),
7447 SHELL_ALIGN(sizeof(struct nredir)),
7448 SHELL_ALIGN(sizeof(struct nredir)),
7449 SHELL_ALIGN(sizeof(struct nredir)),
7450 SHELL_ALIGN(sizeof(struct nbinary)),
7451 SHELL_ALIGN(sizeof(struct nbinary)),
7452 SHELL_ALIGN(sizeof(struct nbinary)),
7453 SHELL_ALIGN(sizeof(struct nif)),
7454 SHELL_ALIGN(sizeof(struct nbinary)),
7455 SHELL_ALIGN(sizeof(struct nbinary)),
7456 SHELL_ALIGN(sizeof(struct nfor)),
7457 SHELL_ALIGN(sizeof(struct ncase)),
7458 SHELL_ALIGN(sizeof(struct nclist)),
7459 SHELL_ALIGN(sizeof(struct narg)),
7460 SHELL_ALIGN(sizeof(struct narg)),
7461 SHELL_ALIGN(sizeof(struct nfile)),
7462 SHELL_ALIGN(sizeof(struct nfile)),
7463 SHELL_ALIGN(sizeof(struct nfile)),
7464 SHELL_ALIGN(sizeof(struct nfile)),
7465 SHELL_ALIGN(sizeof(struct nfile)),
7466 SHELL_ALIGN(sizeof(struct ndup)),
7467 SHELL_ALIGN(sizeof(struct ndup)),
7468 SHELL_ALIGN(sizeof(struct nhere)),
7469 SHELL_ALIGN(sizeof(struct nhere)),
7470 SHELL_ALIGN(sizeof(struct nnot)),
7471};
7472
7473static void calcsize(union node *n);
7474
7475static void
7476sizenodelist(struct nodelist *lp)
7477{
7478 while (lp) {
7479 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7480 calcsize(lp->n);
7481 lp = lp->next;
7482 }
7483}
7484
7485static void
7486calcsize(union node *n)
7487{
7488 if (n == NULL)
7489 return;
7490 funcblocksize += nodesize[n->type];
7491 switch (n->type) {
7492 case NCMD:
7493 calcsize(n->ncmd.redirect);
7494 calcsize(n->ncmd.args);
7495 calcsize(n->ncmd.assign);
7496 break;
7497 case NPIPE:
7498 sizenodelist(n->npipe.cmdlist);
7499 break;
7500 case NREDIR:
7501 case NBACKGND:
7502 case NSUBSHELL:
7503 calcsize(n->nredir.redirect);
7504 calcsize(n->nredir.n);
7505 break;
7506 case NAND:
7507 case NOR:
7508 case NSEMI:
7509 case NWHILE:
7510 case NUNTIL:
7511 calcsize(n->nbinary.ch2);
7512 calcsize(n->nbinary.ch1);
7513 break;
7514 case NIF:
7515 calcsize(n->nif.elsepart);
7516 calcsize(n->nif.ifpart);
7517 calcsize(n->nif.test);
7518 break;
7519 case NFOR:
7520 funcstringsize += strlen(n->nfor.var) + 1;
7521 calcsize(n->nfor.body);
7522 calcsize(n->nfor.args);
7523 break;
7524 case NCASE:
7525 calcsize(n->ncase.cases);
7526 calcsize(n->ncase.expr);
7527 break;
7528 case NCLIST:
7529 calcsize(n->nclist.body);
7530 calcsize(n->nclist.pattern);
7531 calcsize(n->nclist.next);
7532 break;
7533 case NDEFUN:
7534 case NARG:
7535 sizenodelist(n->narg.backquote);
7536 funcstringsize += strlen(n->narg.text) + 1;
7537 calcsize(n->narg.next);
7538 break;
7539 case NTO:
7540 case NCLOBBER:
7541 case NFROM:
7542 case NFROMTO:
7543 case NAPPEND:
7544 calcsize(n->nfile.fname);
7545 calcsize(n->nfile.next);
7546 break;
7547 case NTOFD:
7548 case NFROMFD:
7549 calcsize(n->ndup.vname);
7550 calcsize(n->ndup.next);
7551 break;
7552 case NHERE:
7553 case NXHERE:
7554 calcsize(n->nhere.doc);
7555 calcsize(n->nhere.next);
7556 break;
7557 case NNOT:
7558 calcsize(n->nnot.com);
7559 break;
7560 };
7561}
7562
7563static char *
7564nodeckstrdup(char *s)
7565{
7566 char *rtn = funcstring;
7567
7568 strcpy(funcstring, s);
7569 funcstring += strlen(s) + 1;
7570 return rtn;
7571}
7572
7573static union node *copynode(union node *);
7574
7575static struct nodelist *
7576copynodelist(struct nodelist *lp)
7577{
7578 struct nodelist *start;
7579 struct nodelist **lpp;
7580
7581 lpp = &start;
7582 while (lp) {
7583 *lpp = funcblock;
7584 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7585 (*lpp)->n = copynode(lp->n);
7586 lp = lp->next;
7587 lpp = &(*lpp)->next;
7588 }
7589 *lpp = NULL;
7590 return start;
7591}
7592
7593static union node *
7594copynode(union node *n)
7595{
7596 union node *new;
7597
7598 if (n == NULL)
7599 return NULL;
7600 new = funcblock;
7601 funcblock = (char *) funcblock + nodesize[n->type];
7602
7603 switch (n->type) {
7604 case NCMD:
7605 new->ncmd.redirect = copynode(n->ncmd.redirect);
7606 new->ncmd.args = copynode(n->ncmd.args);
7607 new->ncmd.assign = copynode(n->ncmd.assign);
7608 break;
7609 case NPIPE:
7610 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7611 new->npipe.backgnd = n->npipe.backgnd;
7612 break;
7613 case NREDIR:
7614 case NBACKGND:
7615 case NSUBSHELL:
7616 new->nredir.redirect = copynode(n->nredir.redirect);
7617 new->nredir.n = copynode(n->nredir.n);
7618 break;
7619 case NAND:
7620 case NOR:
7621 case NSEMI:
7622 case NWHILE:
7623 case NUNTIL:
7624 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7625 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7626 break;
7627 case NIF:
7628 new->nif.elsepart = copynode(n->nif.elsepart);
7629 new->nif.ifpart = copynode(n->nif.ifpart);
7630 new->nif.test = copynode(n->nif.test);
7631 break;
7632 case NFOR:
7633 new->nfor.var = nodeckstrdup(n->nfor.var);
7634 new->nfor.body = copynode(n->nfor.body);
7635 new->nfor.args = copynode(n->nfor.args);
7636 break;
7637 case NCASE:
7638 new->ncase.cases = copynode(n->ncase.cases);
7639 new->ncase.expr = copynode(n->ncase.expr);
7640 break;
7641 case NCLIST:
7642 new->nclist.body = copynode(n->nclist.body);
7643 new->nclist.pattern = copynode(n->nclist.pattern);
7644 new->nclist.next = copynode(n->nclist.next);
7645 break;
7646 case NDEFUN:
7647 case NARG:
7648 new->narg.backquote = copynodelist(n->narg.backquote);
7649 new->narg.text = nodeckstrdup(n->narg.text);
7650 new->narg.next = copynode(n->narg.next);
7651 break;
7652 case NTO:
7653 case NCLOBBER:
7654 case NFROM:
7655 case NFROMTO:
7656 case NAPPEND:
7657 new->nfile.fname = copynode(n->nfile.fname);
7658 new->nfile.fd = n->nfile.fd;
7659 new->nfile.next = copynode(n->nfile.next);
7660 break;
7661 case NTOFD:
7662 case NFROMFD:
7663 new->ndup.vname = copynode(n->ndup.vname);
7664 new->ndup.dupfd = n->ndup.dupfd;
7665 new->ndup.fd = n->ndup.fd;
7666 new->ndup.next = copynode(n->ndup.next);
7667 break;
7668 case NHERE:
7669 case NXHERE:
7670 new->nhere.doc = copynode(n->nhere.doc);
7671 new->nhere.fd = n->nhere.fd;
7672 new->nhere.next = copynode(n->nhere.next);
7673 break;
7674 case NNOT:
7675 new->nnot.com = copynode(n->nnot.com);
7676 break;
7677 };
7678 new->type = n->type;
7679 return new;
7680}
7681
7682/*
7683 * Make a copy of a parse tree.
7684 */
7685static struct funcnode *
7686copyfunc(union node *n)
7687{
7688 struct funcnode *f;
7689 size_t blocksize;
7690
7691 funcblocksize = offsetof(struct funcnode, n);
7692 funcstringsize = 0;
7693 calcsize(n);
7694 blocksize = funcblocksize;
7695 f = ckmalloc(blocksize + funcstringsize);
7696 funcblock = (char *) f + offsetof(struct funcnode, n);
7697 funcstring = (char *) f + blocksize;
7698 copynode(n);
7699 f->count = 0;
7700 return f;
7701}
7702
7703/*
7704 * Define a shell function.
7705 */
7706static void
7707defun(char *name, union node *func)
7708{
7709 struct cmdentry entry;
7710
7711 INT_OFF;
7712 entry.cmdtype = CMDFUNCTION;
7713 entry.u.func = copyfunc(func);
7714 addcmdentry(name, &entry);
7715 INT_ON;
7716}
7717
7718static int evalskip; /* set if we are skipping commands */
7719/* reasons for skipping commands (see comment on breakcmd routine) */
7720#define SKIPBREAK (1 << 0)
7721#define SKIPCONT (1 << 1)
7722#define SKIPFUNC (1 << 2)
7723#define SKIPFILE (1 << 3)
7724#define SKIPEVAL (1 << 4)
7725static int skipcount; /* number of levels to skip */
7726static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007727static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007728
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007729/* forward decl way out to parsing code - dotrap needs it */
7730static int evalstring(char *s, int mask);
7731
7732/*
7733 * Called to execute a trap. Perhaps we should avoid entering new trap
7734 * handlers while we are executing a trap handler.
7735 */
7736static int
7737dotrap(void)
7738{
7739 char *p;
7740 char *q;
7741 int i;
7742 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007743 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007744
7745 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007746 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007747 xbarrier();
7748
7749 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7750 if (!*q)
7751 continue;
7752 *q = '\0';
7753
7754 p = trap[i + 1];
7755 if (!p)
7756 continue;
7757 skip = evalstring(p, SKIPEVAL);
7758 exitstatus = savestatus;
7759 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007760 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007761 }
7762
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007763 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007764}
7765
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007766/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007767static void evalloop(union node *, int);
7768static void evalfor(union node *, int);
7769static void evalcase(union node *, int);
7770static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007771static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007772static void evalpipe(union node *, int);
7773static void evalcommand(union node *, int);
7774static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007775static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007776
Eric Andersen62483552001-07-10 06:09:16 +00007777/*
Eric Andersenc470f442003-07-28 09:56:35 +00007778 * Evaluate a parse tree. The value is left in the global variable
7779 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007780 */
Eric Andersenc470f442003-07-28 09:56:35 +00007781static void
7782evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007783{
Eric Andersenc470f442003-07-28 09:56:35 +00007784 int checkexit = 0;
7785 void (*evalfn)(union node *, int);
7786 unsigned isor;
7787 int status;
7788 if (n == NULL) {
7789 TRACE(("evaltree(NULL) called\n"));
7790 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007791 }
Eric Andersenc470f442003-07-28 09:56:35 +00007792 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007793 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007794 switch (n->type) {
7795 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007796#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007797 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007798 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007799 break;
7800#endif
7801 case NNOT:
7802 evaltree(n->nnot.com, EV_TESTED);
7803 status = !exitstatus;
7804 goto setstatus;
7805 case NREDIR:
7806 expredir(n->nredir.redirect);
7807 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7808 if (!status) {
7809 evaltree(n->nredir.n, flags & EV_TESTED);
7810 status = exitstatus;
7811 }
7812 popredir(0);
7813 goto setstatus;
7814 case NCMD:
7815 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007816 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007817 if (eflag && !(flags & EV_TESTED))
7818 checkexit = ~0;
7819 goto calleval;
7820 case NFOR:
7821 evalfn = evalfor;
7822 goto calleval;
7823 case NWHILE:
7824 case NUNTIL:
7825 evalfn = evalloop;
7826 goto calleval;
7827 case NSUBSHELL:
7828 case NBACKGND:
7829 evalfn = evalsubshell;
7830 goto calleval;
7831 case NPIPE:
7832 evalfn = evalpipe;
7833 goto checkexit;
7834 case NCASE:
7835 evalfn = evalcase;
7836 goto calleval;
7837 case NAND:
7838 case NOR:
7839 case NSEMI:
7840#if NAND + 1 != NOR
7841#error NAND + 1 != NOR
7842#endif
7843#if NOR + 1 != NSEMI
7844#error NOR + 1 != NSEMI
7845#endif
7846 isor = n->type - NAND;
7847 evaltree(
7848 n->nbinary.ch1,
7849 (flags | ((isor >> 1) - 1)) & EV_TESTED
7850 );
7851 if (!exitstatus == isor)
7852 break;
7853 if (!evalskip) {
7854 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007855 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007856 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007857 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007858 evalfn(n, flags);
7859 break;
7860 }
7861 break;
7862 case NIF:
7863 evaltree(n->nif.test, EV_TESTED);
7864 if (evalskip)
7865 break;
7866 if (exitstatus == 0) {
7867 n = n->nif.ifpart;
7868 goto evaln;
7869 } else if (n->nif.elsepart) {
7870 n = n->nif.elsepart;
7871 goto evaln;
7872 }
7873 goto success;
7874 case NDEFUN:
7875 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007876 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007877 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007878 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007879 exitstatus = status;
7880 break;
7881 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007882 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007883 if ((checkexit & exitstatus))
7884 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007885 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007886 goto exexit;
7887
7888 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007889 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007890 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007891 }
Eric Andersen62483552001-07-10 06:09:16 +00007892}
7893
Eric Andersenc470f442003-07-28 09:56:35 +00007894#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7895static
7896#endif
7897void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7898
Eric Andersenc470f442003-07-28 09:56:35 +00007899static void
7900evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007901{
7902 int status;
7903
7904 loopnest++;
7905 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007906 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007907 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007908 int i;
7909
Eric Andersencb57d552001-06-28 07:25:16 +00007910 evaltree(n->nbinary.ch1, EV_TESTED);
7911 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007912 skipping:
7913 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007914 evalskip = 0;
7915 continue;
7916 }
7917 if (evalskip == SKIPBREAK && --skipcount <= 0)
7918 evalskip = 0;
7919 break;
7920 }
Eric Andersenc470f442003-07-28 09:56:35 +00007921 i = exitstatus;
7922 if (n->type != NWHILE)
7923 i = !i;
7924 if (i != 0)
7925 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007926 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007927 status = exitstatus;
7928 if (evalskip)
7929 goto skipping;
7930 }
7931 loopnest--;
7932 exitstatus = status;
7933}
7934
Eric Andersenc470f442003-07-28 09:56:35 +00007935static void
7936evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007937{
7938 struct arglist arglist;
7939 union node *argp;
7940 struct strlist *sp;
7941 struct stackmark smark;
7942
7943 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007944 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007945 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007946 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007947 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007948 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007949 if (evalskip)
7950 goto out;
7951 }
7952 *arglist.lastp = NULL;
7953
7954 exitstatus = 0;
7955 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007956 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007957 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007958 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007959 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007960 if (evalskip) {
7961 if (evalskip == SKIPCONT && --skipcount <= 0) {
7962 evalskip = 0;
7963 continue;
7964 }
7965 if (evalskip == SKIPBREAK && --skipcount <= 0)
7966 evalskip = 0;
7967 break;
7968 }
7969 }
7970 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007971 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007972 popstackmark(&smark);
7973}
7974
Eric Andersenc470f442003-07-28 09:56:35 +00007975static void
7976evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007977{
7978 union node *cp;
7979 union node *patp;
7980 struct arglist arglist;
7981 struct stackmark smark;
7982
7983 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007984 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007985 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007986 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007988 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7989 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007990 if (casematch(patp, arglist.list->text)) {
7991 if (evalskip == 0) {
7992 evaltree(cp->nclist.body, flags);
7993 }
7994 goto out;
7995 }
7996 }
7997 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007998 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007999 popstackmark(&smark);
8000}
8001
Eric Andersenc470f442003-07-28 09:56:35 +00008002/*
8003 * Kick off a subshell to evaluate a tree.
8004 */
Eric Andersenc470f442003-07-28 09:56:35 +00008005static void
8006evalsubshell(union node *n, int flags)
8007{
8008 struct job *jp;
8009 int backgnd = (n->type == NBACKGND);
8010 int status;
8011
8012 expredir(n->nredir.redirect);
8013 if (!backgnd && flags & EV_EXIT && !trap[0])
8014 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008015 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008016 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008017 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008018 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008019 flags |= EV_EXIT;
8020 if (backgnd)
8021 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008022 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008023 redirect(n->nredir.redirect, 0);
8024 evaltreenr(n->nredir.n, flags);
8025 /* never returns */
8026 }
8027 status = 0;
8028 if (! backgnd)
8029 status = waitforjob(jp);
8030 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008031 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008032}
8033
Eric Andersenc470f442003-07-28 09:56:35 +00008034/*
8035 * Compute the names of the files in a redirection list.
8036 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008037static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008038static void
8039expredir(union node *n)
8040{
8041 union node *redir;
8042
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008043 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008044 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008045
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008046 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008047 fn.lastp = &fn.list;
8048 switch (redir->type) {
8049 case NFROMTO:
8050 case NFROM:
8051 case NTO:
8052 case NCLOBBER:
8053 case NAPPEND:
8054 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8055 redir->nfile.expfname = fn.list->text;
8056 break;
8057 case NFROMFD:
8058 case NTOFD:
8059 if (redir->ndup.vname) {
8060 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008061 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008062 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008063 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008064 }
8065 break;
8066 }
8067 }
8068}
8069
Eric Andersencb57d552001-06-28 07:25:16 +00008070/*
Eric Andersencb57d552001-06-28 07:25:16 +00008071 * Evaluate a pipeline. All the processes in the pipeline are children
8072 * of the process creating the pipeline. (This differs from some versions
8073 * of the shell, which make the last process in a pipeline the parent
8074 * of all the rest.)
8075 */
Eric Andersenc470f442003-07-28 09:56:35 +00008076static void
8077evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008078{
8079 struct job *jp;
8080 struct nodelist *lp;
8081 int pipelen;
8082 int prevfd;
8083 int pip[2];
8084
Eric Andersenc470f442003-07-28 09:56:35 +00008085 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008086 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008087 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008088 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008089 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008090 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008091 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008092 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008093 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008094 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008095 pip[1] = -1;
8096 if (lp->next) {
8097 if (pipe(pip) < 0) {
8098 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008099 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008100 }
8101 }
8102 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008103 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008104 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008105 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008106 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008107 if (prevfd > 0) {
8108 dup2(prevfd, 0);
8109 close(prevfd);
8110 }
8111 if (pip[1] > 1) {
8112 dup2(pip[1], 1);
8113 close(pip[1]);
8114 }
Eric Andersenc470f442003-07-28 09:56:35 +00008115 evaltreenr(lp->n, flags);
8116 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008117 }
8118 if (prevfd >= 0)
8119 close(prevfd);
8120 prevfd = pip[0];
8121 close(pip[1]);
8122 }
Eric Andersencb57d552001-06-28 07:25:16 +00008123 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008124 exitstatus = waitforjob(jp);
8125 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008126 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008127 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008128}
8129
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008130/*
8131 * Controls whether the shell is interactive or not.
8132 */
8133static void
8134setinteractive(int on)
8135{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008136 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008137
8138 if (++on == is_interactive)
8139 return;
8140 is_interactive = on;
8141 setsignal(SIGINT);
8142 setsignal(SIGQUIT);
8143 setsignal(SIGTERM);
8144#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8145 if (is_interactive > 1) {
8146 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008147 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008148
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008149 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008150 out1fmt(
8151 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008152 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008153 "Enter 'help' for a list of built-in commands."
8154 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008155 bb_banner);
8156 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008157 }
8158 }
8159#endif
8160}
8161
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008162static void
8163optschanged(void)
8164{
8165#if DEBUG
8166 opentrace();
8167#endif
8168 setinteractive(iflag);
8169 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008170#if ENABLE_FEATURE_EDITING_VI
8171 if (viflag)
8172 line_input_state->flags |= VI_MODE;
8173 else
8174 line_input_state->flags &= ~VI_MODE;
8175#else
8176 viflag = 0; /* forcibly keep the option off */
8177#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008178}
8179
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008180static struct localvar *localvars;
8181
8182/*
8183 * Called after a function returns.
8184 * Interrupts must be off.
8185 */
8186static void
8187poplocalvars(void)
8188{
8189 struct localvar *lvp;
8190 struct var *vp;
8191
8192 while ((lvp = localvars) != NULL) {
8193 localvars = lvp->next;
8194 vp = lvp->vp;
8195 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8196 if (vp == NULL) { /* $- saved */
8197 memcpy(optlist, lvp->text, sizeof(optlist));
8198 free((char*)lvp->text);
8199 optschanged();
8200 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8201 unsetvar(vp->text);
8202 } else {
8203 if (vp->func)
8204 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8205 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8206 free((char*)vp->text);
8207 vp->flags = lvp->flags;
8208 vp->text = lvp->text;
8209 }
8210 free(lvp);
8211 }
8212}
8213
8214static int
8215evalfun(struct funcnode *func, int argc, char **argv, int flags)
8216{
8217 volatile struct shparam saveparam;
8218 struct localvar *volatile savelocalvars;
8219 struct jmploc *volatile savehandler;
8220 struct jmploc jmploc;
8221 int e;
8222
8223 saveparam = shellparam;
8224 savelocalvars = localvars;
8225 e = setjmp(jmploc.loc);
8226 if (e) {
8227 goto funcdone;
8228 }
8229 INT_OFF;
8230 savehandler = exception_handler;
8231 exception_handler = &jmploc;
8232 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008233 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008234 func->count++;
8235 funcnest++;
8236 INT_ON;
8237 shellparam.nparam = argc - 1;
8238 shellparam.p = argv + 1;
8239#if ENABLE_ASH_GETOPTS
8240 shellparam.optind = 1;
8241 shellparam.optoff = -1;
8242#endif
8243 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008244 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008245 INT_OFF;
8246 funcnest--;
8247 freefunc(func);
8248 poplocalvars();
8249 localvars = savelocalvars;
8250 freeparam(&shellparam);
8251 shellparam = saveparam;
8252 exception_handler = savehandler;
8253 INT_ON;
8254 evalskip &= ~SKIPFUNC;
8255 return e;
8256}
8257
Denis Vlasenko131ae172007-02-18 13:00:19 +00008258#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008259static char **
8260parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008261{
8262 char *cp, c;
8263
8264 for (;;) {
8265 cp = *++argv;
8266 if (!cp)
8267 return 0;
8268 if (*cp++ != '-')
8269 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008270 c = *cp++;
8271 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008272 break;
8273 if (c == '-' && !*cp) {
8274 argv++;
8275 break;
8276 }
8277 do {
8278 switch (c) {
8279 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008280 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008281 break;
8282 default:
8283 /* run 'typecmd' for other options */
8284 return 0;
8285 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008286 c = *cp++;
8287 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008288 }
8289 return argv;
8290}
8291#endif
8292
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008293/*
8294 * Make a variable a local variable. When a variable is made local, it's
8295 * value and flags are saved in a localvar structure. The saved values
8296 * will be restored when the shell function returns. We handle the name
8297 * "-" as a special case.
8298 */
8299static void
8300mklocal(char *name)
8301{
8302 struct localvar *lvp;
8303 struct var **vpp;
8304 struct var *vp;
8305
8306 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008307 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008308 if (LONE_DASH(name)) {
8309 char *p;
8310 p = ckmalloc(sizeof(optlist));
8311 lvp->text = memcpy(p, optlist, sizeof(optlist));
8312 vp = NULL;
8313 } else {
8314 char *eq;
8315
8316 vpp = hashvar(name);
8317 vp = *findvar(vpp, name);
8318 eq = strchr(name, '=');
8319 if (vp == NULL) {
8320 if (eq)
8321 setvareq(name, VSTRFIXED);
8322 else
8323 setvar(name, NULL, VSTRFIXED);
8324 vp = *vpp; /* the new variable */
8325 lvp->flags = VUNSET;
8326 } else {
8327 lvp->text = vp->text;
8328 lvp->flags = vp->flags;
8329 vp->flags |= VSTRFIXED|VTEXTFIXED;
8330 if (eq)
8331 setvareq(name, 0);
8332 }
8333 }
8334 lvp->vp = vp;
8335 lvp->next = localvars;
8336 localvars = lvp;
8337 INT_ON;
8338}
8339
8340/*
8341 * The "local" command.
8342 */
8343static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008344localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008345{
8346 char *name;
8347
8348 argv = argptr;
8349 while ((name = *argv++) != NULL) {
8350 mklocal(name);
8351 }
8352 return 0;
8353}
8354
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008355static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008356falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008357{
8358 return 1;
8359}
8360
8361static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008362truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008363{
8364 return 0;
8365}
8366
8367static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008368execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008369{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008370 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008371 iflag = 0; /* exit on error */
8372 mflag = 0;
8373 optschanged();
8374 shellexec(argv + 1, pathval(), 0);
8375 }
8376 return 0;
8377}
8378
8379/*
8380 * The return command.
8381 */
8382static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008383returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008384{
8385 /*
8386 * If called outside a function, do what ksh does;
8387 * skip the rest of the file.
8388 */
8389 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8390 return argv[1] ? number(argv[1]) : exitstatus;
8391}
8392
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008393/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008394static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008395static int dotcmd(int, char **);
8396static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008397static int exitcmd(int, char **);
8398static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008399#if ENABLE_ASH_GETOPTS
8400static int getoptscmd(int, char **);
8401#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008402#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008403static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008404#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008405#if ENABLE_ASH_MATH_SUPPORT
8406static int letcmd(int, char **);
8407#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008408static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008409static int setcmd(int, char **);
8410static int shiftcmd(int, char **);
8411static int timescmd(int, char **);
8412static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008413static int umaskcmd(int, char **);
8414static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008416
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008417#define BUILTIN_NOSPEC "0"
8418#define BUILTIN_SPECIAL "1"
8419#define BUILTIN_REGULAR "2"
8420#define BUILTIN_SPEC_REG "3"
8421#define BUILTIN_ASSIGN "4"
8422#define BUILTIN_SPEC_ASSG "5"
8423#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008424#define BUILTIN_SPEC_REG_ASSG "7"
8425
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008426/* We do not handle [[ expr ]] bashism bash-compatibly,
8427 * we make it a synonym of [ expr ].
8428 * Basically, word splitting and pathname expansion should NOT be performed
8429 * Examples:
8430 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8431 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8432 * Additional operators:
8433 * || and && should work as -o and -a
8434 * =~ regexp match
8435 * Apart from the above, [[ expr ]] should work as [ expr ]
8436 */
8437
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008438#define echocmd echo_main
8439#define printfcmd printf_main
8440#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008441
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008442/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008443static const struct builtincmd builtintab[] = {
8444 { BUILTIN_SPEC_REG ".", dotcmd },
8445 { BUILTIN_SPEC_REG ":", truecmd },
8446#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008447 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008448#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008449 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008450#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008451#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008452#if ENABLE_ASH_ALIAS
8453 { BUILTIN_REG_ASSG "alias", aliascmd },
8454#endif
8455#if JOBS
8456 { BUILTIN_REGULAR "bg", fg_bgcmd },
8457#endif
8458 { BUILTIN_SPEC_REG "break", breakcmd },
8459 { BUILTIN_REGULAR "cd", cdcmd },
8460 { BUILTIN_NOSPEC "chdir", cdcmd },
8461#if ENABLE_ASH_CMDCMD
8462 { BUILTIN_REGULAR "command", commandcmd },
8463#endif
8464 { BUILTIN_SPEC_REG "continue", breakcmd },
8465#if ENABLE_ASH_BUILTIN_ECHO
8466 { BUILTIN_REGULAR "echo", echocmd },
8467#endif
8468 { BUILTIN_SPEC_REG "eval", evalcmd },
8469 { BUILTIN_SPEC_REG "exec", execcmd },
8470 { BUILTIN_SPEC_REG "exit", exitcmd },
8471 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8472 { BUILTIN_REGULAR "false", falsecmd },
8473#if JOBS
8474 { BUILTIN_REGULAR "fg", fg_bgcmd },
8475#endif
8476#if ENABLE_ASH_GETOPTS
8477 { BUILTIN_REGULAR "getopts", getoptscmd },
8478#endif
8479 { BUILTIN_NOSPEC "hash", hashcmd },
8480#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8481 { BUILTIN_NOSPEC "help", helpcmd },
8482#endif
8483#if JOBS
8484 { BUILTIN_REGULAR "jobs", jobscmd },
8485 { BUILTIN_REGULAR "kill", killcmd },
8486#endif
8487#if ENABLE_ASH_MATH_SUPPORT
8488 { BUILTIN_NOSPEC "let", letcmd },
8489#endif
8490 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008491#if ENABLE_ASH_BUILTIN_PRINTF
8492 { BUILTIN_REGULAR "printf", printfcmd },
8493#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008494 { BUILTIN_NOSPEC "pwd", pwdcmd },
8495 { BUILTIN_REGULAR "read", readcmd },
8496 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8497 { BUILTIN_SPEC_REG "return", returncmd },
8498 { BUILTIN_SPEC_REG "set", setcmd },
8499 { BUILTIN_SPEC_REG "shift", shiftcmd },
8500 { BUILTIN_SPEC_REG "source", dotcmd },
8501#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008502 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008503#endif
8504 { BUILTIN_SPEC_REG "times", timescmd },
8505 { BUILTIN_SPEC_REG "trap", trapcmd },
8506 { BUILTIN_REGULAR "true", truecmd },
8507 { BUILTIN_NOSPEC "type", typecmd },
8508 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8509 { BUILTIN_REGULAR "umask", umaskcmd },
8510#if ENABLE_ASH_ALIAS
8511 { BUILTIN_REGULAR "unalias", unaliascmd },
8512#endif
8513 { BUILTIN_SPEC_REG "unset", unsetcmd },
8514 { BUILTIN_REGULAR "wait", waitcmd },
8515};
8516
Denis Vlasenko80591b02008-03-25 07:49:43 +00008517/* Should match the above table! */
8518#define COMMANDCMD (builtintab + \
8519 2 + \
8520 1 * ENABLE_ASH_BUILTIN_TEST + \
8521 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8522 1 * ENABLE_ASH_ALIAS + \
8523 1 * ENABLE_ASH_JOB_CONTROL + \
8524 3)
8525#define EXECCMD (builtintab + \
8526 2 + \
8527 1 * ENABLE_ASH_BUILTIN_TEST + \
8528 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8529 1 * ENABLE_ASH_ALIAS + \
8530 1 * ENABLE_ASH_JOB_CONTROL + \
8531 3 + \
8532 1 * ENABLE_ASH_CMDCMD + \
8533 1 + \
8534 ENABLE_ASH_BUILTIN_ECHO + \
8535 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008536
8537/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008538 * Search the table of builtin commands.
8539 */
8540static struct builtincmd *
8541find_builtin(const char *name)
8542{
8543 struct builtincmd *bp;
8544
8545 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008546 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008547 pstrcmp
8548 );
8549 return bp;
8550}
8551
8552/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008553 * Execute a simple command.
8554 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008555static int
8556isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008557{
8558 const char *q = endofname(p);
8559 if (p == q)
8560 return 0;
8561 return *q == '=';
8562}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008563static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008564bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565{
8566 /* Preserve exitstatus of a previous possible redirection
8567 * as POSIX mandates */
8568 return back_exitstatus;
8569}
Eric Andersenc470f442003-07-28 09:56:35 +00008570static void
8571evalcommand(union node *cmd, int flags)
8572{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008573 static const struct builtincmd null_bltin = {
8574 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008575 };
Eric Andersenc470f442003-07-28 09:56:35 +00008576 struct stackmark smark;
8577 union node *argp;
8578 struct arglist arglist;
8579 struct arglist varlist;
8580 char **argv;
8581 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008582 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008583 struct cmdentry cmdentry;
8584 struct job *jp;
8585 char *lastarg;
8586 const char *path;
8587 int spclbltin;
8588 int cmd_is_exec;
8589 int status;
8590 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008591 struct builtincmd *bcmd;
8592 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008593
8594 /* First expand the arguments. */
8595 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8596 setstackmark(&smark);
8597 back_exitstatus = 0;
8598
8599 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008600 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008601 varlist.lastp = &varlist.list;
8602 *varlist.lastp = NULL;
8603 arglist.lastp = &arglist.list;
8604 *arglist.lastp = NULL;
8605
8606 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008607 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008608 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8609 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8610 }
8611
Eric Andersenc470f442003-07-28 09:56:35 +00008612 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8613 struct strlist **spp;
8614
8615 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008616 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008617 expandarg(argp, &arglist, EXP_VARTILDE);
8618 else
8619 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8620
Eric Andersenc470f442003-07-28 09:56:35 +00008621 for (sp = *spp; sp; sp = sp->next)
8622 argc++;
8623 }
8624
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008625 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008626 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008627 TRACE(("evalcommand arg: %s\n", sp->text));
8628 *nargv++ = sp->text;
8629 }
8630 *nargv = NULL;
8631
8632 lastarg = NULL;
8633 if (iflag && funcnest == 0 && argc > 0)
8634 lastarg = nargv[-1];
8635
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008636 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008637 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008638 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008639
8640 path = vpath.text;
8641 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8642 struct strlist **spp;
8643 char *p;
8644
8645 spp = varlist.lastp;
8646 expandarg(argp, &varlist, EXP_VARTILDE);
8647
8648 /*
8649 * Modify the command lookup path, if a PATH= assignment
8650 * is present
8651 */
8652 p = (*spp)->text;
8653 if (varequal(p, path))
8654 path = p;
8655 }
8656
8657 /* Print the command if xflag is set. */
8658 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008659 int n;
8660 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008661
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008662 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008663 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008664
8665 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008666 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008667 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008668 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008669 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008670 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008671 p--;
8672 }
8673 }
8674 sp = arglist.list;
8675 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008676 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008677 }
8678
8679 cmd_is_exec = 0;
8680 spclbltin = -1;
8681
8682 /* Now locate the command. */
8683 if (argc) {
8684 const char *oldpath;
8685 int cmd_flag = DO_ERR;
8686
8687 path += 5;
8688 oldpath = path;
8689 for (;;) {
8690 find_command(argv[0], &cmdentry, cmd_flag, path);
8691 if (cmdentry.cmdtype == CMDUNKNOWN) {
8692 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008693 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008694 goto bail;
8695 }
8696
8697 /* implement bltin and command here */
8698 if (cmdentry.cmdtype != CMDBUILTIN)
8699 break;
8700 if (spclbltin < 0)
8701 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8702 if (cmdentry.u.cmd == EXECCMD)
8703 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008704#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008705 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008706 path = oldpath;
8707 nargv = parse_command_args(argv, &path);
8708 if (!nargv)
8709 break;
8710 argc -= nargv - argv;
8711 argv = nargv;
8712 cmd_flag |= DO_NOFUNC;
8713 } else
8714#endif
8715 break;
8716 }
8717 }
8718
8719 if (status) {
8720 /* We have a redirection error. */
8721 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008722 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008723 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008724 exitstatus = status;
8725 goto out;
8726 }
8727
8728 /* Execute the command. */
8729 switch (cmdentry.cmdtype) {
8730 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008731#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008732 {
8733 /* find_command() encodes applet_no as (-2 - applet_no) */
8734 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008735 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008736 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008737 /* run <applet>_main() */
8738 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008739 break;
8740 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008741 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008742#endif
8743
Eric Andersenc470f442003-07-28 09:56:35 +00008744 /* Fork off a child process if necessary. */
8745 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008746 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008747 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008748 if (forkshell(jp, cmd, FORK_FG) != 0) {
8749 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008750 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008751 break;
8752 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008753 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008754 }
8755 listsetvar(varlist.list, VEXPORT|VSTACK);
8756 shellexec(argv, path, cmdentry.u.index);
8757 /* NOTREACHED */
8758
8759 case CMDBUILTIN:
8760 cmdenviron = varlist.list;
8761 if (cmdenviron) {
8762 struct strlist *list = cmdenviron;
8763 int i = VNOSET;
8764 if (spclbltin > 0 || argc == 0) {
8765 i = 0;
8766 if (cmd_is_exec && argc > 1)
8767 i = VEXPORT;
8768 }
8769 listsetvar(list, i);
8770 }
8771 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8772 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008773 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008774 if (i == EXEXIT)
8775 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008776 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008777 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008778 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008779 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008780 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008781 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008782 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008783 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008784 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008785 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008786 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008787 }
8788 break;
8789
8790 case CMDFUNCTION:
8791 listsetvar(varlist.list, 0);
8792 if (evalfun(cmdentry.u.func, argc, argv, flags))
8793 goto raise;
8794 break;
8795 }
8796
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008797 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008798 popredir(cmd_is_exec);
8799 if (lastarg)
8800 /* dsl: I think this is intended to be used to support
8801 * '_' in 'vi' command mode during line editing...
8802 * However I implemented that within libedit itself.
8803 */
8804 setvar("_", lastarg, 0);
8805 popstackmark(&smark);
8806}
8807
8808static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008809evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8810{
Eric Andersenc470f442003-07-28 09:56:35 +00008811 char *volatile savecmdname;
8812 struct jmploc *volatile savehandler;
8813 struct jmploc jmploc;
8814 int i;
8815
8816 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008817 i = setjmp(jmploc.loc);
8818 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008819 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008820 savehandler = exception_handler;
8821 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008822 commandname = argv[0];
8823 argptr = argv + 1;
8824 optptr = NULL; /* initialize nextopt */
8825 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008826 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008827 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008828 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008829 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008830 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008831// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008832 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008833
8834 return i;
8835}
8836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008837static int
8838goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008839{
8840 return !*endofname(p);
8841}
8842
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008843
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008844/*
8845 * Search for a command. This is called before we fork so that the
8846 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008847 * the child. The check for "goodname" is an overly conservative
8848 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008849 */
Eric Andersenc470f442003-07-28 09:56:35 +00008850static void
8851prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008852{
8853 struct cmdentry entry;
8854
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008855 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8856 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008857}
8858
Eric Andersencb57d552001-06-28 07:25:16 +00008859
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008860/* ============ Builtin commands
8861 *
8862 * Builtin commands whose functions are closely tied to evaluation
8863 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008864 */
8865
8866/*
Eric Andersencb57d552001-06-28 07:25:16 +00008867 * Handle break and continue commands. Break, continue, and return are
8868 * all handled by setting the evalskip flag. The evaluation routines
8869 * above all check this flag, and if it is set they start skipping
8870 * commands rather than executing them. The variable skipcount is
8871 * the number of loops to break/continue, or the number of function
8872 * levels to return. (The latter is always 1.) It should probably
8873 * be an error to break out of more loops than exist, but it isn't
8874 * in the standard shell so we don't make it one here.
8875 */
Eric Andersenc470f442003-07-28 09:56:35 +00008876static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008877breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008878{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008879 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008880
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008881 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008883 if (n > loopnest)
8884 n = loopnest;
8885 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008886 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008887 skipcount = n;
8888 }
8889 return 0;
8890}
8891
Eric Andersenc470f442003-07-28 09:56:35 +00008892
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008893/* ============ input.c
8894 *
Eric Andersen90898442003-08-06 11:20:52 +00008895 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008896 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008897
Eric Andersenc470f442003-07-28 09:56:35 +00008898#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008899
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008900enum {
8901 INPUT_PUSH_FILE = 1,
8902 INPUT_NOFILE_OK = 2,
8903};
Eric Andersencb57d552001-06-28 07:25:16 +00008904
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008905static int plinno = 1; /* input line number */
8906/* number of characters left in input buffer */
8907static int parsenleft; /* copy of parsefile->nleft */
8908static int parselleft; /* copy of parsefile->lleft */
8909/* next character in input buffer */
8910static char *parsenextc; /* copy of parsefile->nextc */
8911
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008912static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008913/* values of checkkwd variable */
8914#define CHKALIAS 0x1
8915#define CHKKWD 0x2
8916#define CHKNL 0x4
8917
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008918static void
8919popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008920{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008921 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008922
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008923 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008924#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008925 if (sp->ap) {
8926 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8927 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008928 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008929 if (sp->string != sp->ap->val) {
8930 free(sp->string);
8931 }
8932 sp->ap->flag &= ~ALIASINUSE;
8933 if (sp->ap->flag & ALIASDEAD) {
8934 unalias(sp->ap->name);
8935 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008936 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008937#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008938 parsenextc = sp->prevstring;
8939 parsenleft = sp->prevnleft;
8940/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008941 g_parsefile->strpush = sp->prev;
8942 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008943 free(sp);
8944 INT_ON;
8945}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008946
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008947static int
8948preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008949{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008950 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008951 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008952 parsenextc = buf;
8953
Denis Vlasenko38f63192007-01-22 09:03:07 +00008954#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008955 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008956 if (!iflag || g_parsefile->fd)
8957 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008958 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008959#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008960 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008961#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008962 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8963 if (nr == 0) {
8964 /* Ctrl+C pressed */
8965 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008966 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008967 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008968 raise(SIGINT);
8969 return 1;
8970 }
Eric Andersenc470f442003-07-28 09:56:35 +00008971 goto retry;
8972 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008973 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008974 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008975 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008976 }
Eric Andersencb57d552001-06-28 07:25:16 +00008977 }
8978#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008979 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008980#endif
8981
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008982#if 0
8983/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008984 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008985 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008986 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008987 if (flags >= 0 && (flags & O_NONBLOCK)) {
8988 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008989 if (fcntl(0, F_SETFL, flags) >= 0) {
8990 out2str("sh: turning off NDELAY mode\n");
8991 goto retry;
8992 }
8993 }
8994 }
8995 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008996#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008997 return nr;
8998}
8999
9000/*
9001 * Refill the input buffer and return the next input character:
9002 *
9003 * 1) If a string was pushed back on the input, pop it;
9004 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9005 * from a string so we can't refill the buffer, return EOF.
9006 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9007 * 4) Process input up to the next newline, deleting nul characters.
9008 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009009static int
Eric Andersenc470f442003-07-28 09:56:35 +00009010preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009011{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009012 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009013 int more;
9014 char savec;
9015
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009016 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009017#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009018 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009019 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009020 return PEOA;
9021 }
Eric Andersen2870d962001-07-02 17:27:21 +00009022#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009023 popstring();
9024 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009025 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009026 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009027 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009028 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009029 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009030
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009031 more = parselleft;
9032 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009033 again:
9034 more = preadfd();
9035 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009036 parselleft = parsenleft = EOF_NLEFT;
9037 return PEOF;
9038 }
9039 }
9040
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009041 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009042
9043 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009044 for (;;) {
9045 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009046
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009047 more--;
9048 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009049
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009050 if (!c)
9051 memmove(q, q + 1, more);
9052 else {
9053 q++;
9054 if (c == '\n') {
9055 parsenleft = q - parsenextc - 1;
9056 break;
9057 }
Eric Andersencb57d552001-06-28 07:25:16 +00009058 }
9059
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009060 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009061 parsenleft = q - parsenextc - 1;
9062 if (parsenleft < 0)
9063 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009064 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009065 }
9066 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009067 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009068
9069 savec = *q;
9070 *q = '\0';
9071
9072 if (vflag) {
9073 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009074 }
9075
9076 *q = savec;
9077
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009078 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009079}
9080
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009081#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009082static int
9083pgetc(void)
9084{
9085 return pgetc_as_macro();
9086}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009087
9088#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9089#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009090#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009091#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092#endif
9093
9094/*
9095 * Same as pgetc(), but ignores PEOA.
9096 */
9097#if ENABLE_ASH_ALIAS
9098static int
9099pgetc2(void)
9100{
9101 int c;
9102
9103 do {
9104 c = pgetc_macro();
9105 } while (c == PEOA);
9106 return c;
9107}
9108#else
9109static int
9110pgetc2(void)
9111{
9112 return pgetc_macro();
9113}
9114#endif
9115
9116/*
9117 * Read a line from the script.
9118 */
9119static char *
9120pfgets(char *line, int len)
9121{
9122 char *p = line;
9123 int nleft = len;
9124 int c;
9125
9126 while (--nleft > 0) {
9127 c = pgetc2();
9128 if (c == PEOF) {
9129 if (p == line)
9130 return NULL;
9131 break;
9132 }
9133 *p++ = c;
9134 if (c == '\n')
9135 break;
9136 }
9137 *p = '\0';
9138 return line;
9139}
9140
Eric Andersenc470f442003-07-28 09:56:35 +00009141/*
9142 * Undo the last call to pgetc. Only one character may be pushed back.
9143 * PEOF may be pushed back.
9144 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009145static void
Eric Andersenc470f442003-07-28 09:56:35 +00009146pungetc(void)
9147{
9148 parsenleft++;
9149 parsenextc--;
9150}
Eric Andersencb57d552001-06-28 07:25:16 +00009151
9152/*
9153 * Push a string back onto the input at this current parsefile level.
9154 * We handle aliases this way.
9155 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009156#if !ENABLE_ASH_ALIAS
9157#define pushstring(s, ap) pushstring(s)
9158#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009159static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009160pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009161{
Eric Andersencb57d552001-06-28 07:25:16 +00009162 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009163 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009164
Eric Andersenc470f442003-07-28 09:56:35 +00009165 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009166 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009167/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009168 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009169 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009170 sp->prev = g_parsefile->strpush;
9171 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009172 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009173 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009174 sp->prevstring = parsenextc;
9175 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009176#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009177 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009178 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009179 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009180 sp->string = s;
9181 }
Eric Andersen2870d962001-07-02 17:27:21 +00009182#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009183 parsenextc = s;
9184 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009185 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009186}
9187
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009188/*
9189 * To handle the "." command, a stack of input files is used. Pushfile
9190 * adds a new entry to the stack and popfile restores the previous level.
9191 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009192static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009193pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009194{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009195 struct parsefile *pf;
9196
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009197 g_parsefile->nleft = parsenleft;
9198 g_parsefile->lleft = parselleft;
9199 g_parsefile->nextc = parsenextc;
9200 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009201 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009202 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009204 /*pf->strpush = NULL; - ckzalloc did it */
9205 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009206 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009207}
9208
9209static void
9210popfile(void)
9211{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009212 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009213
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215 if (pf->fd >= 0)
9216 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009217 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218 while (pf->strpush)
9219 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009220 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009221 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009222 parsenleft = g_parsefile->nleft;
9223 parselleft = g_parsefile->lleft;
9224 parsenextc = g_parsefile->nextc;
9225 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009226 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009227}
9228
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009229/*
9230 * Return to top level.
9231 */
9232static void
9233popallfiles(void)
9234{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009235 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009236 popfile();
9237}
9238
9239/*
9240 * Close the file(s) that the shell is reading commands from. Called
9241 * after a fork is done.
9242 */
9243static void
9244closescript(void)
9245{
9246 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009247 if (g_parsefile->fd > 0) {
9248 close(g_parsefile->fd);
9249 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009250 }
9251}
9252
9253/*
9254 * Like setinputfile, but takes an open file descriptor. Call this with
9255 * interrupts off.
9256 */
9257static void
9258setinputfd(int fd, int push)
9259{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009260 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009261 if (push) {
9262 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009263 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009264 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009265 g_parsefile->fd = fd;
9266 if (g_parsefile->buf == NULL)
9267 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009268 parselleft = parsenleft = 0;
9269 plinno = 1;
9270}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009271
Eric Andersenc470f442003-07-28 09:56:35 +00009272/*
9273 * Set the input to take input from a file. If push is set, push the
9274 * old input onto the stack first.
9275 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009276static int
9277setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009278{
9279 int fd;
9280 int fd2;
9281
Denis Vlasenkob012b102007-02-19 22:43:01 +00009282 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009283 fd = open(fname, O_RDONLY);
9284 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009285 if (flags & INPUT_NOFILE_OK)
9286 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009287 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009288 }
Eric Andersenc470f442003-07-28 09:56:35 +00009289 if (fd < 10) {
9290 fd2 = copyfd(fd, 10);
9291 close(fd);
9292 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009293 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009294 fd = fd2;
9295 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009296 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009297 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009298 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009299 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009300}
9301
Eric Andersencb57d552001-06-28 07:25:16 +00009302/*
9303 * Like setinputfile, but takes input from a string.
9304 */
Eric Andersenc470f442003-07-28 09:56:35 +00009305static void
9306setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009307{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009309 pushfile();
9310 parsenextc = string;
9311 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009312 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009313 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009314 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009315}
9316
9317
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009318/* ============ mail.c
9319 *
9320 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009321 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009322
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009323#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009324
Eric Andersencb57d552001-06-28 07:25:16 +00009325#define MAXMBOXES 10
9326
Eric Andersenc470f442003-07-28 09:56:35 +00009327/* times of mailboxes */
9328static time_t mailtime[MAXMBOXES];
9329/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009330static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Eric Andersencb57d552001-06-28 07:25:16 +00009332/*
Eric Andersenc470f442003-07-28 09:56:35 +00009333 * Print appropriate message(s) if mail has arrived.
9334 * If mail_var_path_changed is set,
9335 * then the value of MAIL has mail_var_path_changed,
9336 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009337 */
Eric Andersenc470f442003-07-28 09:56:35 +00009338static void
9339chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009340{
Eric Andersencb57d552001-06-28 07:25:16 +00009341 const char *mpath;
9342 char *p;
9343 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009344 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009345 struct stackmark smark;
9346 struct stat statb;
9347
Eric Andersencb57d552001-06-28 07:25:16 +00009348 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009349 mpath = mpathset() ? mpathval() : mailval();
9350 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009351 p = padvance(&mpath, nullstr);
9352 if (p == NULL)
9353 break;
9354 if (*p == '\0')
9355 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009356 for (q = p; *q; q++)
9357 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009358#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009359 if (q[-1] != '/')
9360 abort();
9361#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009362 q[-1] = '\0'; /* delete trailing '/' */
9363 if (stat(p, &statb) < 0) {
9364 *mtp = 0;
9365 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009366 }
Eric Andersenc470f442003-07-28 09:56:35 +00009367 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9368 fprintf(
9369 stderr, snlfmt,
9370 pathopt ? pathopt : "you have mail"
9371 );
9372 }
9373 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009374 }
Eric Andersenc470f442003-07-28 09:56:35 +00009375 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009376 popstackmark(&smark);
9377}
Eric Andersencb57d552001-06-28 07:25:16 +00009378
Eric Andersenc470f442003-07-28 09:56:35 +00009379static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009380changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009381{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009382 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009383}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009384
Denis Vlasenko131ae172007-02-18 13:00:19 +00009385#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009386
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009387
9388/* ============ ??? */
9389
Eric Andersencb57d552001-06-28 07:25:16 +00009390/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009391 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009392 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009393static void
9394setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009395{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009396 char **newparam;
9397 char **ap;
9398 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009399
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009400 for (nparam = 0; argv[nparam]; nparam++)
9401 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009402 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9403 while (*argv) {
9404 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009405 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009406 *ap = NULL;
9407 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009408 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009409 shellparam.nparam = nparam;
9410 shellparam.p = newparam;
9411#if ENABLE_ASH_GETOPTS
9412 shellparam.optind = 1;
9413 shellparam.optoff = -1;
9414#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009415}
9416
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009417/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009418 * Process shell options. The global variable argptr contains a pointer
9419 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009420 *
9421 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9422 * For a non-interactive shell, an error condition encountered
9423 * by a special built-in ... shall cause the shell to write a diagnostic message
9424 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009425 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009426 * ...
9427 * Utility syntax error (option or operand error) Shall exit
9428 * ...
9429 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9430 * we see that bash does not do that (set "finishes" with error code 1 instead,
9431 * and shell continues), and people rely on this behavior!
9432 * Testcase:
9433 * set -o barfoo 2>/dev/null
9434 * echo $?
9435 *
9436 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009437 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009438static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009439plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009440{
9441 int i;
9442
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009443 if (name) {
9444 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009445 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009447 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009448 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009449 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009450 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009451 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009452 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009453 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009454 if (val) {
9455 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9456 } else {
9457 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9458 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009459 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009460 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009461}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009462static void
9463setoption(int flag, int val)
9464{
9465 int i;
9466
9467 for (i = 0; i < NOPTS; i++) {
9468 if (optletters(i) == flag) {
9469 optlist[i] = val;
9470 return;
9471 }
9472 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009473 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009474 /* NOTREACHED */
9475}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009476static int
Eric Andersenc470f442003-07-28 09:56:35 +00009477options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009478{
9479 char *p;
9480 int val;
9481 int c;
9482
9483 if (cmdline)
9484 minusc = NULL;
9485 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009486 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009487 if (c != '-' && c != '+')
9488 break;
9489 argptr++;
9490 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009491 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009492 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009493 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009494 if (!cmdline) {
9495 /* "-" means turn off -x and -v */
9496 if (p[0] == '\0')
9497 xflag = vflag = 0;
9498 /* "--" means reset params */
9499 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009500 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009501 }
Eric Andersenc470f442003-07-28 09:56:35 +00009502 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009503 }
Eric Andersencb57d552001-06-28 07:25:16 +00009504 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009505 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009506 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009507 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009508 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009509 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009510 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009511 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009512 /* it already printed err message */
9513 return 1; /* error */
9514 }
Eric Andersencb57d552001-06-28 07:25:16 +00009515 if (*argptr)
9516 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009517 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9518 isloginsh = 1;
9519 /* bash does not accept +-login, we also won't */
9520 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009521 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009522 isloginsh = 1;
9523 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009524 } else {
9525 setoption(c, val);
9526 }
9527 }
9528 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009529 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009530}
9531
Eric Andersencb57d552001-06-28 07:25:16 +00009532/*
Eric Andersencb57d552001-06-28 07:25:16 +00009533 * The shift builtin command.
9534 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009535static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009536shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009537{
9538 int n;
9539 char **ap1, **ap2;
9540
9541 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009542 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009543 n = number(argv[1]);
9544 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009545 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009546 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009547 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009548 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009549 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009550 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009551 }
9552 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009553 while ((*ap2++ = *ap1++) != NULL)
9554 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009555#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009556 shellparam.optind = 1;
9557 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009558#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009559 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009560 return 0;
9561}
9562
Eric Andersencb57d552001-06-28 07:25:16 +00009563/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009564 * POSIX requires that 'set' (but not export or readonly) output the
9565 * variables in lexicographic order - by the locale's collating order (sigh).
9566 * Maybe we could keep them in an ordered balanced binary tree
9567 * instead of hashed lists.
9568 * For now just roll 'em through qsort for printing...
9569 */
9570static int
9571showvars(const char *sep_prefix, int on, int off)
9572{
9573 const char *sep;
9574 char **ep, **epend;
9575
9576 ep = listvars(on, off, &epend);
9577 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9578
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009579 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009580
9581 for (; ep < epend; ep++) {
9582 const char *p;
9583 const char *q;
9584
9585 p = strchrnul(*ep, '=');
9586 q = nullstr;
9587 if (*p)
9588 q = single_quote(++p);
9589 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9590 }
9591 return 0;
9592}
9593
9594/*
Eric Andersencb57d552001-06-28 07:25:16 +00009595 * The set command builtin.
9596 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009597static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009598setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009599{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009600 int retval;
9601
Denis Vlasenko68404f12008-03-17 09:00:54 +00009602 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009603 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009604 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009605 retval = 1;
9606 if (!options(0)) { /* if no parse error... */
9607 retval = 0;
9608 optschanged();
9609 if (*argptr != NULL) {
9610 setparam(argptr);
9611 }
Eric Andersencb57d552001-06-28 07:25:16 +00009612 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009613 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009614 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009615}
9616
Denis Vlasenko131ae172007-02-18 13:00:19 +00009617#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009618static void
9619change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009620{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009621 /* Galois LFSR parameter */
9622 /* Taps at 32 31 29 1: */
9623 enum { MASK = 0x8000000b };
9624 /* Another example - taps at 32 31 30 10: */
9625 /* MASK = 0x00400007 */
9626
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009627 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009628 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009629 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009630
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009631 /* LCG has period of 2^32 and alternating lowest bit */
9632 random_LCG = 1664525 * random_LCG + 1013904223;
9633 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9634 t = (random_galois_LFSR << 1);
9635 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9636 t ^= MASK;
9637 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009638 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009639 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009640 * for $RANDOM range. Combining with subtraction is
9641 * just for fun. + and ^ would work equally well. */
9642 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009643 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009644 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009645 vrandom.flags &= ~VNOFUNC;
9646 } else {
9647 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009648 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009649 }
Eric Andersenef02f822004-03-11 13:34:24 +00009650}
Eric Andersen16767e22004-03-16 05:14:10 +00009651#endif
9652
Denis Vlasenko131ae172007-02-18 13:00:19 +00009653#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009654static int
Eric Andersenc470f442003-07-28 09:56:35 +00009655getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009656{
9657 char *p, *q;
9658 char c = '?';
9659 int done = 0;
9660 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009661 char s[12];
9662 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009663
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009664 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009665 return 1;
9666 optnext = optfirst + *param_optind - 1;
9667
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009668 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009669 p = NULL;
9670 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009671 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009672 if (p == NULL || *p == '\0') {
9673 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009674 p = *optnext;
9675 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009676 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009677 p = NULL;
9678 done = 1;
9679 goto out;
9680 }
9681 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009682 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009683 goto atend;
9684 }
9685
9686 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009687 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009688 if (*q == '\0') {
9689 if (optstr[0] == ':') {
9690 s[0] = c;
9691 s[1] = '\0';
9692 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009693 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009694 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009695 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009696 }
9697 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009698 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009699 }
9700 if (*++q == ':')
9701 q++;
9702 }
9703
9704 if (*++q == ':') {
9705 if (*p == '\0' && (p = *optnext) == NULL) {
9706 if (optstr[0] == ':') {
9707 s[0] = c;
9708 s[1] = '\0';
9709 err |= setvarsafe("OPTARG", s, 0);
9710 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009711 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009712 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009713 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009714 c = '?';
9715 }
Eric Andersenc470f442003-07-28 09:56:35 +00009716 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009717 }
9718
9719 if (p == *optnext)
9720 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009721 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009722 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009723 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009724 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009725 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009726 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009727 *param_optind = optnext - optfirst + 1;
9728 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009729 err |= setvarsafe("OPTIND", s, VNOFUNC);
9730 s[0] = c;
9731 s[1] = '\0';
9732 err |= setvarsafe(optvar, s, 0);
9733 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009734 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009735 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009736 flush_stdout_stderr();
9737 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009738 }
9739 return done;
9740}
Eric Andersenc470f442003-07-28 09:56:35 +00009741
9742/*
9743 * The getopts builtin. Shellparam.optnext points to the next argument
9744 * to be processed. Shellparam.optptr points to the next character to
9745 * be processed in the current argument. If shellparam.optnext is NULL,
9746 * then it's the first time getopts has been called.
9747 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009748static int
Eric Andersenc470f442003-07-28 09:56:35 +00009749getoptscmd(int argc, char **argv)
9750{
9751 char **optbase;
9752
9753 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009754 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009755 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009756 optbase = shellparam.p;
9757 if (shellparam.optind > shellparam.nparam + 1) {
9758 shellparam.optind = 1;
9759 shellparam.optoff = -1;
9760 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009761 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009762 optbase = &argv[3];
9763 if (shellparam.optind > argc - 2) {
9764 shellparam.optind = 1;
9765 shellparam.optoff = -1;
9766 }
9767 }
9768
9769 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009770 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009771}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009772#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009773
Eric Andersencb57d552001-06-28 07:25:16 +00009774
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009775/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009776
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009777struct heredoc {
9778 struct heredoc *next; /* next here document in list */
9779 union node *here; /* redirection node */
9780 char *eofmark; /* string indicating end of input */
9781 smallint striptabs; /* if set, strip leading tabs */
9782};
9783
9784static smallint tokpushback; /* last token pushed back */
9785static smallint parsebackquote; /* nonzero if we are inside backquotes */
9786static smallint quoteflag; /* set if (part of) last token was quoted */
9787static token_id_t lasttoken; /* last token read (integer id Txxx) */
9788static struct heredoc *heredoclist; /* list of here documents to read */
9789static char *wordtext; /* text of last word returned by readtoken */
9790static struct nodelist *backquotelist;
9791static union node *redirnode;
9792static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009793/*
9794 * NEOF is returned by parsecmd when it encounters an end of file. It
9795 * must be distinct from NULL, so we use the address of a variable that
9796 * happens to be handy.
9797 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009798#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009799
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009800static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9801static void
9802raise_error_syntax(const char *msg)
9803{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009804 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009805 /* NOTREACHED */
9806}
9807
9808/*
9809 * Called when an unexpected token is read during the parse. The argument
9810 * is the token that is expected, or -1 if more than one type of token can
9811 * occur at this point.
9812 */
9813static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9814static void
9815raise_error_unexpected_syntax(int token)
9816{
9817 char msg[64];
9818 int l;
9819
9820 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9821 if (token >= 0)
9822 sprintf(msg + l, " (expecting %s)", tokname(token));
9823 raise_error_syntax(msg);
9824 /* NOTREACHED */
9825}
Eric Andersencb57d552001-06-28 07:25:16 +00009826
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009827#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009828
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009829/* parsing is heavily cross-recursive, need these forward decls */
9830static union node *andor(void);
9831static union node *pipeline(void);
9832static union node *parse_command(void);
9833static void parseheredoc(void);
9834static char peektoken(void);
9835static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009836
Eric Andersenc470f442003-07-28 09:56:35 +00009837static union node *
9838list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009839{
9840 union node *n1, *n2, *n3;
9841 int tok;
9842
Eric Andersenc470f442003-07-28 09:56:35 +00009843 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9844 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009845 return NULL;
9846 n1 = NULL;
9847 for (;;) {
9848 n2 = andor();
9849 tok = readtoken();
9850 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009851 if (n2->type == NPIPE) {
9852 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009853 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009854 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009855 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009856 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009857 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009858 n2 = n3;
9859 }
9860 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009861 }
9862 }
9863 if (n1 == NULL) {
9864 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009865 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009866 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009867 n3->type = NSEMI;
9868 n3->nbinary.ch1 = n1;
9869 n3->nbinary.ch2 = n2;
9870 n1 = n3;
9871 }
9872 switch (tok) {
9873 case TBACKGND:
9874 case TSEMI:
9875 tok = readtoken();
9876 /* fall through */
9877 case TNL:
9878 if (tok == TNL) {
9879 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009880 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009881 return n1;
9882 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009883 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009884 }
Eric Andersenc470f442003-07-28 09:56:35 +00009885 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009886 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009887 return n1;
9888 break;
9889 case TEOF:
9890 if (heredoclist)
9891 parseheredoc();
9892 else
Eric Andersenc470f442003-07-28 09:56:35 +00009893 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009894 return n1;
9895 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009896 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009897 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009898 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009899 return n1;
9900 }
9901 }
9902}
9903
Eric Andersenc470f442003-07-28 09:56:35 +00009904static union node *
9905andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009906{
Eric Andersencb57d552001-06-28 07:25:16 +00009907 union node *n1, *n2, *n3;
9908 int t;
9909
Eric Andersencb57d552001-06-28 07:25:16 +00009910 n1 = pipeline();
9911 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009912 t = readtoken();
9913 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009914 t = NAND;
9915 } else if (t == TOR) {
9916 t = NOR;
9917 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009918 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009919 return n1;
9920 }
Eric Andersenc470f442003-07-28 09:56:35 +00009921 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009922 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009923 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009924 n3->type = t;
9925 n3->nbinary.ch1 = n1;
9926 n3->nbinary.ch2 = n2;
9927 n1 = n3;
9928 }
9929}
9930
Eric Andersenc470f442003-07-28 09:56:35 +00009931static union node *
9932pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009933{
Eric Andersencb57d552001-06-28 07:25:16 +00009934 union node *n1, *n2, *pipenode;
9935 struct nodelist *lp, *prev;
9936 int negate;
9937
9938 negate = 0;
9939 TRACE(("pipeline: entered\n"));
9940 if (readtoken() == TNOT) {
9941 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009942 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009943 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009944 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009945 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009946 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009947 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009948 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009949 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009950 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009951 pipenode->npipe.cmdlist = lp;
9952 lp->n = n1;
9953 do {
9954 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009955 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009956 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009957 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009958 prev->next = lp;
9959 } while (readtoken() == TPIPE);
9960 lp->next = NULL;
9961 n1 = pipenode;
9962 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009963 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009964 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009965 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009966 n2->type = NNOT;
9967 n2->nnot.com = n1;
9968 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009969 }
9970 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009971}
9972
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009973static union node *
9974makename(void)
9975{
9976 union node *n;
9977
Denis Vlasenko597906c2008-02-20 16:38:54 +00009978 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009979 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009980 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009981 n->narg.text = wordtext;
9982 n->narg.backquote = backquotelist;
9983 return n;
9984}
9985
9986static void
9987fixredir(union node *n, const char *text, int err)
9988{
9989 TRACE(("Fix redir %s %d\n", text, err));
9990 if (!err)
9991 n->ndup.vname = NULL;
9992
9993 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009994 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009995 else if (LONE_DASH(text))
9996 n->ndup.dupfd = -1;
9997 else {
9998 if (err)
9999 raise_error_syntax("Bad fd number");
10000 n->ndup.vname = makename();
10001 }
10002}
10003
10004/*
10005 * Returns true if the text contains nothing to expand (no dollar signs
10006 * or backquotes).
10007 */
10008static int
10009noexpand(char *text)
10010{
10011 char *p;
10012 char c;
10013
10014 p = text;
10015 while ((c = *p++) != '\0') {
10016 if (c == CTLQUOTEMARK)
10017 continue;
10018 if (c == CTLESC)
10019 p++;
10020 else if (SIT(c, BASESYNTAX) == CCTL)
10021 return 0;
10022 }
10023 return 1;
10024}
10025
10026static void
10027parsefname(void)
10028{
10029 union node *n = redirnode;
10030
10031 if (readtoken() != TWORD)
10032 raise_error_unexpected_syntax(-1);
10033 if (n->type == NHERE) {
10034 struct heredoc *here = heredoc;
10035 struct heredoc *p;
10036 int i;
10037
10038 if (quoteflag == 0)
10039 n->type = NXHERE;
10040 TRACE(("Here document %d\n", n->type));
10041 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10042 raise_error_syntax("Illegal eof marker for << redirection");
10043 rmescapes(wordtext);
10044 here->eofmark = wordtext;
10045 here->next = NULL;
10046 if (heredoclist == NULL)
10047 heredoclist = here;
10048 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010049 for (p = heredoclist; p->next; p = p->next)
10050 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010051 p->next = here;
10052 }
10053 } else if (n->type == NTOFD || n->type == NFROMFD) {
10054 fixredir(n, wordtext, 0);
10055 } else {
10056 n->nfile.fname = makename();
10057 }
10058}
Eric Andersencb57d552001-06-28 07:25:16 +000010059
Eric Andersenc470f442003-07-28 09:56:35 +000010060static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010061simplecmd(void)
10062{
10063 union node *args, **app;
10064 union node *n = NULL;
10065 union node *vars, **vpp;
10066 union node **rpp, *redir;
10067 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010068#if ENABLE_ASH_BASH_COMPAT
10069 smallint double_brackets_flag = 0;
10070#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010071
10072 args = NULL;
10073 app = &args;
10074 vars = NULL;
10075 vpp = &vars;
10076 redir = NULL;
10077 rpp = &redir;
10078
10079 savecheckkwd = CHKALIAS;
10080 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010081 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010082 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010083 t = readtoken();
10084 switch (t) {
10085#if ENABLE_ASH_BASH_COMPAT
10086 case TAND: /* "&&" */
10087 case TOR: /* "||" */
10088 if (!double_brackets_flag) {
10089 tokpushback = 1;
10090 goto out;
10091 }
10092 wordtext = (char *) (t == TAND ? "-a" : "-o");
10093#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010094 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010095 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010096 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010097 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010098 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010099#if ENABLE_ASH_BASH_COMPAT
10100 if (strcmp("[[", wordtext) == 0)
10101 double_brackets_flag = 1;
10102 else if (strcmp("]]", wordtext) == 0)
10103 double_brackets_flag = 0;
10104#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010105 n->narg.backquote = backquotelist;
10106 if (savecheckkwd && isassignment(wordtext)) {
10107 *vpp = n;
10108 vpp = &n->narg.next;
10109 } else {
10110 *app = n;
10111 app = &n->narg.next;
10112 savecheckkwd = 0;
10113 }
10114 break;
10115 case TREDIR:
10116 *rpp = n = redirnode;
10117 rpp = &n->nfile.next;
10118 parsefname(); /* read name of redirection file */
10119 break;
10120 case TLP:
10121 if (args && app == &args->narg.next
10122 && !vars && !redir
10123 ) {
10124 struct builtincmd *bcmd;
10125 const char *name;
10126
10127 /* We have a function */
10128 if (readtoken() != TRP)
10129 raise_error_unexpected_syntax(TRP);
10130 name = n->narg.text;
10131 if (!goodname(name)
10132 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10133 ) {
10134 raise_error_syntax("Bad function name");
10135 }
10136 n->type = NDEFUN;
10137 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10138 n->narg.next = parse_command();
10139 return n;
10140 }
10141 /* fall through */
10142 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010143 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010144 goto out;
10145 }
10146 }
10147 out:
10148 *app = NULL;
10149 *vpp = NULL;
10150 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010151 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010152 n->type = NCMD;
10153 n->ncmd.args = args;
10154 n->ncmd.assign = vars;
10155 n->ncmd.redirect = redir;
10156 return n;
10157}
10158
10159static union node *
10160parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010161{
Eric Andersencb57d552001-06-28 07:25:16 +000010162 union node *n1, *n2;
10163 union node *ap, **app;
10164 union node *cp, **cpp;
10165 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010166 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010167 int t;
10168
10169 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010170 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010171
Eric Andersencb57d552001-06-28 07:25:16 +000010172 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010173 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010174 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010175 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010176 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010177 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010178 n1->type = NIF;
10179 n1->nif.test = list(0);
10180 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010181 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010182 n1->nif.ifpart = list(0);
10183 n2 = n1;
10184 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010185 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010186 n2 = n2->nif.elsepart;
10187 n2->type = NIF;
10188 n2->nif.test = list(0);
10189 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010190 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010191 n2->nif.ifpart = list(0);
10192 }
10193 if (lasttoken == TELSE)
10194 n2->nif.elsepart = list(0);
10195 else {
10196 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010197 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010198 }
Eric Andersenc470f442003-07-28 09:56:35 +000010199 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010200 break;
10201 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010202 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010203 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010204 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010205 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010206 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010207 got = readtoken();
10208 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010209 TRACE(("expecting DO got %s %s\n", tokname(got),
10210 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010211 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010212 }
10213 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010214 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010215 break;
10216 }
10217 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010218 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010219 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010220 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010221 n1->type = NFOR;
10222 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010223 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010224 if (readtoken() == TIN) {
10225 app = &ap;
10226 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010227 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010228 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010229 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010230 n2->narg.text = wordtext;
10231 n2->narg.backquote = backquotelist;
10232 *app = n2;
10233 app = &n2->narg.next;
10234 }
10235 *app = NULL;
10236 n1->nfor.args = ap;
10237 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010238 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010239 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010240 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010241 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010242 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010243 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010244 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010245 n1->nfor.args = n2;
10246 /*
10247 * Newline or semicolon here is optional (but note
10248 * that the original Bourne shell only allowed NL).
10249 */
10250 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010251 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010252 }
Eric Andersenc470f442003-07-28 09:56:35 +000010253 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010254 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010255 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010256 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010257 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010258 break;
10259 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010260 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010261 n1->type = NCASE;
10262 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010263 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010264 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010265 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010266 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010267 n2->narg.text = wordtext;
10268 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010269 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010270 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010271 } while (readtoken() == TNL);
10272 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010273 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010274 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010275 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010276 checkkwd = CHKNL | CHKKWD;
10277 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010278 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010279 if (lasttoken == TLP)
10280 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010281 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010282 cp->type = NCLIST;
10283 app = &cp->nclist.pattern;
10284 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010285 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010286 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010287 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010288 ap->narg.text = wordtext;
10289 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010290 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010291 break;
10292 app = &ap->narg.next;
10293 readtoken();
10294 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010295 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010296 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010297 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010298 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010299
Eric Andersenc470f442003-07-28 09:56:35 +000010300 cpp = &cp->nclist.next;
10301
10302 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010303 t = readtoken();
10304 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010305 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010306 raise_error_unexpected_syntax(TENDCASE);
10307 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010308 }
Eric Andersenc470f442003-07-28 09:56:35 +000010309 }
Eric Andersencb57d552001-06-28 07:25:16 +000010310 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010311 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010312 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010313 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010314 n1->type = NSUBSHELL;
10315 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010316 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010317 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010318 break;
10319 case TBEGIN:
10320 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010321 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010322 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010323 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010324 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010325 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010326 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010327 }
10328
Eric Andersenc470f442003-07-28 09:56:35 +000010329 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010330 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010331
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010332 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010333 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010334 checkkwd = CHKKWD | CHKALIAS;
10335 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010336 while (readtoken() == TREDIR) {
10337 *rpp = n2 = redirnode;
10338 rpp = &n2->nfile.next;
10339 parsefname();
10340 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010341 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010342 *rpp = NULL;
10343 if (redir) {
10344 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010345 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010346 n2->type = NREDIR;
10347 n2->nredir.n = n1;
10348 n1 = n2;
10349 }
10350 n1->nredir.redirect = redir;
10351 }
Eric Andersencb57d552001-06-28 07:25:16 +000010352 return n1;
10353}
10354
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010355#if ENABLE_ASH_BASH_COMPAT
10356static int decode_dollar_squote(void)
10357{
10358 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10359 int c, cnt;
10360 char *p;
10361 char buf[4];
10362
10363 c = pgetc();
10364 p = strchr(C_escapes, c);
10365 if (p) {
10366 buf[0] = c;
10367 p = buf;
10368 cnt = 3;
10369 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10370 do {
10371 c = pgetc();
10372 *++p = c;
10373 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10374 pungetc();
10375 } else if (c == 'x') { /* \xHH */
10376 do {
10377 c = pgetc();
10378 *++p = c;
10379 } while (isxdigit(c) && --cnt);
10380 pungetc();
10381 if (cnt == 3) { /* \x but next char is "bad" */
10382 c = 'x';
10383 goto unrecognized;
10384 }
10385 } else { /* simple seq like \\ or \t */
10386 p++;
10387 }
10388 *p = '\0';
10389 p = buf;
10390 c = bb_process_escape_sequence((void*)&p);
10391 } else { /* unrecognized "\z": print both chars unless ' or " */
10392 if (c != '\'' && c != '"') {
10393 unrecognized:
10394 c |= 0x100; /* "please encode \, then me" */
10395 }
10396 }
10397 return c;
10398}
10399#endif
10400
Eric Andersencb57d552001-06-28 07:25:16 +000010401/*
10402 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10403 * is not NULL, read a here document. In the latter case, eofmark is the
10404 * word which marks the end of the document and striptabs is true if
10405 * leading tabs should be stripped from the document. The argument firstc
10406 * is the first character of the input token or document.
10407 *
10408 * Because C does not have internal subroutines, I have simulated them
10409 * using goto's to implement the subroutine linkage. The following macros
10410 * will run code that appears at the end of readtoken1.
10411 */
Eric Andersen2870d962001-07-02 17:27:21 +000010412#define CHECKEND() {goto checkend; checkend_return:;}
10413#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10414#define PARSESUB() {goto parsesub; parsesub_return:;}
10415#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10416#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10417#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010418static int
Eric Andersenc470f442003-07-28 09:56:35 +000010419readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010420{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010421 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010422 int c = firstc;
10423 char *out;
10424 int len;
10425 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010426 struct nodelist *bqlist;
10427 smallint quotef;
10428 smallint dblquote;
10429 smallint oldstyle;
10430 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010431#if ENABLE_ASH_EXPAND_PRMT
10432 smallint pssyntax; /* we are expanding a prompt string */
10433#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010434 int varnest; /* levels of variables expansion */
10435 int arinest; /* levels of arithmetic expansion */
10436 int parenlevel; /* levels of parens in arithmetic */
10437 int dqvarnest; /* levels of variables expansion within double quotes */
10438
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010439 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10440
Eric Andersencb57d552001-06-28 07:25:16 +000010441#if __GNUC__
10442 /* Avoid longjmp clobbering */
10443 (void) &out;
10444 (void) &quotef;
10445 (void) &dblquote;
10446 (void) &varnest;
10447 (void) &arinest;
10448 (void) &parenlevel;
10449 (void) &dqvarnest;
10450 (void) &oldstyle;
10451 (void) &prevsyntax;
10452 (void) &syntax;
10453#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010454 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010455 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010456 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010457 oldstyle = 0;
10458 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010459#if ENABLE_ASH_EXPAND_PRMT
10460 pssyntax = (syntax == PSSYNTAX);
10461 if (pssyntax)
10462 syntax = DQSYNTAX;
10463#endif
10464 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010465 varnest = 0;
10466 arinest = 0;
10467 parenlevel = 0;
10468 dqvarnest = 0;
10469
10470 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010471 loop: { /* for each line, until end of word */
10472 CHECKEND(); /* set c to PEOF if at end of here document */
10473 for (;;) { /* until end of line or end of word */
10474 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010475 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010476 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010477 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010478 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010479 USTPUTC(c, out);
10480 plinno++;
10481 if (doprompt)
10482 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010483 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010484 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010485 case CWORD:
10486 USTPUTC(c, out);
10487 break;
10488 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010489 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010490 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010491#if ENABLE_ASH_BASH_COMPAT
10492 if (c == '\\' && bash_dollar_squote) {
10493 c = decode_dollar_squote();
10494 if (c & 0x100) {
10495 USTPUTC('\\', out);
10496 c = (unsigned char)c;
10497 }
10498 }
10499#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010500 USTPUTC(c, out);
10501 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010502 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010503 c = pgetc2();
10504 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010505 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010506 USTPUTC('\\', out);
10507 pungetc();
10508 } else if (c == '\n') {
10509 if (doprompt)
10510 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010511 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010512#if ENABLE_ASH_EXPAND_PRMT
10513 if (c == '$' && pssyntax) {
10514 USTPUTC(CTLESC, out);
10515 USTPUTC('\\', out);
10516 }
10517#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010518 if (dblquote && c != '\\'
10519 && c != '`' && c != '$'
10520 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010521 ) {
10522 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010523 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010524 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010525 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010526 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010527 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010528 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010529 }
10530 break;
10531 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010532 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010533 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010534 if (eofmark == NULL) {
10535 USTPUTC(CTLQUOTEMARK, out);
10536 }
Eric Andersencb57d552001-06-28 07:25:16 +000010537 break;
10538 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010539 syntax = DQSYNTAX;
10540 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010541 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010542 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010543 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010544 if (eofmark != NULL && arinest == 0
10545 && varnest == 0
10546 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010547 USTPUTC(c, out);
10548 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010549 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010550 syntax = BASESYNTAX;
10551 dblquote = 0;
10552 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010553 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010554 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010555 }
10556 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010557 case CVAR: /* '$' */
10558 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010559 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010560 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010561 if (varnest > 0) {
10562 varnest--;
10563 if (dqvarnest > 0) {
10564 dqvarnest--;
10565 }
10566 USTPUTC(CTLENDVAR, out);
10567 } else {
10568 USTPUTC(c, out);
10569 }
10570 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010571#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010572 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010573 parenlevel++;
10574 USTPUTC(c, out);
10575 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010576 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010577 if (parenlevel > 0) {
10578 USTPUTC(c, out);
10579 --parenlevel;
10580 } else {
10581 if (pgetc() == ')') {
10582 if (--arinest == 0) {
10583 USTPUTC(CTLENDARI, out);
10584 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010585 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010586 } else
10587 USTPUTC(')', out);
10588 } else {
10589 /*
10590 * unbalanced parens
10591 * (don't 2nd guess - no error)
10592 */
10593 pungetc();
10594 USTPUTC(')', out);
10595 }
10596 }
10597 break;
10598#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010599 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010600 PARSEBACKQOLD();
10601 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010602 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010603 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010604 case CIGN:
10605 break;
10606 default:
10607 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010608 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010609#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010610 if (c != PEOA)
10611#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010612 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010613
Eric Andersencb57d552001-06-28 07:25:16 +000010614 }
10615 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010616 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010617 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010618 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010619#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010620 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010621 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010622#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010623 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010624 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010625 if (varnest != 0) {
10626 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010627 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010628 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010629 }
10630 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010631 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010632 out = stackblock();
10633 if (eofmark == NULL) {
10634 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010635 && quotef == 0
10636 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010637 && (*out == '\0' || isdigit(*out))
10638 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010639 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010640 lasttoken = TREDIR;
10641 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010642 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010643 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010644 }
10645 quoteflag = quotef;
10646 backquotelist = bqlist;
10647 grabstackblock(len);
10648 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010649 lasttoken = TWORD;
10650 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010651/* end of readtoken routine */
10652
Eric Andersencb57d552001-06-28 07:25:16 +000010653/*
10654 * Check to see whether we are at the end of the here document. When this
10655 * is called, c is set to the first character of the next input line. If
10656 * we are at the end of the here document, this routine sets the c to PEOF.
10657 */
Eric Andersenc470f442003-07-28 09:56:35 +000010658checkend: {
10659 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010660#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010661 if (c == PEOA) {
10662 c = pgetc2();
10663 }
10664#endif
10665 if (striptabs) {
10666 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010667 c = pgetc2();
10668 }
Eric Andersenc470f442003-07-28 09:56:35 +000010669 }
10670 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010671 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010672 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010673
Eric Andersenc470f442003-07-28 09:56:35 +000010674 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010675 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10676 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010677 if (*p == '\n' && *q == '\0') {
10678 c = PEOF;
10679 plinno++;
10680 needprompt = doprompt;
10681 } else {
10682 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010683 }
10684 }
10685 }
10686 }
Eric Andersenc470f442003-07-28 09:56:35 +000010687 goto checkend_return;
10688}
Eric Andersencb57d552001-06-28 07:25:16 +000010689
Eric Andersencb57d552001-06-28 07:25:16 +000010690/*
10691 * Parse a redirection operator. The variable "out" points to a string
10692 * specifying the fd to be redirected. The variable "c" contains the
10693 * first character of the redirection operator.
10694 */
Eric Andersenc470f442003-07-28 09:56:35 +000010695parseredir: {
10696 char fd = *out;
10697 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010698
Denis Vlasenko597906c2008-02-20 16:38:54 +000010699 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010700 if (c == '>') {
10701 np->nfile.fd = 1;
10702 c = pgetc();
10703 if (c == '>')
10704 np->type = NAPPEND;
10705 else if (c == '|')
10706 np->type = NCLOBBER;
10707 else if (c == '&')
10708 np->type = NTOFD;
10709 else {
10710 np->type = NTO;
10711 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010712 }
Eric Andersenc470f442003-07-28 09:56:35 +000010713 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010714 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010715 c = pgetc();
10716 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010717 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010718 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010719 np = stzalloc(sizeof(struct nhere));
10720 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010721 }
10722 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010723 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010724 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010725 c = pgetc();
10726 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010727 heredoc->striptabs = 1;
10728 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010729 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010730 pungetc();
10731 }
10732 break;
10733
10734 case '&':
10735 np->type = NFROMFD;
10736 break;
10737
10738 case '>':
10739 np->type = NFROMTO;
10740 break;
10741
10742 default:
10743 np->type = NFROM;
10744 pungetc();
10745 break;
10746 }
Eric Andersencb57d552001-06-28 07:25:16 +000010747 }
Eric Andersenc470f442003-07-28 09:56:35 +000010748 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010749 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010750 redirnode = np;
10751 goto parseredir_return;
10752}
Eric Andersencb57d552001-06-28 07:25:16 +000010753
Eric Andersencb57d552001-06-28 07:25:16 +000010754/*
10755 * Parse a substitution. At this point, we have read the dollar sign
10756 * and nothing else.
10757 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010758
10759/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10760 * (assuming ascii char codes, as the original implementation did) */
10761#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010762 (((unsigned)(c) - 33 < 32) \
10763 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010764parsesub: {
10765 int subtype;
10766 int typeloc;
10767 int flags;
10768 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010769 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010770
Eric Andersenc470f442003-07-28 09:56:35 +000010771 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010772 if (c <= PEOA_OR_PEOF
10773 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010774 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010775#if ENABLE_ASH_BASH_COMPAT
10776 if (c == '\'')
10777 bash_dollar_squote = 1;
10778 else
10779#endif
10780 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010781 pungetc();
10782 } else if (c == '(') { /* $(command) or $((arith)) */
10783 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010784#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010785 PARSEARITH();
10786#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010787 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010788#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010789 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010790 pungetc();
10791 PARSEBACKQNEW();
10792 }
10793 } else {
10794 USTPUTC(CTLVAR, out);
10795 typeloc = out - (char *)stackblock();
10796 USTPUTC(VSNORMAL, out);
10797 subtype = VSNORMAL;
10798 if (c == '{') {
10799 c = pgetc();
10800 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010801 c = pgetc();
10802 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010803 c = '#';
10804 else
10805 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010806 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010807 subtype = 0;
10808 }
10809 if (c > PEOA_OR_PEOF && is_name(c)) {
10810 do {
10811 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010812 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010813 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010815 do {
10816 STPUTC(c, out);
10817 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010818 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010819 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010820 USTPUTC(c, out);
10821 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010822 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010823 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010824
Eric Andersenc470f442003-07-28 09:56:35 +000010825 STPUTC('=', out);
10826 flags = 0;
10827 if (subtype == 0) {
10828 switch (c) {
10829 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010830 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010831#if ENABLE_ASH_BASH_COMPAT
10832 if (c == ':' || c == '$' || isdigit(c)) {
10833 pungetc();
10834 subtype = VSSUBSTR;
10835 break;
10836 }
10837#endif
10838 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010839 /*FALLTHROUGH*/
10840 default:
10841 p = strchr(types, c);
10842 if (p == NULL)
10843 goto badsub;
10844 subtype = p - types + VSNORMAL;
10845 break;
10846 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010847 case '#': {
10848 int cc = c;
10849 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10850 c = pgetc();
10851 if (c == cc)
10852 subtype++;
10853 else
10854 pungetc();
10855 break;
10856 }
10857#if ENABLE_ASH_BASH_COMPAT
10858 case '/':
10859 subtype = VSREPLACE;
10860 c = pgetc();
10861 if (c == '/')
10862 subtype++; /* VSREPLACEALL */
10863 else
10864 pungetc();
10865 break;
10866#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010867 }
Eric Andersenc470f442003-07-28 09:56:35 +000010868 } else {
10869 pungetc();
10870 }
10871 if (dblquote || arinest)
10872 flags |= VSQUOTE;
10873 *((char *)stackblock() + typeloc) = subtype | flags;
10874 if (subtype != VSNORMAL) {
10875 varnest++;
10876 if (dblquote || arinest) {
10877 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010878 }
10879 }
10880 }
Eric Andersenc470f442003-07-28 09:56:35 +000010881 goto parsesub_return;
10882}
Eric Andersencb57d552001-06-28 07:25:16 +000010883
Eric Andersencb57d552001-06-28 07:25:16 +000010884/*
10885 * Called to parse command substitutions. Newstyle is set if the command
10886 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10887 * list of commands (passed by reference), and savelen is the number of
10888 * characters on the top of the stack which must be preserved.
10889 */
Eric Andersenc470f442003-07-28 09:56:35 +000010890parsebackq: {
10891 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010892 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010893 union node *n;
10894 char *volatile str;
10895 struct jmploc jmploc;
10896 struct jmploc *volatile savehandler;
10897 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010898 smallint saveprompt = 0;
10899
Eric Andersencb57d552001-06-28 07:25:16 +000010900#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010901 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010902#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010903 savepbq = parsebackquote;
10904 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010905 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010906 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010907 exception_handler = savehandler;
10908 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010909 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010910 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010911 str = NULL;
10912 savelen = out - (char *)stackblock();
10913 if (savelen > 0) {
10914 str = ckmalloc(savelen);
10915 memcpy(str, stackblock(), savelen);
10916 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010917 savehandler = exception_handler;
10918 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010919 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010920 if (oldstyle) {
10921 /* We must read until the closing backquote, giving special
10922 treatment to some slashes, and then push the string and
10923 reread it as input, interpreting it normally. */
10924 char *pout;
10925 int pc;
10926 size_t psavelen;
10927 char *pstr;
10928
10929
10930 STARTSTACKSTR(pout);
10931 for (;;) {
10932 if (needprompt) {
10933 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010934 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010935 pc = pgetc();
10936 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010937 case '`':
10938 goto done;
10939
10940 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010941 pc = pgetc();
10942 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010943 plinno++;
10944 if (doprompt)
10945 setprompt(2);
10946 /*
10947 * If eating a newline, avoid putting
10948 * the newline into the new character
10949 * stream (via the STPUTC after the
10950 * switch).
10951 */
10952 continue;
10953 }
10954 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010955 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010956 STPUTC('\\', pout);
10957 if (pc > PEOA_OR_PEOF) {
10958 break;
10959 }
10960 /* fall through */
10961
10962 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010963#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010964 case PEOA:
10965#endif
10966 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010967 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010968
10969 case '\n':
10970 plinno++;
10971 needprompt = doprompt;
10972 break;
10973
10974 default:
10975 break;
10976 }
10977 STPUTC(pc, pout);
10978 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010979 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010980 STPUTC('\0', pout);
10981 psavelen = pout - (char *)stackblock();
10982 if (psavelen > 0) {
10983 pstr = grabstackstr(pout);
10984 setinputstring(pstr);
10985 }
10986 }
10987 nlpp = &bqlist;
10988 while (*nlpp)
10989 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010990 *nlpp = stzalloc(sizeof(**nlpp));
10991 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010992 parsebackquote = oldstyle;
10993
10994 if (oldstyle) {
10995 saveprompt = doprompt;
10996 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010997 }
10998
Eric Andersenc470f442003-07-28 09:56:35 +000010999 n = list(2);
11000
11001 if (oldstyle)
11002 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011003 else if (readtoken() != TRP)
11004 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011005
11006 (*nlpp)->n = n;
11007 if (oldstyle) {
11008 /*
11009 * Start reading from old file again, ignoring any pushed back
11010 * tokens left from the backquote parsing
11011 */
11012 popfile();
11013 tokpushback = 0;
11014 }
11015 while (stackblocksize() <= savelen)
11016 growstackblock();
11017 STARTSTACKSTR(out);
11018 if (str) {
11019 memcpy(out, str, savelen);
11020 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011021 INT_OFF;
11022 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011023 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011024 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011025 }
11026 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011027 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011028 if (arinest || dblquote)
11029 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11030 else
11031 USTPUTC(CTLBACKQ, out);
11032 if (oldstyle)
11033 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011034 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011035}
11036
Denis Vlasenko131ae172007-02-18 13:00:19 +000011037#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011038/*
11039 * Parse an arithmetic expansion (indicate start of one and set state)
11040 */
Eric Andersenc470f442003-07-28 09:56:35 +000011041parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011042 if (++arinest == 1) {
11043 prevsyntax = syntax;
11044 syntax = ARISYNTAX;
11045 USTPUTC(CTLARI, out);
11046 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011047 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011048 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011049 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011050 } else {
11051 /*
11052 * we collapse embedded arithmetic expansion to
11053 * parenthesis, which should be equivalent
11054 */
11055 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011056 }
Eric Andersenc470f442003-07-28 09:56:35 +000011057 goto parsearith_return;
11058}
11059#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011060
Eric Andersenc470f442003-07-28 09:56:35 +000011061} /* end of readtoken */
11062
Eric Andersencb57d552001-06-28 07:25:16 +000011063/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011064 * Read the next input token.
11065 * If the token is a word, we set backquotelist to the list of cmds in
11066 * backquotes. We set quoteflag to true if any part of the word was
11067 * quoted.
11068 * If the token is TREDIR, then we set redirnode to a structure containing
11069 * the redirection.
11070 * In all cases, the variable startlinno is set to the number of the line
11071 * on which the token starts.
11072 *
11073 * [Change comment: here documents and internal procedures]
11074 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11075 * word parsing code into a separate routine. In this case, readtoken
11076 * doesn't need to have any internal procedures, but parseword does.
11077 * We could also make parseoperator in essence the main routine, and
11078 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011079 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011080#define NEW_xxreadtoken
11081#ifdef NEW_xxreadtoken
11082/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011083static const char xxreadtoken_chars[7] ALIGN1 = {
11084 '\n', '(', ')', '&', '|', ';', 0
11085};
Eric Andersencb57d552001-06-28 07:25:16 +000011086
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011087static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011088 TNL, TLP, TRP, /* only single occurrence allowed */
11089 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11090 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011091 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011092};
11093
11094#define xxreadtoken_doubles \
11095 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11096#define xxreadtoken_singles \
11097 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11098
11099static int
11100xxreadtoken(void)
11101{
11102 int c;
11103
11104 if (tokpushback) {
11105 tokpushback = 0;
11106 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011107 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011108 if (needprompt) {
11109 setprompt(2);
11110 }
11111 startlinno = plinno;
11112 for (;;) { /* until token or start of word found */
11113 c = pgetc_macro();
11114
11115 if ((c != ' ') && (c != '\t')
11116#if ENABLE_ASH_ALIAS
11117 && (c != PEOA)
11118#endif
11119 ) {
11120 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011121 while ((c = pgetc()) != '\n' && c != PEOF)
11122 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011123 pungetc();
11124 } else if (c == '\\') {
11125 if (pgetc() != '\n') {
11126 pungetc();
11127 goto READTOKEN1;
11128 }
11129 startlinno = ++plinno;
11130 if (doprompt)
11131 setprompt(2);
11132 } else {
11133 const char *p
11134 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11135
11136 if (c != PEOF) {
11137 if (c == '\n') {
11138 plinno++;
11139 needprompt = doprompt;
11140 }
11141
11142 p = strchr(xxreadtoken_chars, c);
11143 if (p == NULL) {
11144 READTOKEN1:
11145 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11146 }
11147
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011148 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011149 if (pgetc() == *p) { /* double occurrence? */
11150 p += xxreadtoken_doubles + 1;
11151 } else {
11152 pungetc();
11153 }
11154 }
11155 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011156 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11157 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011158 }
11159 }
11160 } /* for */
11161}
11162#else
11163#define RETURN(token) return lasttoken = token
11164static int
11165xxreadtoken(void)
11166{
11167 int c;
11168
11169 if (tokpushback) {
11170 tokpushback = 0;
11171 return lasttoken;
11172 }
11173 if (needprompt) {
11174 setprompt(2);
11175 }
11176 startlinno = plinno;
11177 for (;;) { /* until token or start of word found */
11178 c = pgetc_macro();
11179 switch (c) {
11180 case ' ': case '\t':
11181#if ENABLE_ASH_ALIAS
11182 case PEOA:
11183#endif
11184 continue;
11185 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011186 while ((c = pgetc()) != '\n' && c != PEOF)
11187 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011188 pungetc();
11189 continue;
11190 case '\\':
11191 if (pgetc() == '\n') {
11192 startlinno = ++plinno;
11193 if (doprompt)
11194 setprompt(2);
11195 continue;
11196 }
11197 pungetc();
11198 goto breakloop;
11199 case '\n':
11200 plinno++;
11201 needprompt = doprompt;
11202 RETURN(TNL);
11203 case PEOF:
11204 RETURN(TEOF);
11205 case '&':
11206 if (pgetc() == '&')
11207 RETURN(TAND);
11208 pungetc();
11209 RETURN(TBACKGND);
11210 case '|':
11211 if (pgetc() == '|')
11212 RETURN(TOR);
11213 pungetc();
11214 RETURN(TPIPE);
11215 case ';':
11216 if (pgetc() == ';')
11217 RETURN(TENDCASE);
11218 pungetc();
11219 RETURN(TSEMI);
11220 case '(':
11221 RETURN(TLP);
11222 case ')':
11223 RETURN(TRP);
11224 default:
11225 goto breakloop;
11226 }
11227 }
11228 breakloop:
11229 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11230#undef RETURN
11231}
11232#endif /* NEW_xxreadtoken */
11233
11234static int
11235readtoken(void)
11236{
11237 int t;
11238#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011239 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011240#endif
11241
11242#if ENABLE_ASH_ALIAS
11243 top:
11244#endif
11245
11246 t = xxreadtoken();
11247
11248 /*
11249 * eat newlines
11250 */
11251 if (checkkwd & CHKNL) {
11252 while (t == TNL) {
11253 parseheredoc();
11254 t = xxreadtoken();
11255 }
11256 }
11257
11258 if (t != TWORD || quoteflag) {
11259 goto out;
11260 }
11261
11262 /*
11263 * check for keywords
11264 */
11265 if (checkkwd & CHKKWD) {
11266 const char *const *pp;
11267
11268 pp = findkwd(wordtext);
11269 if (pp) {
11270 lasttoken = t = pp - tokname_array;
11271 TRACE(("keyword %s recognized\n", tokname(t)));
11272 goto out;
11273 }
11274 }
11275
11276 if (checkkwd & CHKALIAS) {
11277#if ENABLE_ASH_ALIAS
11278 struct alias *ap;
11279 ap = lookupalias(wordtext, 1);
11280 if (ap != NULL) {
11281 if (*ap->val) {
11282 pushstring(ap->val, ap);
11283 }
11284 goto top;
11285 }
11286#endif
11287 }
11288 out:
11289 checkkwd = 0;
11290#if DEBUG
11291 if (!alreadyseen)
11292 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11293 else
11294 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11295#endif
11296 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011297}
11298
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011299static char
11300peektoken(void)
11301{
11302 int t;
11303
11304 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011305 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011306 return tokname_array[t][0];
11307}
Eric Andersencb57d552001-06-28 07:25:16 +000011308
11309/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011310 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11311 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011312 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011313static union node *
11314parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011315{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011316 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011317
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011318 tokpushback = 0;
11319 doprompt = interact;
11320 if (doprompt)
11321 setprompt(doprompt);
11322 needprompt = 0;
11323 t = readtoken();
11324 if (t == TEOF)
11325 return NEOF;
11326 if (t == TNL)
11327 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011328 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011329 return list(1);
11330}
11331
11332/*
11333 * Input any here documents.
11334 */
11335static void
11336parseheredoc(void)
11337{
11338 struct heredoc *here;
11339 union node *n;
11340
11341 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011342 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011343
11344 while (here) {
11345 if (needprompt) {
11346 setprompt(2);
11347 }
11348 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11349 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011350 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011351 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011352 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011353 n->narg.text = wordtext;
11354 n->narg.backquote = backquotelist;
11355 here->here->nhere.doc = n;
11356 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011357 }
Eric Andersencb57d552001-06-28 07:25:16 +000011358}
11359
11360
11361/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011362 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011363 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011364#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011365static const char *
11366expandstr(const char *ps)
11367{
11368 union node n;
11369
11370 /* XXX Fix (char *) cast. */
11371 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011372 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011373 popfile();
11374
11375 n.narg.type = NARG;
11376 n.narg.next = NULL;
11377 n.narg.text = wordtext;
11378 n.narg.backquote = backquotelist;
11379
11380 expandarg(&n, NULL, 0);
11381 return stackblock();
11382}
11383#endif
11384
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011385/*
11386 * Execute a command or commands contained in a string.
11387 */
11388static int
11389evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011390{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011391 union node *n;
11392 struct stackmark smark;
11393 int skip;
11394
11395 setinputstring(s);
11396 setstackmark(&smark);
11397
11398 skip = 0;
11399 while ((n = parsecmd(0)) != NEOF) {
11400 evaltree(n, 0);
11401 popstackmark(&smark);
11402 skip = evalskip;
11403 if (skip)
11404 break;
11405 }
11406 popfile();
11407
11408 skip &= mask;
11409 evalskip = skip;
11410 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011411}
11412
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011413/*
11414 * The eval command.
11415 */
11416static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011417evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011418{
11419 char *p;
11420 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011421
Denis Vlasenko68404f12008-03-17 09:00:54 +000011422 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011423 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011424 argv += 2;
11425 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011426 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011427 for (;;) {
11428 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011429 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011430 if (p == NULL)
11431 break;
11432 STPUTC(' ', concat);
11433 }
11434 STPUTC('\0', concat);
11435 p = grabstackstr(concat);
11436 }
11437 evalstring(p, ~SKIPEVAL);
11438
11439 }
11440 return exitstatus;
11441}
11442
11443/*
11444 * Read and execute commands. "Top" is nonzero for the top level command
11445 * loop; it turns on prompting if the shell is interactive.
11446 */
11447static int
11448cmdloop(int top)
11449{
11450 union node *n;
11451 struct stackmark smark;
11452 int inter;
11453 int numeof = 0;
11454
11455 TRACE(("cmdloop(%d) called\n", top));
11456 for (;;) {
11457 int skip;
11458
11459 setstackmark(&smark);
11460#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011461 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011462 showjobs(stderr, SHOW_CHANGED);
11463#endif
11464 inter = 0;
11465 if (iflag && top) {
11466 inter++;
11467#if ENABLE_ASH_MAIL
11468 chkmail();
11469#endif
11470 }
11471 n = parsecmd(inter);
11472 /* showtree(n); DEBUG */
11473 if (n == NEOF) {
11474 if (!top || numeof >= 50)
11475 break;
11476 if (!stoppedjobs()) {
11477 if (!Iflag)
11478 break;
11479 out2str("\nUse \"exit\" to leave shell.\n");
11480 }
11481 numeof++;
11482 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011483 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11484 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011485 numeof = 0;
11486 evaltree(n, 0);
11487 }
11488 popstackmark(&smark);
11489 skip = evalskip;
11490
11491 if (skip) {
11492 evalskip = 0;
11493 return skip & SKIPEVAL;
11494 }
11495 }
11496 return 0;
11497}
11498
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011499/*
11500 * Take commands from a file. To be compatible we should do a path
11501 * search for the file, which is necessary to find sub-commands.
11502 */
11503static char *
11504find_dot_file(char *name)
11505{
11506 char *fullname;
11507 const char *path = pathval();
11508 struct stat statb;
11509
11510 /* don't try this for absolute or relative paths */
11511 if (strchr(name, '/'))
11512 return name;
11513
11514 while ((fullname = padvance(&path, name)) != NULL) {
11515 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11516 /*
11517 * Don't bother freeing here, since it will
11518 * be freed by the caller.
11519 */
11520 return fullname;
11521 }
11522 stunalloc(fullname);
11523 }
11524
11525 /* not found in the PATH */
11526 ash_msg_and_raise_error("%s: not found", name);
11527 /* NOTREACHED */
11528}
11529
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011530static int
11531dotcmd(int argc, char **argv)
11532{
11533 struct strlist *sp;
11534 volatile struct shparam saveparam;
11535 int status = 0;
11536
11537 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011538 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011539
Denis Vlasenko68404f12008-03-17 09:00:54 +000011540 if (argv[1]) { /* That's what SVR2 does */
11541 char *fullname = find_dot_file(argv[1]);
11542 argv += 2;
11543 argc -= 2;
11544 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011545 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011546 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011547 shellparam.nparam = argc;
11548 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011549 };
11550
11551 setinputfile(fullname, INPUT_PUSH_FILE);
11552 commandname = fullname;
11553 cmdloop(0);
11554 popfile();
11555
Denis Vlasenko68404f12008-03-17 09:00:54 +000011556 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011557 freeparam(&shellparam);
11558 shellparam = saveparam;
11559 };
11560 status = exitstatus;
11561 }
11562 return status;
11563}
11564
11565static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011566exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011567{
11568 if (stoppedjobs())
11569 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011570 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011571 exitstatus = number(argv[1]);
11572 raise_exception(EXEXIT);
11573 /* NOTREACHED */
11574}
11575
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011576/*
11577 * Read a file containing shell functions.
11578 */
11579static void
11580readcmdfile(char *name)
11581{
11582 setinputfile(name, INPUT_PUSH_FILE);
11583 cmdloop(0);
11584 popfile();
11585}
11586
11587
Denis Vlasenkocc571512007-02-23 21:10:35 +000011588/* ============ find_command inplementation */
11589
11590/*
11591 * Resolve a command name. If you change this routine, you may have to
11592 * change the shellexec routine as well.
11593 */
11594static void
11595find_command(char *name, struct cmdentry *entry, int act, const char *path)
11596{
11597 struct tblentry *cmdp;
11598 int idx;
11599 int prev;
11600 char *fullname;
11601 struct stat statb;
11602 int e;
11603 int updatetbl;
11604 struct builtincmd *bcmd;
11605
11606 /* If name contains a slash, don't use PATH or hash table */
11607 if (strchr(name, '/') != NULL) {
11608 entry->u.index = -1;
11609 if (act & DO_ABS) {
11610 while (stat(name, &statb) < 0) {
11611#ifdef SYSV
11612 if (errno == EINTR)
11613 continue;
11614#endif
11615 entry->cmdtype = CMDUNKNOWN;
11616 return;
11617 }
11618 }
11619 entry->cmdtype = CMDNORMAL;
11620 return;
11621 }
11622
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011623/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011624
11625 updatetbl = (path == pathval());
11626 if (!updatetbl) {
11627 act |= DO_ALTPATH;
11628 if (strstr(path, "%builtin") != NULL)
11629 act |= DO_ALTBLTIN;
11630 }
11631
11632 /* If name is in the table, check answer will be ok */
11633 cmdp = cmdlookup(name, 0);
11634 if (cmdp != NULL) {
11635 int bit;
11636
11637 switch (cmdp->cmdtype) {
11638 default:
11639#if DEBUG
11640 abort();
11641#endif
11642 case CMDNORMAL:
11643 bit = DO_ALTPATH;
11644 break;
11645 case CMDFUNCTION:
11646 bit = DO_NOFUNC;
11647 break;
11648 case CMDBUILTIN:
11649 bit = DO_ALTBLTIN;
11650 break;
11651 }
11652 if (act & bit) {
11653 updatetbl = 0;
11654 cmdp = NULL;
11655 } else if (cmdp->rehash == 0)
11656 /* if not invalidated by cd, we're done */
11657 goto success;
11658 }
11659
11660 /* If %builtin not in path, check for builtin next */
11661 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011662 if (bcmd) {
11663 if (IS_BUILTIN_REGULAR(bcmd))
11664 goto builtin_success;
11665 if (act & DO_ALTPATH) {
11666 if (!(act & DO_ALTBLTIN))
11667 goto builtin_success;
11668 } else if (builtinloc <= 0) {
11669 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011670 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011671 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011672
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011673#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011674 {
11675 int applet_no = find_applet_by_name(name);
11676 if (applet_no >= 0) {
11677 entry->cmdtype = CMDNORMAL;
11678 entry->u.index = -2 - applet_no;
11679 return;
11680 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011681 }
11682#endif
11683
Denis Vlasenkocc571512007-02-23 21:10:35 +000011684 /* We have to search path. */
11685 prev = -1; /* where to start */
11686 if (cmdp && cmdp->rehash) { /* doing a rehash */
11687 if (cmdp->cmdtype == CMDBUILTIN)
11688 prev = builtinloc;
11689 else
11690 prev = cmdp->param.index;
11691 }
11692
11693 e = ENOENT;
11694 idx = -1;
11695 loop:
11696 while ((fullname = padvance(&path, name)) != NULL) {
11697 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011698 /* NB: code below will still use fullname
11699 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011700 idx++;
11701 if (pathopt) {
11702 if (prefix(pathopt, "builtin")) {
11703 if (bcmd)
11704 goto builtin_success;
11705 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011706 }
11707 if ((act & DO_NOFUNC)
11708 || !prefix(pathopt, "func")
11709 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011710 continue;
11711 }
11712 }
11713 /* if rehash, don't redo absolute path names */
11714 if (fullname[0] == '/' && idx <= prev) {
11715 if (idx < prev)
11716 continue;
11717 TRACE(("searchexec \"%s\": no change\n", name));
11718 goto success;
11719 }
11720 while (stat(fullname, &statb) < 0) {
11721#ifdef SYSV
11722 if (errno == EINTR)
11723 continue;
11724#endif
11725 if (errno != ENOENT && errno != ENOTDIR)
11726 e = errno;
11727 goto loop;
11728 }
11729 e = EACCES; /* if we fail, this will be the error */
11730 if (!S_ISREG(statb.st_mode))
11731 continue;
11732 if (pathopt) { /* this is a %func directory */
11733 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011734 /* NB: stalloc will return space pointed by fullname
11735 * (because we don't have any intervening allocations
11736 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011737 readcmdfile(fullname);
11738 cmdp = cmdlookup(name, 0);
11739 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11740 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11741 stunalloc(fullname);
11742 goto success;
11743 }
11744 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11745 if (!updatetbl) {
11746 entry->cmdtype = CMDNORMAL;
11747 entry->u.index = idx;
11748 return;
11749 }
11750 INT_OFF;
11751 cmdp = cmdlookup(name, 1);
11752 cmdp->cmdtype = CMDNORMAL;
11753 cmdp->param.index = idx;
11754 INT_ON;
11755 goto success;
11756 }
11757
11758 /* We failed. If there was an entry for this command, delete it */
11759 if (cmdp && updatetbl)
11760 delete_cmd_entry();
11761 if (act & DO_ERR)
11762 ash_msg("%s: %s", name, errmsg(e, "not found"));
11763 entry->cmdtype = CMDUNKNOWN;
11764 return;
11765
11766 builtin_success:
11767 if (!updatetbl) {
11768 entry->cmdtype = CMDBUILTIN;
11769 entry->u.cmd = bcmd;
11770 return;
11771 }
11772 INT_OFF;
11773 cmdp = cmdlookup(name, 1);
11774 cmdp->cmdtype = CMDBUILTIN;
11775 cmdp->param.cmd = bcmd;
11776 INT_ON;
11777 success:
11778 cmdp->rehash = 0;
11779 entry->cmdtype = cmdp->cmdtype;
11780 entry->u = cmdp->param;
11781}
11782
11783
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011784/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011785
Eric Andersencb57d552001-06-28 07:25:16 +000011786/*
Eric Andersencb57d552001-06-28 07:25:16 +000011787 * The trap builtin.
11788 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011789static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011790trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011791{
11792 char *action;
11793 char **ap;
11794 int signo;
11795
Eric Andersenc470f442003-07-28 09:56:35 +000011796 nextopt(nullstr);
11797 ap = argptr;
11798 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011799 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011800 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011801 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011802
Rob Landleyc9c1a412006-07-12 19:17:55 +000011803 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011804 out1fmt("trap -- %s %s\n",
11805 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011806 }
11807 }
11808 return 0;
11809 }
Eric Andersenc470f442003-07-28 09:56:35 +000011810 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011811 action = NULL;
11812 else
11813 action = *ap++;
11814 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011815 signo = get_signum(*ap);
11816 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011817 ash_msg_and_raise_error("%s: bad trap", *ap);
11818 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011819 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011820 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011821 action = NULL;
11822 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011823 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011824 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011825 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011826 trap[signo] = action;
11827 if (signo != 0)
11828 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011829 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011830 ap++;
11831 }
11832 return 0;
11833}
11834
Eric Andersenc470f442003-07-28 09:56:35 +000011835
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011836/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011837
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011838#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011839/*
11840 * Lists available builtins
11841 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011842static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011843helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011844{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011845 unsigned col;
11846 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011847
11848 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011849 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011850 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011851 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011852 if (col > 60) {
11853 out1fmt("\n");
11854 col = 0;
11855 }
11856 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011857#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011858 {
11859 const char *a = applet_names;
11860 while (*a) {
11861 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11862 if (col > 60) {
11863 out1fmt("\n");
11864 col = 0;
11865 }
11866 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011867 }
11868 }
11869#endif
11870 out1fmt("\n\n");
11871 return EXIT_SUCCESS;
11872}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011873#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011874
Eric Andersencb57d552001-06-28 07:25:16 +000011875/*
Eric Andersencb57d552001-06-28 07:25:16 +000011876 * The export and readonly commands.
11877 */
Eric Andersenc470f442003-07-28 09:56:35 +000011878static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011879exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011880{
11881 struct var *vp;
11882 char *name;
11883 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011884 char **aptr;
11885 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011886
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011887 if (nextopt("p") != 'p') {
11888 aptr = argptr;
11889 name = *aptr;
11890 if (name) {
11891 do {
11892 p = strchr(name, '=');
11893 if (p != NULL) {
11894 p++;
11895 } else {
11896 vp = *findvar(hashvar(name), name);
11897 if (vp) {
11898 vp->flags |= flag;
11899 continue;
11900 }
Eric Andersencb57d552001-06-28 07:25:16 +000011901 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011902 setvar(name, p, flag);
11903 } while ((name = *++aptr) != NULL);
11904 return 0;
11905 }
Eric Andersencb57d552001-06-28 07:25:16 +000011906 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011907 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011908 return 0;
11909}
11910
Eric Andersencb57d552001-06-28 07:25:16 +000011911/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011912 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011913 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011914static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011915unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011916{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011917 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011918
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011919 cmdp = cmdlookup(name, 0);
11920 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11921 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011922}
11923
Eric Andersencb57d552001-06-28 07:25:16 +000011924/*
Eric Andersencb57d552001-06-28 07:25:16 +000011925 * The unset builtin command. We unset the function before we unset the
11926 * variable to allow a function to be unset when there is a readonly variable
11927 * with the same name.
11928 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011929static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011930unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011931{
11932 char **ap;
11933 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011934 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011935 int ret = 0;
11936
11937 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011938 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011939 }
Eric Andersencb57d552001-06-28 07:25:16 +000011940
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011941 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011942 if (flag != 'f') {
11943 i = unsetvar(*ap);
11944 ret |= i;
11945 if (!(i & 2))
11946 continue;
11947 }
11948 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011949 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011950 }
Eric Andersenc470f442003-07-28 09:56:35 +000011951 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011952}
11953
11954
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011955/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011956
Eric Andersenc470f442003-07-28 09:56:35 +000011957#include <sys/times.h>
11958
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011959static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011960 ' ', offsetof(struct tms, tms_utime),
11961 '\n', offsetof(struct tms, tms_stime),
11962 ' ', offsetof(struct tms, tms_cutime),
11963 '\n', offsetof(struct tms, tms_cstime),
11964 0
11965};
Eric Andersencb57d552001-06-28 07:25:16 +000011966
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011967static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011968timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011969{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011970 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011971 const unsigned char *p;
11972 struct tms buf;
11973
11974 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011975 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011976
11977 p = timescmd_str;
11978 do {
11979 t = *(clock_t *)(((char *) &buf) + p[1]);
11980 s = t / clk_tck;
11981 out1fmt("%ldm%ld.%.3lds%c",
11982 s/60, s%60,
11983 ((t - s * clk_tck) * 1000) / clk_tck,
11984 p[0]);
11985 } while (*(p += 2));
11986
Eric Andersencb57d552001-06-28 07:25:16 +000011987 return 0;
11988}
11989
Denis Vlasenko131ae172007-02-18 13:00:19 +000011990#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011991static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011992dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011993{
Eric Andersened9ecf72004-06-22 08:29:45 +000011994 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011995 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011996
Denis Vlasenkob012b102007-02-19 22:43:01 +000011997 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011998 result = arith(s, &errcode);
11999 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012000 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012001 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012002 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012003 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012004 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012005 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012006 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012007 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012008 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012009
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012010 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012011}
Eric Andersenc470f442003-07-28 09:56:35 +000012012
Eric Andersenc470f442003-07-28 09:56:35 +000012013/*
Eric Andersen90898442003-08-06 11:20:52 +000012014 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12015 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12016 *
12017 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012018 */
12019static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012020letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012021{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012022 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012023
Denis Vlasenko68404f12008-03-17 09:00:54 +000012024 argv++;
12025 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012026 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012027 do {
12028 i = dash_arith(*argv);
12029 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012030
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012031 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012032}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012033#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012034
Eric Andersenc470f442003-07-28 09:56:35 +000012035
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012036/* ============ miscbltin.c
12037 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012038 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012039 */
12040
12041#undef rflag
12042
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012043#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012044typedef enum __rlimit_resource rlim_t;
12045#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012046
Eric Andersenc470f442003-07-28 09:56:35 +000012047/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012048 * The read builtin. Options:
12049 * -r Do not interpret '\' specially
12050 * -s Turn off echo (tty only)
12051 * -n NCHARS Read NCHARS max
12052 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12053 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12054 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012055 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012056 * TODO: bash also has:
12057 * -a ARRAY Read into array[0],[1],etc
12058 * -d DELIM End on DELIM char, not newline
12059 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012060 */
Eric Andersenc470f442003-07-28 09:56:35 +000012061static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012062readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012063{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012064 static const char *const arg_REPLY[] = { "REPLY", NULL };
12065
Eric Andersenc470f442003-07-28 09:56:35 +000012066 char **ap;
12067 int backslash;
12068 char c;
12069 int rflag;
12070 char *prompt;
12071 const char *ifs;
12072 char *p;
12073 int startword;
12074 int status;
12075 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012076 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012077#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012078 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012079 int silent = 0;
12080 struct termios tty, old_tty;
12081#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012082#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012083 unsigned end_ms = 0;
12084 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012085#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012086
12087 rflag = 0;
12088 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012089 while ((i = nextopt("p:u:r"
12090 USE_ASH_READ_TIMEOUT("t:")
12091 USE_ASH_READ_NCHARS("n:s")
12092 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012093 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012094 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012095 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012096 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012097#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012098 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012099 nchars = bb_strtou(optionarg, NULL, 10);
12100 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012101 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012102 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012103 break;
12104 case 's':
12105 silent = 1;
12106 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012107#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012108#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012109 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012110 timeout = bb_strtou(optionarg, NULL, 10);
12111 if (errno || timeout > UINT_MAX / 2048)
12112 ash_msg_and_raise_error("invalid timeout");
12113 timeout *= 1000;
12114#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012115 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012116 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012117 /* EINVAL means number is ok, but not terminated by NUL */
12118 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012119 char *p2;
12120 if (*++p) {
12121 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012122 ts.tv_usec = bb_strtou(p, &p2, 10);
12123 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012124 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012125 scale = p2 - p;
12126 /* normalize to usec */
12127 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012128 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012129 while (scale++ < 6)
12130 ts.tv_usec *= 10;
12131 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012132 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012133 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012134 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012135 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012136 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012137 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012138#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012139 break;
12140#endif
12141 case 'r':
12142 rflag = 1;
12143 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012144 case 'u':
12145 fd = bb_strtou(optionarg, NULL, 10);
12146 if (fd < 0 || errno)
12147 ash_msg_and_raise_error("invalid file descriptor");
12148 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012149 default:
12150 break;
12151 }
Eric Andersenc470f442003-07-28 09:56:35 +000012152 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012153 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012154 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012155 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012156 ap = argptr;
12157 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012158 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012159 ifs = bltinlookup("IFS");
12160 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012161 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012162#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012163 tcgetattr(fd, &tty);
12164 old_tty = tty;
12165 if (nchars || silent) {
12166 if (nchars) {
12167 tty.c_lflag &= ~ICANON;
12168 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012169 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012170 if (silent) {
12171 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12172 }
12173 /* if tcgetattr failed, tcsetattr will fail too.
12174 * Ignoring, it's harmless. */
12175 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012176 }
12177#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012178
Eric Andersenc470f442003-07-28 09:56:35 +000012179 status = 0;
12180 startword = 1;
12181 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012182#if ENABLE_ASH_READ_TIMEOUT
12183 if (timeout) /* NB: ensuring end_ms is nonzero */
12184 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12185#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012186 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012187 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012188#if ENABLE_ASH_READ_TIMEOUT
12189 if (end_ms) {
12190 struct pollfd pfd[1];
12191 pfd[0].fd = fd;
12192 pfd[0].events = POLLIN;
12193 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12194 if ((int)timeout <= 0 /* already late? */
12195 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12196 ) { /* timed out! */
12197#if ENABLE_ASH_READ_NCHARS
12198 tcsetattr(fd, TCSANOW, &old_tty);
12199#endif
12200 return 1;
12201 }
12202 }
12203#endif
12204 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012205 status = 1;
12206 break;
12207 }
12208 if (c == '\0')
12209 continue;
12210 if (backslash) {
12211 backslash = 0;
12212 if (c != '\n')
12213 goto put;
12214 continue;
12215 }
12216 if (!rflag && c == '\\') {
12217 backslash++;
12218 continue;
12219 }
12220 if (c == '\n')
12221 break;
12222 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12223 continue;
12224 }
12225 startword = 0;
12226 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12227 STACKSTRNUL(p);
12228 setvar(*ap, stackblock(), 0);
12229 ap++;
12230 startword = 1;
12231 STARTSTACKSTR(p);
12232 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012233 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012234 STPUTC(c, p);
12235 }
12236 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012237/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012238#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012239 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012240#else
12241 while (1);
12242#endif
12243
12244#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012245 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012246#endif
12247
Eric Andersenc470f442003-07-28 09:56:35 +000012248 STACKSTRNUL(p);
12249 /* Remove trailing blanks */
12250 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12251 *p = '\0';
12252 setvar(*ap, stackblock(), 0);
12253 while (*++ap != NULL)
12254 setvar(*ap, nullstr, 0);
12255 return status;
12256}
12257
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012258static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012259umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012260{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012261 static const char permuser[3] ALIGN1 = "ugo";
12262 static const char permmode[3] ALIGN1 = "rwx";
12263 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012264 S_IRUSR, S_IWUSR, S_IXUSR,
12265 S_IRGRP, S_IWGRP, S_IXGRP,
12266 S_IROTH, S_IWOTH, S_IXOTH
12267 };
12268
12269 char *ap;
12270 mode_t mask;
12271 int i;
12272 int symbolic_mode = 0;
12273
12274 while (nextopt("S") != '\0') {
12275 symbolic_mode = 1;
12276 }
12277
Denis Vlasenkob012b102007-02-19 22:43:01 +000012278 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012279 mask = umask(0);
12280 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012281 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012282
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012283 ap = *argptr;
12284 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012285 if (symbolic_mode) {
12286 char buf[18];
12287 char *p = buf;
12288
12289 for (i = 0; i < 3; i++) {
12290 int j;
12291
12292 *p++ = permuser[i];
12293 *p++ = '=';
12294 for (j = 0; j < 3; j++) {
12295 if ((mask & permmask[3 * i + j]) == 0) {
12296 *p++ = permmode[j];
12297 }
12298 }
12299 *p++ = ',';
12300 }
12301 *--p = 0;
12302 puts(buf);
12303 } else {
12304 out1fmt("%.4o\n", mask);
12305 }
12306 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012307 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012308 mask = 0;
12309 do {
12310 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012311 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012312 mask = (mask << 3) + (*ap - '0');
12313 } while (*++ap != '\0');
12314 umask(mask);
12315 } else {
12316 mask = ~mask & 0777;
12317 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012318 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012319 }
12320 umask(~mask & 0777);
12321 }
12322 }
12323 return 0;
12324}
12325
12326/*
12327 * ulimit builtin
12328 *
12329 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12330 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12331 * ash by J.T. Conklin.
12332 *
12333 * Public domain.
12334 */
12335
12336struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012337 uint8_t cmd; /* RLIMIT_xxx fit into it */
12338 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012339 char option;
12340};
12341
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012342static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012343#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012344 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012345#endif
12346#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012347 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012348#endif
12349#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012350 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012351#endif
12352#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012353 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012354#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012355#ifdef RLIMIT_CORE
12356 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012357#endif
12358#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012359 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012360#endif
12361#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012362 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012363#endif
12364#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012365 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012366#endif
12367#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012368 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012369#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012370#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012371 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012372#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012373#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012374 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012375#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012376};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012377static const char limits_name[] =
12378#ifdef RLIMIT_CPU
12379 "time(seconds)" "\0"
12380#endif
12381#ifdef RLIMIT_FSIZE
12382 "file(blocks)" "\0"
12383#endif
12384#ifdef RLIMIT_DATA
12385 "data(kb)" "\0"
12386#endif
12387#ifdef RLIMIT_STACK
12388 "stack(kb)" "\0"
12389#endif
12390#ifdef RLIMIT_CORE
12391 "coredump(blocks)" "\0"
12392#endif
12393#ifdef RLIMIT_RSS
12394 "memory(kb)" "\0"
12395#endif
12396#ifdef RLIMIT_MEMLOCK
12397 "locked memory(kb)" "\0"
12398#endif
12399#ifdef RLIMIT_NPROC
12400 "process" "\0"
12401#endif
12402#ifdef RLIMIT_NOFILE
12403 "nofiles" "\0"
12404#endif
12405#ifdef RLIMIT_AS
12406 "vmemory(kb)" "\0"
12407#endif
12408#ifdef RLIMIT_LOCKS
12409 "locks" "\0"
12410#endif
12411;
Eric Andersenc470f442003-07-28 09:56:35 +000012412
Glenn L McGrath76620622004-01-13 10:19:37 +000012413enum limtype { SOFT = 0x1, HARD = 0x2 };
12414
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012415static void
12416printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012417 const struct limits *l)
12418{
12419 rlim_t val;
12420
12421 val = limit->rlim_max;
12422 if (how & SOFT)
12423 val = limit->rlim_cur;
12424
12425 if (val == RLIM_INFINITY)
12426 out1fmt("unlimited\n");
12427 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012428 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012429 out1fmt("%lld\n", (long long) val);
12430 }
12431}
12432
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012433static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012434ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012435{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012436 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012437 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012438 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012439 const struct limits *l;
12440 int set, all = 0;
12441 int optc, what;
12442 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012443
12444 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012445 while ((optc = nextopt("HSa"
12446#ifdef RLIMIT_CPU
12447 "t"
12448#endif
12449#ifdef RLIMIT_FSIZE
12450 "f"
12451#endif
12452#ifdef RLIMIT_DATA
12453 "d"
12454#endif
12455#ifdef RLIMIT_STACK
12456 "s"
12457#endif
12458#ifdef RLIMIT_CORE
12459 "c"
12460#endif
12461#ifdef RLIMIT_RSS
12462 "m"
12463#endif
12464#ifdef RLIMIT_MEMLOCK
12465 "l"
12466#endif
12467#ifdef RLIMIT_NPROC
12468 "p"
12469#endif
12470#ifdef RLIMIT_NOFILE
12471 "n"
12472#endif
12473#ifdef RLIMIT_AS
12474 "v"
12475#endif
12476#ifdef RLIMIT_LOCKS
12477 "w"
12478#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012479 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012480 switch (optc) {
12481 case 'H':
12482 how = HARD;
12483 break;
12484 case 'S':
12485 how = SOFT;
12486 break;
12487 case 'a':
12488 all = 1;
12489 break;
12490 default:
12491 what = optc;
12492 }
12493
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012494 for (l = limits_tbl; l->option != what; l++)
12495 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012496
12497 set = *argptr ? 1 : 0;
12498 if (set) {
12499 char *p = *argptr;
12500
12501 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012502 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012503 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012504 val = RLIM_INFINITY;
12505 else {
12506 val = (rlim_t) 0;
12507
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012508 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012509 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012510 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012511 if (val < (rlim_t) 0)
12512 break;
12513 }
12514 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012515 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012516 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012517 }
12518 }
12519 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012520 const char *lname = limits_name;
12521 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012522 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012523 out1fmt("%-20s ", lname);
12524 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012525 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012526 }
12527 return 0;
12528 }
12529
12530 getrlimit(l->cmd, &limit);
12531 if (set) {
12532 if (how & HARD)
12533 limit.rlim_max = val;
12534 if (how & SOFT)
12535 limit.rlim_cur = val;
12536 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012537 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012538 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012539 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012540 }
12541 return 0;
12542}
12543
Eric Andersen90898442003-08-06 11:20:52 +000012544
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012545/* ============ Math support */
12546
Denis Vlasenko131ae172007-02-18 13:00:19 +000012547#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012548
12549/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12550
12551 Permission is hereby granted, free of charge, to any person obtaining
12552 a copy of this software and associated documentation files (the
12553 "Software"), to deal in the Software without restriction, including
12554 without limitation the rights to use, copy, modify, merge, publish,
12555 distribute, sublicense, and/or sell copies of the Software, and to
12556 permit persons to whom the Software is furnished to do so, subject to
12557 the following conditions:
12558
12559 The above copyright notice and this permission notice shall be
12560 included in all copies or substantial portions of the Software.
12561
12562 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12563 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12564 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12565 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12566 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12567 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12568 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12569*/
12570
12571/* This is my infix parser/evaluator. It is optimized for size, intended
12572 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012573 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012574 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012575 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012576 * be that which POSIX specifies for shells. */
12577
12578/* The code uses a simple two-stack algorithm. See
12579 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012580 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012581 * this is based (this code differs in that it applies operators immediately
12582 * to the stack instead of adding them to a queue to end up with an
12583 * expression). */
12584
12585/* To use the routine, call it with an expression string and error return
12586 * pointer */
12587
12588/*
12589 * Aug 24, 2001 Manuel Novoa III
12590 *
12591 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12592 *
12593 * 1) In arith_apply():
12594 * a) Cached values of *numptr and &(numptr[-1]).
12595 * b) Removed redundant test for zero denominator.
12596 *
12597 * 2) In arith():
12598 * a) Eliminated redundant code for processing operator tokens by moving
12599 * to a table-based implementation. Also folded handling of parens
12600 * into the table.
12601 * b) Combined all 3 loops which called arith_apply to reduce generated
12602 * code size at the cost of speed.
12603 *
12604 * 3) The following expressions were treated as valid by the original code:
12605 * 1() , 0! , 1 ( *3 ) .
12606 * These bugs have been fixed by internally enclosing the expression in
12607 * parens and then checking that all binary ops and right parens are
12608 * preceded by a valid expression (NUM_TOKEN).
12609 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012610 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012611 * ctype's isspace() if it is used by another busybox applet or if additional
12612 * whitespace chars should be considered. Look below the "#include"s for a
12613 * precompiler test.
12614 */
12615
12616/*
12617 * Aug 26, 2001 Manuel Novoa III
12618 *
12619 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12620 *
12621 * Merge in Aaron's comments previously posted to the busybox list,
12622 * modified slightly to take account of my changes to the code.
12623 *
12624 */
12625
12626/*
12627 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12628 *
12629 * - allow access to variable,
12630 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12631 * - realize assign syntax (VAR=expr, +=, *= etc)
12632 * - realize exponentiation (** operator)
12633 * - realize comma separated - expr, expr
12634 * - realise ++expr --expr expr++ expr--
12635 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012636 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012637 * - was restored loses XOR operator
12638 * - remove one goto label, added three ;-)
12639 * - protect $((num num)) as true zero expr (Manuel`s error)
12640 * - always use special isspace(), see comment from bash ;-)
12641 */
12642
Eric Andersen90898442003-08-06 11:20:52 +000012643#define arith_isspace(arithval) \
12644 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12645
Eric Andersen90898442003-08-06 11:20:52 +000012646typedef unsigned char operator;
12647
12648/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012649 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012650 * precedence. The ID portion is so that multiple operators can have the
12651 * same precedence, ensuring that the leftmost one is evaluated first.
12652 * Consider * and /. */
12653
12654#define tok_decl(prec,id) (((id)<<5)|(prec))
12655#define PREC(op) ((op) & 0x1F)
12656
12657#define TOK_LPAREN tok_decl(0,0)
12658
12659#define TOK_COMMA tok_decl(1,0)
12660
12661#define TOK_ASSIGN tok_decl(2,0)
12662#define TOK_AND_ASSIGN tok_decl(2,1)
12663#define TOK_OR_ASSIGN tok_decl(2,2)
12664#define TOK_XOR_ASSIGN tok_decl(2,3)
12665#define TOK_PLUS_ASSIGN tok_decl(2,4)
12666#define TOK_MINUS_ASSIGN tok_decl(2,5)
12667#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12668#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12669
12670#define TOK_MUL_ASSIGN tok_decl(3,0)
12671#define TOK_DIV_ASSIGN tok_decl(3,1)
12672#define TOK_REM_ASSIGN tok_decl(3,2)
12673
12674/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012675#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012676
12677/* conditional is right associativity too */
12678#define TOK_CONDITIONAL tok_decl(4,0)
12679#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12680
12681#define TOK_OR tok_decl(5,0)
12682
12683#define TOK_AND tok_decl(6,0)
12684
12685#define TOK_BOR tok_decl(7,0)
12686
12687#define TOK_BXOR tok_decl(8,0)
12688
12689#define TOK_BAND tok_decl(9,0)
12690
12691#define TOK_EQ tok_decl(10,0)
12692#define TOK_NE tok_decl(10,1)
12693
12694#define TOK_LT tok_decl(11,0)
12695#define TOK_GT tok_decl(11,1)
12696#define TOK_GE tok_decl(11,2)
12697#define TOK_LE tok_decl(11,3)
12698
12699#define TOK_LSHIFT tok_decl(12,0)
12700#define TOK_RSHIFT tok_decl(12,1)
12701
12702#define TOK_ADD tok_decl(13,0)
12703#define TOK_SUB tok_decl(13,1)
12704
12705#define TOK_MUL tok_decl(14,0)
12706#define TOK_DIV tok_decl(14,1)
12707#define TOK_REM tok_decl(14,2)
12708
12709/* exponent is right associativity */
12710#define TOK_EXPONENT tok_decl(15,1)
12711
12712/* For now unary operators. */
12713#define UNARYPREC 16
12714#define TOK_BNOT tok_decl(UNARYPREC,0)
12715#define TOK_NOT tok_decl(UNARYPREC,1)
12716
12717#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12718#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12719
12720#define PREC_PRE (UNARYPREC+2)
12721
12722#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12723#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12724
12725#define PREC_POST (UNARYPREC+3)
12726
12727#define TOK_POST_INC tok_decl(PREC_POST, 0)
12728#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12729
12730#define SPEC_PREC (UNARYPREC+4)
12731
12732#define TOK_NUM tok_decl(SPEC_PREC, 0)
12733#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12734
12735#define NUMPTR (*numstackptr)
12736
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012737static int
12738tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012739{
12740 operator prec = PREC(op);
12741
12742 convert_prec_is_assing(prec);
12743 return (prec == PREC(TOK_ASSIGN) ||
12744 prec == PREC_PRE || prec == PREC_POST);
12745}
12746
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012747static int
12748is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012749{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012750 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12751 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012752}
12753
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012754typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012755 arith_t val;
12756 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012757 char contidional_second_val_initialized;
12758 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012759 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012760} v_n_t;
12761
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012762typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012763 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012764 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012765} chk_var_recursive_looped_t;
12766
12767static chk_var_recursive_looped_t *prev_chk_var_recursive;
12768
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012769static int
12770arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012771{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012772 if (t->var) {
12773 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012774
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012775 if (p) {
12776 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012777
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012778 /* recursive try as expression */
12779 chk_var_recursive_looped_t *cur;
12780 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012781
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012782 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12783 if (strcmp(cur->var, t->var) == 0) {
12784 /* expression recursion loop detected */
12785 return -5;
12786 }
12787 }
12788 /* save current lookuped var name */
12789 cur = prev_chk_var_recursive;
12790 cur_save.var = t->var;
12791 cur_save.next = cur;
12792 prev_chk_var_recursive = &cur_save;
12793
12794 t->val = arith (p, &errcode);
12795 /* restore previous ptr after recursiving */
12796 prev_chk_var_recursive = cur;
12797 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012798 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012799 /* allow undefined var as 0 */
12800 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012801 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012802 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012803}
12804
12805/* "applying" a token means performing it on the top elements on the integer
12806 * stack. For a unary operator it will only change the top element, but a
12807 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012808static int
12809arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012810{
Eric Andersen90898442003-08-06 11:20:52 +000012811 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012812 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012813 int ret_arith_lookup_val;
12814
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012815 /* There is no operator that can work without arguments */
12816 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012817 numptr_m1 = NUMPTR - 1;
12818
12819 /* check operand is var with noninteger value */
12820 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012821 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012822 return ret_arith_lookup_val;
12823
12824 rez = numptr_m1->val;
12825 if (op == TOK_UMINUS)
12826 rez *= -1;
12827 else if (op == TOK_NOT)
12828 rez = !rez;
12829 else if (op == TOK_BNOT)
12830 rez = ~rez;
12831 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12832 rez++;
12833 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12834 rez--;
12835 else if (op != TOK_UPLUS) {
12836 /* Binary operators */
12837
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012838 /* check and binary operators need two arguments */
12839 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012840
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012841 /* ... and they pop one */
12842 --NUMPTR;
12843 numptr_val = rez;
12844 if (op == TOK_CONDITIONAL) {
12845 if (! numptr_m1->contidional_second_val_initialized) {
12846 /* protect $((expr1 ? expr2)) without ": expr" */
12847 goto err;
12848 }
12849 rez = numptr_m1->contidional_second_val;
12850 } else if (numptr_m1->contidional_second_val_initialized) {
12851 /* protect $((expr1 : expr2)) without "expr ? " */
12852 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012853 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012854 numptr_m1 = NUMPTR - 1;
12855 if (op != TOK_ASSIGN) {
12856 /* check operand is var with noninteger value for not '=' */
12857 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12858 if (ret_arith_lookup_val)
12859 return ret_arith_lookup_val;
12860 }
12861 if (op == TOK_CONDITIONAL) {
12862 numptr_m1->contidional_second_val = rez;
12863 }
12864 rez = numptr_m1->val;
12865 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012866 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012867 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012868 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012869 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012870 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012871 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012872 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012873 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012874 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012875 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012876 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012877 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012878 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012879 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012880 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012881 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012882 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012883 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012884 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012885 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012886 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012887 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012888 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012889 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012890 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012891 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012892 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012893 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012894 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012895 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012896 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012897 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012898 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012899 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012900 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012901 /* protect $((expr : expr)) without "expr ? " */
12902 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012903 }
12904 numptr_m1->contidional_second_val_initialized = op;
12905 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012906 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012907 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012908 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012909 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012910 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012911 return -3; /* exponent less than 0 */
12912 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012913 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012914
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012915 if (numptr_val)
12916 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012917 c *= rez;
12918 rez = c;
12919 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012920 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012921 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012922 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012923 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012924 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012925 rez %= numptr_val;
12926 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012927 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012928 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012929
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012930 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012931 /* Hmm, 1=2 ? */
12932 goto err;
12933 }
12934 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012935#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012936 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012937#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012938 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012939#endif
Eric Andersen90898442003-08-06 11:20:52 +000012940 setvar(numptr_m1->var, buf, 0);
12941 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012942 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012943 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012944 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012945 rez++;
12946 }
12947 numptr_m1->val = rez;
12948 /* protect geting var value, is number now */
12949 numptr_m1->var = NULL;
12950 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012951 err:
12952 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012953}
12954
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012955/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012956static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012957 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12958 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12959 '<','<', 0, TOK_LSHIFT,
12960 '>','>', 0, TOK_RSHIFT,
12961 '|','|', 0, TOK_OR,
12962 '&','&', 0, TOK_AND,
12963 '!','=', 0, TOK_NE,
12964 '<','=', 0, TOK_LE,
12965 '>','=', 0, TOK_GE,
12966 '=','=', 0, TOK_EQ,
12967 '|','=', 0, TOK_OR_ASSIGN,
12968 '&','=', 0, TOK_AND_ASSIGN,
12969 '*','=', 0, TOK_MUL_ASSIGN,
12970 '/','=', 0, TOK_DIV_ASSIGN,
12971 '%','=', 0, TOK_REM_ASSIGN,
12972 '+','=', 0, TOK_PLUS_ASSIGN,
12973 '-','=', 0, TOK_MINUS_ASSIGN,
12974 '-','-', 0, TOK_POST_DEC,
12975 '^','=', 0, TOK_XOR_ASSIGN,
12976 '+','+', 0, TOK_POST_INC,
12977 '*','*', 0, TOK_EXPONENT,
12978 '!', 0, TOK_NOT,
12979 '<', 0, TOK_LT,
12980 '>', 0, TOK_GT,
12981 '=', 0, TOK_ASSIGN,
12982 '|', 0, TOK_BOR,
12983 '&', 0, TOK_BAND,
12984 '*', 0, TOK_MUL,
12985 '/', 0, TOK_DIV,
12986 '%', 0, TOK_REM,
12987 '+', 0, TOK_ADD,
12988 '-', 0, TOK_SUB,
12989 '^', 0, TOK_BXOR,
12990 /* uniq */
12991 '~', 0, TOK_BNOT,
12992 ',', 0, TOK_COMMA,
12993 '?', 0, TOK_CONDITIONAL,
12994 ':', 0, TOK_CONDITIONAL_SEP,
12995 ')', 0, TOK_RPAREN,
12996 '(', 0, TOK_LPAREN,
12997 0
12998};
12999/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013000#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013001
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013002static arith_t
13003arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013004{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013005 char arithval; /* Current character under analysis */
13006 operator lasttok, op;
13007 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013008 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 const char *p = endexpression;
13010 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013011 v_n_t *numstack, *numstackptr;
13012 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013013
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 /* Stack of integers */
13015 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13016 * in any given correct or incorrect expression is left as an exercise to
13017 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013018 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013019 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013020 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013021
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013022 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13023 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013024
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013025 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013026 arithval = *expr;
13027 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013028 if (p == endexpression) {
13029 /* Null expression. */
13030 return 0;
13031 }
13032
13033 /* This is only reached after all tokens have been extracted from the
13034 * input stream. If there are still tokens on the operator stack, they
13035 * are to be applied in order. At the end, there should be a final
13036 * result on the integer stack */
13037
13038 if (expr != endexpression + 1) {
13039 /* If we haven't done so already, */
13040 /* append a closing right paren */
13041 expr = endexpression;
13042 /* and let the loop process it. */
13043 continue;
13044 }
13045 /* At this point, we're done with the expression. */
13046 if (numstackptr != numstack+1) {
13047 /* ... but if there isn't, it's bad */
13048 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013049 *perrcode = -1;
13050 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013051 }
13052 if (numstack->var) {
13053 /* expression is $((var)) only, lookup now */
13054 errcode = arith_lookup_val(numstack);
13055 }
13056 ret:
13057 *perrcode = errcode;
13058 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013059 }
13060
Eric Andersen90898442003-08-06 11:20:52 +000013061 /* Continue processing the expression. */
13062 if (arith_isspace(arithval)) {
13063 /* Skip whitespace */
13064 goto prologue;
13065 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013066 p = endofname(expr);
13067 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013068 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013069
13070 numstackptr->var = alloca(var_name_size);
13071 safe_strncpy(numstackptr->var, expr, var_name_size);
13072 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013073 num:
Eric Andersen90898442003-08-06 11:20:52 +000013074 numstackptr->contidional_second_val_initialized = 0;
13075 numstackptr++;
13076 lasttok = TOK_NUM;
13077 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013078 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013079 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013080 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013081#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013082 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013083#else
13084 numstackptr->val = strtol(expr, (char **) &expr, 0);
13085#endif
Eric Andersen90898442003-08-06 11:20:52 +000013086 goto num;
13087 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013089 const char *o;
13090
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013091 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013092 /* strange operator not found */
13093 goto err;
13094 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013095 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013096 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013097 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013098 /* found */
13099 expr = o - 1;
13100 break;
13101 }
13102 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013103 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013104 p++;
13105 /* skip zero delim */
13106 p++;
13107 }
13108 op = p[1];
13109
13110 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13112 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013113
13114 /* Plus and minus are binary (not unary) _only_ if the last
13115 * token was as number, or a right paren (which pretends to be
13116 * a number, since it evaluates to one). Think about it.
13117 * It makes sense. */
13118 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013119 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013120 case TOK_ADD:
13121 op = TOK_UPLUS;
13122 break;
13123 case TOK_SUB:
13124 op = TOK_UMINUS;
13125 break;
13126 case TOK_POST_INC:
13127 op = TOK_PRE_INC;
13128 break;
13129 case TOK_POST_DEC:
13130 op = TOK_PRE_DEC;
13131 break;
Eric Andersen90898442003-08-06 11:20:52 +000013132 }
13133 }
13134 /* We don't want a unary operator to cause recursive descent on the
13135 * stack, because there can be many in a row and it could cause an
13136 * operator to be evaluated before its argument is pushed onto the
13137 * integer stack. */
13138 /* But for binary operators, "apply" everything on the operator
13139 * stack until we find an operator with a lesser priority than the
13140 * one we have just extracted. */
13141 /* Left paren is given the lowest priority so it will never be
13142 * "applied" in this way.
13143 * if associativity is right and priority eq, applied also skip
13144 */
13145 prec = PREC(op);
13146 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13147 /* not left paren or unary */
13148 if (lasttok != TOK_NUM) {
13149 /* binary op must be preceded by a num */
13150 goto err;
13151 }
13152 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013153 if (op == TOK_RPAREN) {
13154 /* The algorithm employed here is simple: while we don't
13155 * hit an open paren nor the bottom of the stack, pop
13156 * tokens and apply them */
13157 if (stackptr[-1] == TOK_LPAREN) {
13158 --stackptr;
13159 /* Any operator directly after a */
13160 lasttok = TOK_NUM;
13161 /* close paren should consider itself binary */
13162 goto prologue;
13163 }
13164 } else {
13165 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013166
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013167 convert_prec_is_assing(prec);
13168 convert_prec_is_assing(prev_prec);
13169 if (prev_prec < prec)
13170 break;
13171 /* check right assoc */
13172 if (prev_prec == prec && is_right_associativity(prec))
13173 break;
13174 }
13175 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13176 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013177 }
13178 if (op == TOK_RPAREN) {
13179 goto err;
13180 }
13181 }
13182
13183 /* Push this operator to the stack and remember it. */
13184 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013185 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013186 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013187 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013188}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013189#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013190
13191
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013192/* ============ main() and helpers */
13193
13194/*
13195 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013196 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013197static void exitshell(void) ATTRIBUTE_NORETURN;
13198static void
13199exitshell(void)
13200{
13201 struct jmploc loc;
13202 char *p;
13203 int status;
13204
13205 status = exitstatus;
13206 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13207 if (setjmp(loc.loc)) {
13208 if (exception == EXEXIT)
13209/* dash bug: it just does _exit(exitstatus) here
13210 * but we have to do setjobctl(0) first!
13211 * (bug is still not fixed in dash-0.5.3 - if you run dash
13212 * under Midnight Commander, on exit from dash MC is backgrounded) */
13213 status = exitstatus;
13214 goto out;
13215 }
13216 exception_handler = &loc;
13217 p = trap[0];
13218 if (p) {
13219 trap[0] = NULL;
13220 evalstring(p, 0);
13221 }
13222 flush_stdout_stderr();
13223 out:
13224 setjobctl(0);
13225 _exit(status);
13226 /* NOTREACHED */
13227}
13228
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013229static void
13230init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013231{
13232 /* from input.c: */
13233 basepf.nextc = basepf.buf = basebuf;
13234
13235 /* from trap.c: */
13236 signal(SIGCHLD, SIG_DFL);
13237
13238 /* from var.c: */
13239 {
13240 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013241 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013242 const char *p;
13243 struct stat st1, st2;
13244
13245 initvar();
13246 for (envp = environ; envp && *envp; envp++) {
13247 if (strchr(*envp, '=')) {
13248 setvareq(*envp, VEXPORT|VTEXTFIXED);
13249 }
13250 }
13251
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013252 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013253 setvar("PPID", ppid, 0);
13254
13255 p = lookupvar("PWD");
13256 if (p)
13257 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13258 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13259 p = '\0';
13260 setpwd(p, 0);
13261 }
13262}
13263
13264/*
13265 * Process the shell command line arguments.
13266 */
13267static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013268procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013269{
13270 int i;
13271 const char *xminusc;
13272 char **xargv;
13273
13274 xargv = argv;
13275 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013276 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013277 xargv++;
13278 for (i = 0; i < NOPTS; i++)
13279 optlist[i] = 2;
13280 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013281 if (options(1)) {
13282 /* it already printed err message */
13283 raise_exception(EXERROR);
13284 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013285 xargv = argptr;
13286 xminusc = minusc;
13287 if (*xargv == NULL) {
13288 if (xminusc)
13289 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13290 sflag = 1;
13291 }
13292 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13293 iflag = 1;
13294 if (mflag == 2)
13295 mflag = iflag;
13296 for (i = 0; i < NOPTS; i++)
13297 if (optlist[i] == 2)
13298 optlist[i] = 0;
13299#if DEBUG == 2
13300 debug = 1;
13301#endif
13302 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13303 if (xminusc) {
13304 minusc = *xargv++;
13305 if (*xargv)
13306 goto setarg0;
13307 } else if (!sflag) {
13308 setinputfile(*xargv, 0);
13309 setarg0:
13310 arg0 = *xargv++;
13311 commandname = arg0;
13312 }
13313
13314 shellparam.p = xargv;
13315#if ENABLE_ASH_GETOPTS
13316 shellparam.optind = 1;
13317 shellparam.optoff = -1;
13318#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013319 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013320 while (*xargv) {
13321 shellparam.nparam++;
13322 xargv++;
13323 }
13324 optschanged();
13325}
13326
13327/*
13328 * Read /etc/profile or .profile.
13329 */
13330static void
13331read_profile(const char *name)
13332{
13333 int skip;
13334
13335 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13336 return;
13337 skip = cmdloop(0);
13338 popfile();
13339 if (skip)
13340 exitshell();
13341}
13342
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013343/*
13344 * This routine is called when an error or an interrupt occurs in an
13345 * interactive shell and control is returned to the main command loop.
13346 */
13347static void
13348reset(void)
13349{
13350 /* from eval.c: */
13351 evalskip = 0;
13352 loopnest = 0;
13353 /* from input.c: */
13354 parselleft = parsenleft = 0; /* clear input buffer */
13355 popallfiles();
13356 /* from parser.c: */
13357 tokpushback = 0;
13358 checkkwd = 0;
13359 /* from redir.c: */
13360 clearredir(0);
13361}
13362
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013363#if PROFILE
13364static short profile_buf[16384];
13365extern int etext();
13366#endif
13367
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013368/*
13369 * Main routine. We initialize things, parse the arguments, execute
13370 * profiles if we're a login shell, and then call cmdloop to execute
13371 * commands. The setjmp call sets up the location to jump to when an
13372 * exception occurs. When an exception occurs the variable "state"
13373 * is used to figure out how far we had gotten.
13374 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013375int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013376int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013377{
13378 char *shinit;
13379 volatile int state;
13380 struct jmploc jmploc;
13381 struct stackmark smark;
13382
Denis Vlasenko01631112007-12-16 17:20:38 +000013383 /* Initialize global data */
13384 INIT_G_misc();
13385 INIT_G_memstack();
13386 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013387#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013388 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013389#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013390 INIT_G_cmdtable();
13391
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013392#if PROFILE
13393 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13394#endif
13395
13396#if ENABLE_FEATURE_EDITING
13397 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13398#endif
13399 state = 0;
13400 if (setjmp(jmploc.loc)) {
13401 int e;
13402 int s;
13403
13404 reset();
13405
13406 e = exception;
13407 if (e == EXERROR)
13408 exitstatus = 2;
13409 s = state;
13410 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13411 exitshell();
13412
13413 if (e == EXINT) {
13414 outcslow('\n', stderr);
13415 }
13416 popstackmark(&smark);
13417 FORCE_INT_ON; /* enable interrupts */
13418 if (s == 1)
13419 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013420 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013421 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013422 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013423 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013424 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013425 }
13426 exception_handler = &jmploc;
13427#if DEBUG
13428 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013429 trace_puts("Shell args: ");
13430 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013431#endif
13432 rootpid = getpid();
13433
13434#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013435 /* Can use monotonic_ns() for better randomness but for now it is
13436 * not used anywhere else in busybox... so avoid bloat */
13437 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013438#endif
13439 init();
13440 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013441 procargs(argv);
13442
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013443#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13444 if (iflag) {
13445 const char *hp = lookupvar("HISTFILE");
13446
13447 if (hp == NULL) {
13448 hp = lookupvar("HOME");
13449 if (hp != NULL) {
13450 char *defhp = concat_path_file(hp, ".ash_history");
13451 setvar("HISTFILE", defhp, 0);
13452 free(defhp);
13453 }
13454 }
13455 }
13456#endif
13457 if (argv[0] && argv[0][0] == '-')
13458 isloginsh = 1;
13459 if (isloginsh) {
13460 state = 1;
13461 read_profile("/etc/profile");
13462 state1:
13463 state = 2;
13464 read_profile(".profile");
13465 }
13466 state2:
13467 state = 3;
13468 if (
13469#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013470 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013471#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013472 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013473 ) {
13474 shinit = lookupvar("ENV");
13475 if (shinit != NULL && *shinit != '\0') {
13476 read_profile(shinit);
13477 }
13478 }
13479 state3:
13480 state = 4;
13481 if (minusc)
13482 evalstring(minusc, 0);
13483
13484 if (sflag || minusc == NULL) {
13485#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013486 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013487 const char *hp = lookupvar("HISTFILE");
13488
13489 if (hp != NULL)
13490 line_input_state->hist_file = hp;
13491 }
13492#endif
13493 state4: /* XXX ??? - why isn't this before the "if" statement */
13494 cmdloop(1);
13495 }
13496#if PROFILE
13497 monitor(0);
13498#endif
13499#ifdef GPROF
13500 {
13501 extern void _mcleanup(void);
13502 _mcleanup();
13503 }
13504#endif
13505 exitshell();
13506 /* NOTREACHED */
13507}
13508
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013509#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013510const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013511int main(int argc, char **argv)
13512{
13513 return ash_main(argc, argv);
13514}
13515#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013516
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013517
Eric Andersendf82f612001-06-28 07:46:40 +000013518/*-
13519 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013520 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013521 *
13522 * This code is derived from software contributed to Berkeley by
13523 * Kenneth Almquist.
13524 *
13525 * Redistribution and use in source and binary forms, with or without
13526 * modification, are permitted provided that the following conditions
13527 * are met:
13528 * 1. Redistributions of source code must retain the above copyright
13529 * notice, this list of conditions and the following disclaimer.
13530 * 2. Redistributions in binary form must reproduce the above copyright
13531 * notice, this list of conditions and the following disclaimer in the
13532 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013533 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013534 * may be used to endorse or promote products derived from this software
13535 * without specific prior written permission.
13536 *
13537 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13538 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13539 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13540 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13541 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13542 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13543 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13544 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13545 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13546 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13547 * SUCH DAMAGE.
13548 */