blob: 08bdfc3b3fd7e62b8f7919d509bcf713a9d89b06 [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 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000264static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000265static 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 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000284static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000285static 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 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001051static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001052static 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
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001071static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001072static 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
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001083static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001084static 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/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001578 * XXX - should get rid of. Have all builtins use getopt(3).
1579 * The library getopt must have the BSD extension static variable
1580 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001581 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001582 * Standard option processing (a la getopt) for builtin routines.
1583 * The only argument that is passed to nextopt is the option string;
1584 * the other arguments are unnecessary. It returns the character,
1585 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001586 */
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') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001596 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001597 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001598 if (p == NULL)
1599 return '\0';
1600 if (*p != '-')
1601 return '\0';
1602 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001603 return '\0';
1604 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001605 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001606 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001607 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001608 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001609 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001610 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001611 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001612 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001613 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001614 if (*++q == ':')
1615 q++;
1616 }
1617 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001618 if (*p == '\0') {
1619 p = *argptr++;
1620 if (p == NULL)
1621 ash_msg_and_raise_error("no arg for -%c option", c);
1622 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001623 optionarg = p;
1624 p = NULL;
1625 }
1626 optptr = p;
1627 return c;
1628}
1629
1630
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001631/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001632
Denis Vlasenko01631112007-12-16 17:20:38 +00001633/*
1634 * The parsefile structure pointed to by the global variable parsefile
1635 * contains information about the current file being read.
1636 */
1637struct redirtab {
1638 struct redirtab *next;
1639 int renamed[10];
1640 int nullredirs;
1641};
1642
1643struct shparam {
1644 int nparam; /* # of positional parameters (without $0) */
1645#if ENABLE_ASH_GETOPTS
1646 int optind; /* next parameter to be processed by getopts */
1647 int optoff; /* used by getopts */
1648#endif
1649 unsigned char malloced; /* if parameter list dynamically allocated */
1650 char **p; /* parameter list */
1651};
1652
1653/*
1654 * Free the list of positional parameters.
1655 */
1656static void
1657freeparam(volatile struct shparam *param)
1658{
1659 char **ap;
1660
1661 if (param->malloced) {
1662 for (ap = param->p; *ap; ap++)
1663 free(*ap);
1664 free(param->p);
1665 }
1666}
1667
1668#if ENABLE_ASH_GETOPTS
1669static void getoptsreset(const char *value);
1670#endif
1671
1672struct var {
1673 struct var *next; /* next entry in hash list */
1674 int flags; /* flags are defined above */
1675 const char *text; /* name=value */
1676 void (*func)(const char *); /* function to be called when */
1677 /* the variable gets set/unset */
1678};
1679
1680struct localvar {
1681 struct localvar *next; /* next local variable in list */
1682 struct var *vp; /* the variable that was made local */
1683 int flags; /* saved flags */
1684 const char *text; /* saved text */
1685};
1686
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687/* flags */
1688#define VEXPORT 0x01 /* variable is exported */
1689#define VREADONLY 0x02 /* variable cannot be modified */
1690#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1691#define VTEXTFIXED 0x08 /* text is statically allocated */
1692#define VSTACK 0x10 /* text is allocated on the stack */
1693#define VUNSET 0x20 /* the variable is not set */
1694#define VNOFUNC 0x40 /* don't call the callback function */
1695#define VNOSET 0x80 /* do not set variable - just readonly test */
1696#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001697#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001698# define VDYNAMIC 0x200 /* dynamic variable */
1699#else
1700# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#endif
1702
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001703#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001704static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705#define defifs (defifsvar + 4)
1706#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001707static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708#endif
1709
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001710
Denis Vlasenko01631112007-12-16 17:20:38 +00001711/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001712#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001713static void
1714change_lc_all(const char *value)
1715{
1716 if (value && *value != '\0')
1717 setlocale(LC_ALL, value);
1718}
1719static void
1720change_lc_ctype(const char *value)
1721{
1722 if (value && *value != '\0')
1723 setlocale(LC_CTYPE, value);
1724}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001725#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726#if ENABLE_ASH_MAIL
1727static void chkmail(void);
1728static void changemail(const char *);
1729#endif
1730static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#if ENABLE_ASH_RANDOM_SUPPORT
1732static void change_random(const char *);
1733#endif
1734
Denis Vlasenko01631112007-12-16 17:20:38 +00001735static const struct {
1736 int flags;
1737 const char *text;
1738 void (*func)(const char *);
1739} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001741 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001746 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1747 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1750 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1751 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1752 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
1756#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001757 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#endif
1759#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1761 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
1766};
1767
Denis Vlasenko01631112007-12-16 17:20:38 +00001768
1769struct globals_var {
1770 struct shparam shellparam; /* $@ current positional parameters */
1771 struct redirtab *redirlist;
1772 int g_nullredirs;
1773 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1774 struct var *vartab[VTABSIZE];
1775 struct var varinit[ARRAY_SIZE(varinit_data)];
1776};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001777extern struct globals_var *const ash_ptr_to_globals_var;
1778#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001779#define shellparam (G_var.shellparam )
1780#define redirlist (G_var.redirlist )
1781#define g_nullredirs (G_var.g_nullredirs )
1782#define preverrout_fd (G_var.preverrout_fd)
1783#define vartab (G_var.vartab )
1784#define varinit (G_var.varinit )
1785#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001786 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001787 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1788 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001789 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1790 varinit[i].flags = varinit_data[i].flags; \
1791 varinit[i].text = varinit_data[i].text; \
1792 varinit[i].func = varinit_data[i].func; \
1793 } \
1794} while (0)
1795
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001796#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001798# define vmail (&vifs)[1]
1799# define vmpath (&vmail)[1]
1800# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001801#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001802# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001804#define vps1 (&vpath)[1]
1805#define vps2 (&vps1)[1]
1806#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001807#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001808# define voptind (&vps4)[1]
1809# if ENABLE_ASH_RANDOM_SUPPORT
1810# define vrandom (&voptind)[1]
1811# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001812#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001813# if ENABLE_ASH_RANDOM_SUPPORT
1814# define vrandom (&vps4)[1]
1815# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001816#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001817
1818/*
1819 * The following macros access the values of the above variables.
1820 * They have to skip over the name. They return the null string
1821 * for unset variables.
1822 */
1823#define ifsval() (vifs.text + 4)
1824#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001825#if ENABLE_ASH_MAIL
1826# define mailval() (vmail.text + 5)
1827# define mpathval() (vmpath.text + 9)
1828# define mpathset() ((vmpath.flags & VUNSET) == 0)
1829#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#define pathval() (vpath.text + 5)
1831#define ps1val() (vps1.text + 4)
1832#define ps2val() (vps2.text + 4)
1833#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001834#if ENABLE_ASH_GETOPTS
1835# define optindval() (voptind.text + 7)
1836#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001838
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001839#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1840#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1841
Denis Vlasenko01631112007-12-16 17:20:38 +00001842#if ENABLE_ASH_GETOPTS
1843static void
1844getoptsreset(const char *value)
1845{
1846 shellparam.optind = number(value);
1847 shellparam.optoff = -1;
1848}
1849#endif
1850
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001851/*
1852 * Return of a legal variable name (a letter or underscore followed by zero or
1853 * more letters, underscores, and digits).
1854 */
1855static char *
1856endofname(const char *name)
1857{
1858 char *p;
1859
1860 p = (char *) name;
1861 if (!is_name(*p))
1862 return p;
1863 while (*++p) {
1864 if (!is_in_name(*p))
1865 break;
1866 }
1867 return p;
1868}
1869
1870/*
1871 * Compares two strings up to the first = or '\0'. The first
1872 * string must be terminated by '='; the second may be terminated by
1873 * either '=' or '\0'.
1874 */
1875static int
1876varcmp(const char *p, const char *q)
1877{
1878 int c, d;
1879
1880 while ((c = *p) == (d = *q)) {
1881 if (!c || c == '=')
1882 goto out;
1883 p++;
1884 q++;
1885 }
1886 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001887 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001888 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001889 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890 out:
1891 return c - d;
1892}
1893
1894static int
1895varequal(const char *a, const char *b)
1896{
1897 return !varcmp(a, b);
1898}
1899
1900/*
1901 * Find the appropriate entry in the hash table from the name.
1902 */
1903static struct var **
1904hashvar(const char *p)
1905{
1906 unsigned hashval;
1907
1908 hashval = ((unsigned char) *p) << 4;
1909 while (*p && *p != '=')
1910 hashval += (unsigned char) *p++;
1911 return &vartab[hashval % VTABSIZE];
1912}
1913
1914static int
1915vpcmp(const void *a, const void *b)
1916{
1917 return varcmp(*(const char **)a, *(const char **)b);
1918}
1919
1920/*
1921 * This routine initializes the builtin variables.
1922 */
1923static void
1924initvar(void)
1925{
1926 struct var *vp;
1927 struct var *end;
1928 struct var **vpp;
1929
1930 /*
1931 * PS1 depends on uid
1932 */
1933#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1934 vps1.text = "PS1=\\w \\$ ";
1935#else
1936 if (!geteuid())
1937 vps1.text = "PS1=# ";
1938#endif
1939 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001940 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001941 do {
1942 vpp = hashvar(vp->text);
1943 vp->next = *vpp;
1944 *vpp = vp;
1945 } while (++vp < end);
1946}
1947
1948static struct var **
1949findvar(struct var **vpp, const char *name)
1950{
1951 for (; *vpp; vpp = &(*vpp)->next) {
1952 if (varequal((*vpp)->text, name)) {
1953 break;
1954 }
1955 }
1956 return vpp;
1957}
1958
1959/*
1960 * Find the value of a variable. Returns NULL if not set.
1961 */
1962static char *
1963lookupvar(const char *name)
1964{
1965 struct var *v;
1966
1967 v = *findvar(hashvar(name), name);
1968 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001969#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970 /*
1971 * Dynamic variables are implemented roughly the same way they are
1972 * in bash. Namely, they're "special" so long as they aren't unset.
1973 * As soon as they're unset, they're no longer dynamic, and dynamic
1974 * lookup will no longer happen at that point. -- PFM.
1975 */
1976 if ((v->flags & VDYNAMIC))
1977 (*v->func)(NULL);
1978#endif
1979 if (!(v->flags & VUNSET))
1980 return strchrnul(v->text, '=') + 1;
1981 }
1982 return NULL;
1983}
1984
1985/*
1986 * Search the environment of a builtin command.
1987 */
1988static char *
1989bltinlookup(const char *name)
1990{
1991 struct strlist *sp;
1992
1993 for (sp = cmdenviron; sp; sp = sp->next) {
1994 if (varequal(sp->text, name))
1995 return strchrnul(sp->text, '=') + 1;
1996 }
1997 return lookupvar(name);
1998}
1999
2000/*
2001 * Same as setvar except that the variable and value are passed in
2002 * the first argument as name=value. Since the first argument will
2003 * be actually stored in the table, it should not be a string that
2004 * will go away.
2005 * Called with interrupts off.
2006 */
2007static void
2008setvareq(char *s, int flags)
2009{
2010 struct var *vp, **vpp;
2011
2012 vpp = hashvar(s);
2013 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2014 vp = *findvar(vpp, s);
2015 if (vp) {
2016 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2017 const char *n;
2018
2019 if (flags & VNOSAVE)
2020 free(s);
2021 n = vp->text;
2022 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2023 }
2024
2025 if (flags & VNOSET)
2026 return;
2027
2028 if (vp->func && (flags & VNOFUNC) == 0)
2029 (*vp->func)(strchrnul(s, '=') + 1);
2030
2031 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2032 free((char*)vp->text);
2033
2034 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2035 } else {
2036 if (flags & VNOSET)
2037 return;
2038 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002039 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002040 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002041 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002042 *vpp = vp;
2043 }
2044 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2045 s = ckstrdup(s);
2046 vp->text = s;
2047 vp->flags = flags;
2048}
2049
2050/*
2051 * Set the value of a variable. The flags argument is ored with the
2052 * flags of the variable. If val is NULL, the variable is unset.
2053 */
2054static void
2055setvar(const char *name, const char *val, int flags)
2056{
2057 char *p, *q;
2058 size_t namelen;
2059 char *nameeq;
2060 size_t vallen;
2061
2062 q = endofname(name);
2063 p = strchrnul(q, '=');
2064 namelen = p - name;
2065 if (!namelen || p != q)
2066 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2067 vallen = 0;
2068 if (val == NULL) {
2069 flags |= VUNSET;
2070 } else {
2071 vallen = strlen(val);
2072 }
2073 INT_OFF;
2074 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002075 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002076 if (val) {
2077 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002078 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079 }
2080 *p = '\0';
2081 setvareq(nameeq, flags | VNOSAVE);
2082 INT_ON;
2083}
2084
2085#if ENABLE_ASH_GETOPTS
2086/*
2087 * Safe version of setvar, returns 1 on success 0 on failure.
2088 */
2089static int
2090setvarsafe(const char *name, const char *val, int flags)
2091{
2092 int err;
2093 volatile int saveint;
2094 struct jmploc *volatile savehandler = exception_handler;
2095 struct jmploc jmploc;
2096
2097 SAVE_INT(saveint);
2098 if (setjmp(jmploc.loc))
2099 err = 1;
2100 else {
2101 exception_handler = &jmploc;
2102 setvar(name, val, flags);
2103 err = 0;
2104 }
2105 exception_handler = savehandler;
2106 RESTORE_INT(saveint);
2107 return err;
2108}
2109#endif
2110
2111/*
2112 * Unset the specified variable.
2113 */
2114static int
2115unsetvar(const char *s)
2116{
2117 struct var **vpp;
2118 struct var *vp;
2119 int retval;
2120
2121 vpp = findvar(hashvar(s), s);
2122 vp = *vpp;
2123 retval = 2;
2124 if (vp) {
2125 int flags = vp->flags;
2126
2127 retval = 1;
2128 if (flags & VREADONLY)
2129 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002130#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002131 vp->flags &= ~VDYNAMIC;
2132#endif
2133 if (flags & VUNSET)
2134 goto ok;
2135 if ((flags & VSTRFIXED) == 0) {
2136 INT_OFF;
2137 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2138 free((char*)vp->text);
2139 *vpp = vp->next;
2140 free(vp);
2141 INT_ON;
2142 } else {
2143 setvar(s, 0, 0);
2144 vp->flags &= ~VEXPORT;
2145 }
2146 ok:
2147 retval = 0;
2148 }
2149 out:
2150 return retval;
2151}
2152
2153/*
2154 * Process a linked list of variable assignments.
2155 */
2156static void
2157listsetvar(struct strlist *list_set_var, int flags)
2158{
2159 struct strlist *lp = list_set_var;
2160
2161 if (!lp)
2162 return;
2163 INT_OFF;
2164 do {
2165 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002166 lp = lp->next;
2167 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002168 INT_ON;
2169}
2170
2171/*
2172 * Generate a list of variables satisfying the given conditions.
2173 */
2174static char **
2175listvars(int on, int off, char ***end)
2176{
2177 struct var **vpp;
2178 struct var *vp;
2179 char **ep;
2180 int mask;
2181
2182 STARTSTACKSTR(ep);
2183 vpp = vartab;
2184 mask = on | off;
2185 do {
2186 for (vp = *vpp; vp; vp = vp->next) {
2187 if ((vp->flags & mask) == on) {
2188 if (ep == stackstrend())
2189 ep = growstackstr();
2190 *ep++ = (char *) vp->text;
2191 }
2192 }
2193 } while (++vpp < vartab + VTABSIZE);
2194 if (ep == stackstrend())
2195 ep = growstackstr();
2196 if (end)
2197 *end = ep;
2198 *ep++ = NULL;
2199 return grabstackstr(ep);
2200}
2201
2202
2203/* ============ Path search helper
2204 *
2205 * The variable path (passed by reference) should be set to the start
2206 * of the path before the first call; padvance will update
2207 * this value as it proceeds. Successive calls to padvance will return
2208 * the possible path expansions in sequence. If an option (indicated by
2209 * a percent sign) appears in the path entry then the global variable
2210 * pathopt will be set to point to it; otherwise pathopt will be set to
2211 * NULL.
2212 */
2213static const char *pathopt; /* set by padvance */
2214
2215static char *
2216padvance(const char **path, const char *name)
2217{
2218 const char *p;
2219 char *q;
2220 const char *start;
2221 size_t len;
2222
2223 if (*path == NULL)
2224 return NULL;
2225 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002226 for (p = start; *p && *p != ':' && *p != '%'; p++)
2227 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002228 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2229 while (stackblocksize() < len)
2230 growstackblock();
2231 q = stackblock();
2232 if (p != start) {
2233 memcpy(q, start, p - start);
2234 q += p - start;
2235 *q++ = '/';
2236 }
2237 strcpy(q, name);
2238 pathopt = NULL;
2239 if (*p == '%') {
2240 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002241 while (*p && *p != ':')
2242 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002243 }
2244 if (*p == ':')
2245 *path = p + 1;
2246 else
2247 *path = NULL;
2248 return stalloc(len);
2249}
2250
2251
2252/* ============ Prompt */
2253
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002254static smallint doprompt; /* if set, prompt the user */
2255static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256
2257#if ENABLE_FEATURE_EDITING
2258static line_input_t *line_input_state;
2259static const char *cmdedit_prompt;
2260static void
2261putprompt(const char *s)
2262{
2263 if (ENABLE_ASH_EXPAND_PRMT) {
2264 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002265 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002266 return;
2267 }
2268 cmdedit_prompt = s;
2269}
2270#else
2271static void
2272putprompt(const char *s)
2273{
2274 out2str(s);
2275}
2276#endif
2277
2278#if ENABLE_ASH_EXPAND_PRMT
2279/* expandstr() needs parsing machinery, so it is far away ahead... */
2280static const char *expandstr(const char *ps);
2281#else
2282#define expandstr(s) s
2283#endif
2284
2285static void
2286setprompt(int whichprompt)
2287{
2288 const char *prompt;
2289#if ENABLE_ASH_EXPAND_PRMT
2290 struct stackmark smark;
2291#endif
2292
2293 needprompt = 0;
2294
2295 switch (whichprompt) {
2296 case 1:
2297 prompt = ps1val();
2298 break;
2299 case 2:
2300 prompt = ps2val();
2301 break;
2302 default: /* 0 */
2303 prompt = nullstr;
2304 }
2305#if ENABLE_ASH_EXPAND_PRMT
2306 setstackmark(&smark);
2307 stalloc(stackblocksize());
2308#endif
2309 putprompt(expandstr(prompt));
2310#if ENABLE_ASH_EXPAND_PRMT
2311 popstackmark(&smark);
2312#endif
2313}
2314
2315
2316/* ============ The cd and pwd commands */
2317
2318#define CD_PHYSICAL 1
2319#define CD_PRINT 2
2320
2321static int docd(const char *, int);
2322
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002323static int
2324cdopt(void)
2325{
2326 int flags = 0;
2327 int i, j;
2328
2329 j = 'L';
2330 while ((i = nextopt("LP"))) {
2331 if (i != j) {
2332 flags ^= CD_PHYSICAL;
2333 j = i;
2334 }
2335 }
2336
2337 return flags;
2338}
2339
2340/*
2341 * Update curdir (the name of the current directory) in response to a
2342 * cd command.
2343 */
2344static const char *
2345updatepwd(const char *dir)
2346{
2347 char *new;
2348 char *p;
2349 char *cdcomppath;
2350 const char *lim;
2351
2352 cdcomppath = ststrdup(dir);
2353 STARTSTACKSTR(new);
2354 if (*dir != '/') {
2355 if (curdir == nullstr)
2356 return 0;
2357 new = stack_putstr(curdir, new);
2358 }
2359 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002360 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002361 if (*dir != '/') {
2362 if (new[-1] != '/')
2363 USTPUTC('/', new);
2364 if (new > lim && *lim == '/')
2365 lim++;
2366 } else {
2367 USTPUTC('/', new);
2368 cdcomppath++;
2369 if (dir[1] == '/' && dir[2] != '/') {
2370 USTPUTC('/', new);
2371 cdcomppath++;
2372 lim++;
2373 }
2374 }
2375 p = strtok(cdcomppath, "/");
2376 while (p) {
2377 switch (*p) {
2378 case '.':
2379 if (p[1] == '.' && p[2] == '\0') {
2380 while (new > lim) {
2381 STUNPUTC(new);
2382 if (new[-1] == '/')
2383 break;
2384 }
2385 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002386 }
2387 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002388 break;
2389 /* fall through */
2390 default:
2391 new = stack_putstr(p, new);
2392 USTPUTC('/', new);
2393 }
2394 p = strtok(0, "/");
2395 }
2396 if (new > lim)
2397 STUNPUTC(new);
2398 *new = 0;
2399 return stackblock();
2400}
2401
2402/*
2403 * Find out what the current directory is. If we already know the current
2404 * directory, this routine returns immediately.
2405 */
2406static char *
2407getpwd(void)
2408{
Denis Vlasenko01631112007-12-16 17:20:38 +00002409 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002410 return dir ? dir : nullstr;
2411}
2412
2413static void
2414setpwd(const char *val, int setold)
2415{
2416 char *oldcur, *dir;
2417
2418 oldcur = dir = curdir;
2419
2420 if (setold) {
2421 setvar("OLDPWD", oldcur, VEXPORT);
2422 }
2423 INT_OFF;
2424 if (physdir != nullstr) {
2425 if (physdir != oldcur)
2426 free(physdir);
2427 physdir = nullstr;
2428 }
2429 if (oldcur == val || !val) {
2430 char *s = getpwd();
2431 physdir = s;
2432 if (!val)
2433 dir = s;
2434 } else
2435 dir = ckstrdup(val);
2436 if (oldcur != dir && oldcur != nullstr) {
2437 free(oldcur);
2438 }
2439 curdir = dir;
2440 INT_ON;
2441 setvar("PWD", dir, VEXPORT);
2442}
2443
2444static void hashcd(void);
2445
2446/*
2447 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2448 * know that the current directory has changed.
2449 */
2450static int
2451docd(const char *dest, int flags)
2452{
2453 const char *dir = 0;
2454 int err;
2455
2456 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2457
2458 INT_OFF;
2459 if (!(flags & CD_PHYSICAL)) {
2460 dir = updatepwd(dest);
2461 if (dir)
2462 dest = dir;
2463 }
2464 err = chdir(dest);
2465 if (err)
2466 goto out;
2467 setpwd(dir, 1);
2468 hashcd();
2469 out:
2470 INT_ON;
2471 return err;
2472}
2473
2474static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002475cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476{
2477 const char *dest;
2478 const char *path;
2479 const char *p;
2480 char c;
2481 struct stat statb;
2482 int flags;
2483
2484 flags = cdopt();
2485 dest = *argptr;
2486 if (!dest)
2487 dest = bltinlookup(homestr);
2488 else if (LONE_DASH(dest)) {
2489 dest = bltinlookup("OLDPWD");
2490 flags |= CD_PRINT;
2491 }
2492 if (!dest)
2493 dest = nullstr;
2494 if (*dest == '/')
2495 goto step7;
2496 if (*dest == '.') {
2497 c = dest[1];
2498 dotdot:
2499 switch (c) {
2500 case '\0':
2501 case '/':
2502 goto step6;
2503 case '.':
2504 c = dest[2];
2505 if (c != '.')
2506 goto dotdot;
2507 }
2508 }
2509 if (!*dest)
2510 dest = ".";
2511 path = bltinlookup("CDPATH");
2512 if (!path) {
2513 step6:
2514 step7:
2515 p = dest;
2516 goto docd;
2517 }
2518 do {
2519 c = *path;
2520 p = padvance(&path, dest);
2521 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2522 if (c && c != ':')
2523 flags |= CD_PRINT;
2524 docd:
2525 if (!docd(p, flags))
2526 goto out;
2527 break;
2528 }
2529 } while (path);
2530 ash_msg_and_raise_error("can't cd to %s", dest);
2531 /* NOTREACHED */
2532 out:
2533 if (flags & CD_PRINT)
2534 out1fmt(snlfmt, curdir);
2535 return 0;
2536}
2537
2538static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002539pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002540{
2541 int flags;
2542 const char *dir = curdir;
2543
2544 flags = cdopt();
2545 if (flags) {
2546 if (physdir == nullstr)
2547 setpwd(dir, 0);
2548 dir = physdir;
2549 }
2550 out1fmt(snlfmt, dir);
2551 return 0;
2552}
2553
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002554
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002555/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002556
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002557#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002558#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002559
Eric Andersenc470f442003-07-28 09:56:35 +00002560/* Syntax classes */
2561#define CWORD 0 /* character is nothing special */
2562#define CNL 1 /* newline character */
2563#define CBACK 2 /* a backslash character */
2564#define CSQUOTE 3 /* single quote */
2565#define CDQUOTE 4 /* double quote */
2566#define CENDQUOTE 5 /* a terminating quote */
2567#define CBQUOTE 6 /* backwards single quote */
2568#define CVAR 7 /* a dollar sign */
2569#define CENDVAR 8 /* a '}' character */
2570#define CLP 9 /* a left paren in arithmetic */
2571#define CRP 10 /* a right paren in arithmetic */
2572#define CENDFILE 11 /* end of file */
2573#define CCTL 12 /* like CWORD, except it must be escaped */
2574#define CSPCL 13 /* these terminate a word */
2575#define CIGN 14 /* character should be ignored */
2576
Denis Vlasenko131ae172007-02-18 13:00:19 +00002577#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002578#define SYNBASE 130
2579#define PEOF -130
2580#define PEOA -129
2581#define PEOA_OR_PEOF PEOA
2582#else
2583#define SYNBASE 129
2584#define PEOF -129
2585#define PEOA_OR_PEOF PEOF
2586#endif
2587
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002588/* number syntax index */
2589#define BASESYNTAX 0 /* not in quotes */
2590#define DQSYNTAX 1 /* in double quotes */
2591#define SQSYNTAX 2 /* in single quotes */
2592#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002593#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596#define USE_SIT_FUNCTION
2597#endif
2598
Denis Vlasenko131ae172007-02-18 13:00:19 +00002599#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002600static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002601#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002602 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002603#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002604 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2605 { CNL, CNL, CNL, CNL }, /* 2, \n */
2606 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2607 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2608 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2609 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2610 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2611 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2612 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2613 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2614 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002615#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002616 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2617 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2618 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002619#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002620};
Eric Andersenc470f442003-07-28 09:56:35 +00002621#else
2622static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002623#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002624 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002625#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002626 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2627 { CNL, CNL, CNL }, /* 2, \n */
2628 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2629 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2630 { CVAR, CVAR, CWORD }, /* 5, $ */
2631 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2632 { CSPCL, CWORD, CWORD }, /* 7, ( */
2633 { CSPCL, CWORD, CWORD }, /* 8, ) */
2634 { CBACK, CBACK, CCTL }, /* 9, \ */
2635 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2636 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002637#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002638 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2639 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2640 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002641#endif
2642};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002643#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002644
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002645#ifdef USE_SIT_FUNCTION
2646
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002647static int
2648SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002649{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002650 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002651#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002652 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002653 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2654 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2655 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2656 11, 3 /* "}~" */
2657 };
2658#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002659 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002660 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2661 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2662 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2663 10, 2 /* "}~" */
2664 };
2665#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002666 const char *s;
2667 int indx;
2668
Eric Andersenc470f442003-07-28 09:56:35 +00002669 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002671#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002672 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002674 else
2675#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002676#define U_C(c) ((unsigned char)(c))
2677
2678 if ((unsigned char)c >= (unsigned char)(CTLESC)
2679 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2680 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002681 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002682 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002683 s = strchrnul(spec_symbls, c);
2684 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002685 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002686 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002687 }
2688 return S_I_T[indx][syntax];
2689}
2690
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002691#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692
Denis Vlasenko131ae172007-02-18 13:00:19 +00002693#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694#define CSPCL_CIGN_CIGN_CIGN 0
2695#define CSPCL_CWORD_CWORD_CWORD 1
2696#define CNL_CNL_CNL_CNL 2
2697#define CWORD_CCTL_CCTL_CWORD 3
2698#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2699#define CVAR_CVAR_CWORD_CVAR 5
2700#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2701#define CSPCL_CWORD_CWORD_CLP 7
2702#define CSPCL_CWORD_CWORD_CRP 8
2703#define CBACK_CBACK_CCTL_CBACK 9
2704#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2705#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2706#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2707#define CWORD_CWORD_CWORD_CWORD 13
2708#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002709#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002710#define CSPCL_CWORD_CWORD_CWORD 0
2711#define CNL_CNL_CNL_CNL 1
2712#define CWORD_CCTL_CCTL_CWORD 2
2713#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2714#define CVAR_CVAR_CWORD_CVAR 4
2715#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2716#define CSPCL_CWORD_CWORD_CLP 6
2717#define CSPCL_CWORD_CWORD_CRP 7
2718#define CBACK_CBACK_CCTL_CBACK 8
2719#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2720#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2721#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2722#define CWORD_CWORD_CWORD_CWORD 12
2723#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002724#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002725
2726static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002727 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002728 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002729#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002730 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2731#endif
2732 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2734 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2735 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2736 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2737 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2738 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2739 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2740 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002741 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2870 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2871 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2893 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002894 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002895 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2897 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002899 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002900 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2901 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2902 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2905 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2906 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2907 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2919 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2920 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2921 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2922 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2923 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2953 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2954 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2957 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2985 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2986 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2987 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002988};
2989
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002990#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2991
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002992#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002993
Eric Andersen2870d962001-07-02 17:27:21 +00002994
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002995/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002996
Denis Vlasenko131ae172007-02-18 13:00:19 +00002997#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002998
2999#define ALIASINUSE 1
3000#define ALIASDEAD 2
3001
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003002struct alias {
3003 struct alias *next;
3004 char *name;
3005 char *val;
3006 int flag;
3007};
3008
Denis Vlasenko01631112007-12-16 17:20:38 +00003009
3010static struct alias **atab; // [ATABSIZE];
3011#define INIT_G_alias() do { \
3012 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3013} while (0)
3014
Eric Andersen2870d962001-07-02 17:27:21 +00003015
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003016static struct alias **
3017__lookupalias(const char *name) {
3018 unsigned int hashval;
3019 struct alias **app;
3020 const char *p;
3021 unsigned int ch;
3022
3023 p = name;
3024
3025 ch = (unsigned char)*p;
3026 hashval = ch << 4;
3027 while (ch) {
3028 hashval += ch;
3029 ch = (unsigned char)*++p;
3030 }
3031 app = &atab[hashval % ATABSIZE];
3032
3033 for (; *app; app = &(*app)->next) {
3034 if (strcmp(name, (*app)->name) == 0) {
3035 break;
3036 }
3037 }
3038
3039 return app;
3040}
3041
3042static struct alias *
3043lookupalias(const char *name, int check)
3044{
3045 struct alias *ap = *__lookupalias(name);
3046
3047 if (check && ap && (ap->flag & ALIASINUSE))
3048 return NULL;
3049 return ap;
3050}
3051
3052static struct alias *
3053freealias(struct alias *ap)
3054{
3055 struct alias *next;
3056
3057 if (ap->flag & ALIASINUSE) {
3058 ap->flag |= ALIASDEAD;
3059 return ap;
3060 }
3061
3062 next = ap->next;
3063 free(ap->name);
3064 free(ap->val);
3065 free(ap);
3066 return next;
3067}
Eric Andersencb57d552001-06-28 07:25:16 +00003068
Eric Andersenc470f442003-07-28 09:56:35 +00003069static void
3070setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003071{
3072 struct alias *ap, **app;
3073
3074 app = __lookupalias(name);
3075 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003076 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003077 if (ap) {
3078 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003079 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003080 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003081 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003082 ap->flag &= ~ALIASDEAD;
3083 } else {
3084 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003085 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003086 ap->name = ckstrdup(name);
3087 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003088 /*ap->flag = 0; - ckzalloc did it */
3089 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003090 *app = ap;
3091 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003092 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003093}
3094
Eric Andersenc470f442003-07-28 09:56:35 +00003095static int
3096unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003097{
Eric Andersencb57d552001-06-28 07:25:16 +00003098 struct alias **app;
3099
3100 app = __lookupalias(name);
3101
3102 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003103 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003104 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003106 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003107 }
3108
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003109 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003110}
3111
Eric Andersenc470f442003-07-28 09:56:35 +00003112static void
3113rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003114{
Eric Andersencb57d552001-06-28 07:25:16 +00003115 struct alias *ap, **app;
3116 int i;
3117
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003119 for (i = 0; i < ATABSIZE; i++) {
3120 app = &atab[i];
3121 for (ap = *app; ap; ap = *app) {
3122 *app = freealias(*app);
3123 if (ap == *app) {
3124 app = &ap->next;
3125 }
3126 }
3127 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003128 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003129}
3130
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003131static void
3132printalias(const struct alias *ap)
3133{
3134 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3135}
3136
Eric Andersencb57d552001-06-28 07:25:16 +00003137/*
3138 * TODO - sort output
3139 */
Eric Andersenc470f442003-07-28 09:56:35 +00003140static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003141aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003142{
3143 char *n, *v;
3144 int ret = 0;
3145 struct alias *ap;
3146
Denis Vlasenko68404f12008-03-17 09:00:54 +00003147 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003148 int i;
3149
Denis Vlasenko68404f12008-03-17 09:00:54 +00003150 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003151 for (ap = atab[i]; ap; ap = ap->next) {
3152 printalias(ap);
3153 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003154 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003155 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003156 }
3157 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003158 v = strchr(n+1, '=');
3159 if (v == NULL) { /* n+1: funny ksh stuff */
3160 ap = *__lookupalias(n);
3161 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003162 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003163 ret = 1;
3164 } else
3165 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003166 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003167 *v++ = '\0';
3168 setalias(n, v);
3169 }
3170 }
3171
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003172 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003173}
3174
Eric Andersenc470f442003-07-28 09:56:35 +00003175static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003176unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003177{
3178 int i;
3179
3180 while ((i = nextopt("a")) != '\0') {
3181 if (i == 'a') {
3182 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003183 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003184 }
3185 }
3186 for (i = 0; *argptr; argptr++) {
3187 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003188 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003189 i = 1;
3190 }
3191 }
3192
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003193 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003194}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003195
Denis Vlasenko131ae172007-02-18 13:00:19 +00003196#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003197
Eric Andersenc470f442003-07-28 09:56:35 +00003198
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003199/* ============ jobs.c */
3200
3201/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3202#define FORK_FG 0
3203#define FORK_BG 1
3204#define FORK_NOJOB 2
3205
3206/* mode flags for showjob(s) */
3207#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3208#define SHOW_PID 0x04 /* include process pid */
3209#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3210
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003211/*
3212 * A job structure contains information about a job. A job is either a
3213 * single process or a set of processes contained in a pipeline. In the
3214 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3215 * array of pids.
3216 */
3217
3218struct procstat {
3219 pid_t pid; /* process id */
3220 int status; /* last process status from wait() */
3221 char *cmd; /* text of command being run */
3222};
3223
3224struct job {
3225 struct procstat ps0; /* status of process */
3226 struct procstat *ps; /* status or processes when more than one */
3227#if JOBS
3228 int stopstatus; /* status of a stopped job */
3229#endif
3230 uint32_t
3231 nprocs: 16, /* number of processes */
3232 state: 8,
3233#define JOBRUNNING 0 /* at least one proc running */
3234#define JOBSTOPPED 1 /* all procs are stopped */
3235#define JOBDONE 2 /* all procs are completed */
3236#if JOBS
3237 sigint: 1, /* job was killed by SIGINT */
3238 jobctl: 1, /* job running under job control */
3239#endif
3240 waited: 1, /* true if this entry has been waited for */
3241 used: 1, /* true if this entry is in used */
3242 changed: 1; /* true if status has changed */
3243 struct job *prev_job; /* previous job */
3244};
3245
Denis Vlasenko68404f12008-03-17 09:00:54 +00003246static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003247#if !JOBS
3248#define forkshell(job, node, mode) forkshell(job, mode)
3249#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003250static int forkshell(struct job *, union node *, int);
3251static int waitforjob(struct job *);
3252
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003253#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003254enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003255#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003256#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003257static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003258static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259#endif
3260
3261/*
3262 * Set the signal handler for the specified signal. The routine figures
3263 * out what it should be set to.
3264 */
3265static void
3266setsignal(int signo)
3267{
3268 int action;
3269 char *t, tsig;
3270 struct sigaction act;
3271
3272 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003273 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003274 if (t == NULL)
3275 action = S_DFL;
3276 else if (*t != '\0')
3277 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003278 if (rootshell && action == S_DFL) {
3279 switch (signo) {
3280 case SIGINT:
3281 if (iflag || minusc || sflag == 0)
3282 action = S_CATCH;
3283 break;
3284 case SIGQUIT:
3285#if DEBUG
3286 if (debug)
3287 break;
3288#endif
3289 /* FALLTHROUGH */
3290 case SIGTERM:
3291 if (iflag)
3292 action = S_IGN;
3293 break;
3294#if JOBS
3295 case SIGTSTP:
3296 case SIGTTOU:
3297 if (mflag)
3298 action = S_IGN;
3299 break;
3300#endif
3301 }
3302 }
3303
3304 t = &sigmode[signo - 1];
3305 tsig = *t;
3306 if (tsig == 0) {
3307 /*
3308 * current setting unknown
3309 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003310 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003311 /*
3312 * Pretend it worked; maybe we should give a warning
3313 * here, but other shells don't. We don't alter
3314 * sigmode, so that we retry every time.
3315 */
3316 return;
3317 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003318 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003319 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003320 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321 if (mflag
3322 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3323 ) {
3324 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003325 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326 }
3327 }
3328 if (tsig == S_HARD_IGN || tsig == action)
3329 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003330 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331 switch (action) {
3332 case S_CATCH:
3333 act.sa_handler = onsig;
3334 break;
3335 case S_IGN:
3336 act.sa_handler = SIG_IGN;
3337 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338 }
3339 *t = action;
3340 act.sa_flags = 0;
3341 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003342 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003343}
3344
3345/* mode flags for set_curjob */
3346#define CUR_DELETE 2
3347#define CUR_RUNNING 1
3348#define CUR_STOPPED 0
3349
3350/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003351#define DOWAIT_NONBLOCK WNOHANG
3352#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353
3354#if JOBS
3355/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003356static int initialpgrp; //references:2
3357static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003358#endif
3359/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003360static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003362static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003363/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003364static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003365/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003366static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003367
3368static void
3369set_curjob(struct job *jp, unsigned mode)
3370{
3371 struct job *jp1;
3372 struct job **jpp, **curp;
3373
3374 /* first remove from list */
3375 jpp = curp = &curjob;
3376 do {
3377 jp1 = *jpp;
3378 if (jp1 == jp)
3379 break;
3380 jpp = &jp1->prev_job;
3381 } while (1);
3382 *jpp = jp1->prev_job;
3383
3384 /* Then re-insert in correct position */
3385 jpp = curp;
3386 switch (mode) {
3387 default:
3388#if DEBUG
3389 abort();
3390#endif
3391 case CUR_DELETE:
3392 /* job being deleted */
3393 break;
3394 case CUR_RUNNING:
3395 /* newly created job or backgrounded job,
3396 put after all stopped jobs. */
3397 do {
3398 jp1 = *jpp;
3399#if JOBS
3400 if (!jp1 || jp1->state != JOBSTOPPED)
3401#endif
3402 break;
3403 jpp = &jp1->prev_job;
3404 } while (1);
3405 /* FALLTHROUGH */
3406#if JOBS
3407 case CUR_STOPPED:
3408#endif
3409 /* newly stopped job - becomes curjob */
3410 jp->prev_job = *jpp;
3411 *jpp = jp;
3412 break;
3413 }
3414}
3415
3416#if JOBS || DEBUG
3417static int
3418jobno(const struct job *jp)
3419{
3420 return jp - jobtab + 1;
3421}
3422#endif
3423
3424/*
3425 * Convert a job name to a job structure.
3426 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003427#if !JOBS
3428#define getjob(name, getctl) getjob(name)
3429#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003430static struct job *
3431getjob(const char *name, int getctl)
3432{
3433 struct job *jp;
3434 struct job *found;
3435 const char *err_msg = "No such job: %s";
3436 unsigned num;
3437 int c;
3438 const char *p;
3439 char *(*match)(const char *, const char *);
3440
3441 jp = curjob;
3442 p = name;
3443 if (!p)
3444 goto currentjob;
3445
3446 if (*p != '%')
3447 goto err;
3448
3449 c = *++p;
3450 if (!c)
3451 goto currentjob;
3452
3453 if (!p[1]) {
3454 if (c == '+' || c == '%') {
3455 currentjob:
3456 err_msg = "No current job";
3457 goto check;
3458 }
3459 if (c == '-') {
3460 if (jp)
3461 jp = jp->prev_job;
3462 err_msg = "No previous job";
3463 check:
3464 if (!jp)
3465 goto err;
3466 goto gotit;
3467 }
3468 }
3469
3470 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003471// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003472 num = atoi(p);
3473 if (num < njobs) {
3474 jp = jobtab + num - 1;
3475 if (jp->used)
3476 goto gotit;
3477 goto err;
3478 }
3479 }
3480
3481 match = prefix;
3482 if (*p == '?') {
3483 match = strstr;
3484 p++;
3485 }
3486
3487 found = 0;
3488 while (1) {
3489 if (!jp)
3490 goto err;
3491 if (match(jp->ps[0].cmd, p)) {
3492 if (found)
3493 goto err;
3494 found = jp;
3495 err_msg = "%s: ambiguous";
3496 }
3497 jp = jp->prev_job;
3498 }
3499
3500 gotit:
3501#if JOBS
3502 err_msg = "job %s not created under job control";
3503 if (getctl && jp->jobctl == 0)
3504 goto err;
3505#endif
3506 return jp;
3507 err:
3508 ash_msg_and_raise_error(err_msg, name);
3509}
3510
3511/*
3512 * Mark a job structure as unused.
3513 */
3514static void
3515freejob(struct job *jp)
3516{
3517 struct procstat *ps;
3518 int i;
3519
3520 INT_OFF;
3521 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3522 if (ps->cmd != nullstr)
3523 free(ps->cmd);
3524 }
3525 if (jp->ps != &jp->ps0)
3526 free(jp->ps);
3527 jp->used = 0;
3528 set_curjob(jp, CUR_DELETE);
3529 INT_ON;
3530}
3531
3532#if JOBS
3533static void
3534xtcsetpgrp(int fd, pid_t pgrp)
3535{
3536 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003537 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003538}
3539
3540/*
3541 * Turn job control on and off.
3542 *
3543 * Note: This code assumes that the third arg to ioctl is a character
3544 * pointer, which is true on Berkeley systems but not System V. Since
3545 * System V doesn't have job control yet, this isn't a problem now.
3546 *
3547 * Called with interrupts off.
3548 */
3549static void
3550setjobctl(int on)
3551{
3552 int fd;
3553 int pgrp;
3554
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003555 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003556 return;
3557 if (on) {
3558 int ofd;
3559 ofd = fd = open(_PATH_TTY, O_RDWR);
3560 if (fd < 0) {
3561 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3562 * That sometimes helps to acquire controlling tty.
3563 * Obviously, a workaround for bugs when someone
3564 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003565 fd = 2;
3566 while (!isatty(fd))
3567 if (--fd < 0)
3568 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003569 }
3570 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003571 if (ofd >= 0)
3572 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003573 if (fd < 0)
3574 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003575 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003576 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003577 do { /* while we are in the background */
3578 pgrp = tcgetpgrp(fd);
3579 if (pgrp < 0) {
3580 out:
3581 ash_msg("can't access tty; job control turned off");
3582 mflag = on = 0;
3583 goto close;
3584 }
3585 if (pgrp == getpgrp())
3586 break;
3587 killpg(0, SIGTTIN);
3588 } while (1);
3589 initialpgrp = pgrp;
3590
3591 setsignal(SIGTSTP);
3592 setsignal(SIGTTOU);
3593 setsignal(SIGTTIN);
3594 pgrp = rootpid;
3595 setpgid(0, pgrp);
3596 xtcsetpgrp(fd, pgrp);
3597 } else {
3598 /* turning job control off */
3599 fd = ttyfd;
3600 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003601 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003602 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003603 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003604 setpgid(0, pgrp);
3605 setsignal(SIGTSTP);
3606 setsignal(SIGTTOU);
3607 setsignal(SIGTTIN);
3608 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003609 if (fd >= 0)
3610 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003611 fd = -1;
3612 }
3613 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003614 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003615}
3616
3617static int
3618killcmd(int argc, char **argv)
3619{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003620 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003621 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003622 do {
3623 if (argv[i][0] == '%') {
3624 struct job *jp = getjob(argv[i], 0);
3625 unsigned pid = jp->ps[0].pid;
3626 /* Enough space for ' -NNN<nul>' */
3627 argv[i] = alloca(sizeof(int)*3 + 3);
3628 /* kill_main has matching code to expect
3629 * leading space. Needed to not confuse
3630 * negative pids with "kill -SIGNAL_NO" syntax */
3631 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003632 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003633 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003635 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003636}
3637
3638static void
3639showpipe(struct job *jp, FILE *out)
3640{
3641 struct procstat *sp;
3642 struct procstat *spend;
3643
3644 spend = jp->ps + jp->nprocs;
3645 for (sp = jp->ps + 1; sp < spend; sp++)
3646 fprintf(out, " | %s", sp->cmd);
3647 outcslow('\n', out);
3648 flush_stdout_stderr();
3649}
3650
3651
3652static int
3653restartjob(struct job *jp, int mode)
3654{
3655 struct procstat *ps;
3656 int i;
3657 int status;
3658 pid_t pgid;
3659
3660 INT_OFF;
3661 if (jp->state == JOBDONE)
3662 goto out;
3663 jp->state = JOBRUNNING;
3664 pgid = jp->ps->pid;
3665 if (mode == FORK_FG)
3666 xtcsetpgrp(ttyfd, pgid);
3667 killpg(pgid, SIGCONT);
3668 ps = jp->ps;
3669 i = jp->nprocs;
3670 do {
3671 if (WIFSTOPPED(ps->status)) {
3672 ps->status = -1;
3673 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003674 ps++;
3675 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003676 out:
3677 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3678 INT_ON;
3679 return status;
3680}
3681
3682static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003683fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003684{
3685 struct job *jp;
3686 FILE *out;
3687 int mode;
3688 int retval;
3689
3690 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3691 nextopt(nullstr);
3692 argv = argptr;
3693 out = stdout;
3694 do {
3695 jp = getjob(*argv, 1);
3696 if (mode == FORK_BG) {
3697 set_curjob(jp, CUR_RUNNING);
3698 fprintf(out, "[%d] ", jobno(jp));
3699 }
3700 outstr(jp->ps->cmd, out);
3701 showpipe(jp, out);
3702 retval = restartjob(jp, mode);
3703 } while (*argv && *++argv);
3704 return retval;
3705}
3706#endif
3707
3708static int
3709sprint_status(char *s, int status, int sigonly)
3710{
3711 int col;
3712 int st;
3713
3714 col = 0;
3715 if (!WIFEXITED(status)) {
3716#if JOBS
3717 if (WIFSTOPPED(status))
3718 st = WSTOPSIG(status);
3719 else
3720#endif
3721 st = WTERMSIG(status);
3722 if (sigonly) {
3723 if (st == SIGINT || st == SIGPIPE)
3724 goto out;
3725#if JOBS
3726 if (WIFSTOPPED(status))
3727 goto out;
3728#endif
3729 }
3730 st &= 0x7f;
3731 col = fmtstr(s, 32, strsignal(st));
3732 if (WCOREDUMP(status)) {
3733 col += fmtstr(s + col, 16, " (core dumped)");
3734 }
3735 } else if (!sigonly) {
3736 st = WEXITSTATUS(status);
3737 if (st)
3738 col = fmtstr(s, 16, "Done(%d)", st);
3739 else
3740 col = fmtstr(s, 16, "Done");
3741 }
3742 out:
3743 return col;
3744}
3745
3746/*
3747 * Do a wait system call. If job control is compiled in, we accept
3748 * stopped processes. If block is zero, we return a value of zero
3749 * rather than blocking.
3750 *
3751 * System V doesn't have a non-blocking wait system call. It does
3752 * have a SIGCLD signal that is sent to a process when one of it's
3753 * children dies. The obvious way to use SIGCLD would be to install
3754 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3755 * was received, and have waitproc bump another counter when it got
3756 * the status of a process. Waitproc would then know that a wait
3757 * system call would not block if the two counters were different.
3758 * This approach doesn't work because if a process has children that
3759 * have not been waited for, System V will send it a SIGCLD when it
3760 * installs a signal handler for SIGCLD. What this means is that when
3761 * a child exits, the shell will be sent SIGCLD signals continuously
3762 * until is runs out of stack space, unless it does a wait call before
3763 * restoring the signal handler. The code below takes advantage of
3764 * this (mis)feature by installing a signal handler for SIGCLD and
3765 * then checking to see whether it was called. If there are any
3766 * children to be waited for, it will be.
3767 *
3768 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3769 * waits at all. In this case, the user will not be informed when
3770 * a background process until the next time she runs a real program
3771 * (as opposed to running a builtin command or just typing return),
3772 * and the jobs command may give out of date information.
3773 */
3774static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003775waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003778 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003779 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003781 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3782 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003783}
3784
3785/*
3786 * Wait for a process to terminate.
3787 */
3788static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003789dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790{
3791 int pid;
3792 int status;
3793 struct job *jp;
3794 struct job *thisjob;
3795 int state;
3796
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003797 TRACE(("dowait(%d) called\n", wait_flags));
3798 pid = waitproc(wait_flags, &status);
3799 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003800 if (pid <= 0) {
3801 /* If we were doing blocking wait and (probably) got EINTR,
3802 * check for pending sigs received while waiting.
3803 * (NB: can be moved into callers if needed) */
3804 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3805 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003806 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003807 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003808 INT_OFF;
3809 thisjob = NULL;
3810 for (jp = curjob; jp; jp = jp->prev_job) {
3811 struct procstat *sp;
3812 struct procstat *spend;
3813 if (jp->state == JOBDONE)
3814 continue;
3815 state = JOBDONE;
3816 spend = jp->ps + jp->nprocs;
3817 sp = jp->ps;
3818 do {
3819 if (sp->pid == pid) {
3820 TRACE(("Job %d: changing status of proc %d "
3821 "from 0x%x to 0x%x\n",
3822 jobno(jp), pid, sp->status, status));
3823 sp->status = status;
3824 thisjob = jp;
3825 }
3826 if (sp->status == -1)
3827 state = JOBRUNNING;
3828#if JOBS
3829 if (state == JOBRUNNING)
3830 continue;
3831 if (WIFSTOPPED(sp->status)) {
3832 jp->stopstatus = sp->status;
3833 state = JOBSTOPPED;
3834 }
3835#endif
3836 } while (++sp < spend);
3837 if (thisjob)
3838 goto gotjob;
3839 }
3840#if JOBS
3841 if (!WIFSTOPPED(status))
3842#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003843 jobless--;
3844 goto out;
3845
3846 gotjob:
3847 if (state != JOBRUNNING) {
3848 thisjob->changed = 1;
3849
3850 if (thisjob->state != state) {
3851 TRACE(("Job %d: changing state from %d to %d\n",
3852 jobno(thisjob), thisjob->state, state));
3853 thisjob->state = state;
3854#if JOBS
3855 if (state == JOBSTOPPED) {
3856 set_curjob(thisjob, CUR_STOPPED);
3857 }
3858#endif
3859 }
3860 }
3861
3862 out:
3863 INT_ON;
3864
3865 if (thisjob && thisjob == job) {
3866 char s[48 + 1];
3867 int len;
3868
3869 len = sprint_status(s, status, 1);
3870 if (len) {
3871 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003872 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003873 out2str(s);
3874 }
3875 }
3876 return pid;
3877}
3878
3879#if JOBS
3880static void
3881showjob(FILE *out, struct job *jp, int mode)
3882{
3883 struct procstat *ps;
3884 struct procstat *psend;
3885 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003886 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003887 char s[80];
3888
3889 ps = jp->ps;
3890
3891 if (mode & SHOW_PGID) {
3892 /* just output process (group) id of pipeline */
3893 fprintf(out, "%d\n", ps->pid);
3894 return;
3895 }
3896
3897 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003898 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003899
3900 if (jp == curjob)
3901 s[col - 2] = '+';
3902 else if (curjob && jp == curjob->prev_job)
3903 s[col - 2] = '-';
3904
3905 if (mode & SHOW_PID)
3906 col += fmtstr(s + col, 16, "%d ", ps->pid);
3907
3908 psend = ps + jp->nprocs;
3909
3910 if (jp->state == JOBRUNNING) {
3911 strcpy(s + col, "Running");
3912 col += sizeof("Running") - 1;
3913 } else {
3914 int status = psend[-1].status;
3915 if (jp->state == JOBSTOPPED)
3916 status = jp->stopstatus;
3917 col += sprint_status(s + col, status, 0);
3918 }
3919
3920 goto start;
3921
3922 do {
3923 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003924 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925 start:
3926 fprintf(out, "%s%*c%s",
3927 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3928 );
3929 if (!(mode & SHOW_PID)) {
3930 showpipe(jp, out);
3931 break;
3932 }
3933 if (++ps == psend) {
3934 outcslow('\n', out);
3935 break;
3936 }
3937 } while (1);
3938
3939 jp->changed = 0;
3940
3941 if (jp->state == JOBDONE) {
3942 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3943 freejob(jp);
3944 }
3945}
3946
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003947/*
3948 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3949 * statuses have changed since the last call to showjobs.
3950 */
3951static void
3952showjobs(FILE *out, int mode)
3953{
3954 struct job *jp;
3955
3956 TRACE(("showjobs(%x) called\n", mode));
3957
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003958 /* If not even one job changed, there is nothing to do */
3959 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003960 continue;
3961
3962 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003963 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003965 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 }
3967}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003968
3969static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003970jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003971{
3972 int mode, m;
3973
3974 mode = 0;
3975 while ((m = nextopt("lp"))) {
3976 if (m == 'l')
3977 mode = SHOW_PID;
3978 else
3979 mode = SHOW_PGID;
3980 }
3981
3982 argv = argptr;
3983 if (*argv) {
3984 do
3985 showjob(stdout, getjob(*argv,0), mode);
3986 while (*++argv);
3987 } else
3988 showjobs(stdout, mode);
3989
3990 return 0;
3991}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003992#endif /* JOBS */
3993
3994static int
3995getstatus(struct job *job)
3996{
3997 int status;
3998 int retval;
3999
4000 status = job->ps[job->nprocs - 1].status;
4001 retval = WEXITSTATUS(status);
4002 if (!WIFEXITED(status)) {
4003#if JOBS
4004 retval = WSTOPSIG(status);
4005 if (!WIFSTOPPED(status))
4006#endif
4007 {
4008 /* XXX: limits number of signals */
4009 retval = WTERMSIG(status);
4010#if JOBS
4011 if (retval == SIGINT)
4012 job->sigint = 1;
4013#endif
4014 }
4015 retval += 128;
4016 }
4017 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4018 jobno(job), job->nprocs, status, retval));
4019 return retval;
4020}
4021
4022static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004023waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004024{
4025 struct job *job;
4026 int retval;
4027 struct job *jp;
4028
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004029// exsig++;
4030// xbarrier();
4031 if (pendingsig)
4032 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004033
4034 nextopt(nullstr);
4035 retval = 0;
4036
4037 argv = argptr;
4038 if (!*argv) {
4039 /* wait for all jobs */
4040 for (;;) {
4041 jp = curjob;
4042 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004043 if (!jp) /* no running procs */
4044 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004045 if (jp->state == JOBRUNNING)
4046 break;
4047 jp->waited = 1;
4048 jp = jp->prev_job;
4049 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004050 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 }
4052 }
4053
4054 retval = 127;
4055 do {
4056 if (**argv != '%') {
4057 pid_t pid = number(*argv);
4058 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004059 while (1) {
4060 if (!job)
4061 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 if (job->ps[job->nprocs - 1].pid == pid)
4063 break;
4064 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004065 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066 } else
4067 job = getjob(*argv, 0);
4068 /* loop until process terminated or stopped */
4069 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004070 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071 job->waited = 1;
4072 retval = getstatus(job);
4073 repeat:
4074 ;
4075 } while (*++argv);
4076
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004077 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004078 return retval;
4079}
4080
4081static struct job *
4082growjobtab(void)
4083{
4084 size_t len;
4085 ptrdiff_t offset;
4086 struct job *jp, *jq;
4087
4088 len = njobs * sizeof(*jp);
4089 jq = jobtab;
4090 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4091
4092 offset = (char *)jp - (char *)jq;
4093 if (offset) {
4094 /* Relocate pointers */
4095 size_t l = len;
4096
4097 jq = (struct job *)((char *)jq + l);
4098 while (l) {
4099 l -= sizeof(*jp);
4100 jq--;
4101#define joff(p) ((struct job *)((char *)(p) + l))
4102#define jmove(p) (p) = (void *)((char *)(p) + offset)
4103 if (joff(jp)->ps == &jq->ps0)
4104 jmove(joff(jp)->ps);
4105 if (joff(jp)->prev_job)
4106 jmove(joff(jp)->prev_job);
4107 }
4108 if (curjob)
4109 jmove(curjob);
4110#undef joff
4111#undef jmove
4112 }
4113
4114 njobs += 4;
4115 jobtab = jp;
4116 jp = (struct job *)((char *)jp + len);
4117 jq = jp + 3;
4118 do {
4119 jq->used = 0;
4120 } while (--jq >= jp);
4121 return jp;
4122}
4123
4124/*
4125 * Return a new job structure.
4126 * Called with interrupts off.
4127 */
4128static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004129makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004130{
4131 int i;
4132 struct job *jp;
4133
4134 for (i = njobs, jp = jobtab; ; jp++) {
4135 if (--i < 0) {
4136 jp = growjobtab();
4137 break;
4138 }
4139 if (jp->used == 0)
4140 break;
4141 if (jp->state != JOBDONE || !jp->waited)
4142 continue;
4143#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004144 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004145 continue;
4146#endif
4147 freejob(jp);
4148 break;
4149 }
4150 memset(jp, 0, sizeof(*jp));
4151#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004152 /* jp->jobctl is a bitfield.
4153 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004154 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155 jp->jobctl = 1;
4156#endif
4157 jp->prev_job = curjob;
4158 curjob = jp;
4159 jp->used = 1;
4160 jp->ps = &jp->ps0;
4161 if (nprocs > 1) {
4162 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4163 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004164 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004165 jobno(jp)));
4166 return jp;
4167}
4168
4169#if JOBS
4170/*
4171 * Return a string identifying a command (to be printed by the
4172 * jobs command).
4173 */
4174static char *cmdnextc;
4175
4176static void
4177cmdputs(const char *s)
4178{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004179 static const char vstype[VSTYPE + 1][3] = {
4180 "", "}", "-", "+", "?", "=",
4181 "%", "%%", "#", "##"
4182 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4183 };
4184
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004185 const char *p, *str;
4186 char c, cc[2] = " ";
4187 char *nextc;
4188 int subtype = 0;
4189 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004190
4191 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4192 p = s;
4193 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004194 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 switch (c) {
4196 case CTLESC:
4197 c = *p++;
4198 break;
4199 case CTLVAR:
4200 subtype = *p++;
4201 if ((subtype & VSTYPE) == VSLENGTH)
4202 str = "${#";
4203 else
4204 str = "${";
4205 if (!(subtype & VSQUOTE) == !(quoted & 1))
4206 goto dostr;
4207 quoted ^= 1;
4208 c = '"';
4209 break;
4210 case CTLENDVAR:
4211 str = "\"}" + !(quoted & 1);
4212 quoted >>= 1;
4213 subtype = 0;
4214 goto dostr;
4215 case CTLBACKQ:
4216 str = "$(...)";
4217 goto dostr;
4218 case CTLBACKQ+CTLQUOTE:
4219 str = "\"$(...)\"";
4220 goto dostr;
4221#if ENABLE_ASH_MATH_SUPPORT
4222 case CTLARI:
4223 str = "$((";
4224 goto dostr;
4225 case CTLENDARI:
4226 str = "))";
4227 goto dostr;
4228#endif
4229 case CTLQUOTEMARK:
4230 quoted ^= 1;
4231 c = '"';
4232 break;
4233 case '=':
4234 if (subtype == 0)
4235 break;
4236 if ((subtype & VSTYPE) != VSNORMAL)
4237 quoted <<= 1;
4238 str = vstype[subtype & VSTYPE];
4239 if (subtype & VSNUL)
4240 c = ':';
4241 else
4242 goto checkstr;
4243 break;
4244 case '\'':
4245 case '\\':
4246 case '"':
4247 case '$':
4248 /* These can only happen inside quotes */
4249 cc[0] = c;
4250 str = cc;
4251 c = '\\';
4252 break;
4253 default:
4254 break;
4255 }
4256 USTPUTC(c, nextc);
4257 checkstr:
4258 if (!str)
4259 continue;
4260 dostr:
4261 while ((c = *str++)) {
4262 USTPUTC(c, nextc);
4263 }
4264 }
4265 if (quoted & 1) {
4266 USTPUTC('"', nextc);
4267 }
4268 *nextc = 0;
4269 cmdnextc = nextc;
4270}
4271
4272/* cmdtxt() and cmdlist() call each other */
4273static void cmdtxt(union node *n);
4274
4275static void
4276cmdlist(union node *np, int sep)
4277{
4278 for (; np; np = np->narg.next) {
4279 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004280 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004281 cmdtxt(np);
4282 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004283 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004284 }
4285}
4286
4287static void
4288cmdtxt(union node *n)
4289{
4290 union node *np;
4291 struct nodelist *lp;
4292 const char *p;
4293 char s[2];
4294
4295 if (!n)
4296 return;
4297 switch (n->type) {
4298 default:
4299#if DEBUG
4300 abort();
4301#endif
4302 case NPIPE:
4303 lp = n->npipe.cmdlist;
4304 for (;;) {
4305 cmdtxt(lp->n);
4306 lp = lp->next;
4307 if (!lp)
4308 break;
4309 cmdputs(" | ");
4310 }
4311 break;
4312 case NSEMI:
4313 p = "; ";
4314 goto binop;
4315 case NAND:
4316 p = " && ";
4317 goto binop;
4318 case NOR:
4319 p = " || ";
4320 binop:
4321 cmdtxt(n->nbinary.ch1);
4322 cmdputs(p);
4323 n = n->nbinary.ch2;
4324 goto donode;
4325 case NREDIR:
4326 case NBACKGND:
4327 n = n->nredir.n;
4328 goto donode;
4329 case NNOT:
4330 cmdputs("!");
4331 n = n->nnot.com;
4332 donode:
4333 cmdtxt(n);
4334 break;
4335 case NIF:
4336 cmdputs("if ");
4337 cmdtxt(n->nif.test);
4338 cmdputs("; then ");
4339 n = n->nif.ifpart;
4340 if (n->nif.elsepart) {
4341 cmdtxt(n);
4342 cmdputs("; else ");
4343 n = n->nif.elsepart;
4344 }
4345 p = "; fi";
4346 goto dotail;
4347 case NSUBSHELL:
4348 cmdputs("(");
4349 n = n->nredir.n;
4350 p = ")";
4351 goto dotail;
4352 case NWHILE:
4353 p = "while ";
4354 goto until;
4355 case NUNTIL:
4356 p = "until ";
4357 until:
4358 cmdputs(p);
4359 cmdtxt(n->nbinary.ch1);
4360 n = n->nbinary.ch2;
4361 p = "; done";
4362 dodo:
4363 cmdputs("; do ");
4364 dotail:
4365 cmdtxt(n);
4366 goto dotail2;
4367 case NFOR:
4368 cmdputs("for ");
4369 cmdputs(n->nfor.var);
4370 cmdputs(" in ");
4371 cmdlist(n->nfor.args, 1);
4372 n = n->nfor.body;
4373 p = "; done";
4374 goto dodo;
4375 case NDEFUN:
4376 cmdputs(n->narg.text);
4377 p = "() { ... }";
4378 goto dotail2;
4379 case NCMD:
4380 cmdlist(n->ncmd.args, 1);
4381 cmdlist(n->ncmd.redirect, 0);
4382 break;
4383 case NARG:
4384 p = n->narg.text;
4385 dotail2:
4386 cmdputs(p);
4387 break;
4388 case NHERE:
4389 case NXHERE:
4390 p = "<<...";
4391 goto dotail2;
4392 case NCASE:
4393 cmdputs("case ");
4394 cmdputs(n->ncase.expr->narg.text);
4395 cmdputs(" in ");
4396 for (np = n->ncase.cases; np; np = np->nclist.next) {
4397 cmdtxt(np->nclist.pattern);
4398 cmdputs(") ");
4399 cmdtxt(np->nclist.body);
4400 cmdputs(";; ");
4401 }
4402 p = "esac";
4403 goto dotail2;
4404 case NTO:
4405 p = ">";
4406 goto redir;
4407 case NCLOBBER:
4408 p = ">|";
4409 goto redir;
4410 case NAPPEND:
4411 p = ">>";
4412 goto redir;
4413 case NTOFD:
4414 p = ">&";
4415 goto redir;
4416 case NFROM:
4417 p = "<";
4418 goto redir;
4419 case NFROMFD:
4420 p = "<&";
4421 goto redir;
4422 case NFROMTO:
4423 p = "<>";
4424 redir:
4425 s[0] = n->nfile.fd + '0';
4426 s[1] = '\0';
4427 cmdputs(s);
4428 cmdputs(p);
4429 if (n->type == NTOFD || n->type == NFROMFD) {
4430 s[0] = n->ndup.dupfd + '0';
4431 p = s;
4432 goto dotail2;
4433 }
4434 n = n->nfile.fname;
4435 goto donode;
4436 }
4437}
4438
4439static char *
4440commandtext(union node *n)
4441{
4442 char *name;
4443
4444 STARTSTACKSTR(cmdnextc);
4445 cmdtxt(n);
4446 name = stackblock();
4447 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4448 name, cmdnextc, cmdnextc));
4449 return ckstrdup(name);
4450}
4451#endif /* JOBS */
4452
4453/*
4454 * Fork off a subshell. If we are doing job control, give the subshell its
4455 * own process group. Jp is a job structure that the job is to be added to.
4456 * N is the command that will be evaluated by the child. Both jp and n may
4457 * be NULL. The mode parameter can be one of the following:
4458 * FORK_FG - Fork off a foreground process.
4459 * FORK_BG - Fork off a background process.
4460 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4461 * process group even if job control is on.
4462 *
4463 * When job control is turned off, background processes have their standard
4464 * input redirected to /dev/null (except for the second and later processes
4465 * in a pipeline).
4466 *
4467 * Called with interrupts off.
4468 */
4469/*
4470 * Clear traps on a fork.
4471 */
4472static void
4473clear_traps(void)
4474{
4475 char **tp;
4476
4477 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004478 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004479 INT_OFF;
4480 free(*tp);
4481 *tp = NULL;
4482 if (tp != &trap[0])
4483 setsignal(tp - trap);
4484 INT_ON;
4485 }
4486 }
4487}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004488
4489/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004491
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004492/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004494forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495{
4496 int oldlvl;
4497
4498 TRACE(("Child shell %d\n", getpid()));
4499 oldlvl = shlvl;
4500 shlvl++;
4501
4502 closescript();
4503 clear_traps();
4504#if JOBS
4505 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004506 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004507 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4508 pid_t pgrp;
4509
4510 if (jp->nprocs == 0)
4511 pgrp = getpid();
4512 else
4513 pgrp = jp->ps[0].pid;
4514 /* This can fail because we are doing it in the parent also */
4515 (void)setpgid(0, pgrp);
4516 if (mode == FORK_FG)
4517 xtcsetpgrp(ttyfd, pgrp);
4518 setsignal(SIGTSTP);
4519 setsignal(SIGTTOU);
4520 } else
4521#endif
4522 if (mode == FORK_BG) {
4523 ignoresig(SIGINT);
4524 ignoresig(SIGQUIT);
4525 if (jp->nprocs == 0) {
4526 close(0);
4527 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004528 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004529 }
4530 }
4531 if (!oldlvl && iflag) {
4532 setsignal(SIGINT);
4533 setsignal(SIGQUIT);
4534 setsignal(SIGTERM);
4535 }
4536 for (jp = curjob; jp; jp = jp->prev_job)
4537 freejob(jp);
4538 jobless = 0;
4539}
4540
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004541/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004542#if !JOBS
4543#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4544#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545static void
4546forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4547{
4548 TRACE(("In parent shell: child = %d\n", pid));
4549 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004550 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4551 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004552 jobless++;
4553 return;
4554 }
4555#if JOBS
4556 if (mode != FORK_NOJOB && jp->jobctl) {
4557 int pgrp;
4558
4559 if (jp->nprocs == 0)
4560 pgrp = pid;
4561 else
4562 pgrp = jp->ps[0].pid;
4563 /* This can fail because we are doing it in the child also */
4564 setpgid(pid, pgrp);
4565 }
4566#endif
4567 if (mode == FORK_BG) {
4568 backgndpid = pid; /* set $! */
4569 set_curjob(jp, CUR_RUNNING);
4570 }
4571 if (jp) {
4572 struct procstat *ps = &jp->ps[jp->nprocs++];
4573 ps->pid = pid;
4574 ps->status = -1;
4575 ps->cmd = nullstr;
4576#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004577 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004578 ps->cmd = commandtext(n);
4579#endif
4580 }
4581}
4582
4583static int
4584forkshell(struct job *jp, union node *n, int mode)
4585{
4586 int pid;
4587
4588 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4589 pid = fork();
4590 if (pid < 0) {
4591 TRACE(("Fork failed, errno=%d", errno));
4592 if (jp)
4593 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004594 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 }
4596 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004597 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004598 else
4599 forkparent(jp, n, mode, pid);
4600 return pid;
4601}
4602
4603/*
4604 * Wait for job to finish.
4605 *
4606 * Under job control we have the problem that while a child process is
4607 * running interrupts generated by the user are sent to the child but not
4608 * to the shell. This means that an infinite loop started by an inter-
4609 * active user may be hard to kill. With job control turned off, an
4610 * interactive user may place an interactive program inside a loop. If
4611 * the interactive program catches interrupts, the user doesn't want
4612 * these interrupts to also abort the loop. The approach we take here
4613 * is to have the shell ignore interrupt signals while waiting for a
4614 * foreground process to terminate, and then send itself an interrupt
4615 * signal if the child process was terminated by an interrupt signal.
4616 * Unfortunately, some programs want to do a bit of cleanup and then
4617 * exit on interrupt; unless these processes terminate themselves by
4618 * sending a signal to themselves (instead of calling exit) they will
4619 * confuse this approach.
4620 *
4621 * Called with interrupts off.
4622 */
4623static int
4624waitforjob(struct job *jp)
4625{
4626 int st;
4627
4628 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4629 while (jp->state == JOBRUNNING) {
4630 dowait(DOWAIT_BLOCK, jp);
4631 }
4632 st = getstatus(jp);
4633#if JOBS
4634 if (jp->jobctl) {
4635 xtcsetpgrp(ttyfd, rootpid);
4636 /*
4637 * This is truly gross.
4638 * If we're doing job control, then we did a TIOCSPGRP which
4639 * caused us (the shell) to no longer be in the controlling
4640 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4641 * intuit from the subprocess exit status whether a SIGINT
4642 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4643 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004644 if (jp->sigint) /* TODO: do the same with all signals */
4645 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004646 }
4647 if (jp->state == JOBDONE)
4648#endif
4649 freejob(jp);
4650 return st;
4651}
4652
4653/*
4654 * return 1 if there are stopped jobs, otherwise 0
4655 */
4656static int
4657stoppedjobs(void)
4658{
4659 struct job *jp;
4660 int retval;
4661
4662 retval = 0;
4663 if (job_warning)
4664 goto out;
4665 jp = curjob;
4666 if (jp && jp->state == JOBSTOPPED) {
4667 out2str("You have stopped jobs.\n");
4668 job_warning = 2;
4669 retval++;
4670 }
4671 out:
4672 return retval;
4673}
4674
4675
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004676/* ============ redir.c
4677 *
4678 * Code for dealing with input/output redirection.
4679 */
4680
4681#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004682#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004683#ifndef PIPE_BUF
4684# define PIPESIZE 4096 /* amount of buffering in a pipe */
4685#else
4686# define PIPESIZE PIPE_BUF
4687#endif
4688
4689/*
4690 * Open a file in noclobber mode.
4691 * The code was copied from bash.
4692 */
4693static int
4694noclobberopen(const char *fname)
4695{
4696 int r, fd;
4697 struct stat finfo, finfo2;
4698
4699 /*
4700 * If the file exists and is a regular file, return an error
4701 * immediately.
4702 */
4703 r = stat(fname, &finfo);
4704 if (r == 0 && S_ISREG(finfo.st_mode)) {
4705 errno = EEXIST;
4706 return -1;
4707 }
4708
4709 /*
4710 * If the file was not present (r != 0), make sure we open it
4711 * exclusively so that if it is created before we open it, our open
4712 * will fail. Make sure that we do not truncate an existing file.
4713 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4714 * file was not a regular file, we leave O_EXCL off.
4715 */
4716 if (r != 0)
4717 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4718 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4719
4720 /* If the open failed, return the file descriptor right away. */
4721 if (fd < 0)
4722 return fd;
4723
4724 /*
4725 * OK, the open succeeded, but the file may have been changed from a
4726 * non-regular file to a regular file between the stat and the open.
4727 * We are assuming that the O_EXCL open handles the case where FILENAME
4728 * did not exist and is symlinked to an existing file between the stat
4729 * and open.
4730 */
4731
4732 /*
4733 * If we can open it and fstat the file descriptor, and neither check
4734 * revealed that it was a regular file, and the file has not been
4735 * replaced, return the file descriptor.
4736 */
4737 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4738 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4739 return fd;
4740
4741 /* The file has been replaced. badness. */
4742 close(fd);
4743 errno = EEXIST;
4744 return -1;
4745}
4746
4747/*
4748 * Handle here documents. Normally we fork off a process to write the
4749 * data to a pipe. If the document is short, we can stuff the data in
4750 * the pipe without forking.
4751 */
4752/* openhere needs this forward reference */
4753static void expandhere(union node *arg, int fd);
4754static int
4755openhere(union node *redir)
4756{
4757 int pip[2];
4758 size_t len = 0;
4759
4760 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004761 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004762 if (redir->type == NHERE) {
4763 len = strlen(redir->nhere.doc->narg.text);
4764 if (len <= PIPESIZE) {
4765 full_write(pip[1], redir->nhere.doc->narg.text, len);
4766 goto out;
4767 }
4768 }
4769 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4770 close(pip[0]);
4771 signal(SIGINT, SIG_IGN);
4772 signal(SIGQUIT, SIG_IGN);
4773 signal(SIGHUP, SIG_IGN);
4774#ifdef SIGTSTP
4775 signal(SIGTSTP, SIG_IGN);
4776#endif
4777 signal(SIGPIPE, SIG_DFL);
4778 if (redir->type == NHERE)
4779 full_write(pip[1], redir->nhere.doc->narg.text, len);
4780 else
4781 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004782 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004783 }
4784 out:
4785 close(pip[1]);
4786 return pip[0];
4787}
4788
4789static int
4790openredirect(union node *redir)
4791{
4792 char *fname;
4793 int f;
4794
4795 switch (redir->nfile.type) {
4796 case NFROM:
4797 fname = redir->nfile.expfname;
4798 f = open(fname, O_RDONLY);
4799 if (f < 0)
4800 goto eopen;
4801 break;
4802 case NFROMTO:
4803 fname = redir->nfile.expfname;
4804 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4805 if (f < 0)
4806 goto ecreate;
4807 break;
4808 case NTO:
4809 /* Take care of noclobber mode. */
4810 if (Cflag) {
4811 fname = redir->nfile.expfname;
4812 f = noclobberopen(fname);
4813 if (f < 0)
4814 goto ecreate;
4815 break;
4816 }
4817 /* FALLTHROUGH */
4818 case NCLOBBER:
4819 fname = redir->nfile.expfname;
4820 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4821 if (f < 0)
4822 goto ecreate;
4823 break;
4824 case NAPPEND:
4825 fname = redir->nfile.expfname;
4826 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4827 if (f < 0)
4828 goto ecreate;
4829 break;
4830 default:
4831#if DEBUG
4832 abort();
4833#endif
4834 /* Fall through to eliminate warning. */
4835 case NTOFD:
4836 case NFROMFD:
4837 f = -1;
4838 break;
4839 case NHERE:
4840 case NXHERE:
4841 f = openhere(redir);
4842 break;
4843 }
4844
4845 return f;
4846 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004847 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004848 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004849 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004850}
4851
4852/*
4853 * Copy a file descriptor to be >= to. Returns -1
4854 * if the source file descriptor is closed, EMPTY if there are no unused
4855 * file descriptors left.
4856 */
4857static int
4858copyfd(int from, int to)
4859{
4860 int newfd;
4861
4862 newfd = fcntl(from, F_DUPFD, to);
4863 if (newfd < 0) {
4864 if (errno == EMFILE)
4865 return EMPTY;
4866 ash_msg_and_raise_error("%d: %m", from);
4867 }
4868 return newfd;
4869}
4870
4871static void
4872dupredirect(union node *redir, int f)
4873{
4874 int fd = redir->nfile.fd;
4875
4876 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4877 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4878 copyfd(redir->ndup.dupfd, fd);
4879 }
4880 return;
4881 }
4882
4883 if (f != fd) {
4884 copyfd(f, fd);
4885 close(f);
4886 }
4887}
4888
4889/*
4890 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4891 * old file descriptors are stashed away so that the redirection can be
4892 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4893 * standard output, and the standard error if it becomes a duplicate of
4894 * stdout, is saved in memory.
4895 */
4896/* flags passed to redirect */
4897#define REDIR_PUSH 01 /* save previous values of file descriptors */
4898#define REDIR_SAVEFD2 03 /* set preverrout */
4899static void
4900redirect(union node *redir, int flags)
4901{
4902 union node *n;
4903 struct redirtab *sv;
4904 int i;
4905 int fd;
4906 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004907
Denis Vlasenko01631112007-12-16 17:20:38 +00004908 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004909 if (!redir) {
4910 return;
4911 }
4912 sv = NULL;
4913 INT_OFF;
4914 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004915 sv = ckmalloc(sizeof(*sv));
4916 sv->next = redirlist;
4917 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004918 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004920 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004921 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004922 }
4923 n = redir;
4924 do {
4925 fd = n->nfile.fd;
4926 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4927 && n->ndup.dupfd == fd)
4928 continue; /* redirect from/to same file descriptor */
4929
4930 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004931 if (fd == newfd) {
4932 /* Descriptor wasn't open before redirect.
4933 * Mark it for close in the future */
4934 if (sv && sv->renamed[fd] == EMPTY)
4935 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004937 }
4938 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004939 i = fcntl(fd, F_DUPFD, 10);
4940
4941 if (i == -1) {
4942 i = errno;
4943 if (i != EBADF) {
4944 close(newfd);
4945 errno = i;
4946 ash_msg_and_raise_error("%d: %m", fd);
4947 /* NOTREACHED */
4948 }
4949 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004950 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951 close(fd);
4952 }
4953 } else {
4954 close(fd);
4955 }
4956 dupredirect(n, newfd);
4957 } while ((n = n->nfile.next));
4958 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004959 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004960 preverrout_fd = sv->renamed[2];
4961}
4962
4963/*
4964 * Undo the effects of the last redirection.
4965 */
4966static void
4967popredir(int drop)
4968{
4969 struct redirtab *rp;
4970 int i;
4971
Denis Vlasenko01631112007-12-16 17:20:38 +00004972 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004973 return;
4974 INT_OFF;
4975 rp = redirlist;
4976 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004977 if (rp->renamed[i] == CLOSED) {
4978 if (!drop)
4979 close(i);
4980 continue;
4981 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982 if (rp->renamed[i] != EMPTY) {
4983 if (!drop) {
4984 close(i);
4985 copyfd(rp->renamed[i], i);
4986 }
4987 close(rp->renamed[i]);
4988 }
4989 }
4990 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004991 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004992 free(rp);
4993 INT_ON;
4994}
4995
4996/*
4997 * Undo all redirections. Called on error or interrupt.
4998 */
4999
5000/*
5001 * Discard all saved file descriptors.
5002 */
5003static void
5004clearredir(int drop)
5005{
5006 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005007 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005008 if (!redirlist)
5009 break;
5010 popredir(drop);
5011 }
5012}
5013
5014static int
5015redirectsafe(union node *redir, int flags)
5016{
5017 int err;
5018 volatile int saveint;
5019 struct jmploc *volatile savehandler = exception_handler;
5020 struct jmploc jmploc;
5021
5022 SAVE_INT(saveint);
5023 err = setjmp(jmploc.loc) * 2;
5024 if (!err) {
5025 exception_handler = &jmploc;
5026 redirect(redir, flags);
5027 }
5028 exception_handler = savehandler;
5029 if (err && exception != EXERROR)
5030 longjmp(exception_handler->loc, 1);
5031 RESTORE_INT(saveint);
5032 return err;
5033}
5034
5035
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005036/* ============ Routines to expand arguments to commands
5037 *
5038 * We have to deal with backquotes, shell variables, and file metacharacters.
5039 */
5040
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005041#if ENABLE_ASH_MATH_SUPPORT_64
5042typedef int64_t arith_t;
5043#define arith_t_type long long
5044#else
5045typedef long arith_t;
5046#define arith_t_type long
5047#endif
5048
5049#if ENABLE_ASH_MATH_SUPPORT
5050static arith_t dash_arith(const char *);
5051static arith_t arith(const char *expr, int *perrcode);
5052#endif
5053
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005054/*
5055 * expandarg flags
5056 */
5057#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5058#define EXP_TILDE 0x2 /* do normal tilde expansion */
5059#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5060#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5061#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5062#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5063#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5064#define EXP_WORD 0x80 /* expand word in parameter expansion */
5065#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5066/*
5067 * _rmescape() flags
5068 */
5069#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5070#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5071#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5072#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5073#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5074
5075/*
5076 * Structure specifying which parts of the string should be searched
5077 * for IFS characters.
5078 */
5079struct ifsregion {
5080 struct ifsregion *next; /* next region in list */
5081 int begoff; /* offset of start of region */
5082 int endoff; /* offset of end of region */
5083 int nulonly; /* search for nul bytes only */
5084};
5085
5086struct arglist {
5087 struct strlist *list;
5088 struct strlist **lastp;
5089};
5090
5091/* output of current string */
5092static char *expdest;
5093/* list of back quote expressions */
5094static struct nodelist *argbackq;
5095/* first struct in list of ifs regions */
5096static struct ifsregion ifsfirst;
5097/* last struct in list */
5098static struct ifsregion *ifslastp;
5099/* holds expanded arg list */
5100static struct arglist exparg;
5101
5102/*
5103 * Our own itoa().
5104 */
5105static int
5106cvtnum(arith_t num)
5107{
5108 int len;
5109
5110 expdest = makestrspace(32, expdest);
5111#if ENABLE_ASH_MATH_SUPPORT_64
5112 len = fmtstr(expdest, 32, "%lld", (long long) num);
5113#else
5114 len = fmtstr(expdest, 32, "%ld", num);
5115#endif
5116 STADJUST(len, expdest);
5117 return len;
5118}
5119
5120static size_t
5121esclen(const char *start, const char *p)
5122{
5123 size_t esc = 0;
5124
5125 while (p > start && *--p == CTLESC) {
5126 esc++;
5127 }
5128 return esc;
5129}
5130
5131/*
5132 * Remove any CTLESC characters from a string.
5133 */
5134static char *
5135_rmescapes(char *str, int flag)
5136{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005137 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005138
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005139 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005140 unsigned inquotes;
5141 int notescaped;
5142 int globbing;
5143
5144 p = strpbrk(str, qchars);
5145 if (!p) {
5146 return str;
5147 }
5148 q = p;
5149 r = str;
5150 if (flag & RMESCAPE_ALLOC) {
5151 size_t len = p - str;
5152 size_t fulllen = len + strlen(p) + 1;
5153
5154 if (flag & RMESCAPE_GROW) {
5155 r = makestrspace(fulllen, expdest);
5156 } else if (flag & RMESCAPE_HEAP) {
5157 r = ckmalloc(fulllen);
5158 } else {
5159 r = stalloc(fulllen);
5160 }
5161 q = r;
5162 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005163 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005164 }
5165 }
5166 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5167 globbing = flag & RMESCAPE_GLOB;
5168 notescaped = globbing;
5169 while (*p) {
5170 if (*p == CTLQUOTEMARK) {
5171 inquotes = ~inquotes;
5172 p++;
5173 notescaped = globbing;
5174 continue;
5175 }
5176 if (*p == '\\') {
5177 /* naked back slash */
5178 notescaped = 0;
5179 goto copy;
5180 }
5181 if (*p == CTLESC) {
5182 p++;
5183 if (notescaped && inquotes && *p != '/') {
5184 *q++ = '\\';
5185 }
5186 }
5187 notescaped = globbing;
5188 copy:
5189 *q++ = *p++;
5190 }
5191 *q = '\0';
5192 if (flag & RMESCAPE_GROW) {
5193 expdest = r;
5194 STADJUST(q - r + 1, expdest);
5195 }
5196 return r;
5197}
5198#define rmescapes(p) _rmescapes((p), 0)
5199
5200#define pmatch(a, b) !fnmatch((a), (b), 0)
5201
5202/*
5203 * Prepare a pattern for a expmeta (internal glob(3)) call.
5204 *
5205 * Returns an stalloced string.
5206 */
5207static char *
5208preglob(const char *pattern, int quoted, int flag)
5209{
5210 flag |= RMESCAPE_GLOB;
5211 if (quoted) {
5212 flag |= RMESCAPE_QUOTED;
5213 }
5214 return _rmescapes((char *)pattern, flag);
5215}
5216
5217/*
5218 * Put a string on the stack.
5219 */
5220static void
5221memtodest(const char *p, size_t len, int syntax, int quotes)
5222{
5223 char *q = expdest;
5224
5225 q = makestrspace(len * 2, q);
5226
5227 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005228 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005229 if (!c)
5230 continue;
5231 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5232 USTPUTC(CTLESC, q);
5233 USTPUTC(c, q);
5234 }
5235
5236 expdest = q;
5237}
5238
5239static void
5240strtodest(const char *p, int syntax, int quotes)
5241{
5242 memtodest(p, strlen(p), syntax, quotes);
5243}
5244
5245/*
5246 * Record the fact that we have to scan this region of the
5247 * string for IFS characters.
5248 */
5249static void
5250recordregion(int start, int end, int nulonly)
5251{
5252 struct ifsregion *ifsp;
5253
5254 if (ifslastp == NULL) {
5255 ifsp = &ifsfirst;
5256 } else {
5257 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005258 ifsp = ckzalloc(sizeof(*ifsp));
5259 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005260 ifslastp->next = ifsp;
5261 INT_ON;
5262 }
5263 ifslastp = ifsp;
5264 ifslastp->begoff = start;
5265 ifslastp->endoff = end;
5266 ifslastp->nulonly = nulonly;
5267}
5268
5269static void
5270removerecordregions(int endoff)
5271{
5272 if (ifslastp == NULL)
5273 return;
5274
5275 if (ifsfirst.endoff > endoff) {
5276 while (ifsfirst.next != NULL) {
5277 struct ifsregion *ifsp;
5278 INT_OFF;
5279 ifsp = ifsfirst.next->next;
5280 free(ifsfirst.next);
5281 ifsfirst.next = ifsp;
5282 INT_ON;
5283 }
5284 if (ifsfirst.begoff > endoff)
5285 ifslastp = NULL;
5286 else {
5287 ifslastp = &ifsfirst;
5288 ifsfirst.endoff = endoff;
5289 }
5290 return;
5291 }
5292
5293 ifslastp = &ifsfirst;
5294 while (ifslastp->next && ifslastp->next->begoff < endoff)
5295 ifslastp=ifslastp->next;
5296 while (ifslastp->next != NULL) {
5297 struct ifsregion *ifsp;
5298 INT_OFF;
5299 ifsp = ifslastp->next->next;
5300 free(ifslastp->next);
5301 ifslastp->next = ifsp;
5302 INT_ON;
5303 }
5304 if (ifslastp->endoff > endoff)
5305 ifslastp->endoff = endoff;
5306}
5307
5308static char *
5309exptilde(char *startp, char *p, int flag)
5310{
5311 char c;
5312 char *name;
5313 struct passwd *pw;
5314 const char *home;
5315 int quotes = flag & (EXP_FULL | EXP_CASE);
5316 int startloc;
5317
5318 name = p + 1;
5319
5320 while ((c = *++p) != '\0') {
5321 switch (c) {
5322 case CTLESC:
5323 return startp;
5324 case CTLQUOTEMARK:
5325 return startp;
5326 case ':':
5327 if (flag & EXP_VARTILDE)
5328 goto done;
5329 break;
5330 case '/':
5331 case CTLENDVAR:
5332 goto done;
5333 }
5334 }
5335 done:
5336 *p = '\0';
5337 if (*name == '\0') {
5338 home = lookupvar(homestr);
5339 } else {
5340 pw = getpwnam(name);
5341 if (pw == NULL)
5342 goto lose;
5343 home = pw->pw_dir;
5344 }
5345 if (!home || !*home)
5346 goto lose;
5347 *p = c;
5348 startloc = expdest - (char *)stackblock();
5349 strtodest(home, SQSYNTAX, quotes);
5350 recordregion(startloc, expdest - (char *)stackblock(), 0);
5351 return p;
5352 lose:
5353 *p = c;
5354 return startp;
5355}
5356
5357/*
5358 * Execute a command inside back quotes. If it's a builtin command, we
5359 * want to save its output in a block obtained from malloc. Otherwise
5360 * we fork off a subprocess and get the output of the command via a pipe.
5361 * Should be called with interrupts off.
5362 */
5363struct backcmd { /* result of evalbackcmd */
5364 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005365 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005366 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005367 struct job *jp; /* job structure for command */
5368};
5369
5370/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005371static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005372#define EV_EXIT 01 /* exit after evaluating tree */
5373static void evaltree(union node *, int);
5374
5375static void
5376evalbackcmd(union node *n, struct backcmd *result)
5377{
5378 int saveherefd;
5379
5380 result->fd = -1;
5381 result->buf = NULL;
5382 result->nleft = 0;
5383 result->jp = NULL;
5384 if (n == NULL) {
5385 goto out;
5386 }
5387
5388 saveherefd = herefd;
5389 herefd = -1;
5390
5391 {
5392 int pip[2];
5393 struct job *jp;
5394
5395 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005396 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005397 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005398 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5399 FORCE_INT_ON;
5400 close(pip[0]);
5401 if (pip[1] != 1) {
5402 close(1);
5403 copyfd(pip[1], 1);
5404 close(pip[1]);
5405 }
5406 eflag = 0;
5407 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5408 /* NOTREACHED */
5409 }
5410 close(pip[1]);
5411 result->fd = pip[0];
5412 result->jp = jp;
5413 }
5414 herefd = saveherefd;
5415 out:
5416 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5417 result->fd, result->buf, result->nleft, result->jp));
5418}
5419
5420/*
5421 * Expand stuff in backwards quotes.
5422 */
5423static void
5424expbackq(union node *cmd, int quoted, int quotes)
5425{
5426 struct backcmd in;
5427 int i;
5428 char buf[128];
5429 char *p;
5430 char *dest;
5431 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005432 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005433 struct stackmark smark;
5434
5435 INT_OFF;
5436 setstackmark(&smark);
5437 dest = expdest;
5438 startloc = dest - (char *)stackblock();
5439 grabstackstr(dest);
5440 evalbackcmd(cmd, &in);
5441 popstackmark(&smark);
5442
5443 p = in.buf;
5444 i = in.nleft;
5445 if (i == 0)
5446 goto read;
5447 for (;;) {
5448 memtodest(p, i, syntax, quotes);
5449 read:
5450 if (in.fd < 0)
5451 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005452 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005453 TRACE(("expbackq: read returns %d\n", i));
5454 if (i <= 0)
5455 break;
5456 p = buf;
5457 }
5458
Denis Vlasenko60818682007-09-28 22:07:23 +00005459 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005460 if (in.fd >= 0) {
5461 close(in.fd);
5462 back_exitstatus = waitforjob(in.jp);
5463 }
5464 INT_ON;
5465
5466 /* Eat all trailing newlines */
5467 dest = expdest;
5468 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5469 STUNPUTC(dest);
5470 expdest = dest;
5471
5472 if (quoted == 0)
5473 recordregion(startloc, dest - (char *)stackblock(), 0);
5474 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5475 (dest - (char *)stackblock()) - startloc,
5476 (dest - (char *)stackblock()) - startloc,
5477 stackblock() + startloc));
5478}
5479
5480#if ENABLE_ASH_MATH_SUPPORT
5481/*
5482 * Expand arithmetic expression. Backup to start of expression,
5483 * evaluate, place result in (backed up) result, adjust string position.
5484 */
5485static void
5486expari(int quotes)
5487{
5488 char *p, *start;
5489 int begoff;
5490 int flag;
5491 int len;
5492
5493 /* ifsfree(); */
5494
5495 /*
5496 * This routine is slightly over-complicated for
5497 * efficiency. Next we scan backwards looking for the
5498 * start of arithmetic.
5499 */
5500 start = stackblock();
5501 p = expdest - 1;
5502 *p = '\0';
5503 p--;
5504 do {
5505 int esc;
5506
5507 while (*p != CTLARI) {
5508 p--;
5509#if DEBUG
5510 if (p < start) {
5511 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5512 }
5513#endif
5514 }
5515
5516 esc = esclen(start, p);
5517 if (!(esc % 2)) {
5518 break;
5519 }
5520
5521 p -= esc + 1;
5522 } while (1);
5523
5524 begoff = p - start;
5525
5526 removerecordregions(begoff);
5527
5528 flag = p[1];
5529
5530 expdest = p;
5531
5532 if (quotes)
5533 rmescapes(p + 2);
5534
5535 len = cvtnum(dash_arith(p + 2));
5536
5537 if (flag != '"')
5538 recordregion(begoff, begoff + len, 0);
5539}
5540#endif
5541
5542/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005543static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005544
5545/*
5546 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5547 * characters to allow for further processing. Otherwise treat
5548 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005549 *
5550 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5551 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5552 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553 */
5554static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005555argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005556{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005557 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005558 '=',
5559 ':',
5560 CTLQUOTEMARK,
5561 CTLENDVAR,
5562 CTLESC,
5563 CTLVAR,
5564 CTLBACKQ,
5565 CTLBACKQ | CTLQUOTE,
5566#if ENABLE_ASH_MATH_SUPPORT
5567 CTLENDARI,
5568#endif
5569 0
5570 };
5571 const char *reject = spclchars;
5572 int c;
5573 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5574 int breakall = flag & EXP_WORD;
5575 int inquotes;
5576 size_t length;
5577 int startloc;
5578
5579 if (!(flag & EXP_VARTILDE)) {
5580 reject += 2;
5581 } else if (flag & EXP_VARTILDE2) {
5582 reject++;
5583 }
5584 inquotes = 0;
5585 length = 0;
5586 if (flag & EXP_TILDE) {
5587 char *q;
5588
5589 flag &= ~EXP_TILDE;
5590 tilde:
5591 q = p;
5592 if (*q == CTLESC && (flag & EXP_QWORD))
5593 q++;
5594 if (*q == '~')
5595 p = exptilde(p, q, flag);
5596 }
5597 start:
5598 startloc = expdest - (char *)stackblock();
5599 for (;;) {
5600 length += strcspn(p + length, reject);
5601 c = p[length];
5602 if (c && (!(c & 0x80)
5603#if ENABLE_ASH_MATH_SUPPORT
5604 || c == CTLENDARI
5605#endif
5606 )) {
5607 /* c == '=' || c == ':' || c == CTLENDARI */
5608 length++;
5609 }
5610 if (length > 0) {
5611 int newloc;
5612 expdest = stack_nputstr(p, length, expdest);
5613 newloc = expdest - (char *)stackblock();
5614 if (breakall && !inquotes && newloc > startloc) {
5615 recordregion(startloc, newloc, 0);
5616 }
5617 startloc = newloc;
5618 }
5619 p += length + 1;
5620 length = 0;
5621
5622 switch (c) {
5623 case '\0':
5624 goto breakloop;
5625 case '=':
5626 if (flag & EXP_VARTILDE2) {
5627 p--;
5628 continue;
5629 }
5630 flag |= EXP_VARTILDE2;
5631 reject++;
5632 /* fall through */
5633 case ':':
5634 /*
5635 * sort of a hack - expand tildes in variable
5636 * assignments (after the first '=' and after ':'s).
5637 */
5638 if (*--p == '~') {
5639 goto tilde;
5640 }
5641 continue;
5642 }
5643
5644 switch (c) {
5645 case CTLENDVAR: /* ??? */
5646 goto breakloop;
5647 case CTLQUOTEMARK:
5648 /* "$@" syntax adherence hack */
5649 if (
5650 !inquotes &&
5651 !memcmp(p, dolatstr, 4) &&
5652 (p[4] == CTLQUOTEMARK || (
5653 p[4] == CTLENDVAR &&
5654 p[5] == CTLQUOTEMARK
5655 ))
5656 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005657 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005658 goto start;
5659 }
5660 inquotes = !inquotes;
5661 addquote:
5662 if (quotes) {
5663 p--;
5664 length++;
5665 startloc++;
5666 }
5667 break;
5668 case CTLESC:
5669 startloc++;
5670 length++;
5671 goto addquote;
5672 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005673 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005674 goto start;
5675 case CTLBACKQ:
5676 c = 0;
5677 case CTLBACKQ|CTLQUOTE:
5678 expbackq(argbackq->n, c, quotes);
5679 argbackq = argbackq->next;
5680 goto start;
5681#if ENABLE_ASH_MATH_SUPPORT
5682 case CTLENDARI:
5683 p--;
5684 expari(quotes);
5685 goto start;
5686#endif
5687 }
5688 }
5689 breakloop:
5690 ;
5691}
5692
5693static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005694scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005695 int zero)
5696{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005697// This commented out code was added by James Simmons <jsimmons@infradead.org>
5698// as part of a larger change when he added support for ${var/a/b}.
5699// However, it broke # and % operators:
5700//
5701//var=ababcdcd
5702// ok bad
5703//echo ${var#ab} abcdcd abcdcd
5704//echo ${var##ab} abcdcd abcdcd
5705//echo ${var#a*b} abcdcd ababcdcd (!)
5706//echo ${var##a*b} cdcd cdcd
5707//echo ${var#?} babcdcd ababcdcd (!)
5708//echo ${var##?} babcdcd babcdcd
5709//echo ${var#*} ababcdcd babcdcd (!)
5710//echo ${var##*}
5711//echo ${var%cd} ababcd ababcd
5712//echo ${var%%cd} ababcd abab (!)
5713//echo ${var%c*d} ababcd ababcd
5714//echo ${var%%c*d} abab ababcdcd (!)
5715//echo ${var%?} ababcdc ababcdc
5716//echo ${var%%?} ababcdc ababcdcd (!)
5717//echo ${var%*} ababcdcd ababcdcd
5718//echo ${var%%*}
5719//
5720// Commenting it back out helped. Remove it completely if it really
5721// is not needed.
5722
5723 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005724 char c;
5725
5726 loc = startp;
5727 loc2 = rmesc;
5728 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005729 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005731
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005732 c = *loc2;
5733 if (zero) {
5734 *loc2 = '\0';
5735 s = rmesc;
5736 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005737 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005738
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005739// // chop off end if its '*'
5740// full = strrchr(str, '*');
5741// if (full && full != str)
5742// match--;
5743//
5744// // If str starts with '*' replace with s.
5745// if ((*str == '*') && strlen(s) >= match) {
5746// full = xstrdup(s);
5747// strncpy(full+strlen(s)-match+1, str+1, match-1);
5748// } else
5749// full = xstrndup(str, match);
5750// match = strncmp(s, full, strlen(full));
5751// free(full);
5752//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005753 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005754 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005755 return loc;
5756 if (quotes && *loc == CTLESC)
5757 loc++;
5758 loc++;
5759 loc2++;
5760 } while (c);
5761 return 0;
5762}
5763
5764static char *
5765scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5766 int zero)
5767{
5768 int esc = 0;
5769 char *loc;
5770 char *loc2;
5771
5772 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5773 int match;
5774 char c = *loc2;
5775 const char *s = loc2;
5776 if (zero) {
5777 *loc2 = '\0';
5778 s = rmesc;
5779 }
5780 match = pmatch(str, s);
5781 *loc2 = c;
5782 if (match)
5783 return loc;
5784 loc--;
5785 if (quotes) {
5786 if (--esc < 0) {
5787 esc = esclen(startp, loc);
5788 }
5789 if (esc % 2) {
5790 esc--;
5791 loc--;
5792 }
5793 }
5794 }
5795 return 0;
5796}
5797
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005798static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799static void
5800varunset(const char *end, const char *var, const char *umsg, int varflags)
5801{
5802 const char *msg;
5803 const char *tail;
5804
5805 tail = nullstr;
5806 msg = "parameter not set";
5807 if (umsg) {
5808 if (*end == CTLENDVAR) {
5809 if (varflags & VSNUL)
5810 tail = " or null";
5811 } else
5812 msg = umsg;
5813 }
5814 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5815}
5816
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005817#if ENABLE_ASH_BASH_COMPAT
5818static char *
5819parse_sub_pattern(char *arg, int inquotes)
5820{
5821 char *idx, *repl = NULL;
5822 unsigned char c;
5823
Denis Vlasenko2659c632008-06-14 06:04:59 +00005824 idx = arg;
5825 while (1) {
5826 c = *arg;
5827 if (!c)
5828 break;
5829 if (c == '/') {
5830 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005831 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005832 repl = idx + 1;
5833 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005834 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005835 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005836 *idx++ = c;
5837 if (!inquotes && c == '\\' && arg[1] == '\\')
5838 arg++; /* skip both \\, not just first one */
5839 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005840 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005841 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005842
5843 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005844}
5845#endif /* ENABLE_ASH_BASH_COMPAT */
5846
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005847static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005848subevalvar(char *p, char *str, int strloc, int subtype,
5849 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005851 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005852 char *startp;
5853 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005854 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005855 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5856 USE_ASH_BASH_COMPAT(char null = '\0';)
5857 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5858 int saveherefd = herefd;
5859 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005861 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005862
5863 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005864 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5865 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 STPUTC('\0', expdest);
5867 herefd = saveherefd;
5868 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005869 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005870
5871 switch (subtype) {
5872 case VSASSIGN:
5873 setvar(str, startp, 0);
5874 amount = startp - expdest;
5875 STADJUST(amount, expdest);
5876 return startp;
5877
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005878#if ENABLE_ASH_BASH_COMPAT
5879 case VSSUBSTR:
5880 loc = str = stackblock() + strloc;
5881// TODO: number() instead? It does error checking...
5882 pos = atoi(loc);
5883 len = str - startp - 1;
5884
5885 /* *loc != '\0', guaranteed by parser */
5886 if (quotes) {
5887 char *ptr;
5888
5889 /* We must adjust the length by the number of escapes we find. */
5890 for (ptr = startp; ptr < (str - 1); ptr++) {
5891 if(*ptr == CTLESC) {
5892 len--;
5893 ptr++;
5894 }
5895 }
5896 }
5897 orig_len = len;
5898
5899 if (*loc++ == ':') {
5900// TODO: number() instead? It does error checking...
5901 len = atoi(loc);
5902 } else {
5903 len = orig_len;
5904 while (*loc && *loc != ':')
5905 loc++;
5906 if (*loc++ == ':')
5907// TODO: number() instead? It does error checking...
5908 len = atoi(loc);
5909 }
5910 if (pos >= orig_len) {
5911 pos = 0;
5912 len = 0;
5913 }
5914 if (len > (orig_len - pos))
5915 len = orig_len - pos;
5916
5917 for (str = startp; pos; str++, pos--) {
5918 if (quotes && *str == CTLESC)
5919 str++;
5920 }
5921 for (loc = startp; len; len--) {
5922 if (quotes && *str == CTLESC)
5923 *loc++ = *str++;
5924 *loc++ = *str++;
5925 }
5926 *loc = '\0';
5927 amount = loc - expdest;
5928 STADJUST(amount, expdest);
5929 return loc;
5930#endif
5931
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005932 case VSQUESTION:
5933 varunset(p, str, startp, varflags);
5934 /* NOTREACHED */
5935 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005936 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005937
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005938 /* We'll comeback here if we grow the stack while handling
5939 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5940 * stack will need rebasing, and we'll need to remove our work
5941 * areas each time
5942 */
5943 USE_ASH_BASH_COMPAT(restart:)
5944
5945 amount = expdest - ((char *)stackblock() + resetloc);
5946 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005947 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005948
5949 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005950 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951 if (quotes) {
5952 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5953 if (rmesc != startp) {
5954 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005955 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005956 }
5957 }
5958 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005959 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005960 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005961 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005962
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005963#if ENABLE_ASH_BASH_COMPAT
5964 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5965 char *idx, *end, *restart_detect;
5966
5967 if(!repl) {
5968 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5969 if (!repl)
5970 repl = &null;
5971 }
5972
5973 /* If there's no pattern to match, return the expansion unmolested */
5974 if (*str == '\0')
5975 return 0;
5976
5977 len = 0;
5978 idx = startp;
5979 end = str - 1;
5980 while (idx < end) {
5981 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5982 if (!loc) {
5983 /* No match, advance */
5984 restart_detect = stackblock();
5985 STPUTC(*idx, expdest);
5986 if (quotes && *idx == CTLESC) {
5987 idx++;
5988 len++;
5989 STPUTC(*idx, expdest);
5990 }
5991 if (stackblock() != restart_detect)
5992 goto restart;
5993 idx++;
5994 len++;
5995 rmesc++;
5996 continue;
5997 }
5998
5999 if (subtype == VSREPLACEALL) {
6000 while (idx < loc) {
6001 if (quotes && *idx == CTLESC)
6002 idx++;
6003 idx++;
6004 rmesc++;
6005 }
6006 } else
6007 idx = loc;
6008
6009 for (loc = repl; *loc; loc++) {
6010 restart_detect = stackblock();
6011 STPUTC(*loc, expdest);
6012 if (stackblock() != restart_detect)
6013 goto restart;
6014 len++;
6015 }
6016
6017 if (subtype == VSREPLACE) {
6018 while (*idx) {
6019 restart_detect = stackblock();
6020 STPUTC(*idx, expdest);
6021 if (stackblock() != restart_detect)
6022 goto restart;
6023 len++;
6024 idx++;
6025 }
6026 break;
6027 }
6028 }
6029
6030 /* We've put the replaced text into a buffer at workloc, now
6031 * move it to the right place and adjust the stack.
6032 */
6033 startp = stackblock() + startloc;
6034 STPUTC('\0', expdest);
6035 memmove(startp, stackblock() + workloc, len);
6036 startp[len++] = '\0';
6037 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6038 STADJUST(-amount, expdest);
6039 return startp;
6040 }
6041#endif /* ENABLE_ASH_BASH_COMPAT */
6042
6043 subtype -= VSTRIMRIGHT;
6044#if DEBUG
6045 if (subtype < 0 || subtype > 7)
6046 abort();
6047#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6049 zero = subtype >> 1;
6050 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6051 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6052
6053 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6054 if (loc) {
6055 if (zero) {
6056 memmove(startp, loc, str - loc);
6057 loc = startp + (str - loc) - 1;
6058 }
6059 *loc = '\0';
6060 amount = loc - expdest;
6061 STADJUST(amount, expdest);
6062 }
6063 return loc;
6064}
6065
6066/*
6067 * Add the value of a specialized variable to the stack string.
6068 */
6069static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006070varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071{
6072 int num;
6073 char *p;
6074 int i;
6075 int sep = 0;
6076 int sepq = 0;
6077 ssize_t len = 0;
6078 char **ap;
6079 int syntax;
6080 int quoted = varflags & VSQUOTE;
6081 int subtype = varflags & VSTYPE;
6082 int quotes = flags & (EXP_FULL | EXP_CASE);
6083
6084 if (quoted && (flags & EXP_FULL))
6085 sep = 1 << CHAR_BIT;
6086
6087 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6088 switch (*name) {
6089 case '$':
6090 num = rootpid;
6091 goto numvar;
6092 case '?':
6093 num = exitstatus;
6094 goto numvar;
6095 case '#':
6096 num = shellparam.nparam;
6097 goto numvar;
6098 case '!':
6099 num = backgndpid;
6100 if (num == 0)
6101 return -1;
6102 numvar:
6103 len = cvtnum(num);
6104 break;
6105 case '-':
6106 p = makestrspace(NOPTS, expdest);
6107 for (i = NOPTS - 1; i >= 0; i--) {
6108 if (optlist[i]) {
6109 USTPUTC(optletters(i), p);
6110 len++;
6111 }
6112 }
6113 expdest = p;
6114 break;
6115 case '@':
6116 if (sep)
6117 goto param;
6118 /* fall through */
6119 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006120 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006121 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6122 sepq = 1;
6123 param:
6124 ap = shellparam.p;
6125 if (!ap)
6126 return -1;
6127 while ((p = *ap++)) {
6128 size_t partlen;
6129
6130 partlen = strlen(p);
6131 len += partlen;
6132
6133 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6134 memtodest(p, partlen, syntax, quotes);
6135
6136 if (*ap && sep) {
6137 char *q;
6138
6139 len++;
6140 if (subtype == VSPLUS || subtype == VSLENGTH) {
6141 continue;
6142 }
6143 q = expdest;
6144 if (sepq)
6145 STPUTC(CTLESC, q);
6146 STPUTC(sep, q);
6147 expdest = q;
6148 }
6149 }
6150 return len;
6151 case '0':
6152 case '1':
6153 case '2':
6154 case '3':
6155 case '4':
6156 case '5':
6157 case '6':
6158 case '7':
6159 case '8':
6160 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006161// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006162 num = atoi(name);
6163 if (num < 0 || num > shellparam.nparam)
6164 return -1;
6165 p = num ? shellparam.p[num - 1] : arg0;
6166 goto value;
6167 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006168 /* NB: name has form "VAR=..." */
6169
6170 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6171 * which should be considered before we check variables. */
6172 if (var_str_list) {
6173 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6174 p = NULL;
6175 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006176 char *str, *eq;
6177 str = var_str_list->text;
6178 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006179 if (!eq) /* stop at first non-assignment */
6180 break;
6181 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006182 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006183 && strncmp(str, name, name_len) == 0) {
6184 p = eq;
6185 /* goto value; - WRONG! */
6186 /* think "A=1 A=2 B=$A" */
6187 }
6188 var_str_list = var_str_list->next;
6189 } while (var_str_list);
6190 if (p)
6191 goto value;
6192 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006193 p = lookupvar(name);
6194 value:
6195 if (!p)
6196 return -1;
6197
6198 len = strlen(p);
6199 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6200 memtodest(p, len, syntax, quotes);
6201 return len;
6202 }
6203
6204 if (subtype == VSPLUS || subtype == VSLENGTH)
6205 STADJUST(-len, expdest);
6206 return len;
6207}
6208
6209/*
6210 * Expand a variable, and return a pointer to the next character in the
6211 * input string.
6212 */
6213static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006214evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006216 char varflags;
6217 char subtype;
6218 char quoted;
6219 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006220 char *var;
6221 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 int startloc;
6223 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006224
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006225 varflags = *p++;
6226 subtype = varflags & VSTYPE;
6227 quoted = varflags & VSQUOTE;
6228 var = p;
6229 easy = (!quoted || (*var == '@' && shellparam.nparam));
6230 startloc = expdest - (char *)stackblock();
6231 p = strchr(p, '=') + 1;
6232
6233 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006234 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006235 if (varflags & VSNUL)
6236 varlen--;
6237
6238 if (subtype == VSPLUS) {
6239 varlen = -1 - varlen;
6240 goto vsplus;
6241 }
6242
6243 if (subtype == VSMINUS) {
6244 vsplus:
6245 if (varlen < 0) {
6246 argstr(
6247 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006248 (quoted ? EXP_QWORD : EXP_WORD),
6249 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006250 );
6251 goto end;
6252 }
6253 if (easy)
6254 goto record;
6255 goto end;
6256 }
6257
6258 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6259 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006260 if (subevalvar(p, var, /* strloc: */ 0,
6261 subtype, startloc, varflags,
6262 /* quotes: */ 0,
6263 var_str_list)
6264 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006265 varflags &= ~VSNUL;
6266 /*
6267 * Remove any recorded regions beyond
6268 * start of variable
6269 */
6270 removerecordregions(startloc);
6271 goto again;
6272 }
6273 goto end;
6274 }
6275 if (easy)
6276 goto record;
6277 goto end;
6278 }
6279
6280 if (varlen < 0 && uflag)
6281 varunset(p, var, 0, 0);
6282
6283 if (subtype == VSLENGTH) {
6284 cvtnum(varlen > 0 ? varlen : 0);
6285 goto record;
6286 }
6287
6288 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006289 if (easy)
6290 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006291 goto end;
6292 }
6293
6294#if DEBUG
6295 switch (subtype) {
6296 case VSTRIMLEFT:
6297 case VSTRIMLEFTMAX:
6298 case VSTRIMRIGHT:
6299 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006300#if ENABLE_ASH_BASH_COMPAT
6301 case VSSUBSTR:
6302 case VSREPLACE:
6303 case VSREPLACEALL:
6304#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006305 break;
6306 default:
6307 abort();
6308 }
6309#endif
6310
6311 if (varlen >= 0) {
6312 /*
6313 * Terminate the string and start recording the pattern
6314 * right after it
6315 */
6316 STPUTC('\0', expdest);
6317 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006318 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6319 startloc, varflags,
6320 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6321 var_str_list)
6322 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006323 int amount = expdest - (
6324 (char *)stackblock() + patloc - 1
6325 );
6326 STADJUST(-amount, expdest);
6327 }
6328 /* Remove any recorded regions beyond start of variable */
6329 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006330 record:
6331 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332 }
6333
6334 end:
6335 if (subtype != VSNORMAL) { /* skip to end of alternative */
6336 int nesting = 1;
6337 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006338 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006339 if (c == CTLESC)
6340 p++;
6341 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6342 if (varlen >= 0)
6343 argbackq = argbackq->next;
6344 } else if (c == CTLVAR) {
6345 if ((*p++ & VSTYPE) != VSNORMAL)
6346 nesting++;
6347 } else if (c == CTLENDVAR) {
6348 if (--nesting == 0)
6349 break;
6350 }
6351 }
6352 }
6353 return p;
6354}
6355
6356/*
6357 * Break the argument string into pieces based upon IFS and add the
6358 * strings to the argument list. The regions of the string to be
6359 * searched for IFS characters have been stored by recordregion.
6360 */
6361static void
6362ifsbreakup(char *string, struct arglist *arglist)
6363{
6364 struct ifsregion *ifsp;
6365 struct strlist *sp;
6366 char *start;
6367 char *p;
6368 char *q;
6369 const char *ifs, *realifs;
6370 int ifsspc;
6371 int nulonly;
6372
6373 start = string;
6374 if (ifslastp != NULL) {
6375 ifsspc = 0;
6376 nulonly = 0;
6377 realifs = ifsset() ? ifsval() : defifs;
6378 ifsp = &ifsfirst;
6379 do {
6380 p = string + ifsp->begoff;
6381 nulonly = ifsp->nulonly;
6382 ifs = nulonly ? nullstr : realifs;
6383 ifsspc = 0;
6384 while (p < string + ifsp->endoff) {
6385 q = p;
6386 if (*p == CTLESC)
6387 p++;
6388 if (!strchr(ifs, *p)) {
6389 p++;
6390 continue;
6391 }
6392 if (!nulonly)
6393 ifsspc = (strchr(defifs, *p) != NULL);
6394 /* Ignore IFS whitespace at start */
6395 if (q == start && ifsspc) {
6396 p++;
6397 start = p;
6398 continue;
6399 }
6400 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006401 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006402 sp->text = start;
6403 *arglist->lastp = sp;
6404 arglist->lastp = &sp->next;
6405 p++;
6406 if (!nulonly) {
6407 for (;;) {
6408 if (p >= string + ifsp->endoff) {
6409 break;
6410 }
6411 q = p;
6412 if (*p == CTLESC)
6413 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006414 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415 p = q;
6416 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006417 }
6418 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006419 if (ifsspc) {
6420 p++;
6421 ifsspc = 0;
6422 } else {
6423 p = q;
6424 break;
6425 }
6426 } else
6427 p++;
6428 }
6429 }
6430 start = p;
6431 } /* while */
6432 ifsp = ifsp->next;
6433 } while (ifsp != NULL);
6434 if (nulonly)
6435 goto add;
6436 }
6437
6438 if (!*start)
6439 return;
6440
6441 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006442 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 sp->text = start;
6444 *arglist->lastp = sp;
6445 arglist->lastp = &sp->next;
6446}
6447
6448static void
6449ifsfree(void)
6450{
6451 struct ifsregion *p;
6452
6453 INT_OFF;
6454 p = ifsfirst.next;
6455 do {
6456 struct ifsregion *ifsp;
6457 ifsp = p->next;
6458 free(p);
6459 p = ifsp;
6460 } while (p);
6461 ifslastp = NULL;
6462 ifsfirst.next = NULL;
6463 INT_ON;
6464}
6465
6466/*
6467 * Add a file name to the list.
6468 */
6469static void
6470addfname(const char *name)
6471{
6472 struct strlist *sp;
6473
Denis Vlasenko597906c2008-02-20 16:38:54 +00006474 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006475 sp->text = ststrdup(name);
6476 *exparg.lastp = sp;
6477 exparg.lastp = &sp->next;
6478}
6479
6480static char *expdir;
6481
6482/*
6483 * Do metacharacter (i.e. *, ?, [...]) expansion.
6484 */
6485static void
6486expmeta(char *enddir, char *name)
6487{
6488 char *p;
6489 const char *cp;
6490 char *start;
6491 char *endname;
6492 int metaflag;
6493 struct stat statb;
6494 DIR *dirp;
6495 struct dirent *dp;
6496 int atend;
6497 int matchdot;
6498
6499 metaflag = 0;
6500 start = name;
6501 for (p = name; *p; p++) {
6502 if (*p == '*' || *p == '?')
6503 metaflag = 1;
6504 else if (*p == '[') {
6505 char *q = p + 1;
6506 if (*q == '!')
6507 q++;
6508 for (;;) {
6509 if (*q == '\\')
6510 q++;
6511 if (*q == '/' || *q == '\0')
6512 break;
6513 if (*++q == ']') {
6514 metaflag = 1;
6515 break;
6516 }
6517 }
6518 } else if (*p == '\\')
6519 p++;
6520 else if (*p == '/') {
6521 if (metaflag)
6522 goto out;
6523 start = p + 1;
6524 }
6525 }
6526 out:
6527 if (metaflag == 0) { /* we've reached the end of the file name */
6528 if (enddir != expdir)
6529 metaflag++;
6530 p = name;
6531 do {
6532 if (*p == '\\')
6533 p++;
6534 *enddir++ = *p;
6535 } while (*p++);
6536 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6537 addfname(expdir);
6538 return;
6539 }
6540 endname = p;
6541 if (name < start) {
6542 p = name;
6543 do {
6544 if (*p == '\\')
6545 p++;
6546 *enddir++ = *p++;
6547 } while (p < start);
6548 }
6549 if (enddir == expdir) {
6550 cp = ".";
6551 } else if (enddir == expdir + 1 && *expdir == '/') {
6552 cp = "/";
6553 } else {
6554 cp = expdir;
6555 enddir[-1] = '\0';
6556 }
6557 dirp = opendir(cp);
6558 if (dirp == NULL)
6559 return;
6560 if (enddir != expdir)
6561 enddir[-1] = '/';
6562 if (*endname == 0) {
6563 atend = 1;
6564 } else {
6565 atend = 0;
6566 *endname++ = '\0';
6567 }
6568 matchdot = 0;
6569 p = start;
6570 if (*p == '\\')
6571 p++;
6572 if (*p == '.')
6573 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006574 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006575 if (dp->d_name[0] == '.' && ! matchdot)
6576 continue;
6577 if (pmatch(start, dp->d_name)) {
6578 if (atend) {
6579 strcpy(enddir, dp->d_name);
6580 addfname(expdir);
6581 } else {
6582 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6583 continue;
6584 p[-1] = '/';
6585 expmeta(p, endname);
6586 }
6587 }
6588 }
6589 closedir(dirp);
6590 if (! atend)
6591 endname[-1] = '/';
6592}
6593
6594static struct strlist *
6595msort(struct strlist *list, int len)
6596{
6597 struct strlist *p, *q = NULL;
6598 struct strlist **lpp;
6599 int half;
6600 int n;
6601
6602 if (len <= 1)
6603 return list;
6604 half = len >> 1;
6605 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006606 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006607 q = p;
6608 p = p->next;
6609 }
6610 q->next = NULL; /* terminate first half of list */
6611 q = msort(list, half); /* sort first half of list */
6612 p = msort(p, len - half); /* sort second half */
6613 lpp = &list;
6614 for (;;) {
6615#if ENABLE_LOCALE_SUPPORT
6616 if (strcoll(p->text, q->text) < 0)
6617#else
6618 if (strcmp(p->text, q->text) < 0)
6619#endif
6620 {
6621 *lpp = p;
6622 lpp = &p->next;
6623 p = *lpp;
6624 if (p == NULL) {
6625 *lpp = q;
6626 break;
6627 }
6628 } else {
6629 *lpp = q;
6630 lpp = &q->next;
6631 q = *lpp;
6632 if (q == NULL) {
6633 *lpp = p;
6634 break;
6635 }
6636 }
6637 }
6638 return list;
6639}
6640
6641/*
6642 * Sort the results of file name expansion. It calculates the number of
6643 * strings to sort and then calls msort (short for merge sort) to do the
6644 * work.
6645 */
6646static struct strlist *
6647expsort(struct strlist *str)
6648{
6649 int len;
6650 struct strlist *sp;
6651
6652 len = 0;
6653 for (sp = str; sp; sp = sp->next)
6654 len++;
6655 return msort(str, len);
6656}
6657
6658static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006659expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006660{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006661 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006662 '*', '?', '[', 0
6663 };
6664 /* TODO - EXP_REDIR */
6665
6666 while (str) {
6667 struct strlist **savelastp;
6668 struct strlist *sp;
6669 char *p;
6670
6671 if (fflag)
6672 goto nometa;
6673 if (!strpbrk(str->text, metachars))
6674 goto nometa;
6675 savelastp = exparg.lastp;
6676
6677 INT_OFF;
6678 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6679 {
6680 int i = strlen(str->text);
6681 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6682 }
6683
6684 expmeta(expdir, p);
6685 free(expdir);
6686 if (p != str->text)
6687 free(p);
6688 INT_ON;
6689 if (exparg.lastp == savelastp) {
6690 /*
6691 * no matches
6692 */
6693 nometa:
6694 *exparg.lastp = str;
6695 rmescapes(str->text);
6696 exparg.lastp = &str->next;
6697 } else {
6698 *exparg.lastp = NULL;
6699 *savelastp = sp = expsort(*savelastp);
6700 while (sp->next != NULL)
6701 sp = sp->next;
6702 exparg.lastp = &sp->next;
6703 }
6704 str = str->next;
6705 }
6706}
6707
6708/*
6709 * Perform variable substitution and command substitution on an argument,
6710 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6711 * perform splitting and file name expansion. When arglist is NULL, perform
6712 * here document expansion.
6713 */
6714static void
6715expandarg(union node *arg, struct arglist *arglist, int flag)
6716{
6717 struct strlist *sp;
6718 char *p;
6719
6720 argbackq = arg->narg.backquote;
6721 STARTSTACKSTR(expdest);
6722 ifsfirst.next = NULL;
6723 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006724 argstr(arg->narg.text, flag,
6725 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726 p = _STPUTC('\0', expdest);
6727 expdest = p - 1;
6728 if (arglist == NULL) {
6729 return; /* here document expanded */
6730 }
6731 p = grabstackstr(p);
6732 exparg.lastp = &exparg.list;
6733 /*
6734 * TODO - EXP_REDIR
6735 */
6736 if (flag & EXP_FULL) {
6737 ifsbreakup(p, &exparg);
6738 *exparg.lastp = NULL;
6739 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006740 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 } else {
6742 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6743 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006744 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006745 sp->text = p;
6746 *exparg.lastp = sp;
6747 exparg.lastp = &sp->next;
6748 }
6749 if (ifsfirst.next)
6750 ifsfree();
6751 *exparg.lastp = NULL;
6752 if (exparg.list) {
6753 *arglist->lastp = exparg.list;
6754 arglist->lastp = exparg.lastp;
6755 }
6756}
6757
6758/*
6759 * Expand shell variables and backquotes inside a here document.
6760 */
6761static void
6762expandhere(union node *arg, int fd)
6763{
6764 herefd = fd;
6765 expandarg(arg, (struct arglist *)NULL, 0);
6766 full_write(fd, stackblock(), expdest - (char *)stackblock());
6767}
6768
6769/*
6770 * Returns true if the pattern matches the string.
6771 */
6772static int
6773patmatch(char *pattern, const char *string)
6774{
6775 return pmatch(preglob(pattern, 0, 0), string);
6776}
6777
6778/*
6779 * See if a pattern matches in a case statement.
6780 */
6781static int
6782casematch(union node *pattern, char *val)
6783{
6784 struct stackmark smark;
6785 int result;
6786
6787 setstackmark(&smark);
6788 argbackq = pattern->narg.backquote;
6789 STARTSTACKSTR(expdest);
6790 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006791 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6792 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006793 STACKSTRNUL(expdest);
6794 result = patmatch(stackblock(), val);
6795 popstackmark(&smark);
6796 return result;
6797}
6798
6799
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006800/* ============ find_command */
6801
6802struct builtincmd {
6803 const char *name;
6804 int (*builtin)(int, char **);
6805 /* unsigned flags; */
6806};
6807#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006808/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006809 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006810#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006811#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006812
6813struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006814 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006815 union param {
6816 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006817 /* index >= 0 for commands without path (slashes) */
6818 /* (TODO: what exactly does the value mean? PATH position?) */
6819 /* index == -1 for commands with slashes */
6820 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006821 const struct builtincmd *cmd;
6822 struct funcnode *func;
6823 } u;
6824};
6825/* values of cmdtype */
6826#define CMDUNKNOWN -1 /* no entry in table for command */
6827#define CMDNORMAL 0 /* command is an executable program */
6828#define CMDFUNCTION 1 /* command is a shell function */
6829#define CMDBUILTIN 2 /* command is a shell builtin */
6830
6831/* action to find_command() */
6832#define DO_ERR 0x01 /* prints errors */
6833#define DO_ABS 0x02 /* checks absolute paths */
6834#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6835#define DO_ALTPATH 0x08 /* using alternate path */
6836#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6837
6838static void find_command(char *, struct cmdentry *, int, const char *);
6839
6840
6841/* ============ Hashing commands */
6842
6843/*
6844 * When commands are first encountered, they are entered in a hash table.
6845 * This ensures that a full path search will not have to be done for them
6846 * on each invocation.
6847 *
6848 * We should investigate converting to a linear search, even though that
6849 * would make the command name "hash" a misnomer.
6850 */
6851
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006852struct tblentry {
6853 struct tblentry *next; /* next entry in hash chain */
6854 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006855 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006856 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006857 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006858};
6859
Denis Vlasenko01631112007-12-16 17:20:38 +00006860static struct tblentry **cmdtable;
6861#define INIT_G_cmdtable() do { \
6862 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6863} while (0)
6864
6865static int builtinloc = -1; /* index in path of %builtin, or -1 */
6866
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006867
6868static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006869tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006870{
6871 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006872
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006873#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006874 if (applet_no >= 0) {
6875 if (APPLET_IS_NOEXEC(applet_no))
6876 run_applet_no_and_exit(applet_no, argv);
6877 /* re-exec ourselves with the new arguments */
6878 execve(bb_busybox_exec_path, argv, envp);
6879 /* If they called chroot or otherwise made the binary no longer
6880 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006881 }
6882#endif
6883
6884 repeat:
6885#ifdef SYSV
6886 do {
6887 execve(cmd, argv, envp);
6888 } while (errno == EINTR);
6889#else
6890 execve(cmd, argv, envp);
6891#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006892 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006893 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006894 return;
6895 }
6896 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006897 char **ap;
6898 char **new;
6899
6900 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006901 continue;
6902 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006903 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006904 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006905 ap += 2;
6906 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006907 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006908 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006909 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006910 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006911 goto repeat;
6912 }
6913}
6914
6915/*
6916 * Exec a program. Never returns. If you change this routine, you may
6917 * have to change the find_command routine as well.
6918 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006919static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006920static void
6921shellexec(char **argv, const char *path, int idx)
6922{
6923 char *cmdname;
6924 int e;
6925 char **envp;
6926 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006927#if ENABLE_FEATURE_SH_STANDALONE
6928 int applet_no = -1;
6929#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006930
6931 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006932 envp = listvars(VEXPORT, VUNSET, 0);
6933 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006934#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006935 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006936#endif
6937 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006938 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006939 e = errno;
6940 } else {
6941 e = ENOENT;
6942 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6943 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006944 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006945 if (errno != ENOENT && errno != ENOTDIR)
6946 e = errno;
6947 }
6948 stunalloc(cmdname);
6949 }
6950 }
6951
6952 /* Map to POSIX errors */
6953 switch (e) {
6954 case EACCES:
6955 exerrno = 126;
6956 break;
6957 case ENOENT:
6958 exerrno = 127;
6959 break;
6960 default:
6961 exerrno = 2;
6962 break;
6963 }
6964 exitstatus = exerrno;
6965 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006966 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006967 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6968 /* NOTREACHED */
6969}
6970
6971static void
6972printentry(struct tblentry *cmdp)
6973{
6974 int idx;
6975 const char *path;
6976 char *name;
6977
6978 idx = cmdp->param.index;
6979 path = pathval();
6980 do {
6981 name = padvance(&path, cmdp->cmdname);
6982 stunalloc(name);
6983 } while (--idx >= 0);
6984 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6985}
6986
6987/*
6988 * Clear out command entries. The argument specifies the first entry in
6989 * PATH which has changed.
6990 */
6991static void
6992clearcmdentry(int firstchange)
6993{
6994 struct tblentry **tblp;
6995 struct tblentry **pp;
6996 struct tblentry *cmdp;
6997
6998 INT_OFF;
6999 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7000 pp = tblp;
7001 while ((cmdp = *pp) != NULL) {
7002 if ((cmdp->cmdtype == CMDNORMAL &&
7003 cmdp->param.index >= firstchange)
7004 || (cmdp->cmdtype == CMDBUILTIN &&
7005 builtinloc >= firstchange)
7006 ) {
7007 *pp = cmdp->next;
7008 free(cmdp);
7009 } else {
7010 pp = &cmdp->next;
7011 }
7012 }
7013 }
7014 INT_ON;
7015}
7016
7017/*
7018 * Locate a command in the command hash table. If "add" is nonzero,
7019 * add the command to the table if it is not already present. The
7020 * variable "lastcmdentry" is set to point to the address of the link
7021 * pointing to the entry, so that delete_cmd_entry can delete the
7022 * entry.
7023 *
7024 * Interrupts must be off if called with add != 0.
7025 */
7026static struct tblentry **lastcmdentry;
7027
7028static struct tblentry *
7029cmdlookup(const char *name, int add)
7030{
7031 unsigned int hashval;
7032 const char *p;
7033 struct tblentry *cmdp;
7034 struct tblentry **pp;
7035
7036 p = name;
7037 hashval = (unsigned char)*p << 4;
7038 while (*p)
7039 hashval += (unsigned char)*p++;
7040 hashval &= 0x7FFF;
7041 pp = &cmdtable[hashval % CMDTABLESIZE];
7042 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7043 if (strcmp(cmdp->cmdname, name) == 0)
7044 break;
7045 pp = &cmdp->next;
7046 }
7047 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007048 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7049 + strlen(name)
7050 /* + 1 - already done because
7051 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007052 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007053 cmdp->cmdtype = CMDUNKNOWN;
7054 strcpy(cmdp->cmdname, name);
7055 }
7056 lastcmdentry = pp;
7057 return cmdp;
7058}
7059
7060/*
7061 * Delete the command entry returned on the last lookup.
7062 */
7063static void
7064delete_cmd_entry(void)
7065{
7066 struct tblentry *cmdp;
7067
7068 INT_OFF;
7069 cmdp = *lastcmdentry;
7070 *lastcmdentry = cmdp->next;
7071 if (cmdp->cmdtype == CMDFUNCTION)
7072 freefunc(cmdp->param.func);
7073 free(cmdp);
7074 INT_ON;
7075}
7076
7077/*
7078 * Add a new command entry, replacing any existing command entry for
7079 * the same name - except special builtins.
7080 */
7081static void
7082addcmdentry(char *name, struct cmdentry *entry)
7083{
7084 struct tblentry *cmdp;
7085
7086 cmdp = cmdlookup(name, 1);
7087 if (cmdp->cmdtype == CMDFUNCTION) {
7088 freefunc(cmdp->param.func);
7089 }
7090 cmdp->cmdtype = entry->cmdtype;
7091 cmdp->param = entry->u;
7092 cmdp->rehash = 0;
7093}
7094
7095static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007096hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007097{
7098 struct tblentry **pp;
7099 struct tblentry *cmdp;
7100 int c;
7101 struct cmdentry entry;
7102 char *name;
7103
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007104 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007105 clearcmdentry(0);
7106 return 0;
7107 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007108
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007109 if (*argptr == NULL) {
7110 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7111 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7112 if (cmdp->cmdtype == CMDNORMAL)
7113 printentry(cmdp);
7114 }
7115 }
7116 return 0;
7117 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007118
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007119 c = 0;
7120 while ((name = *argptr) != NULL) {
7121 cmdp = cmdlookup(name, 0);
7122 if (cmdp != NULL
7123 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007124 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7125 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007126 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007127 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007128 find_command(name, &entry, DO_ERR, pathval());
7129 if (entry.cmdtype == CMDUNKNOWN)
7130 c = 1;
7131 argptr++;
7132 }
7133 return c;
7134}
7135
7136/*
7137 * Called when a cd is done. Marks all commands so the next time they
7138 * are executed they will be rehashed.
7139 */
7140static void
7141hashcd(void)
7142{
7143 struct tblentry **pp;
7144 struct tblentry *cmdp;
7145
7146 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7147 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007148 if (cmdp->cmdtype == CMDNORMAL
7149 || (cmdp->cmdtype == CMDBUILTIN
7150 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7151 && builtinloc > 0)
7152 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007153 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007154 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007155 }
7156 }
7157}
7158
7159/*
7160 * Fix command hash table when PATH changed.
7161 * Called before PATH is changed. The argument is the new value of PATH;
7162 * pathval() still returns the old value at this point.
7163 * Called with interrupts off.
7164 */
7165static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007166changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007167{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007168 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007169 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007170 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007171 int idx_bltin;
7172
7173 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007174 firstchange = 9999; /* assume no change */
7175 idx = 0;
7176 idx_bltin = -1;
7177 for (;;) {
7178 if (*old != *new) {
7179 firstchange = idx;
7180 if ((*old == '\0' && *new == ':')
7181 || (*old == ':' && *new == '\0'))
7182 firstchange++;
7183 old = new; /* ignore subsequent differences */
7184 }
7185 if (*new == '\0')
7186 break;
7187 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7188 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007189 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007190 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007191 new++, old++;
7192 }
7193 if (builtinloc < 0 && idx_bltin >= 0)
7194 builtinloc = idx_bltin; /* zap builtins */
7195 if (builtinloc >= 0 && idx_bltin < 0)
7196 firstchange = 0;
7197 clearcmdentry(firstchange);
7198 builtinloc = idx_bltin;
7199}
7200
7201#define TEOF 0
7202#define TNL 1
7203#define TREDIR 2
7204#define TWORD 3
7205#define TSEMI 4
7206#define TBACKGND 5
7207#define TAND 6
7208#define TOR 7
7209#define TPIPE 8
7210#define TLP 9
7211#define TRP 10
7212#define TENDCASE 11
7213#define TENDBQUOTE 12
7214#define TNOT 13
7215#define TCASE 14
7216#define TDO 15
7217#define TDONE 16
7218#define TELIF 17
7219#define TELSE 18
7220#define TESAC 19
7221#define TFI 20
7222#define TFOR 21
7223#define TIF 22
7224#define TIN 23
7225#define TTHEN 24
7226#define TUNTIL 25
7227#define TWHILE 26
7228#define TBEGIN 27
7229#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007230typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007231
7232/* first char is indicating which tokens mark the end of a list */
7233static const char *const tokname_array[] = {
7234 "\1end of file",
7235 "\0newline",
7236 "\0redirection",
7237 "\0word",
7238 "\0;",
7239 "\0&",
7240 "\0&&",
7241 "\0||",
7242 "\0|",
7243 "\0(",
7244 "\1)",
7245 "\1;;",
7246 "\1`",
7247#define KWDOFFSET 13
7248 /* the following are keywords */
7249 "\0!",
7250 "\0case",
7251 "\1do",
7252 "\1done",
7253 "\1elif",
7254 "\1else",
7255 "\1esac",
7256 "\1fi",
7257 "\0for",
7258 "\0if",
7259 "\0in",
7260 "\1then",
7261 "\0until",
7262 "\0while",
7263 "\0{",
7264 "\1}",
7265};
7266
7267static const char *
7268tokname(int tok)
7269{
7270 static char buf[16];
7271
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007272//try this:
7273//if (tok < TSEMI) return tokname_array[tok] + 1;
7274//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7275//return buf;
7276
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007277 if (tok >= TSEMI)
7278 buf[0] = '"';
7279 sprintf(buf + (tok >= TSEMI), "%s%c",
7280 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7281 return buf;
7282}
7283
7284/* Wrapper around strcmp for qsort/bsearch/... */
7285static int
7286pstrcmp(const void *a, const void *b)
7287{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007288 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007289}
7290
7291static const char *const *
7292findkwd(const char *s)
7293{
7294 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007295 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7296 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007297}
7298
7299/*
7300 * Locate and print what a word is...
7301 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302static int
7303describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304{
7305 struct cmdentry entry;
7306 struct tblentry *cmdp;
7307#if ENABLE_ASH_ALIAS
7308 const struct alias *ap;
7309#endif
7310 const char *path = pathval();
7311
7312 if (describe_command_verbose) {
7313 out1str(command);
7314 }
7315
7316 /* First look at the keywords */
7317 if (findkwd(command)) {
7318 out1str(describe_command_verbose ? " is a shell keyword" : command);
7319 goto out;
7320 }
7321
7322#if ENABLE_ASH_ALIAS
7323 /* Then look at the aliases */
7324 ap = lookupalias(command, 0);
7325 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007326 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007327 out1str("alias ");
7328 printalias(ap);
7329 return 0;
7330 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007331 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007332 goto out;
7333 }
7334#endif
7335 /* Then check if it is a tracked alias */
7336 cmdp = cmdlookup(command, 0);
7337 if (cmdp != NULL) {
7338 entry.cmdtype = cmdp->cmdtype;
7339 entry.u = cmdp->param;
7340 } else {
7341 /* Finally use brute force */
7342 find_command(command, &entry, DO_ABS, path);
7343 }
7344
7345 switch (entry.cmdtype) {
7346 case CMDNORMAL: {
7347 int j = entry.u.index;
7348 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007349 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350 p = command;
7351 } else {
7352 do {
7353 p = padvance(&path, command);
7354 stunalloc(p);
7355 } while (--j >= 0);
7356 }
7357 if (describe_command_verbose) {
7358 out1fmt(" is%s %s",
7359 (cmdp ? " a tracked alias for" : nullstr), p
7360 );
7361 } else {
7362 out1str(p);
7363 }
7364 break;
7365 }
7366
7367 case CMDFUNCTION:
7368 if (describe_command_verbose) {
7369 out1str(" is a shell function");
7370 } else {
7371 out1str(command);
7372 }
7373 break;
7374
7375 case CMDBUILTIN:
7376 if (describe_command_verbose) {
7377 out1fmt(" is a %sshell builtin",
7378 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7379 "special " : nullstr
7380 );
7381 } else {
7382 out1str(command);
7383 }
7384 break;
7385
7386 default:
7387 if (describe_command_verbose) {
7388 out1str(": not found\n");
7389 }
7390 return 127;
7391 }
7392 out:
7393 outstr("\n", stdout);
7394 return 0;
7395}
7396
7397static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007398typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007399{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007400 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007402 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007403
Denis Vlasenko46846e22007-05-20 13:08:31 +00007404 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007405 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007406 i++;
7407 verbose = 0;
7408 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007409 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007410 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411 }
7412 return err;
7413}
7414
7415#if ENABLE_ASH_CMDCMD
7416static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007417commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418{
7419 int c;
7420 enum {
7421 VERIFY_BRIEF = 1,
7422 VERIFY_VERBOSE = 2,
7423 } verify = 0;
7424
7425 while ((c = nextopt("pvV")) != '\0')
7426 if (c == 'V')
7427 verify |= VERIFY_VERBOSE;
7428 else if (c == 'v')
7429 verify |= VERIFY_BRIEF;
7430#if DEBUG
7431 else if (c != 'p')
7432 abort();
7433#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007434 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7435 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007437 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007438
7439 return 0;
7440}
7441#endif
7442
7443
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007444/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007445
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007446static int funcblocksize; /* size of structures in function */
7447static int funcstringsize; /* size of strings in node */
7448static void *funcblock; /* block to allocate function from */
7449static char *funcstring; /* block to allocate strings from */
7450
Eric Andersencb57d552001-06-28 07:25:16 +00007451/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007452#define EV_EXIT 01 /* exit after evaluating tree */
7453#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7454#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007455
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007456static const short nodesize[26] = {
7457 SHELL_ALIGN(sizeof(struct ncmd)),
7458 SHELL_ALIGN(sizeof(struct npipe)),
7459 SHELL_ALIGN(sizeof(struct nredir)),
7460 SHELL_ALIGN(sizeof(struct nredir)),
7461 SHELL_ALIGN(sizeof(struct nredir)),
7462 SHELL_ALIGN(sizeof(struct nbinary)),
7463 SHELL_ALIGN(sizeof(struct nbinary)),
7464 SHELL_ALIGN(sizeof(struct nbinary)),
7465 SHELL_ALIGN(sizeof(struct nif)),
7466 SHELL_ALIGN(sizeof(struct nbinary)),
7467 SHELL_ALIGN(sizeof(struct nbinary)),
7468 SHELL_ALIGN(sizeof(struct nfor)),
7469 SHELL_ALIGN(sizeof(struct ncase)),
7470 SHELL_ALIGN(sizeof(struct nclist)),
7471 SHELL_ALIGN(sizeof(struct narg)),
7472 SHELL_ALIGN(sizeof(struct narg)),
7473 SHELL_ALIGN(sizeof(struct nfile)),
7474 SHELL_ALIGN(sizeof(struct nfile)),
7475 SHELL_ALIGN(sizeof(struct nfile)),
7476 SHELL_ALIGN(sizeof(struct nfile)),
7477 SHELL_ALIGN(sizeof(struct nfile)),
7478 SHELL_ALIGN(sizeof(struct ndup)),
7479 SHELL_ALIGN(sizeof(struct ndup)),
7480 SHELL_ALIGN(sizeof(struct nhere)),
7481 SHELL_ALIGN(sizeof(struct nhere)),
7482 SHELL_ALIGN(sizeof(struct nnot)),
7483};
7484
7485static void calcsize(union node *n);
7486
7487static void
7488sizenodelist(struct nodelist *lp)
7489{
7490 while (lp) {
7491 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7492 calcsize(lp->n);
7493 lp = lp->next;
7494 }
7495}
7496
7497static void
7498calcsize(union node *n)
7499{
7500 if (n == NULL)
7501 return;
7502 funcblocksize += nodesize[n->type];
7503 switch (n->type) {
7504 case NCMD:
7505 calcsize(n->ncmd.redirect);
7506 calcsize(n->ncmd.args);
7507 calcsize(n->ncmd.assign);
7508 break;
7509 case NPIPE:
7510 sizenodelist(n->npipe.cmdlist);
7511 break;
7512 case NREDIR:
7513 case NBACKGND:
7514 case NSUBSHELL:
7515 calcsize(n->nredir.redirect);
7516 calcsize(n->nredir.n);
7517 break;
7518 case NAND:
7519 case NOR:
7520 case NSEMI:
7521 case NWHILE:
7522 case NUNTIL:
7523 calcsize(n->nbinary.ch2);
7524 calcsize(n->nbinary.ch1);
7525 break;
7526 case NIF:
7527 calcsize(n->nif.elsepart);
7528 calcsize(n->nif.ifpart);
7529 calcsize(n->nif.test);
7530 break;
7531 case NFOR:
7532 funcstringsize += strlen(n->nfor.var) + 1;
7533 calcsize(n->nfor.body);
7534 calcsize(n->nfor.args);
7535 break;
7536 case NCASE:
7537 calcsize(n->ncase.cases);
7538 calcsize(n->ncase.expr);
7539 break;
7540 case NCLIST:
7541 calcsize(n->nclist.body);
7542 calcsize(n->nclist.pattern);
7543 calcsize(n->nclist.next);
7544 break;
7545 case NDEFUN:
7546 case NARG:
7547 sizenodelist(n->narg.backquote);
7548 funcstringsize += strlen(n->narg.text) + 1;
7549 calcsize(n->narg.next);
7550 break;
7551 case NTO:
7552 case NCLOBBER:
7553 case NFROM:
7554 case NFROMTO:
7555 case NAPPEND:
7556 calcsize(n->nfile.fname);
7557 calcsize(n->nfile.next);
7558 break;
7559 case NTOFD:
7560 case NFROMFD:
7561 calcsize(n->ndup.vname);
7562 calcsize(n->ndup.next);
7563 break;
7564 case NHERE:
7565 case NXHERE:
7566 calcsize(n->nhere.doc);
7567 calcsize(n->nhere.next);
7568 break;
7569 case NNOT:
7570 calcsize(n->nnot.com);
7571 break;
7572 };
7573}
7574
7575static char *
7576nodeckstrdup(char *s)
7577{
7578 char *rtn = funcstring;
7579
7580 strcpy(funcstring, s);
7581 funcstring += strlen(s) + 1;
7582 return rtn;
7583}
7584
7585static union node *copynode(union node *);
7586
7587static struct nodelist *
7588copynodelist(struct nodelist *lp)
7589{
7590 struct nodelist *start;
7591 struct nodelist **lpp;
7592
7593 lpp = &start;
7594 while (lp) {
7595 *lpp = funcblock;
7596 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7597 (*lpp)->n = copynode(lp->n);
7598 lp = lp->next;
7599 lpp = &(*lpp)->next;
7600 }
7601 *lpp = NULL;
7602 return start;
7603}
7604
7605static union node *
7606copynode(union node *n)
7607{
7608 union node *new;
7609
7610 if (n == NULL)
7611 return NULL;
7612 new = funcblock;
7613 funcblock = (char *) funcblock + nodesize[n->type];
7614
7615 switch (n->type) {
7616 case NCMD:
7617 new->ncmd.redirect = copynode(n->ncmd.redirect);
7618 new->ncmd.args = copynode(n->ncmd.args);
7619 new->ncmd.assign = copynode(n->ncmd.assign);
7620 break;
7621 case NPIPE:
7622 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7623 new->npipe.backgnd = n->npipe.backgnd;
7624 break;
7625 case NREDIR:
7626 case NBACKGND:
7627 case NSUBSHELL:
7628 new->nredir.redirect = copynode(n->nredir.redirect);
7629 new->nredir.n = copynode(n->nredir.n);
7630 break;
7631 case NAND:
7632 case NOR:
7633 case NSEMI:
7634 case NWHILE:
7635 case NUNTIL:
7636 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7637 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7638 break;
7639 case NIF:
7640 new->nif.elsepart = copynode(n->nif.elsepart);
7641 new->nif.ifpart = copynode(n->nif.ifpart);
7642 new->nif.test = copynode(n->nif.test);
7643 break;
7644 case NFOR:
7645 new->nfor.var = nodeckstrdup(n->nfor.var);
7646 new->nfor.body = copynode(n->nfor.body);
7647 new->nfor.args = copynode(n->nfor.args);
7648 break;
7649 case NCASE:
7650 new->ncase.cases = copynode(n->ncase.cases);
7651 new->ncase.expr = copynode(n->ncase.expr);
7652 break;
7653 case NCLIST:
7654 new->nclist.body = copynode(n->nclist.body);
7655 new->nclist.pattern = copynode(n->nclist.pattern);
7656 new->nclist.next = copynode(n->nclist.next);
7657 break;
7658 case NDEFUN:
7659 case NARG:
7660 new->narg.backquote = copynodelist(n->narg.backquote);
7661 new->narg.text = nodeckstrdup(n->narg.text);
7662 new->narg.next = copynode(n->narg.next);
7663 break;
7664 case NTO:
7665 case NCLOBBER:
7666 case NFROM:
7667 case NFROMTO:
7668 case NAPPEND:
7669 new->nfile.fname = copynode(n->nfile.fname);
7670 new->nfile.fd = n->nfile.fd;
7671 new->nfile.next = copynode(n->nfile.next);
7672 break;
7673 case NTOFD:
7674 case NFROMFD:
7675 new->ndup.vname = copynode(n->ndup.vname);
7676 new->ndup.dupfd = n->ndup.dupfd;
7677 new->ndup.fd = n->ndup.fd;
7678 new->ndup.next = copynode(n->ndup.next);
7679 break;
7680 case NHERE:
7681 case NXHERE:
7682 new->nhere.doc = copynode(n->nhere.doc);
7683 new->nhere.fd = n->nhere.fd;
7684 new->nhere.next = copynode(n->nhere.next);
7685 break;
7686 case NNOT:
7687 new->nnot.com = copynode(n->nnot.com);
7688 break;
7689 };
7690 new->type = n->type;
7691 return new;
7692}
7693
7694/*
7695 * Make a copy of a parse tree.
7696 */
7697static struct funcnode *
7698copyfunc(union node *n)
7699{
7700 struct funcnode *f;
7701 size_t blocksize;
7702
7703 funcblocksize = offsetof(struct funcnode, n);
7704 funcstringsize = 0;
7705 calcsize(n);
7706 blocksize = funcblocksize;
7707 f = ckmalloc(blocksize + funcstringsize);
7708 funcblock = (char *) f + offsetof(struct funcnode, n);
7709 funcstring = (char *) f + blocksize;
7710 copynode(n);
7711 f->count = 0;
7712 return f;
7713}
7714
7715/*
7716 * Define a shell function.
7717 */
7718static void
7719defun(char *name, union node *func)
7720{
7721 struct cmdentry entry;
7722
7723 INT_OFF;
7724 entry.cmdtype = CMDFUNCTION;
7725 entry.u.func = copyfunc(func);
7726 addcmdentry(name, &entry);
7727 INT_ON;
7728}
7729
7730static int evalskip; /* set if we are skipping commands */
7731/* reasons for skipping commands (see comment on breakcmd routine) */
7732#define SKIPBREAK (1 << 0)
7733#define SKIPCONT (1 << 1)
7734#define SKIPFUNC (1 << 2)
7735#define SKIPFILE (1 << 3)
7736#define SKIPEVAL (1 << 4)
7737static int skipcount; /* number of levels to skip */
7738static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007739static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007740
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007741/* forward decl way out to parsing code - dotrap needs it */
7742static int evalstring(char *s, int mask);
7743
7744/*
7745 * Called to execute a trap. Perhaps we should avoid entering new trap
7746 * handlers while we are executing a trap handler.
7747 */
7748static int
7749dotrap(void)
7750{
7751 char *p;
7752 char *q;
7753 int i;
7754 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007755 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007756
7757 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007758 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007759 xbarrier();
7760
7761 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7762 if (!*q)
7763 continue;
7764 *q = '\0';
7765
7766 p = trap[i + 1];
7767 if (!p)
7768 continue;
7769 skip = evalstring(p, SKIPEVAL);
7770 exitstatus = savestatus;
7771 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007772 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007773 }
7774
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007775 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007776}
7777
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007778/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007779static void evalloop(union node *, int);
7780static void evalfor(union node *, int);
7781static void evalcase(union node *, int);
7782static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007783static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007784static void evalpipe(union node *, int);
7785static void evalcommand(union node *, int);
7786static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007787static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007788
Eric Andersen62483552001-07-10 06:09:16 +00007789/*
Eric Andersenc470f442003-07-28 09:56:35 +00007790 * Evaluate a parse tree. The value is left in the global variable
7791 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007792 */
Eric Andersenc470f442003-07-28 09:56:35 +00007793static void
7794evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007795{
Eric Andersenc470f442003-07-28 09:56:35 +00007796 int checkexit = 0;
7797 void (*evalfn)(union node *, int);
7798 unsigned isor;
7799 int status;
7800 if (n == NULL) {
7801 TRACE(("evaltree(NULL) called\n"));
7802 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007803 }
Eric Andersenc470f442003-07-28 09:56:35 +00007804 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007805 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007806 switch (n->type) {
7807 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007808#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007809 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007810 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007811 break;
7812#endif
7813 case NNOT:
7814 evaltree(n->nnot.com, EV_TESTED);
7815 status = !exitstatus;
7816 goto setstatus;
7817 case NREDIR:
7818 expredir(n->nredir.redirect);
7819 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7820 if (!status) {
7821 evaltree(n->nredir.n, flags & EV_TESTED);
7822 status = exitstatus;
7823 }
7824 popredir(0);
7825 goto setstatus;
7826 case NCMD:
7827 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007828 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007829 if (eflag && !(flags & EV_TESTED))
7830 checkexit = ~0;
7831 goto calleval;
7832 case NFOR:
7833 evalfn = evalfor;
7834 goto calleval;
7835 case NWHILE:
7836 case NUNTIL:
7837 evalfn = evalloop;
7838 goto calleval;
7839 case NSUBSHELL:
7840 case NBACKGND:
7841 evalfn = evalsubshell;
7842 goto calleval;
7843 case NPIPE:
7844 evalfn = evalpipe;
7845 goto checkexit;
7846 case NCASE:
7847 evalfn = evalcase;
7848 goto calleval;
7849 case NAND:
7850 case NOR:
7851 case NSEMI:
7852#if NAND + 1 != NOR
7853#error NAND + 1 != NOR
7854#endif
7855#if NOR + 1 != NSEMI
7856#error NOR + 1 != NSEMI
7857#endif
7858 isor = n->type - NAND;
7859 evaltree(
7860 n->nbinary.ch1,
7861 (flags | ((isor >> 1) - 1)) & EV_TESTED
7862 );
7863 if (!exitstatus == isor)
7864 break;
7865 if (!evalskip) {
7866 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007867 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007868 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007869 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007870 evalfn(n, flags);
7871 break;
7872 }
7873 break;
7874 case NIF:
7875 evaltree(n->nif.test, EV_TESTED);
7876 if (evalskip)
7877 break;
7878 if (exitstatus == 0) {
7879 n = n->nif.ifpart;
7880 goto evaln;
7881 } else if (n->nif.elsepart) {
7882 n = n->nif.elsepart;
7883 goto evaln;
7884 }
7885 goto success;
7886 case NDEFUN:
7887 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007888 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007889 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007890 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007891 exitstatus = status;
7892 break;
7893 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007894 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007895 if ((checkexit & exitstatus))
7896 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007897 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007898 goto exexit;
7899
7900 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007901 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007902 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007903 }
Eric Andersen62483552001-07-10 06:09:16 +00007904}
7905
Eric Andersenc470f442003-07-28 09:56:35 +00007906#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7907static
7908#endif
7909void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7910
Eric Andersenc470f442003-07-28 09:56:35 +00007911static void
7912evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007913{
7914 int status;
7915
7916 loopnest++;
7917 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007918 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007919 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007920 int i;
7921
Eric Andersencb57d552001-06-28 07:25:16 +00007922 evaltree(n->nbinary.ch1, EV_TESTED);
7923 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007924 skipping:
7925 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007926 evalskip = 0;
7927 continue;
7928 }
7929 if (evalskip == SKIPBREAK && --skipcount <= 0)
7930 evalskip = 0;
7931 break;
7932 }
Eric Andersenc470f442003-07-28 09:56:35 +00007933 i = exitstatus;
7934 if (n->type != NWHILE)
7935 i = !i;
7936 if (i != 0)
7937 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007938 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007939 status = exitstatus;
7940 if (evalskip)
7941 goto skipping;
7942 }
7943 loopnest--;
7944 exitstatus = status;
7945}
7946
Eric Andersenc470f442003-07-28 09:56:35 +00007947static void
7948evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007949{
7950 struct arglist arglist;
7951 union node *argp;
7952 struct strlist *sp;
7953 struct stackmark smark;
7954
7955 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007956 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007957 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007958 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007959 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007960 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007961 if (evalskip)
7962 goto out;
7963 }
7964 *arglist.lastp = NULL;
7965
7966 exitstatus = 0;
7967 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007968 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007969 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007970 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007971 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007972 if (evalskip) {
7973 if (evalskip == SKIPCONT && --skipcount <= 0) {
7974 evalskip = 0;
7975 continue;
7976 }
7977 if (evalskip == SKIPBREAK && --skipcount <= 0)
7978 evalskip = 0;
7979 break;
7980 }
7981 }
7982 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007983 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007984 popstackmark(&smark);
7985}
7986
Eric Andersenc470f442003-07-28 09:56:35 +00007987static void
7988evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007989{
7990 union node *cp;
7991 union node *patp;
7992 struct arglist arglist;
7993 struct stackmark smark;
7994
7995 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007996 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007997 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007998 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007999 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008000 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8001 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008002 if (casematch(patp, arglist.list->text)) {
8003 if (evalskip == 0) {
8004 evaltree(cp->nclist.body, flags);
8005 }
8006 goto out;
8007 }
8008 }
8009 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008010 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008011 popstackmark(&smark);
8012}
8013
Eric Andersenc470f442003-07-28 09:56:35 +00008014/*
8015 * Kick off a subshell to evaluate a tree.
8016 */
Eric Andersenc470f442003-07-28 09:56:35 +00008017static void
8018evalsubshell(union node *n, int flags)
8019{
8020 struct job *jp;
8021 int backgnd = (n->type == NBACKGND);
8022 int status;
8023
8024 expredir(n->nredir.redirect);
8025 if (!backgnd && flags & EV_EXIT && !trap[0])
8026 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008027 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008028 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008029 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008030 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008031 flags |= EV_EXIT;
8032 if (backgnd)
8033 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008034 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008035 redirect(n->nredir.redirect, 0);
8036 evaltreenr(n->nredir.n, flags);
8037 /* never returns */
8038 }
8039 status = 0;
8040 if (! backgnd)
8041 status = waitforjob(jp);
8042 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008043 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008044}
8045
Eric Andersenc470f442003-07-28 09:56:35 +00008046/*
8047 * Compute the names of the files in a redirection list.
8048 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008049static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008050static void
8051expredir(union node *n)
8052{
8053 union node *redir;
8054
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008055 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008056 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008057
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008058 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008059 fn.lastp = &fn.list;
8060 switch (redir->type) {
8061 case NFROMTO:
8062 case NFROM:
8063 case NTO:
8064 case NCLOBBER:
8065 case NAPPEND:
8066 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8067 redir->nfile.expfname = fn.list->text;
8068 break;
8069 case NFROMFD:
8070 case NTOFD:
8071 if (redir->ndup.vname) {
8072 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008073 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008074 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008075 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008076 }
8077 break;
8078 }
8079 }
8080}
8081
Eric Andersencb57d552001-06-28 07:25:16 +00008082/*
Eric Andersencb57d552001-06-28 07:25:16 +00008083 * Evaluate a pipeline. All the processes in the pipeline are children
8084 * of the process creating the pipeline. (This differs from some versions
8085 * of the shell, which make the last process in a pipeline the parent
8086 * of all the rest.)
8087 */
Eric Andersenc470f442003-07-28 09:56:35 +00008088static void
8089evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008090{
8091 struct job *jp;
8092 struct nodelist *lp;
8093 int pipelen;
8094 int prevfd;
8095 int pip[2];
8096
Eric Andersenc470f442003-07-28 09:56:35 +00008097 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008098 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008099 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008100 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008101 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008102 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008103 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008104 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008105 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008106 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008107 pip[1] = -1;
8108 if (lp->next) {
8109 if (pipe(pip) < 0) {
8110 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008111 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008112 }
8113 }
8114 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008115 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008116 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008117 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008118 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008119 if (prevfd > 0) {
8120 dup2(prevfd, 0);
8121 close(prevfd);
8122 }
8123 if (pip[1] > 1) {
8124 dup2(pip[1], 1);
8125 close(pip[1]);
8126 }
Eric Andersenc470f442003-07-28 09:56:35 +00008127 evaltreenr(lp->n, flags);
8128 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008129 }
8130 if (prevfd >= 0)
8131 close(prevfd);
8132 prevfd = pip[0];
8133 close(pip[1]);
8134 }
Eric Andersencb57d552001-06-28 07:25:16 +00008135 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008136 exitstatus = waitforjob(jp);
8137 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008138 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008139 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008140}
8141
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008142/*
8143 * Controls whether the shell is interactive or not.
8144 */
8145static void
8146setinteractive(int on)
8147{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008148 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008149
8150 if (++on == is_interactive)
8151 return;
8152 is_interactive = on;
8153 setsignal(SIGINT);
8154 setsignal(SIGQUIT);
8155 setsignal(SIGTERM);
8156#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8157 if (is_interactive > 1) {
8158 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008159 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008160
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008161 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008162 out1fmt(
8163 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008164 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008165 "Enter 'help' for a list of built-in commands."
8166 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008167 bb_banner);
8168 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008169 }
8170 }
8171#endif
8172}
8173
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008174static void
8175optschanged(void)
8176{
8177#if DEBUG
8178 opentrace();
8179#endif
8180 setinteractive(iflag);
8181 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008182#if ENABLE_FEATURE_EDITING_VI
8183 if (viflag)
8184 line_input_state->flags |= VI_MODE;
8185 else
8186 line_input_state->flags &= ~VI_MODE;
8187#else
8188 viflag = 0; /* forcibly keep the option off */
8189#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008190}
8191
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192static struct localvar *localvars;
8193
8194/*
8195 * Called after a function returns.
8196 * Interrupts must be off.
8197 */
8198static void
8199poplocalvars(void)
8200{
8201 struct localvar *lvp;
8202 struct var *vp;
8203
8204 while ((lvp = localvars) != NULL) {
8205 localvars = lvp->next;
8206 vp = lvp->vp;
8207 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8208 if (vp == NULL) { /* $- saved */
8209 memcpy(optlist, lvp->text, sizeof(optlist));
8210 free((char*)lvp->text);
8211 optschanged();
8212 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8213 unsetvar(vp->text);
8214 } else {
8215 if (vp->func)
8216 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8217 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8218 free((char*)vp->text);
8219 vp->flags = lvp->flags;
8220 vp->text = lvp->text;
8221 }
8222 free(lvp);
8223 }
8224}
8225
8226static int
8227evalfun(struct funcnode *func, int argc, char **argv, int flags)
8228{
8229 volatile struct shparam saveparam;
8230 struct localvar *volatile savelocalvars;
8231 struct jmploc *volatile savehandler;
8232 struct jmploc jmploc;
8233 int e;
8234
8235 saveparam = shellparam;
8236 savelocalvars = localvars;
8237 e = setjmp(jmploc.loc);
8238 if (e) {
8239 goto funcdone;
8240 }
8241 INT_OFF;
8242 savehandler = exception_handler;
8243 exception_handler = &jmploc;
8244 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008245 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008246 func->count++;
8247 funcnest++;
8248 INT_ON;
8249 shellparam.nparam = argc - 1;
8250 shellparam.p = argv + 1;
8251#if ENABLE_ASH_GETOPTS
8252 shellparam.optind = 1;
8253 shellparam.optoff = -1;
8254#endif
8255 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008256 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008257 INT_OFF;
8258 funcnest--;
8259 freefunc(func);
8260 poplocalvars();
8261 localvars = savelocalvars;
8262 freeparam(&shellparam);
8263 shellparam = saveparam;
8264 exception_handler = savehandler;
8265 INT_ON;
8266 evalskip &= ~SKIPFUNC;
8267 return e;
8268}
8269
Denis Vlasenko131ae172007-02-18 13:00:19 +00008270#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008271static char **
8272parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008273{
8274 char *cp, c;
8275
8276 for (;;) {
8277 cp = *++argv;
8278 if (!cp)
8279 return 0;
8280 if (*cp++ != '-')
8281 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008282 c = *cp++;
8283 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008284 break;
8285 if (c == '-' && !*cp) {
8286 argv++;
8287 break;
8288 }
8289 do {
8290 switch (c) {
8291 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008292 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008293 break;
8294 default:
8295 /* run 'typecmd' for other options */
8296 return 0;
8297 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008298 c = *cp++;
8299 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008300 }
8301 return argv;
8302}
8303#endif
8304
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008305/*
8306 * Make a variable a local variable. When a variable is made local, it's
8307 * value and flags are saved in a localvar structure. The saved values
8308 * will be restored when the shell function returns. We handle the name
8309 * "-" as a special case.
8310 */
8311static void
8312mklocal(char *name)
8313{
8314 struct localvar *lvp;
8315 struct var **vpp;
8316 struct var *vp;
8317
8318 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008319 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008320 if (LONE_DASH(name)) {
8321 char *p;
8322 p = ckmalloc(sizeof(optlist));
8323 lvp->text = memcpy(p, optlist, sizeof(optlist));
8324 vp = NULL;
8325 } else {
8326 char *eq;
8327
8328 vpp = hashvar(name);
8329 vp = *findvar(vpp, name);
8330 eq = strchr(name, '=');
8331 if (vp == NULL) {
8332 if (eq)
8333 setvareq(name, VSTRFIXED);
8334 else
8335 setvar(name, NULL, VSTRFIXED);
8336 vp = *vpp; /* the new variable */
8337 lvp->flags = VUNSET;
8338 } else {
8339 lvp->text = vp->text;
8340 lvp->flags = vp->flags;
8341 vp->flags |= VSTRFIXED|VTEXTFIXED;
8342 if (eq)
8343 setvareq(name, 0);
8344 }
8345 }
8346 lvp->vp = vp;
8347 lvp->next = localvars;
8348 localvars = lvp;
8349 INT_ON;
8350}
8351
8352/*
8353 * The "local" command.
8354 */
8355static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008356localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008357{
8358 char *name;
8359
8360 argv = argptr;
8361 while ((name = *argv++) != NULL) {
8362 mklocal(name);
8363 }
8364 return 0;
8365}
8366
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008367static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008368falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008369{
8370 return 1;
8371}
8372
8373static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008374truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008375{
8376 return 0;
8377}
8378
8379static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008380execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008381{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008382 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008383 iflag = 0; /* exit on error */
8384 mflag = 0;
8385 optschanged();
8386 shellexec(argv + 1, pathval(), 0);
8387 }
8388 return 0;
8389}
8390
8391/*
8392 * The return command.
8393 */
8394static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008395returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008396{
8397 /*
8398 * If called outside a function, do what ksh does;
8399 * skip the rest of the file.
8400 */
8401 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8402 return argv[1] ? number(argv[1]) : exitstatus;
8403}
8404
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008405/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008406static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008407static int dotcmd(int, char **);
8408static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008409static int exitcmd(int, char **);
8410static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008411#if ENABLE_ASH_GETOPTS
8412static int getoptscmd(int, char **);
8413#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008414#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008415static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008416#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008417#if ENABLE_ASH_MATH_SUPPORT
8418static int letcmd(int, char **);
8419#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008420static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008421static int setcmd(int, char **);
8422static int shiftcmd(int, char **);
8423static int timescmd(int, char **);
8424static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008425static int umaskcmd(int, char **);
8426static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008427static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008428
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008429#define BUILTIN_NOSPEC "0"
8430#define BUILTIN_SPECIAL "1"
8431#define BUILTIN_REGULAR "2"
8432#define BUILTIN_SPEC_REG "3"
8433#define BUILTIN_ASSIGN "4"
8434#define BUILTIN_SPEC_ASSG "5"
8435#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008436#define BUILTIN_SPEC_REG_ASSG "7"
8437
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008438/* We do not handle [[ expr ]] bashism bash-compatibly,
8439 * we make it a synonym of [ expr ].
8440 * Basically, word splitting and pathname expansion should NOT be performed
8441 * Examples:
8442 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8443 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8444 * Additional operators:
8445 * || and && should work as -o and -a
8446 * =~ regexp match
8447 * Apart from the above, [[ expr ]] should work as [ expr ]
8448 */
8449
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008450#define echocmd echo_main
8451#define printfcmd printf_main
8452#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008453
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008454/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008455static const struct builtincmd builtintab[] = {
8456 { BUILTIN_SPEC_REG ".", dotcmd },
8457 { BUILTIN_SPEC_REG ":", truecmd },
8458#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008459 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008460#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008461 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008462#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008463#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008464#if ENABLE_ASH_ALIAS
8465 { BUILTIN_REG_ASSG "alias", aliascmd },
8466#endif
8467#if JOBS
8468 { BUILTIN_REGULAR "bg", fg_bgcmd },
8469#endif
8470 { BUILTIN_SPEC_REG "break", breakcmd },
8471 { BUILTIN_REGULAR "cd", cdcmd },
8472 { BUILTIN_NOSPEC "chdir", cdcmd },
8473#if ENABLE_ASH_CMDCMD
8474 { BUILTIN_REGULAR "command", commandcmd },
8475#endif
8476 { BUILTIN_SPEC_REG "continue", breakcmd },
8477#if ENABLE_ASH_BUILTIN_ECHO
8478 { BUILTIN_REGULAR "echo", echocmd },
8479#endif
8480 { BUILTIN_SPEC_REG "eval", evalcmd },
8481 { BUILTIN_SPEC_REG "exec", execcmd },
8482 { BUILTIN_SPEC_REG "exit", exitcmd },
8483 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8484 { BUILTIN_REGULAR "false", falsecmd },
8485#if JOBS
8486 { BUILTIN_REGULAR "fg", fg_bgcmd },
8487#endif
8488#if ENABLE_ASH_GETOPTS
8489 { BUILTIN_REGULAR "getopts", getoptscmd },
8490#endif
8491 { BUILTIN_NOSPEC "hash", hashcmd },
8492#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8493 { BUILTIN_NOSPEC "help", helpcmd },
8494#endif
8495#if JOBS
8496 { BUILTIN_REGULAR "jobs", jobscmd },
8497 { BUILTIN_REGULAR "kill", killcmd },
8498#endif
8499#if ENABLE_ASH_MATH_SUPPORT
8500 { BUILTIN_NOSPEC "let", letcmd },
8501#endif
8502 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008503#if ENABLE_ASH_BUILTIN_PRINTF
8504 { BUILTIN_REGULAR "printf", printfcmd },
8505#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008506 { BUILTIN_NOSPEC "pwd", pwdcmd },
8507 { BUILTIN_REGULAR "read", readcmd },
8508 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8509 { BUILTIN_SPEC_REG "return", returncmd },
8510 { BUILTIN_SPEC_REG "set", setcmd },
8511 { BUILTIN_SPEC_REG "shift", shiftcmd },
8512 { BUILTIN_SPEC_REG "source", dotcmd },
8513#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008514 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008515#endif
8516 { BUILTIN_SPEC_REG "times", timescmd },
8517 { BUILTIN_SPEC_REG "trap", trapcmd },
8518 { BUILTIN_REGULAR "true", truecmd },
8519 { BUILTIN_NOSPEC "type", typecmd },
8520 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8521 { BUILTIN_REGULAR "umask", umaskcmd },
8522#if ENABLE_ASH_ALIAS
8523 { BUILTIN_REGULAR "unalias", unaliascmd },
8524#endif
8525 { BUILTIN_SPEC_REG "unset", unsetcmd },
8526 { BUILTIN_REGULAR "wait", waitcmd },
8527};
8528
Denis Vlasenko80591b02008-03-25 07:49:43 +00008529/* Should match the above table! */
8530#define COMMANDCMD (builtintab + \
8531 2 + \
8532 1 * ENABLE_ASH_BUILTIN_TEST + \
8533 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8534 1 * ENABLE_ASH_ALIAS + \
8535 1 * ENABLE_ASH_JOB_CONTROL + \
8536 3)
8537#define EXECCMD (builtintab + \
8538 2 + \
8539 1 * ENABLE_ASH_BUILTIN_TEST + \
8540 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8541 1 * ENABLE_ASH_ALIAS + \
8542 1 * ENABLE_ASH_JOB_CONTROL + \
8543 3 + \
8544 1 * ENABLE_ASH_CMDCMD + \
8545 1 + \
8546 ENABLE_ASH_BUILTIN_ECHO + \
8547 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008548
8549/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008550 * Search the table of builtin commands.
8551 */
8552static struct builtincmd *
8553find_builtin(const char *name)
8554{
8555 struct builtincmd *bp;
8556
8557 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008558 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008559 pstrcmp
8560 );
8561 return bp;
8562}
8563
8564/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565 * Execute a simple command.
8566 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008567static int
8568isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008569{
8570 const char *q = endofname(p);
8571 if (p == q)
8572 return 0;
8573 return *q == '=';
8574}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008575static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008576bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008577{
8578 /* Preserve exitstatus of a previous possible redirection
8579 * as POSIX mandates */
8580 return back_exitstatus;
8581}
Eric Andersenc470f442003-07-28 09:56:35 +00008582static void
8583evalcommand(union node *cmd, int flags)
8584{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008585 static const struct builtincmd null_bltin = {
8586 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008587 };
Eric Andersenc470f442003-07-28 09:56:35 +00008588 struct stackmark smark;
8589 union node *argp;
8590 struct arglist arglist;
8591 struct arglist varlist;
8592 char **argv;
8593 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008594 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008595 struct cmdentry cmdentry;
8596 struct job *jp;
8597 char *lastarg;
8598 const char *path;
8599 int spclbltin;
8600 int cmd_is_exec;
8601 int status;
8602 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008603 struct builtincmd *bcmd;
8604 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008605
8606 /* First expand the arguments. */
8607 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8608 setstackmark(&smark);
8609 back_exitstatus = 0;
8610
8611 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008612 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008613 varlist.lastp = &varlist.list;
8614 *varlist.lastp = NULL;
8615 arglist.lastp = &arglist.list;
8616 *arglist.lastp = NULL;
8617
8618 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008619 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008620 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8621 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8622 }
8623
Eric Andersenc470f442003-07-28 09:56:35 +00008624 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8625 struct strlist **spp;
8626
8627 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008628 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008629 expandarg(argp, &arglist, EXP_VARTILDE);
8630 else
8631 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8632
Eric Andersenc470f442003-07-28 09:56:35 +00008633 for (sp = *spp; sp; sp = sp->next)
8634 argc++;
8635 }
8636
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008637 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008638 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008639 TRACE(("evalcommand arg: %s\n", sp->text));
8640 *nargv++ = sp->text;
8641 }
8642 *nargv = NULL;
8643
8644 lastarg = NULL;
8645 if (iflag && funcnest == 0 && argc > 0)
8646 lastarg = nargv[-1];
8647
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008648 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008649 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008650 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008651
8652 path = vpath.text;
8653 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8654 struct strlist **spp;
8655 char *p;
8656
8657 spp = varlist.lastp;
8658 expandarg(argp, &varlist, EXP_VARTILDE);
8659
8660 /*
8661 * Modify the command lookup path, if a PATH= assignment
8662 * is present
8663 */
8664 p = (*spp)->text;
8665 if (varequal(p, path))
8666 path = p;
8667 }
8668
8669 /* Print the command if xflag is set. */
8670 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008671 int n;
8672 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008673
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008674 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008675 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008676
8677 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008678 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008679 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008680 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008681 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008682 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008683 p--;
8684 }
8685 }
8686 sp = arglist.list;
8687 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008688 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008689 }
8690
8691 cmd_is_exec = 0;
8692 spclbltin = -1;
8693
8694 /* Now locate the command. */
8695 if (argc) {
8696 const char *oldpath;
8697 int cmd_flag = DO_ERR;
8698
8699 path += 5;
8700 oldpath = path;
8701 for (;;) {
8702 find_command(argv[0], &cmdentry, cmd_flag, path);
8703 if (cmdentry.cmdtype == CMDUNKNOWN) {
8704 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008705 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008706 goto bail;
8707 }
8708
8709 /* implement bltin and command here */
8710 if (cmdentry.cmdtype != CMDBUILTIN)
8711 break;
8712 if (spclbltin < 0)
8713 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8714 if (cmdentry.u.cmd == EXECCMD)
8715 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008716#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008717 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008718 path = oldpath;
8719 nargv = parse_command_args(argv, &path);
8720 if (!nargv)
8721 break;
8722 argc -= nargv - argv;
8723 argv = nargv;
8724 cmd_flag |= DO_NOFUNC;
8725 } else
8726#endif
8727 break;
8728 }
8729 }
8730
8731 if (status) {
8732 /* We have a redirection error. */
8733 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008734 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008735 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008736 exitstatus = status;
8737 goto out;
8738 }
8739
8740 /* Execute the command. */
8741 switch (cmdentry.cmdtype) {
8742 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008743#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008744 {
8745 /* find_command() encodes applet_no as (-2 - applet_no) */
8746 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008747 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008748 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008749 /* run <applet>_main() */
8750 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008751 break;
8752 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008753 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008754#endif
8755
Eric Andersenc470f442003-07-28 09:56:35 +00008756 /* Fork off a child process if necessary. */
8757 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008758 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008759 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008760 if (forkshell(jp, cmd, FORK_FG) != 0) {
8761 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008762 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008763 break;
8764 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008765 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008766 }
8767 listsetvar(varlist.list, VEXPORT|VSTACK);
8768 shellexec(argv, path, cmdentry.u.index);
8769 /* NOTREACHED */
8770
8771 case CMDBUILTIN:
8772 cmdenviron = varlist.list;
8773 if (cmdenviron) {
8774 struct strlist *list = cmdenviron;
8775 int i = VNOSET;
8776 if (spclbltin > 0 || argc == 0) {
8777 i = 0;
8778 if (cmd_is_exec && argc > 1)
8779 i = VEXPORT;
8780 }
8781 listsetvar(list, i);
8782 }
8783 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8784 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008785 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008786 if (i == EXEXIT)
8787 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008788 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008789 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008790 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008791 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008792 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008793 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008794 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008795 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008796 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008797 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008798 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008799 }
8800 break;
8801
8802 case CMDFUNCTION:
8803 listsetvar(varlist.list, 0);
8804 if (evalfun(cmdentry.u.func, argc, argv, flags))
8805 goto raise;
8806 break;
8807 }
8808
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008809 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008810 popredir(cmd_is_exec);
8811 if (lastarg)
8812 /* dsl: I think this is intended to be used to support
8813 * '_' in 'vi' command mode during line editing...
8814 * However I implemented that within libedit itself.
8815 */
8816 setvar("_", lastarg, 0);
8817 popstackmark(&smark);
8818}
8819
8820static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008821evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8822{
Eric Andersenc470f442003-07-28 09:56:35 +00008823 char *volatile savecmdname;
8824 struct jmploc *volatile savehandler;
8825 struct jmploc jmploc;
8826 int i;
8827
8828 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008829 i = setjmp(jmploc.loc);
8830 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008831 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008832 savehandler = exception_handler;
8833 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008834 commandname = argv[0];
8835 argptr = argv + 1;
8836 optptr = NULL; /* initialize nextopt */
8837 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008838 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008839 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008840 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008841 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008842 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008843// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008844 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008845
8846 return i;
8847}
8848
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008849static int
8850goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008851{
8852 return !*endofname(p);
8853}
8854
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008855
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008856/*
8857 * Search for a command. This is called before we fork so that the
8858 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008859 * the child. The check for "goodname" is an overly conservative
8860 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008861 */
Eric Andersenc470f442003-07-28 09:56:35 +00008862static void
8863prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008864{
8865 struct cmdentry entry;
8866
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008867 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8868 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008869}
8870
Eric Andersencb57d552001-06-28 07:25:16 +00008871
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008872/* ============ Builtin commands
8873 *
8874 * Builtin commands whose functions are closely tied to evaluation
8875 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008876 */
8877
8878/*
Eric Andersencb57d552001-06-28 07:25:16 +00008879 * Handle break and continue commands. Break, continue, and return are
8880 * all handled by setting the evalskip flag. The evaluation routines
8881 * above all check this flag, and if it is set they start skipping
8882 * commands rather than executing them. The variable skipcount is
8883 * the number of loops to break/continue, or the number of function
8884 * levels to return. (The latter is always 1.) It should probably
8885 * be an error to break out of more loops than exist, but it isn't
8886 * in the standard shell so we don't make it one here.
8887 */
Eric Andersenc470f442003-07-28 09:56:35 +00008888static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008889breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008890{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008891 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008892
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008893 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008894 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008895 if (n > loopnest)
8896 n = loopnest;
8897 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008898 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008899 skipcount = n;
8900 }
8901 return 0;
8902}
8903
Eric Andersenc470f442003-07-28 09:56:35 +00008904
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008905/* ============ input.c
8906 *
Eric Andersen90898442003-08-06 11:20:52 +00008907 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008908 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008909
Eric Andersenc470f442003-07-28 09:56:35 +00008910#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008911
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008912enum {
8913 INPUT_PUSH_FILE = 1,
8914 INPUT_NOFILE_OK = 2,
8915};
Eric Andersencb57d552001-06-28 07:25:16 +00008916
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008917static int plinno = 1; /* input line number */
8918/* number of characters left in input buffer */
8919static int parsenleft; /* copy of parsefile->nleft */
8920static int parselleft; /* copy of parsefile->lleft */
8921/* next character in input buffer */
8922static char *parsenextc; /* copy of parsefile->nextc */
8923
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008924static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008925/* values of checkkwd variable */
8926#define CHKALIAS 0x1
8927#define CHKKWD 0x2
8928#define CHKNL 0x4
8929
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008930static void
8931popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008932{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008933 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008934
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008935 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008936#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008937 if (sp->ap) {
8938 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8939 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008940 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008941 if (sp->string != sp->ap->val) {
8942 free(sp->string);
8943 }
8944 sp->ap->flag &= ~ALIASINUSE;
8945 if (sp->ap->flag & ALIASDEAD) {
8946 unalias(sp->ap->name);
8947 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008948 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008949#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008950 parsenextc = sp->prevstring;
8951 parsenleft = sp->prevnleft;
8952/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008953 g_parsefile->strpush = sp->prev;
8954 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008955 free(sp);
8956 INT_ON;
8957}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008958
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008959static int
8960preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008961{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008962 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008963 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008964 parsenextc = buf;
8965
Denis Vlasenko38f63192007-01-22 09:03:07 +00008966#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008967 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008968 if (!iflag || g_parsefile->fd)
8969 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008970 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008971#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008972 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008973#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008974 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8975 if (nr == 0) {
8976 /* Ctrl+C pressed */
8977 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008978 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008979 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008980 raise(SIGINT);
8981 return 1;
8982 }
Eric Andersenc470f442003-07-28 09:56:35 +00008983 goto retry;
8984 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008985 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008986 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008987 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008988 }
Eric Andersencb57d552001-06-28 07:25:16 +00008989 }
8990#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008991 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008992#endif
8993
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008994#if 0
8995/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008996 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008997 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008998 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008999 if (flags >= 0 && (flags & O_NONBLOCK)) {
9000 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009001 if (fcntl(0, F_SETFL, flags) >= 0) {
9002 out2str("sh: turning off NDELAY mode\n");
9003 goto retry;
9004 }
9005 }
9006 }
9007 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009008#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009009 return nr;
9010}
9011
9012/*
9013 * Refill the input buffer and return the next input character:
9014 *
9015 * 1) If a string was pushed back on the input, pop it;
9016 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9017 * from a string so we can't refill the buffer, return EOF.
9018 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9019 * 4) Process input up to the next newline, deleting nul characters.
9020 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009021static int
Eric Andersenc470f442003-07-28 09:56:35 +00009022preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009023{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009024 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009025 int more;
9026 char savec;
9027
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009028 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009029#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009030 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009031 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009032 return PEOA;
9033 }
Eric Andersen2870d962001-07-02 17:27:21 +00009034#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009035 popstring();
9036 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009037 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009038 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009039 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009040 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009041 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009042
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009043 more = parselleft;
9044 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009045 again:
9046 more = preadfd();
9047 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009048 parselleft = parsenleft = EOF_NLEFT;
9049 return PEOF;
9050 }
9051 }
9052
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009053 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009054
9055 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009056 for (;;) {
9057 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009058
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009059 more--;
9060 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009061
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009062 if (!c)
9063 memmove(q, q + 1, more);
9064 else {
9065 q++;
9066 if (c == '\n') {
9067 parsenleft = q - parsenextc - 1;
9068 break;
9069 }
Eric Andersencb57d552001-06-28 07:25:16 +00009070 }
9071
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009072 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009073 parsenleft = q - parsenextc - 1;
9074 if (parsenleft < 0)
9075 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009076 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009077 }
9078 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009079 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009080
9081 savec = *q;
9082 *q = '\0';
9083
9084 if (vflag) {
9085 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009086 }
9087
9088 *q = savec;
9089
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009090 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009091}
9092
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009093#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009094static int
9095pgetc(void)
9096{
9097 return pgetc_as_macro();
9098}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009099
9100#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9101#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009102#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009103#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009104#endif
9105
9106/*
9107 * Same as pgetc(), but ignores PEOA.
9108 */
9109#if ENABLE_ASH_ALIAS
9110static int
9111pgetc2(void)
9112{
9113 int c;
9114
9115 do {
9116 c = pgetc_macro();
9117 } while (c == PEOA);
9118 return c;
9119}
9120#else
9121static int
9122pgetc2(void)
9123{
9124 return pgetc_macro();
9125}
9126#endif
9127
9128/*
9129 * Read a line from the script.
9130 */
9131static char *
9132pfgets(char *line, int len)
9133{
9134 char *p = line;
9135 int nleft = len;
9136 int c;
9137
9138 while (--nleft > 0) {
9139 c = pgetc2();
9140 if (c == PEOF) {
9141 if (p == line)
9142 return NULL;
9143 break;
9144 }
9145 *p++ = c;
9146 if (c == '\n')
9147 break;
9148 }
9149 *p = '\0';
9150 return line;
9151}
9152
Eric Andersenc470f442003-07-28 09:56:35 +00009153/*
9154 * Undo the last call to pgetc. Only one character may be pushed back.
9155 * PEOF may be pushed back.
9156 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009157static void
Eric Andersenc470f442003-07-28 09:56:35 +00009158pungetc(void)
9159{
9160 parsenleft++;
9161 parsenextc--;
9162}
Eric Andersencb57d552001-06-28 07:25:16 +00009163
9164/*
9165 * Push a string back onto the input at this current parsefile level.
9166 * We handle aliases this way.
9167 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009168#if !ENABLE_ASH_ALIAS
9169#define pushstring(s, ap) pushstring(s)
9170#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009171static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009172pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009173{
Eric Andersencb57d552001-06-28 07:25:16 +00009174 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009175 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009176
Eric Andersenc470f442003-07-28 09:56:35 +00009177 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009178 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009179/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009180 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009181 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009182 sp->prev = g_parsefile->strpush;
9183 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009184 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009185 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009186 sp->prevstring = parsenextc;
9187 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009188#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009189 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009190 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009191 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009192 sp->string = s;
9193 }
Eric Andersen2870d962001-07-02 17:27:21 +00009194#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009195 parsenextc = s;
9196 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009197 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009198}
9199
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009200/*
9201 * To handle the "." command, a stack of input files is used. Pushfile
9202 * adds a new entry to the stack and popfile restores the previous level.
9203 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009204static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009206{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009207 struct parsefile *pf;
9208
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009209 g_parsefile->nleft = parsenleft;
9210 g_parsefile->lleft = parselleft;
9211 g_parsefile->nextc = parsenextc;
9212 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009213 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009214 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009216 /*pf->strpush = NULL; - ckzalloc did it */
9217 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009218 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009219}
9220
9221static void
9222popfile(void)
9223{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009224 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009225
Denis Vlasenkob012b102007-02-19 22:43:01 +00009226 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009227 if (pf->fd >= 0)
9228 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009229 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009230 while (pf->strpush)
9231 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009232 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009233 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009234 parsenleft = g_parsefile->nleft;
9235 parselleft = g_parsefile->lleft;
9236 parsenextc = g_parsefile->nextc;
9237 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009238 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009239}
9240
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009241/*
9242 * Return to top level.
9243 */
9244static void
9245popallfiles(void)
9246{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009247 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009248 popfile();
9249}
9250
9251/*
9252 * Close the file(s) that the shell is reading commands from. Called
9253 * after a fork is done.
9254 */
9255static void
9256closescript(void)
9257{
9258 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009259 if (g_parsefile->fd > 0) {
9260 close(g_parsefile->fd);
9261 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009262 }
9263}
9264
9265/*
9266 * Like setinputfile, but takes an open file descriptor. Call this with
9267 * interrupts off.
9268 */
9269static void
9270setinputfd(int fd, int push)
9271{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009272 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009273 if (push) {
9274 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009275 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009276 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009277 g_parsefile->fd = fd;
9278 if (g_parsefile->buf == NULL)
9279 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009280 parselleft = parsenleft = 0;
9281 plinno = 1;
9282}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009283
Eric Andersenc470f442003-07-28 09:56:35 +00009284/*
9285 * Set the input to take input from a file. If push is set, push the
9286 * old input onto the stack first.
9287 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009288static int
9289setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009290{
9291 int fd;
9292 int fd2;
9293
Denis Vlasenkob012b102007-02-19 22:43:01 +00009294 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009295 fd = open(fname, O_RDONLY);
9296 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009297 if (flags & INPUT_NOFILE_OK)
9298 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009299 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009300 }
Eric Andersenc470f442003-07-28 09:56:35 +00009301 if (fd < 10) {
9302 fd2 = copyfd(fd, 10);
9303 close(fd);
9304 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009305 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009306 fd = fd2;
9307 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009308 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009309 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009310 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009311 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009312}
9313
Eric Andersencb57d552001-06-28 07:25:16 +00009314/*
9315 * Like setinputfile, but takes input from a string.
9316 */
Eric Andersenc470f442003-07-28 09:56:35 +00009317static void
9318setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009319{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009320 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009321 pushfile();
9322 parsenextc = string;
9323 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009324 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009325 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009326 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009327}
9328
9329
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009330/* ============ mail.c
9331 *
9332 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009333 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009334
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009335#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009336
Eric Andersencb57d552001-06-28 07:25:16 +00009337#define MAXMBOXES 10
9338
Eric Andersenc470f442003-07-28 09:56:35 +00009339/* times of mailboxes */
9340static time_t mailtime[MAXMBOXES];
9341/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009342static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009343
Eric Andersencb57d552001-06-28 07:25:16 +00009344/*
Eric Andersenc470f442003-07-28 09:56:35 +00009345 * Print appropriate message(s) if mail has arrived.
9346 * If mail_var_path_changed is set,
9347 * then the value of MAIL has mail_var_path_changed,
9348 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009349 */
Eric Andersenc470f442003-07-28 09:56:35 +00009350static void
9351chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009352{
Eric Andersencb57d552001-06-28 07:25:16 +00009353 const char *mpath;
9354 char *p;
9355 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009356 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009357 struct stackmark smark;
9358 struct stat statb;
9359
Eric Andersencb57d552001-06-28 07:25:16 +00009360 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009361 mpath = mpathset() ? mpathval() : mailval();
9362 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009363 p = padvance(&mpath, nullstr);
9364 if (p == NULL)
9365 break;
9366 if (*p == '\0')
9367 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009368 for (q = p; *q; q++)
9369 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009370#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009371 if (q[-1] != '/')
9372 abort();
9373#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009374 q[-1] = '\0'; /* delete trailing '/' */
9375 if (stat(p, &statb) < 0) {
9376 *mtp = 0;
9377 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009378 }
Eric Andersenc470f442003-07-28 09:56:35 +00009379 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9380 fprintf(
9381 stderr, snlfmt,
9382 pathopt ? pathopt : "you have mail"
9383 );
9384 }
9385 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009386 }
Eric Andersenc470f442003-07-28 09:56:35 +00009387 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009388 popstackmark(&smark);
9389}
Eric Andersencb57d552001-06-28 07:25:16 +00009390
Eric Andersenc470f442003-07-28 09:56:35 +00009391static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009392changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009393{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009394 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009395}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009396
Denis Vlasenko131ae172007-02-18 13:00:19 +00009397#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009398
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009399
9400/* ============ ??? */
9401
Eric Andersencb57d552001-06-28 07:25:16 +00009402/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009403 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009404 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009405static void
9406setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009407{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009408 char **newparam;
9409 char **ap;
9410 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009411
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009412 for (nparam = 0; argv[nparam]; nparam++)
9413 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009414 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9415 while (*argv) {
9416 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009417 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009418 *ap = NULL;
9419 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009420 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009421 shellparam.nparam = nparam;
9422 shellparam.p = newparam;
9423#if ENABLE_ASH_GETOPTS
9424 shellparam.optind = 1;
9425 shellparam.optoff = -1;
9426#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009427}
9428
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009429/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009430 * Process shell options. The global variable argptr contains a pointer
9431 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009432 *
9433 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9434 * For a non-interactive shell, an error condition encountered
9435 * by a special built-in ... shall cause the shell to write a diagnostic message
9436 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009437 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009438 * ...
9439 * Utility syntax error (option or operand error) Shall exit
9440 * ...
9441 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9442 * we see that bash does not do that (set "finishes" with error code 1 instead,
9443 * and shell continues), and people rely on this behavior!
9444 * Testcase:
9445 * set -o barfoo 2>/dev/null
9446 * echo $?
9447 *
9448 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009449 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009450static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009451plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009452{
9453 int i;
9454
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009455 if (name) {
9456 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009457 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009458 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009459 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009460 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009461 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009462 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009463 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009464 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009465 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009466 if (val) {
9467 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9468 } else {
9469 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9470 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009471 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009472 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009473}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009474static void
9475setoption(int flag, int val)
9476{
9477 int i;
9478
9479 for (i = 0; i < NOPTS; i++) {
9480 if (optletters(i) == flag) {
9481 optlist[i] = val;
9482 return;
9483 }
9484 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009485 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009486 /* NOTREACHED */
9487}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009488static int
Eric Andersenc470f442003-07-28 09:56:35 +00009489options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009490{
9491 char *p;
9492 int val;
9493 int c;
9494
9495 if (cmdline)
9496 minusc = NULL;
9497 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009498 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009499 if (c != '-' && c != '+')
9500 break;
9501 argptr++;
9502 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009503 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009504 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009505 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009506 if (!cmdline) {
9507 /* "-" means turn off -x and -v */
9508 if (p[0] == '\0')
9509 xflag = vflag = 0;
9510 /* "--" means reset params */
9511 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009512 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009513 }
Eric Andersenc470f442003-07-28 09:56:35 +00009514 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009515 }
Eric Andersencb57d552001-06-28 07:25:16 +00009516 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009517 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009518 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009519 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009520 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009521 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009522 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009523 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009524 /* it already printed err message */
9525 return 1; /* error */
9526 }
Eric Andersencb57d552001-06-28 07:25:16 +00009527 if (*argptr)
9528 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009529 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9530 isloginsh = 1;
9531 /* bash does not accept +-login, we also won't */
9532 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009533 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009534 isloginsh = 1;
9535 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009536 } else {
9537 setoption(c, val);
9538 }
9539 }
9540 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009541 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009542}
9543
Eric Andersencb57d552001-06-28 07:25:16 +00009544/*
Eric Andersencb57d552001-06-28 07:25:16 +00009545 * The shift builtin command.
9546 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009547static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009548shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009549{
9550 int n;
9551 char **ap1, **ap2;
9552
9553 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009554 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009555 n = number(argv[1]);
9556 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009557 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009558 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009559 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009560 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009561 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009562 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009563 }
9564 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009565 while ((*ap2++ = *ap1++) != NULL)
9566 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009567#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009568 shellparam.optind = 1;
9569 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009570#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009571 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009572 return 0;
9573}
9574
Eric Andersencb57d552001-06-28 07:25:16 +00009575/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009576 * POSIX requires that 'set' (but not export or readonly) output the
9577 * variables in lexicographic order - by the locale's collating order (sigh).
9578 * Maybe we could keep them in an ordered balanced binary tree
9579 * instead of hashed lists.
9580 * For now just roll 'em through qsort for printing...
9581 */
9582static int
9583showvars(const char *sep_prefix, int on, int off)
9584{
9585 const char *sep;
9586 char **ep, **epend;
9587
9588 ep = listvars(on, off, &epend);
9589 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9590
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009591 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009592
9593 for (; ep < epend; ep++) {
9594 const char *p;
9595 const char *q;
9596
9597 p = strchrnul(*ep, '=');
9598 q = nullstr;
9599 if (*p)
9600 q = single_quote(++p);
9601 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9602 }
9603 return 0;
9604}
9605
9606/*
Eric Andersencb57d552001-06-28 07:25:16 +00009607 * The set command builtin.
9608 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009609static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009610setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009611{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009612 int retval;
9613
Denis Vlasenko68404f12008-03-17 09:00:54 +00009614 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009615 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009616 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009617 retval = 1;
9618 if (!options(0)) { /* if no parse error... */
9619 retval = 0;
9620 optschanged();
9621 if (*argptr != NULL) {
9622 setparam(argptr);
9623 }
Eric Andersencb57d552001-06-28 07:25:16 +00009624 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009625 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009626 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009627}
9628
Denis Vlasenko131ae172007-02-18 13:00:19 +00009629#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009630static void
9631change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009632{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009633 /* Galois LFSR parameter */
9634 /* Taps at 32 31 29 1: */
9635 enum { MASK = 0x8000000b };
9636 /* Another example - taps at 32 31 30 10: */
9637 /* MASK = 0x00400007 */
9638
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009639 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009640 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009641 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009642
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009643 /* LCG has period of 2^32 and alternating lowest bit */
9644 random_LCG = 1664525 * random_LCG + 1013904223;
9645 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9646 t = (random_galois_LFSR << 1);
9647 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9648 t ^= MASK;
9649 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009650 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009651 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009652 * for $RANDOM range. Combining with subtraction is
9653 * just for fun. + and ^ would work equally well. */
9654 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009655 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009656 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009657 vrandom.flags &= ~VNOFUNC;
9658 } else {
9659 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009660 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009661 }
Eric Andersenef02f822004-03-11 13:34:24 +00009662}
Eric Andersen16767e22004-03-16 05:14:10 +00009663#endif
9664
Denis Vlasenko131ae172007-02-18 13:00:19 +00009665#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009666static int
Eric Andersenc470f442003-07-28 09:56:35 +00009667getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009668{
9669 char *p, *q;
9670 char c = '?';
9671 int done = 0;
9672 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009673 char s[12];
9674 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009675
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009676 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009677 return 1;
9678 optnext = optfirst + *param_optind - 1;
9679
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009680 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009681 p = NULL;
9682 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009683 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009684 if (p == NULL || *p == '\0') {
9685 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009686 p = *optnext;
9687 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009688 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009689 p = NULL;
9690 done = 1;
9691 goto out;
9692 }
9693 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009694 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009695 goto atend;
9696 }
9697
9698 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009699 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009700 if (*q == '\0') {
9701 if (optstr[0] == ':') {
9702 s[0] = c;
9703 s[1] = '\0';
9704 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009705 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009706 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009707 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009708 }
9709 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009710 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009711 }
9712 if (*++q == ':')
9713 q++;
9714 }
9715
9716 if (*++q == ':') {
9717 if (*p == '\0' && (p = *optnext) == NULL) {
9718 if (optstr[0] == ':') {
9719 s[0] = c;
9720 s[1] = '\0';
9721 err |= setvarsafe("OPTARG", s, 0);
9722 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009723 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009724 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009725 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009726 c = '?';
9727 }
Eric Andersenc470f442003-07-28 09:56:35 +00009728 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009729 }
9730
9731 if (p == *optnext)
9732 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009733 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009734 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009735 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009736 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009737 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009738 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009739 *param_optind = optnext - optfirst + 1;
9740 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009741 err |= setvarsafe("OPTIND", s, VNOFUNC);
9742 s[0] = c;
9743 s[1] = '\0';
9744 err |= setvarsafe(optvar, s, 0);
9745 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009746 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009747 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009748 flush_stdout_stderr();
9749 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009750 }
9751 return done;
9752}
Eric Andersenc470f442003-07-28 09:56:35 +00009753
9754/*
9755 * The getopts builtin. Shellparam.optnext points to the next argument
9756 * to be processed. Shellparam.optptr points to the next character to
9757 * be processed in the current argument. If shellparam.optnext is NULL,
9758 * then it's the first time getopts has been called.
9759 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009760static int
Eric Andersenc470f442003-07-28 09:56:35 +00009761getoptscmd(int argc, char **argv)
9762{
9763 char **optbase;
9764
9765 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009766 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009767 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009768 optbase = shellparam.p;
9769 if (shellparam.optind > shellparam.nparam + 1) {
9770 shellparam.optind = 1;
9771 shellparam.optoff = -1;
9772 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009773 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009774 optbase = &argv[3];
9775 if (shellparam.optind > argc - 2) {
9776 shellparam.optind = 1;
9777 shellparam.optoff = -1;
9778 }
9779 }
9780
9781 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009782 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009783}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009784#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009785
Eric Andersencb57d552001-06-28 07:25:16 +00009786
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009787/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009788
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009789struct heredoc {
9790 struct heredoc *next; /* next here document in list */
9791 union node *here; /* redirection node */
9792 char *eofmark; /* string indicating end of input */
9793 smallint striptabs; /* if set, strip leading tabs */
9794};
9795
9796static smallint tokpushback; /* last token pushed back */
9797static smallint parsebackquote; /* nonzero if we are inside backquotes */
9798static smallint quoteflag; /* set if (part of) last token was quoted */
9799static token_id_t lasttoken; /* last token read (integer id Txxx) */
9800static struct heredoc *heredoclist; /* list of here documents to read */
9801static char *wordtext; /* text of last word returned by readtoken */
9802static struct nodelist *backquotelist;
9803static union node *redirnode;
9804static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009805/*
9806 * NEOF is returned by parsecmd when it encounters an end of file. It
9807 * must be distinct from NULL, so we use the address of a variable that
9808 * happens to be handy.
9809 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009810#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009811
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009812static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009813static void
9814raise_error_syntax(const char *msg)
9815{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009816 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009817 /* NOTREACHED */
9818}
9819
9820/*
9821 * Called when an unexpected token is read during the parse. The argument
9822 * is the token that is expected, or -1 if more than one type of token can
9823 * occur at this point.
9824 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009825static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009826static void
9827raise_error_unexpected_syntax(int token)
9828{
9829 char msg[64];
9830 int l;
9831
9832 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9833 if (token >= 0)
9834 sprintf(msg + l, " (expecting %s)", tokname(token));
9835 raise_error_syntax(msg);
9836 /* NOTREACHED */
9837}
Eric Andersencb57d552001-06-28 07:25:16 +00009838
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009839#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009840
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009841/* parsing is heavily cross-recursive, need these forward decls */
9842static union node *andor(void);
9843static union node *pipeline(void);
9844static union node *parse_command(void);
9845static void parseheredoc(void);
9846static char peektoken(void);
9847static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009848
Eric Andersenc470f442003-07-28 09:56:35 +00009849static union node *
9850list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009851{
9852 union node *n1, *n2, *n3;
9853 int tok;
9854
Eric Andersenc470f442003-07-28 09:56:35 +00009855 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9856 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009857 return NULL;
9858 n1 = NULL;
9859 for (;;) {
9860 n2 = andor();
9861 tok = readtoken();
9862 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009863 if (n2->type == NPIPE) {
9864 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009865 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009866 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009867 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009868 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009869 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009870 n2 = n3;
9871 }
9872 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009873 }
9874 }
9875 if (n1 == NULL) {
9876 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009877 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009878 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009879 n3->type = NSEMI;
9880 n3->nbinary.ch1 = n1;
9881 n3->nbinary.ch2 = n2;
9882 n1 = n3;
9883 }
9884 switch (tok) {
9885 case TBACKGND:
9886 case TSEMI:
9887 tok = readtoken();
9888 /* fall through */
9889 case TNL:
9890 if (tok == TNL) {
9891 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009892 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009893 return n1;
9894 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009895 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009896 }
Eric Andersenc470f442003-07-28 09:56:35 +00009897 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009898 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009899 return n1;
9900 break;
9901 case TEOF:
9902 if (heredoclist)
9903 parseheredoc();
9904 else
Eric Andersenc470f442003-07-28 09:56:35 +00009905 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009906 return n1;
9907 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009908 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009909 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009910 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 return n1;
9912 }
9913 }
9914}
9915
Eric Andersenc470f442003-07-28 09:56:35 +00009916static union node *
9917andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009918{
Eric Andersencb57d552001-06-28 07:25:16 +00009919 union node *n1, *n2, *n3;
9920 int t;
9921
Eric Andersencb57d552001-06-28 07:25:16 +00009922 n1 = pipeline();
9923 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009924 t = readtoken();
9925 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009926 t = NAND;
9927 } else if (t == TOR) {
9928 t = NOR;
9929 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009930 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009931 return n1;
9932 }
Eric Andersenc470f442003-07-28 09:56:35 +00009933 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009934 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009935 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009936 n3->type = t;
9937 n3->nbinary.ch1 = n1;
9938 n3->nbinary.ch2 = n2;
9939 n1 = n3;
9940 }
9941}
9942
Eric Andersenc470f442003-07-28 09:56:35 +00009943static union node *
9944pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009945{
Eric Andersencb57d552001-06-28 07:25:16 +00009946 union node *n1, *n2, *pipenode;
9947 struct nodelist *lp, *prev;
9948 int negate;
9949
9950 negate = 0;
9951 TRACE(("pipeline: entered\n"));
9952 if (readtoken() == TNOT) {
9953 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009954 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009955 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009956 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009957 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009958 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009959 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009960 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009961 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009962 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009963 pipenode->npipe.cmdlist = lp;
9964 lp->n = n1;
9965 do {
9966 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009967 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009968 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009969 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009970 prev->next = lp;
9971 } while (readtoken() == TPIPE);
9972 lp->next = NULL;
9973 n1 = pipenode;
9974 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009975 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009976 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009977 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009978 n2->type = NNOT;
9979 n2->nnot.com = n1;
9980 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009981 }
9982 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009983}
9984
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009985static union node *
9986makename(void)
9987{
9988 union node *n;
9989
Denis Vlasenko597906c2008-02-20 16:38:54 +00009990 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009991 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009992 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009993 n->narg.text = wordtext;
9994 n->narg.backquote = backquotelist;
9995 return n;
9996}
9997
9998static void
9999fixredir(union node *n, const char *text, int err)
10000{
10001 TRACE(("Fix redir %s %d\n", text, err));
10002 if (!err)
10003 n->ndup.vname = NULL;
10004
10005 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010006 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010007 else if (LONE_DASH(text))
10008 n->ndup.dupfd = -1;
10009 else {
10010 if (err)
10011 raise_error_syntax("Bad fd number");
10012 n->ndup.vname = makename();
10013 }
10014}
10015
10016/*
10017 * Returns true if the text contains nothing to expand (no dollar signs
10018 * or backquotes).
10019 */
10020static int
10021noexpand(char *text)
10022{
10023 char *p;
10024 char c;
10025
10026 p = text;
10027 while ((c = *p++) != '\0') {
10028 if (c == CTLQUOTEMARK)
10029 continue;
10030 if (c == CTLESC)
10031 p++;
10032 else if (SIT(c, BASESYNTAX) == CCTL)
10033 return 0;
10034 }
10035 return 1;
10036}
10037
10038static void
10039parsefname(void)
10040{
10041 union node *n = redirnode;
10042
10043 if (readtoken() != TWORD)
10044 raise_error_unexpected_syntax(-1);
10045 if (n->type == NHERE) {
10046 struct heredoc *here = heredoc;
10047 struct heredoc *p;
10048 int i;
10049
10050 if (quoteflag == 0)
10051 n->type = NXHERE;
10052 TRACE(("Here document %d\n", n->type));
10053 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10054 raise_error_syntax("Illegal eof marker for << redirection");
10055 rmescapes(wordtext);
10056 here->eofmark = wordtext;
10057 here->next = NULL;
10058 if (heredoclist == NULL)
10059 heredoclist = here;
10060 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010061 for (p = heredoclist; p->next; p = p->next)
10062 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010063 p->next = here;
10064 }
10065 } else if (n->type == NTOFD || n->type == NFROMFD) {
10066 fixredir(n, wordtext, 0);
10067 } else {
10068 n->nfile.fname = makename();
10069 }
10070}
Eric Andersencb57d552001-06-28 07:25:16 +000010071
Eric Andersenc470f442003-07-28 09:56:35 +000010072static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010073simplecmd(void)
10074{
10075 union node *args, **app;
10076 union node *n = NULL;
10077 union node *vars, **vpp;
10078 union node **rpp, *redir;
10079 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010080#if ENABLE_ASH_BASH_COMPAT
10081 smallint double_brackets_flag = 0;
10082#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010083
10084 args = NULL;
10085 app = &args;
10086 vars = NULL;
10087 vpp = &vars;
10088 redir = NULL;
10089 rpp = &redir;
10090
10091 savecheckkwd = CHKALIAS;
10092 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010093 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010094 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010095 t = readtoken();
10096 switch (t) {
10097#if ENABLE_ASH_BASH_COMPAT
10098 case TAND: /* "&&" */
10099 case TOR: /* "||" */
10100 if (!double_brackets_flag) {
10101 tokpushback = 1;
10102 goto out;
10103 }
10104 wordtext = (char *) (t == TAND ? "-a" : "-o");
10105#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010106 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010107 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010108 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010109 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010110 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010111#if ENABLE_ASH_BASH_COMPAT
10112 if (strcmp("[[", wordtext) == 0)
10113 double_brackets_flag = 1;
10114 else if (strcmp("]]", wordtext) == 0)
10115 double_brackets_flag = 0;
10116#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010117 n->narg.backquote = backquotelist;
10118 if (savecheckkwd && isassignment(wordtext)) {
10119 *vpp = n;
10120 vpp = &n->narg.next;
10121 } else {
10122 *app = n;
10123 app = &n->narg.next;
10124 savecheckkwd = 0;
10125 }
10126 break;
10127 case TREDIR:
10128 *rpp = n = redirnode;
10129 rpp = &n->nfile.next;
10130 parsefname(); /* read name of redirection file */
10131 break;
10132 case TLP:
10133 if (args && app == &args->narg.next
10134 && !vars && !redir
10135 ) {
10136 struct builtincmd *bcmd;
10137 const char *name;
10138
10139 /* We have a function */
10140 if (readtoken() != TRP)
10141 raise_error_unexpected_syntax(TRP);
10142 name = n->narg.text;
10143 if (!goodname(name)
10144 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10145 ) {
10146 raise_error_syntax("Bad function name");
10147 }
10148 n->type = NDEFUN;
10149 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10150 n->narg.next = parse_command();
10151 return n;
10152 }
10153 /* fall through */
10154 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010155 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010156 goto out;
10157 }
10158 }
10159 out:
10160 *app = NULL;
10161 *vpp = NULL;
10162 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010163 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010164 n->type = NCMD;
10165 n->ncmd.args = args;
10166 n->ncmd.assign = vars;
10167 n->ncmd.redirect = redir;
10168 return n;
10169}
10170
10171static union node *
10172parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010173{
Eric Andersencb57d552001-06-28 07:25:16 +000010174 union node *n1, *n2;
10175 union node *ap, **app;
10176 union node *cp, **cpp;
10177 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010178 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010179 int t;
10180
10181 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010182 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010183
Eric Andersencb57d552001-06-28 07:25:16 +000010184 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010185 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010186 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010187 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010188 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010189 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010190 n1->type = NIF;
10191 n1->nif.test = list(0);
10192 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010193 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010194 n1->nif.ifpart = list(0);
10195 n2 = n1;
10196 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010197 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010198 n2 = n2->nif.elsepart;
10199 n2->type = NIF;
10200 n2->nif.test = list(0);
10201 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010202 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010203 n2->nif.ifpart = list(0);
10204 }
10205 if (lasttoken == TELSE)
10206 n2->nif.elsepart = list(0);
10207 else {
10208 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010209 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010210 }
Eric Andersenc470f442003-07-28 09:56:35 +000010211 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010212 break;
10213 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010214 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010215 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010216 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010217 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010218 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010219 got = readtoken();
10220 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010221 TRACE(("expecting DO got %s %s\n", tokname(got),
10222 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010223 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010224 }
10225 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010226 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010227 break;
10228 }
10229 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010230 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010231 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010232 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010233 n1->type = NFOR;
10234 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010235 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010236 if (readtoken() == TIN) {
10237 app = &ap;
10238 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010239 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010240 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010241 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010242 n2->narg.text = wordtext;
10243 n2->narg.backquote = backquotelist;
10244 *app = n2;
10245 app = &n2->narg.next;
10246 }
10247 *app = NULL;
10248 n1->nfor.args = ap;
10249 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010250 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010251 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010252 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010253 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010254 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010255 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010256 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010257 n1->nfor.args = n2;
10258 /*
10259 * Newline or semicolon here is optional (but note
10260 * that the original Bourne shell only allowed NL).
10261 */
10262 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010263 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010264 }
Eric Andersenc470f442003-07-28 09:56:35 +000010265 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010266 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010267 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010268 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010269 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010270 break;
10271 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010272 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010273 n1->type = NCASE;
10274 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010275 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010276 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010277 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010278 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010279 n2->narg.text = wordtext;
10280 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010282 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010283 } while (readtoken() == TNL);
10284 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010285 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010286 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010287 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010288 checkkwd = CHKNL | CHKKWD;
10289 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010290 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010291 if (lasttoken == TLP)
10292 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010293 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010294 cp->type = NCLIST;
10295 app = &cp->nclist.pattern;
10296 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010297 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010298 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010299 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010300 ap->narg.text = wordtext;
10301 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010302 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010303 break;
10304 app = &ap->narg.next;
10305 readtoken();
10306 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010307 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010308 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010309 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010310 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010311
Eric Andersenc470f442003-07-28 09:56:35 +000010312 cpp = &cp->nclist.next;
10313
10314 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010315 t = readtoken();
10316 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010317 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010318 raise_error_unexpected_syntax(TENDCASE);
10319 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010320 }
Eric Andersenc470f442003-07-28 09:56:35 +000010321 }
Eric Andersencb57d552001-06-28 07:25:16 +000010322 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010323 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010324 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010325 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010326 n1->type = NSUBSHELL;
10327 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010328 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010329 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010330 break;
10331 case TBEGIN:
10332 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010333 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010334 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010335 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010336 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010337 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010338 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010339 }
10340
Eric Andersenc470f442003-07-28 09:56:35 +000010341 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010342 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010343
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010344 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010345 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010346 checkkwd = CHKKWD | CHKALIAS;
10347 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010348 while (readtoken() == TREDIR) {
10349 *rpp = n2 = redirnode;
10350 rpp = &n2->nfile.next;
10351 parsefname();
10352 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010353 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010354 *rpp = NULL;
10355 if (redir) {
10356 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010357 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010358 n2->type = NREDIR;
10359 n2->nredir.n = n1;
10360 n1 = n2;
10361 }
10362 n1->nredir.redirect = redir;
10363 }
Eric Andersencb57d552001-06-28 07:25:16 +000010364 return n1;
10365}
10366
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010367#if ENABLE_ASH_BASH_COMPAT
10368static int decode_dollar_squote(void)
10369{
10370 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10371 int c, cnt;
10372 char *p;
10373 char buf[4];
10374
10375 c = pgetc();
10376 p = strchr(C_escapes, c);
10377 if (p) {
10378 buf[0] = c;
10379 p = buf;
10380 cnt = 3;
10381 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10382 do {
10383 c = pgetc();
10384 *++p = c;
10385 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10386 pungetc();
10387 } else if (c == 'x') { /* \xHH */
10388 do {
10389 c = pgetc();
10390 *++p = c;
10391 } while (isxdigit(c) && --cnt);
10392 pungetc();
10393 if (cnt == 3) { /* \x but next char is "bad" */
10394 c = 'x';
10395 goto unrecognized;
10396 }
10397 } else { /* simple seq like \\ or \t */
10398 p++;
10399 }
10400 *p = '\0';
10401 p = buf;
10402 c = bb_process_escape_sequence((void*)&p);
10403 } else { /* unrecognized "\z": print both chars unless ' or " */
10404 if (c != '\'' && c != '"') {
10405 unrecognized:
10406 c |= 0x100; /* "please encode \, then me" */
10407 }
10408 }
10409 return c;
10410}
10411#endif
10412
Eric Andersencb57d552001-06-28 07:25:16 +000010413/*
10414 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10415 * is not NULL, read a here document. In the latter case, eofmark is the
10416 * word which marks the end of the document and striptabs is true if
10417 * leading tabs should be stripped from the document. The argument firstc
10418 * is the first character of the input token or document.
10419 *
10420 * Because C does not have internal subroutines, I have simulated them
10421 * using goto's to implement the subroutine linkage. The following macros
10422 * will run code that appears at the end of readtoken1.
10423 */
Eric Andersen2870d962001-07-02 17:27:21 +000010424#define CHECKEND() {goto checkend; checkend_return:;}
10425#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10426#define PARSESUB() {goto parsesub; parsesub_return:;}
10427#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10428#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10429#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010430static int
Eric Andersenc470f442003-07-28 09:56:35 +000010431readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010432{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010433 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010434 int c = firstc;
10435 char *out;
10436 int len;
10437 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010438 struct nodelist *bqlist;
10439 smallint quotef;
10440 smallint dblquote;
10441 smallint oldstyle;
10442 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010443#if ENABLE_ASH_EXPAND_PRMT
10444 smallint pssyntax; /* we are expanding a prompt string */
10445#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010446 int varnest; /* levels of variables expansion */
10447 int arinest; /* levels of arithmetic expansion */
10448 int parenlevel; /* levels of parens in arithmetic */
10449 int dqvarnest; /* levels of variables expansion within double quotes */
10450
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010451 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10452
Eric Andersencb57d552001-06-28 07:25:16 +000010453#if __GNUC__
10454 /* Avoid longjmp clobbering */
10455 (void) &out;
10456 (void) &quotef;
10457 (void) &dblquote;
10458 (void) &varnest;
10459 (void) &arinest;
10460 (void) &parenlevel;
10461 (void) &dqvarnest;
10462 (void) &oldstyle;
10463 (void) &prevsyntax;
10464 (void) &syntax;
10465#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010466 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010467 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010468 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010469 oldstyle = 0;
10470 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010471#if ENABLE_ASH_EXPAND_PRMT
10472 pssyntax = (syntax == PSSYNTAX);
10473 if (pssyntax)
10474 syntax = DQSYNTAX;
10475#endif
10476 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010477 varnest = 0;
10478 arinest = 0;
10479 parenlevel = 0;
10480 dqvarnest = 0;
10481
10482 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010483 loop: { /* for each line, until end of word */
10484 CHECKEND(); /* set c to PEOF if at end of here document */
10485 for (;;) { /* until end of line or end of word */
10486 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010487 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010488 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010489 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010490 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010491 USTPUTC(c, out);
10492 plinno++;
10493 if (doprompt)
10494 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010495 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010496 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010497 case CWORD:
10498 USTPUTC(c, out);
10499 break;
10500 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010501 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010502 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010503#if ENABLE_ASH_BASH_COMPAT
10504 if (c == '\\' && bash_dollar_squote) {
10505 c = decode_dollar_squote();
10506 if (c & 0x100) {
10507 USTPUTC('\\', out);
10508 c = (unsigned char)c;
10509 }
10510 }
10511#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010512 USTPUTC(c, out);
10513 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010514 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010515 c = pgetc2();
10516 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010517 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010518 USTPUTC('\\', out);
10519 pungetc();
10520 } else if (c == '\n') {
10521 if (doprompt)
10522 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010523 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010524#if ENABLE_ASH_EXPAND_PRMT
10525 if (c == '$' && pssyntax) {
10526 USTPUTC(CTLESC, out);
10527 USTPUTC('\\', out);
10528 }
10529#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010530 if (dblquote && c != '\\'
10531 && c != '`' && c != '$'
10532 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010533 ) {
10534 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010535 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010536 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010537 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010538 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010539 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010540 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 }
10542 break;
10543 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010544 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010545 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010546 if (eofmark == NULL) {
10547 USTPUTC(CTLQUOTEMARK, out);
10548 }
Eric Andersencb57d552001-06-28 07:25:16 +000010549 break;
10550 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010551 syntax = DQSYNTAX;
10552 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010553 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010554 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010555 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010556 if (eofmark != NULL && arinest == 0
10557 && varnest == 0
10558 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010559 USTPUTC(c, out);
10560 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010561 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010562 syntax = BASESYNTAX;
10563 dblquote = 0;
10564 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010565 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010566 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010567 }
10568 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010569 case CVAR: /* '$' */
10570 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010571 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010572 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010573 if (varnest > 0) {
10574 varnest--;
10575 if (dqvarnest > 0) {
10576 dqvarnest--;
10577 }
10578 USTPUTC(CTLENDVAR, out);
10579 } else {
10580 USTPUTC(c, out);
10581 }
10582 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010583#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010584 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010585 parenlevel++;
10586 USTPUTC(c, out);
10587 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010588 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010589 if (parenlevel > 0) {
10590 USTPUTC(c, out);
10591 --parenlevel;
10592 } else {
10593 if (pgetc() == ')') {
10594 if (--arinest == 0) {
10595 USTPUTC(CTLENDARI, out);
10596 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010597 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010598 } else
10599 USTPUTC(')', out);
10600 } else {
10601 /*
10602 * unbalanced parens
10603 * (don't 2nd guess - no error)
10604 */
10605 pungetc();
10606 USTPUTC(')', out);
10607 }
10608 }
10609 break;
10610#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010611 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010612 PARSEBACKQOLD();
10613 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010614 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010615 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010616 case CIGN:
10617 break;
10618 default:
10619 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010620 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010621#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010622 if (c != PEOA)
10623#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010624 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010625
Eric Andersencb57d552001-06-28 07:25:16 +000010626 }
10627 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010628 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010629 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010630 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010631#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010632 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010633 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010634#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010635 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010636 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010637 if (varnest != 0) {
10638 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010639 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010640 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010641 }
10642 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010643 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010644 out = stackblock();
10645 if (eofmark == NULL) {
10646 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010647 && quotef == 0
10648 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010649 && (*out == '\0' || isdigit(*out))
10650 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010651 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010652 lasttoken = TREDIR;
10653 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010654 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010655 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010656 }
10657 quoteflag = quotef;
10658 backquotelist = bqlist;
10659 grabstackblock(len);
10660 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010661 lasttoken = TWORD;
10662 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010663/* end of readtoken routine */
10664
Eric Andersencb57d552001-06-28 07:25:16 +000010665/*
10666 * Check to see whether we are at the end of the here document. When this
10667 * is called, c is set to the first character of the next input line. If
10668 * we are at the end of the here document, this routine sets the c to PEOF.
10669 */
Eric Andersenc470f442003-07-28 09:56:35 +000010670checkend: {
10671 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010672#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010673 if (c == PEOA) {
10674 c = pgetc2();
10675 }
10676#endif
10677 if (striptabs) {
10678 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010679 c = pgetc2();
10680 }
Eric Andersenc470f442003-07-28 09:56:35 +000010681 }
10682 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010683 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010684 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010685
Eric Andersenc470f442003-07-28 09:56:35 +000010686 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010687 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10688 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010689 if (*p == '\n' && *q == '\0') {
10690 c = PEOF;
10691 plinno++;
10692 needprompt = doprompt;
10693 } else {
10694 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010695 }
10696 }
10697 }
10698 }
Eric Andersenc470f442003-07-28 09:56:35 +000010699 goto checkend_return;
10700}
Eric Andersencb57d552001-06-28 07:25:16 +000010701
Eric Andersencb57d552001-06-28 07:25:16 +000010702/*
10703 * Parse a redirection operator. The variable "out" points to a string
10704 * specifying the fd to be redirected. The variable "c" contains the
10705 * first character of the redirection operator.
10706 */
Eric Andersenc470f442003-07-28 09:56:35 +000010707parseredir: {
10708 char fd = *out;
10709 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010710
Denis Vlasenko597906c2008-02-20 16:38:54 +000010711 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010712 if (c == '>') {
10713 np->nfile.fd = 1;
10714 c = pgetc();
10715 if (c == '>')
10716 np->type = NAPPEND;
10717 else if (c == '|')
10718 np->type = NCLOBBER;
10719 else if (c == '&')
10720 np->type = NTOFD;
10721 else {
10722 np->type = NTO;
10723 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010724 }
Eric Andersenc470f442003-07-28 09:56:35 +000010725 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010726 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010727 c = pgetc();
10728 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010729 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010730 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010731 np = stzalloc(sizeof(struct nhere));
10732 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010733 }
10734 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010735 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010736 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010737 c = pgetc();
10738 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010739 heredoc->striptabs = 1;
10740 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010741 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010742 pungetc();
10743 }
10744 break;
10745
10746 case '&':
10747 np->type = NFROMFD;
10748 break;
10749
10750 case '>':
10751 np->type = NFROMTO;
10752 break;
10753
10754 default:
10755 np->type = NFROM;
10756 pungetc();
10757 break;
10758 }
Eric Andersencb57d552001-06-28 07:25:16 +000010759 }
Eric Andersenc470f442003-07-28 09:56:35 +000010760 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010761 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010762 redirnode = np;
10763 goto parseredir_return;
10764}
Eric Andersencb57d552001-06-28 07:25:16 +000010765
Eric Andersencb57d552001-06-28 07:25:16 +000010766/*
10767 * Parse a substitution. At this point, we have read the dollar sign
10768 * and nothing else.
10769 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010770
10771/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10772 * (assuming ascii char codes, as the original implementation did) */
10773#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010774 (((unsigned)(c) - 33 < 32) \
10775 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010776parsesub: {
10777 int subtype;
10778 int typeloc;
10779 int flags;
10780 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010781 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010782
Eric Andersenc470f442003-07-28 09:56:35 +000010783 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010784 if (c <= PEOA_OR_PEOF
10785 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010786 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010787#if ENABLE_ASH_BASH_COMPAT
10788 if (c == '\'')
10789 bash_dollar_squote = 1;
10790 else
10791#endif
10792 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010793 pungetc();
10794 } else if (c == '(') { /* $(command) or $((arith)) */
10795 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010796#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010797 PARSEARITH();
10798#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010799 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010800#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010801 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010802 pungetc();
10803 PARSEBACKQNEW();
10804 }
10805 } else {
10806 USTPUTC(CTLVAR, out);
10807 typeloc = out - (char *)stackblock();
10808 USTPUTC(VSNORMAL, out);
10809 subtype = VSNORMAL;
10810 if (c == '{') {
10811 c = pgetc();
10812 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010813 c = pgetc();
10814 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010815 c = '#';
10816 else
10817 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010818 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010819 subtype = 0;
10820 }
10821 if (c > PEOA_OR_PEOF && is_name(c)) {
10822 do {
10823 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010824 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010825 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010827 do {
10828 STPUTC(c, out);
10829 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010830 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010831 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010832 USTPUTC(c, out);
10833 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010834 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010835 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010836
Eric Andersenc470f442003-07-28 09:56:35 +000010837 STPUTC('=', out);
10838 flags = 0;
10839 if (subtype == 0) {
10840 switch (c) {
10841 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010842 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010843#if ENABLE_ASH_BASH_COMPAT
10844 if (c == ':' || c == '$' || isdigit(c)) {
10845 pungetc();
10846 subtype = VSSUBSTR;
10847 break;
10848 }
10849#endif
10850 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010851 /*FALLTHROUGH*/
10852 default:
10853 p = strchr(types, c);
10854 if (p == NULL)
10855 goto badsub;
10856 subtype = p - types + VSNORMAL;
10857 break;
10858 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010859 case '#': {
10860 int cc = c;
10861 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10862 c = pgetc();
10863 if (c == cc)
10864 subtype++;
10865 else
10866 pungetc();
10867 break;
10868 }
10869#if ENABLE_ASH_BASH_COMPAT
10870 case '/':
10871 subtype = VSREPLACE;
10872 c = pgetc();
10873 if (c == '/')
10874 subtype++; /* VSREPLACEALL */
10875 else
10876 pungetc();
10877 break;
10878#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010879 }
Eric Andersenc470f442003-07-28 09:56:35 +000010880 } else {
10881 pungetc();
10882 }
10883 if (dblquote || arinest)
10884 flags |= VSQUOTE;
10885 *((char *)stackblock() + typeloc) = subtype | flags;
10886 if (subtype != VSNORMAL) {
10887 varnest++;
10888 if (dblquote || arinest) {
10889 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010890 }
10891 }
10892 }
Eric Andersenc470f442003-07-28 09:56:35 +000010893 goto parsesub_return;
10894}
Eric Andersencb57d552001-06-28 07:25:16 +000010895
Eric Andersencb57d552001-06-28 07:25:16 +000010896/*
10897 * Called to parse command substitutions. Newstyle is set if the command
10898 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10899 * list of commands (passed by reference), and savelen is the number of
10900 * characters on the top of the stack which must be preserved.
10901 */
Eric Andersenc470f442003-07-28 09:56:35 +000010902parsebackq: {
10903 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010904 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010905 union node *n;
10906 char *volatile str;
10907 struct jmploc jmploc;
10908 struct jmploc *volatile savehandler;
10909 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010910 smallint saveprompt = 0;
10911
Eric Andersencb57d552001-06-28 07:25:16 +000010912#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010913 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010914#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010915 savepbq = parsebackquote;
10916 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010917 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010918 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010919 exception_handler = savehandler;
10920 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010921 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010922 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010923 str = NULL;
10924 savelen = out - (char *)stackblock();
10925 if (savelen > 0) {
10926 str = ckmalloc(savelen);
10927 memcpy(str, stackblock(), savelen);
10928 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010929 savehandler = exception_handler;
10930 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010931 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010932 if (oldstyle) {
10933 /* We must read until the closing backquote, giving special
10934 treatment to some slashes, and then push the string and
10935 reread it as input, interpreting it normally. */
10936 char *pout;
10937 int pc;
10938 size_t psavelen;
10939 char *pstr;
10940
10941
10942 STARTSTACKSTR(pout);
10943 for (;;) {
10944 if (needprompt) {
10945 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010946 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010947 pc = pgetc();
10948 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010949 case '`':
10950 goto done;
10951
10952 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010953 pc = pgetc();
10954 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010955 plinno++;
10956 if (doprompt)
10957 setprompt(2);
10958 /*
10959 * If eating a newline, avoid putting
10960 * the newline into the new character
10961 * stream (via the STPUTC after the
10962 * switch).
10963 */
10964 continue;
10965 }
10966 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010967 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010968 STPUTC('\\', pout);
10969 if (pc > PEOA_OR_PEOF) {
10970 break;
10971 }
10972 /* fall through */
10973
10974 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010975#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010976 case PEOA:
10977#endif
10978 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010979 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010980
10981 case '\n':
10982 plinno++;
10983 needprompt = doprompt;
10984 break;
10985
10986 default:
10987 break;
10988 }
10989 STPUTC(pc, pout);
10990 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010991 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010992 STPUTC('\0', pout);
10993 psavelen = pout - (char *)stackblock();
10994 if (psavelen > 0) {
10995 pstr = grabstackstr(pout);
10996 setinputstring(pstr);
10997 }
10998 }
10999 nlpp = &bqlist;
11000 while (*nlpp)
11001 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011002 *nlpp = stzalloc(sizeof(**nlpp));
11003 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011004 parsebackquote = oldstyle;
11005
11006 if (oldstyle) {
11007 saveprompt = doprompt;
11008 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011009 }
11010
Eric Andersenc470f442003-07-28 09:56:35 +000011011 n = list(2);
11012
11013 if (oldstyle)
11014 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011015 else if (readtoken() != TRP)
11016 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011017
11018 (*nlpp)->n = n;
11019 if (oldstyle) {
11020 /*
11021 * Start reading from old file again, ignoring any pushed back
11022 * tokens left from the backquote parsing
11023 */
11024 popfile();
11025 tokpushback = 0;
11026 }
11027 while (stackblocksize() <= savelen)
11028 growstackblock();
11029 STARTSTACKSTR(out);
11030 if (str) {
11031 memcpy(out, str, savelen);
11032 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011033 INT_OFF;
11034 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011035 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011036 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011037 }
11038 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011039 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011040 if (arinest || dblquote)
11041 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11042 else
11043 USTPUTC(CTLBACKQ, out);
11044 if (oldstyle)
11045 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011046 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011047}
11048
Denis Vlasenko131ae172007-02-18 13:00:19 +000011049#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011050/*
11051 * Parse an arithmetic expansion (indicate start of one and set state)
11052 */
Eric Andersenc470f442003-07-28 09:56:35 +000011053parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011054 if (++arinest == 1) {
11055 prevsyntax = syntax;
11056 syntax = ARISYNTAX;
11057 USTPUTC(CTLARI, out);
11058 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011059 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011060 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011061 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011062 } else {
11063 /*
11064 * we collapse embedded arithmetic expansion to
11065 * parenthesis, which should be equivalent
11066 */
11067 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011068 }
Eric Andersenc470f442003-07-28 09:56:35 +000011069 goto parsearith_return;
11070}
11071#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011072
Eric Andersenc470f442003-07-28 09:56:35 +000011073} /* end of readtoken */
11074
Eric Andersencb57d552001-06-28 07:25:16 +000011075/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011076 * Read the next input token.
11077 * If the token is a word, we set backquotelist to the list of cmds in
11078 * backquotes. We set quoteflag to true if any part of the word was
11079 * quoted.
11080 * If the token is TREDIR, then we set redirnode to a structure containing
11081 * the redirection.
11082 * In all cases, the variable startlinno is set to the number of the line
11083 * on which the token starts.
11084 *
11085 * [Change comment: here documents and internal procedures]
11086 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11087 * word parsing code into a separate routine. In this case, readtoken
11088 * doesn't need to have any internal procedures, but parseword does.
11089 * We could also make parseoperator in essence the main routine, and
11090 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011091 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011092#define NEW_xxreadtoken
11093#ifdef NEW_xxreadtoken
11094/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011095static const char xxreadtoken_chars[7] ALIGN1 = {
11096 '\n', '(', ')', '&', '|', ';', 0
11097};
Eric Andersencb57d552001-06-28 07:25:16 +000011098
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011099static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011100 TNL, TLP, TRP, /* only single occurrence allowed */
11101 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11102 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011103 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011104};
11105
11106#define xxreadtoken_doubles \
11107 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11108#define xxreadtoken_singles \
11109 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11110
11111static int
11112xxreadtoken(void)
11113{
11114 int c;
11115
11116 if (tokpushback) {
11117 tokpushback = 0;
11118 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011119 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011120 if (needprompt) {
11121 setprompt(2);
11122 }
11123 startlinno = plinno;
11124 for (;;) { /* until token or start of word found */
11125 c = pgetc_macro();
11126
11127 if ((c != ' ') && (c != '\t')
11128#if ENABLE_ASH_ALIAS
11129 && (c != PEOA)
11130#endif
11131 ) {
11132 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011133 while ((c = pgetc()) != '\n' && c != PEOF)
11134 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011135 pungetc();
11136 } else if (c == '\\') {
11137 if (pgetc() != '\n') {
11138 pungetc();
11139 goto READTOKEN1;
11140 }
11141 startlinno = ++plinno;
11142 if (doprompt)
11143 setprompt(2);
11144 } else {
11145 const char *p
11146 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11147
11148 if (c != PEOF) {
11149 if (c == '\n') {
11150 plinno++;
11151 needprompt = doprompt;
11152 }
11153
11154 p = strchr(xxreadtoken_chars, c);
11155 if (p == NULL) {
11156 READTOKEN1:
11157 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11158 }
11159
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011160 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011161 if (pgetc() == *p) { /* double occurrence? */
11162 p += xxreadtoken_doubles + 1;
11163 } else {
11164 pungetc();
11165 }
11166 }
11167 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011168 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11169 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011170 }
11171 }
11172 } /* for */
11173}
11174#else
11175#define RETURN(token) return lasttoken = token
11176static int
11177xxreadtoken(void)
11178{
11179 int c;
11180
11181 if (tokpushback) {
11182 tokpushback = 0;
11183 return lasttoken;
11184 }
11185 if (needprompt) {
11186 setprompt(2);
11187 }
11188 startlinno = plinno;
11189 for (;;) { /* until token or start of word found */
11190 c = pgetc_macro();
11191 switch (c) {
11192 case ' ': case '\t':
11193#if ENABLE_ASH_ALIAS
11194 case PEOA:
11195#endif
11196 continue;
11197 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011198 while ((c = pgetc()) != '\n' && c != PEOF)
11199 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011200 pungetc();
11201 continue;
11202 case '\\':
11203 if (pgetc() == '\n') {
11204 startlinno = ++plinno;
11205 if (doprompt)
11206 setprompt(2);
11207 continue;
11208 }
11209 pungetc();
11210 goto breakloop;
11211 case '\n':
11212 plinno++;
11213 needprompt = doprompt;
11214 RETURN(TNL);
11215 case PEOF:
11216 RETURN(TEOF);
11217 case '&':
11218 if (pgetc() == '&')
11219 RETURN(TAND);
11220 pungetc();
11221 RETURN(TBACKGND);
11222 case '|':
11223 if (pgetc() == '|')
11224 RETURN(TOR);
11225 pungetc();
11226 RETURN(TPIPE);
11227 case ';':
11228 if (pgetc() == ';')
11229 RETURN(TENDCASE);
11230 pungetc();
11231 RETURN(TSEMI);
11232 case '(':
11233 RETURN(TLP);
11234 case ')':
11235 RETURN(TRP);
11236 default:
11237 goto breakloop;
11238 }
11239 }
11240 breakloop:
11241 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11242#undef RETURN
11243}
11244#endif /* NEW_xxreadtoken */
11245
11246static int
11247readtoken(void)
11248{
11249 int t;
11250#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011251 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011252#endif
11253
11254#if ENABLE_ASH_ALIAS
11255 top:
11256#endif
11257
11258 t = xxreadtoken();
11259
11260 /*
11261 * eat newlines
11262 */
11263 if (checkkwd & CHKNL) {
11264 while (t == TNL) {
11265 parseheredoc();
11266 t = xxreadtoken();
11267 }
11268 }
11269
11270 if (t != TWORD || quoteflag) {
11271 goto out;
11272 }
11273
11274 /*
11275 * check for keywords
11276 */
11277 if (checkkwd & CHKKWD) {
11278 const char *const *pp;
11279
11280 pp = findkwd(wordtext);
11281 if (pp) {
11282 lasttoken = t = pp - tokname_array;
11283 TRACE(("keyword %s recognized\n", tokname(t)));
11284 goto out;
11285 }
11286 }
11287
11288 if (checkkwd & CHKALIAS) {
11289#if ENABLE_ASH_ALIAS
11290 struct alias *ap;
11291 ap = lookupalias(wordtext, 1);
11292 if (ap != NULL) {
11293 if (*ap->val) {
11294 pushstring(ap->val, ap);
11295 }
11296 goto top;
11297 }
11298#endif
11299 }
11300 out:
11301 checkkwd = 0;
11302#if DEBUG
11303 if (!alreadyseen)
11304 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11305 else
11306 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11307#endif
11308 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011309}
11310
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011311static char
11312peektoken(void)
11313{
11314 int t;
11315
11316 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011317 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011318 return tokname_array[t][0];
11319}
Eric Andersencb57d552001-06-28 07:25:16 +000011320
11321/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011322 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11323 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011324 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011325static union node *
11326parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011327{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011328 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011329
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011330 tokpushback = 0;
11331 doprompt = interact;
11332 if (doprompt)
11333 setprompt(doprompt);
11334 needprompt = 0;
11335 t = readtoken();
11336 if (t == TEOF)
11337 return NEOF;
11338 if (t == TNL)
11339 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011340 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011341 return list(1);
11342}
11343
11344/*
11345 * Input any here documents.
11346 */
11347static void
11348parseheredoc(void)
11349{
11350 struct heredoc *here;
11351 union node *n;
11352
11353 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011354 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011355
11356 while (here) {
11357 if (needprompt) {
11358 setprompt(2);
11359 }
11360 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11361 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011362 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011363 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011364 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011365 n->narg.text = wordtext;
11366 n->narg.backquote = backquotelist;
11367 here->here->nhere.doc = n;
11368 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011369 }
Eric Andersencb57d552001-06-28 07:25:16 +000011370}
11371
11372
11373/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011374 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011375 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011376#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011377static const char *
11378expandstr(const char *ps)
11379{
11380 union node n;
11381
11382 /* XXX Fix (char *) cast. */
11383 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011384 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011385 popfile();
11386
11387 n.narg.type = NARG;
11388 n.narg.next = NULL;
11389 n.narg.text = wordtext;
11390 n.narg.backquote = backquotelist;
11391
11392 expandarg(&n, NULL, 0);
11393 return stackblock();
11394}
11395#endif
11396
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011397/*
11398 * Execute a command or commands contained in a string.
11399 */
11400static int
11401evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011402{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011403 union node *n;
11404 struct stackmark smark;
11405 int skip;
11406
11407 setinputstring(s);
11408 setstackmark(&smark);
11409
11410 skip = 0;
11411 while ((n = parsecmd(0)) != NEOF) {
11412 evaltree(n, 0);
11413 popstackmark(&smark);
11414 skip = evalskip;
11415 if (skip)
11416 break;
11417 }
11418 popfile();
11419
11420 skip &= mask;
11421 evalskip = skip;
11422 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011423}
11424
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011425/*
11426 * The eval command.
11427 */
11428static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011429evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011430{
11431 char *p;
11432 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011433
Denis Vlasenko68404f12008-03-17 09:00:54 +000011434 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011435 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011436 argv += 2;
11437 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011438 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011439 for (;;) {
11440 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011441 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011442 if (p == NULL)
11443 break;
11444 STPUTC(' ', concat);
11445 }
11446 STPUTC('\0', concat);
11447 p = grabstackstr(concat);
11448 }
11449 evalstring(p, ~SKIPEVAL);
11450
11451 }
11452 return exitstatus;
11453}
11454
11455/*
11456 * Read and execute commands. "Top" is nonzero for the top level command
11457 * loop; it turns on prompting if the shell is interactive.
11458 */
11459static int
11460cmdloop(int top)
11461{
11462 union node *n;
11463 struct stackmark smark;
11464 int inter;
11465 int numeof = 0;
11466
11467 TRACE(("cmdloop(%d) called\n", top));
11468 for (;;) {
11469 int skip;
11470
11471 setstackmark(&smark);
11472#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011473 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011474 showjobs(stderr, SHOW_CHANGED);
11475#endif
11476 inter = 0;
11477 if (iflag && top) {
11478 inter++;
11479#if ENABLE_ASH_MAIL
11480 chkmail();
11481#endif
11482 }
11483 n = parsecmd(inter);
11484 /* showtree(n); DEBUG */
11485 if (n == NEOF) {
11486 if (!top || numeof >= 50)
11487 break;
11488 if (!stoppedjobs()) {
11489 if (!Iflag)
11490 break;
11491 out2str("\nUse \"exit\" to leave shell.\n");
11492 }
11493 numeof++;
11494 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011495 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11496 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011497 numeof = 0;
11498 evaltree(n, 0);
11499 }
11500 popstackmark(&smark);
11501 skip = evalskip;
11502
11503 if (skip) {
11504 evalskip = 0;
11505 return skip & SKIPEVAL;
11506 }
11507 }
11508 return 0;
11509}
11510
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011511/*
11512 * Take commands from a file. To be compatible we should do a path
11513 * search for the file, which is necessary to find sub-commands.
11514 */
11515static char *
11516find_dot_file(char *name)
11517{
11518 char *fullname;
11519 const char *path = pathval();
11520 struct stat statb;
11521
11522 /* don't try this for absolute or relative paths */
11523 if (strchr(name, '/'))
11524 return name;
11525
11526 while ((fullname = padvance(&path, name)) != NULL) {
11527 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11528 /*
11529 * Don't bother freeing here, since it will
11530 * be freed by the caller.
11531 */
11532 return fullname;
11533 }
11534 stunalloc(fullname);
11535 }
11536
11537 /* not found in the PATH */
11538 ash_msg_and_raise_error("%s: not found", name);
11539 /* NOTREACHED */
11540}
11541
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011542static int
11543dotcmd(int argc, char **argv)
11544{
11545 struct strlist *sp;
11546 volatile struct shparam saveparam;
11547 int status = 0;
11548
11549 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011550 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011551
Denis Vlasenko68404f12008-03-17 09:00:54 +000011552 if (argv[1]) { /* That's what SVR2 does */
11553 char *fullname = find_dot_file(argv[1]);
11554 argv += 2;
11555 argc -= 2;
11556 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011557 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011558 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011559 shellparam.nparam = argc;
11560 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011561 };
11562
11563 setinputfile(fullname, INPUT_PUSH_FILE);
11564 commandname = fullname;
11565 cmdloop(0);
11566 popfile();
11567
Denis Vlasenko68404f12008-03-17 09:00:54 +000011568 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011569 freeparam(&shellparam);
11570 shellparam = saveparam;
11571 };
11572 status = exitstatus;
11573 }
11574 return status;
11575}
11576
11577static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011578exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011579{
11580 if (stoppedjobs())
11581 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011582 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011583 exitstatus = number(argv[1]);
11584 raise_exception(EXEXIT);
11585 /* NOTREACHED */
11586}
11587
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011588/*
11589 * Read a file containing shell functions.
11590 */
11591static void
11592readcmdfile(char *name)
11593{
11594 setinputfile(name, INPUT_PUSH_FILE);
11595 cmdloop(0);
11596 popfile();
11597}
11598
11599
Denis Vlasenkocc571512007-02-23 21:10:35 +000011600/* ============ find_command inplementation */
11601
11602/*
11603 * Resolve a command name. If you change this routine, you may have to
11604 * change the shellexec routine as well.
11605 */
11606static void
11607find_command(char *name, struct cmdentry *entry, int act, const char *path)
11608{
11609 struct tblentry *cmdp;
11610 int idx;
11611 int prev;
11612 char *fullname;
11613 struct stat statb;
11614 int e;
11615 int updatetbl;
11616 struct builtincmd *bcmd;
11617
11618 /* If name contains a slash, don't use PATH or hash table */
11619 if (strchr(name, '/') != NULL) {
11620 entry->u.index = -1;
11621 if (act & DO_ABS) {
11622 while (stat(name, &statb) < 0) {
11623#ifdef SYSV
11624 if (errno == EINTR)
11625 continue;
11626#endif
11627 entry->cmdtype = CMDUNKNOWN;
11628 return;
11629 }
11630 }
11631 entry->cmdtype = CMDNORMAL;
11632 return;
11633 }
11634
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011635/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011636
11637 updatetbl = (path == pathval());
11638 if (!updatetbl) {
11639 act |= DO_ALTPATH;
11640 if (strstr(path, "%builtin") != NULL)
11641 act |= DO_ALTBLTIN;
11642 }
11643
11644 /* If name is in the table, check answer will be ok */
11645 cmdp = cmdlookup(name, 0);
11646 if (cmdp != NULL) {
11647 int bit;
11648
11649 switch (cmdp->cmdtype) {
11650 default:
11651#if DEBUG
11652 abort();
11653#endif
11654 case CMDNORMAL:
11655 bit = DO_ALTPATH;
11656 break;
11657 case CMDFUNCTION:
11658 bit = DO_NOFUNC;
11659 break;
11660 case CMDBUILTIN:
11661 bit = DO_ALTBLTIN;
11662 break;
11663 }
11664 if (act & bit) {
11665 updatetbl = 0;
11666 cmdp = NULL;
11667 } else if (cmdp->rehash == 0)
11668 /* if not invalidated by cd, we're done */
11669 goto success;
11670 }
11671
11672 /* If %builtin not in path, check for builtin next */
11673 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011674 if (bcmd) {
11675 if (IS_BUILTIN_REGULAR(bcmd))
11676 goto builtin_success;
11677 if (act & DO_ALTPATH) {
11678 if (!(act & DO_ALTBLTIN))
11679 goto builtin_success;
11680 } else if (builtinloc <= 0) {
11681 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011682 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011683 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011684
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011685#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011686 {
11687 int applet_no = find_applet_by_name(name);
11688 if (applet_no >= 0) {
11689 entry->cmdtype = CMDNORMAL;
11690 entry->u.index = -2 - applet_no;
11691 return;
11692 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011693 }
11694#endif
11695
Denis Vlasenkocc571512007-02-23 21:10:35 +000011696 /* We have to search path. */
11697 prev = -1; /* where to start */
11698 if (cmdp && cmdp->rehash) { /* doing a rehash */
11699 if (cmdp->cmdtype == CMDBUILTIN)
11700 prev = builtinloc;
11701 else
11702 prev = cmdp->param.index;
11703 }
11704
11705 e = ENOENT;
11706 idx = -1;
11707 loop:
11708 while ((fullname = padvance(&path, name)) != NULL) {
11709 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011710 /* NB: code below will still use fullname
11711 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011712 idx++;
11713 if (pathopt) {
11714 if (prefix(pathopt, "builtin")) {
11715 if (bcmd)
11716 goto builtin_success;
11717 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011718 }
11719 if ((act & DO_NOFUNC)
11720 || !prefix(pathopt, "func")
11721 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011722 continue;
11723 }
11724 }
11725 /* if rehash, don't redo absolute path names */
11726 if (fullname[0] == '/' && idx <= prev) {
11727 if (idx < prev)
11728 continue;
11729 TRACE(("searchexec \"%s\": no change\n", name));
11730 goto success;
11731 }
11732 while (stat(fullname, &statb) < 0) {
11733#ifdef SYSV
11734 if (errno == EINTR)
11735 continue;
11736#endif
11737 if (errno != ENOENT && errno != ENOTDIR)
11738 e = errno;
11739 goto loop;
11740 }
11741 e = EACCES; /* if we fail, this will be the error */
11742 if (!S_ISREG(statb.st_mode))
11743 continue;
11744 if (pathopt) { /* this is a %func directory */
11745 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011746 /* NB: stalloc will return space pointed by fullname
11747 * (because we don't have any intervening allocations
11748 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011749 readcmdfile(fullname);
11750 cmdp = cmdlookup(name, 0);
11751 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11752 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11753 stunalloc(fullname);
11754 goto success;
11755 }
11756 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11757 if (!updatetbl) {
11758 entry->cmdtype = CMDNORMAL;
11759 entry->u.index = idx;
11760 return;
11761 }
11762 INT_OFF;
11763 cmdp = cmdlookup(name, 1);
11764 cmdp->cmdtype = CMDNORMAL;
11765 cmdp->param.index = idx;
11766 INT_ON;
11767 goto success;
11768 }
11769
11770 /* We failed. If there was an entry for this command, delete it */
11771 if (cmdp && updatetbl)
11772 delete_cmd_entry();
11773 if (act & DO_ERR)
11774 ash_msg("%s: %s", name, errmsg(e, "not found"));
11775 entry->cmdtype = CMDUNKNOWN;
11776 return;
11777
11778 builtin_success:
11779 if (!updatetbl) {
11780 entry->cmdtype = CMDBUILTIN;
11781 entry->u.cmd = bcmd;
11782 return;
11783 }
11784 INT_OFF;
11785 cmdp = cmdlookup(name, 1);
11786 cmdp->cmdtype = CMDBUILTIN;
11787 cmdp->param.cmd = bcmd;
11788 INT_ON;
11789 success:
11790 cmdp->rehash = 0;
11791 entry->cmdtype = cmdp->cmdtype;
11792 entry->u = cmdp->param;
11793}
11794
11795
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011796/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011797
Eric Andersencb57d552001-06-28 07:25:16 +000011798/*
Eric Andersencb57d552001-06-28 07:25:16 +000011799 * The trap builtin.
11800 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011801static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011802trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011803{
11804 char *action;
11805 char **ap;
11806 int signo;
11807
Eric Andersenc470f442003-07-28 09:56:35 +000011808 nextopt(nullstr);
11809 ap = argptr;
11810 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011811 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011812 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011813 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011814
Rob Landleyc9c1a412006-07-12 19:17:55 +000011815 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011816 out1fmt("trap -- %s %s\n",
11817 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011818 }
11819 }
11820 return 0;
11821 }
Eric Andersenc470f442003-07-28 09:56:35 +000011822 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011823 action = NULL;
11824 else
11825 action = *ap++;
11826 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011827 signo = get_signum(*ap);
11828 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011829 ash_msg_and_raise_error("%s: bad trap", *ap);
11830 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011831 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011832 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011833 action = NULL;
11834 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011835 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011836 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011837 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011838 trap[signo] = action;
11839 if (signo != 0)
11840 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011841 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011842 ap++;
11843 }
11844 return 0;
11845}
11846
Eric Andersenc470f442003-07-28 09:56:35 +000011847
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011848/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011849
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011850#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011851/*
11852 * Lists available builtins
11853 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011854static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011855helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011856{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011857 unsigned col;
11858 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011859
11860 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011861 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011862 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011863 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011864 if (col > 60) {
11865 out1fmt("\n");
11866 col = 0;
11867 }
11868 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011869#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011870 {
11871 const char *a = applet_names;
11872 while (*a) {
11873 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11874 if (col > 60) {
11875 out1fmt("\n");
11876 col = 0;
11877 }
11878 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011879 }
11880 }
11881#endif
11882 out1fmt("\n\n");
11883 return EXIT_SUCCESS;
11884}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011885#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011886
Eric Andersencb57d552001-06-28 07:25:16 +000011887/*
Eric Andersencb57d552001-06-28 07:25:16 +000011888 * The export and readonly commands.
11889 */
Eric Andersenc470f442003-07-28 09:56:35 +000011890static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011891exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011892{
11893 struct var *vp;
11894 char *name;
11895 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011896 char **aptr;
11897 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011898
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011899 if (nextopt("p") != 'p') {
11900 aptr = argptr;
11901 name = *aptr;
11902 if (name) {
11903 do {
11904 p = strchr(name, '=');
11905 if (p != NULL) {
11906 p++;
11907 } else {
11908 vp = *findvar(hashvar(name), name);
11909 if (vp) {
11910 vp->flags |= flag;
11911 continue;
11912 }
Eric Andersencb57d552001-06-28 07:25:16 +000011913 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011914 setvar(name, p, flag);
11915 } while ((name = *++aptr) != NULL);
11916 return 0;
11917 }
Eric Andersencb57d552001-06-28 07:25:16 +000011918 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011919 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011920 return 0;
11921}
11922
Eric Andersencb57d552001-06-28 07:25:16 +000011923/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011924 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011925 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011926static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011927unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011928{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011929 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011930
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011931 cmdp = cmdlookup(name, 0);
11932 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11933 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011934}
11935
Eric Andersencb57d552001-06-28 07:25:16 +000011936/*
Eric Andersencb57d552001-06-28 07:25:16 +000011937 * The unset builtin command. We unset the function before we unset the
11938 * variable to allow a function to be unset when there is a readonly variable
11939 * with the same name.
11940 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011941static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011942unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011943{
11944 char **ap;
11945 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011946 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011947 int ret = 0;
11948
11949 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011950 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011951 }
Eric Andersencb57d552001-06-28 07:25:16 +000011952
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011953 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011954 if (flag != 'f') {
11955 i = unsetvar(*ap);
11956 ret |= i;
11957 if (!(i & 2))
11958 continue;
11959 }
11960 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011961 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011962 }
Eric Andersenc470f442003-07-28 09:56:35 +000011963 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011964}
11965
11966
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011967/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011968
Eric Andersenc470f442003-07-28 09:56:35 +000011969#include <sys/times.h>
11970
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011971static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011972 ' ', offsetof(struct tms, tms_utime),
11973 '\n', offsetof(struct tms, tms_stime),
11974 ' ', offsetof(struct tms, tms_cutime),
11975 '\n', offsetof(struct tms, tms_cstime),
11976 0
11977};
Eric Andersencb57d552001-06-28 07:25:16 +000011978
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011979static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011980timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011981{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011982 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011983 const unsigned char *p;
11984 struct tms buf;
11985
11986 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011987 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011988
11989 p = timescmd_str;
11990 do {
11991 t = *(clock_t *)(((char *) &buf) + p[1]);
11992 s = t / clk_tck;
11993 out1fmt("%ldm%ld.%.3lds%c",
11994 s/60, s%60,
11995 ((t - s * clk_tck) * 1000) / clk_tck,
11996 p[0]);
11997 } while (*(p += 2));
11998
Eric Andersencb57d552001-06-28 07:25:16 +000011999 return 0;
12000}
12001
Denis Vlasenko131ae172007-02-18 13:00:19 +000012002#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012003static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012004dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012005{
Eric Andersened9ecf72004-06-22 08:29:45 +000012006 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012007 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012008
Denis Vlasenkob012b102007-02-19 22:43:01 +000012009 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012010 result = arith(s, &errcode);
12011 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012012 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012013 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012014 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012015 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012016 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012017 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012018 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012019 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012020 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012021
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012022 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012023}
Eric Andersenc470f442003-07-28 09:56:35 +000012024
Eric Andersenc470f442003-07-28 09:56:35 +000012025/*
Eric Andersen90898442003-08-06 11:20:52 +000012026 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12027 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12028 *
12029 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012030 */
12031static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012032letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012033{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012034 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012035
Denis Vlasenko68404f12008-03-17 09:00:54 +000012036 argv++;
12037 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012038 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012039 do {
12040 i = dash_arith(*argv);
12041 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012042
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012043 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012044}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012045#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012046
Eric Andersenc470f442003-07-28 09:56:35 +000012047
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012048/* ============ miscbltin.c
12049 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012050 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012051 */
12052
12053#undef rflag
12054
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012055#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012056typedef enum __rlimit_resource rlim_t;
12057#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012058
Eric Andersenc470f442003-07-28 09:56:35 +000012059/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012060 * The read builtin. Options:
12061 * -r Do not interpret '\' specially
12062 * -s Turn off echo (tty only)
12063 * -n NCHARS Read NCHARS max
12064 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12065 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12066 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012067 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012068 * TODO: bash also has:
12069 * -a ARRAY Read into array[0],[1],etc
12070 * -d DELIM End on DELIM char, not newline
12071 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012072 */
Eric Andersenc470f442003-07-28 09:56:35 +000012073static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012074readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012075{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012076 static const char *const arg_REPLY[] = { "REPLY", NULL };
12077
Eric Andersenc470f442003-07-28 09:56:35 +000012078 char **ap;
12079 int backslash;
12080 char c;
12081 int rflag;
12082 char *prompt;
12083 const char *ifs;
12084 char *p;
12085 int startword;
12086 int status;
12087 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012088 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012089#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012090 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012091 int silent = 0;
12092 struct termios tty, old_tty;
12093#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012094#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012095 unsigned end_ms = 0;
12096 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012097#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012098
12099 rflag = 0;
12100 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012101 while ((i = nextopt("p:u:r"
12102 USE_ASH_READ_TIMEOUT("t:")
12103 USE_ASH_READ_NCHARS("n:s")
12104 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012105 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012106 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012107 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012108 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012109#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012110 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012111 nchars = bb_strtou(optionarg, NULL, 10);
12112 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012113 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012114 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012115 break;
12116 case 's':
12117 silent = 1;
12118 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012119#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012120#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012121 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012122 timeout = bb_strtou(optionarg, NULL, 10);
12123 if (errno || timeout > UINT_MAX / 2048)
12124 ash_msg_and_raise_error("invalid timeout");
12125 timeout *= 1000;
12126#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012127 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012128 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012129 /* EINVAL means number is ok, but not terminated by NUL */
12130 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012131 char *p2;
12132 if (*++p) {
12133 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012134 ts.tv_usec = bb_strtou(p, &p2, 10);
12135 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012136 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012137 scale = p2 - p;
12138 /* normalize to usec */
12139 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012140 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012141 while (scale++ < 6)
12142 ts.tv_usec *= 10;
12143 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012144 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012145 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012146 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012147 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012148 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012149 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012150#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012151 break;
12152#endif
12153 case 'r':
12154 rflag = 1;
12155 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012156 case 'u':
12157 fd = bb_strtou(optionarg, NULL, 10);
12158 if (fd < 0 || errno)
12159 ash_msg_and_raise_error("invalid file descriptor");
12160 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012161 default:
12162 break;
12163 }
Eric Andersenc470f442003-07-28 09:56:35 +000012164 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012165 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012166 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012167 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012168 ap = argptr;
12169 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012170 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012171 ifs = bltinlookup("IFS");
12172 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012173 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012174#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012175 tcgetattr(fd, &tty);
12176 old_tty = tty;
12177 if (nchars || silent) {
12178 if (nchars) {
12179 tty.c_lflag &= ~ICANON;
12180 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012181 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012182 if (silent) {
12183 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12184 }
12185 /* if tcgetattr failed, tcsetattr will fail too.
12186 * Ignoring, it's harmless. */
12187 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012188 }
12189#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012190
Eric Andersenc470f442003-07-28 09:56:35 +000012191 status = 0;
12192 startword = 1;
12193 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012194#if ENABLE_ASH_READ_TIMEOUT
12195 if (timeout) /* NB: ensuring end_ms is nonzero */
12196 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12197#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012198 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012199 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012200#if ENABLE_ASH_READ_TIMEOUT
12201 if (end_ms) {
12202 struct pollfd pfd[1];
12203 pfd[0].fd = fd;
12204 pfd[0].events = POLLIN;
12205 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12206 if ((int)timeout <= 0 /* already late? */
12207 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12208 ) { /* timed out! */
12209#if ENABLE_ASH_READ_NCHARS
12210 tcsetattr(fd, TCSANOW, &old_tty);
12211#endif
12212 return 1;
12213 }
12214 }
12215#endif
12216 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012217 status = 1;
12218 break;
12219 }
12220 if (c == '\0')
12221 continue;
12222 if (backslash) {
12223 backslash = 0;
12224 if (c != '\n')
12225 goto put;
12226 continue;
12227 }
12228 if (!rflag && c == '\\') {
12229 backslash++;
12230 continue;
12231 }
12232 if (c == '\n')
12233 break;
12234 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12235 continue;
12236 }
12237 startword = 0;
12238 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12239 STACKSTRNUL(p);
12240 setvar(*ap, stackblock(), 0);
12241 ap++;
12242 startword = 1;
12243 STARTSTACKSTR(p);
12244 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012245 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012246 STPUTC(c, p);
12247 }
12248 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012249/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012250#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012251 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012252#else
12253 while (1);
12254#endif
12255
12256#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012257 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012258#endif
12259
Eric Andersenc470f442003-07-28 09:56:35 +000012260 STACKSTRNUL(p);
12261 /* Remove trailing blanks */
12262 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12263 *p = '\0';
12264 setvar(*ap, stackblock(), 0);
12265 while (*++ap != NULL)
12266 setvar(*ap, nullstr, 0);
12267 return status;
12268}
12269
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012270static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012271umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012272{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012273 static const char permuser[3] ALIGN1 = "ugo";
12274 static const char permmode[3] ALIGN1 = "rwx";
12275 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012276 S_IRUSR, S_IWUSR, S_IXUSR,
12277 S_IRGRP, S_IWGRP, S_IXGRP,
12278 S_IROTH, S_IWOTH, S_IXOTH
12279 };
12280
12281 char *ap;
12282 mode_t mask;
12283 int i;
12284 int symbolic_mode = 0;
12285
12286 while (nextopt("S") != '\0') {
12287 symbolic_mode = 1;
12288 }
12289
Denis Vlasenkob012b102007-02-19 22:43:01 +000012290 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012291 mask = umask(0);
12292 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012293 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012294
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012295 ap = *argptr;
12296 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012297 if (symbolic_mode) {
12298 char buf[18];
12299 char *p = buf;
12300
12301 for (i = 0; i < 3; i++) {
12302 int j;
12303
12304 *p++ = permuser[i];
12305 *p++ = '=';
12306 for (j = 0; j < 3; j++) {
12307 if ((mask & permmask[3 * i + j]) == 0) {
12308 *p++ = permmode[j];
12309 }
12310 }
12311 *p++ = ',';
12312 }
12313 *--p = 0;
12314 puts(buf);
12315 } else {
12316 out1fmt("%.4o\n", mask);
12317 }
12318 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012319 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012320 mask = 0;
12321 do {
12322 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012323 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012324 mask = (mask << 3) + (*ap - '0');
12325 } while (*++ap != '\0');
12326 umask(mask);
12327 } else {
12328 mask = ~mask & 0777;
12329 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012330 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012331 }
12332 umask(~mask & 0777);
12333 }
12334 }
12335 return 0;
12336}
12337
12338/*
12339 * ulimit builtin
12340 *
12341 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12342 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12343 * ash by J.T. Conklin.
12344 *
12345 * Public domain.
12346 */
12347
12348struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012349 uint8_t cmd; /* RLIMIT_xxx fit into it */
12350 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012351 char option;
12352};
12353
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012354static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012355#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012356 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012357#endif
12358#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012359 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012360#endif
12361#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012362 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012363#endif
12364#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012365 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012366#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012367#ifdef RLIMIT_CORE
12368 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012369#endif
12370#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012371 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012372#endif
12373#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012374 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012375#endif
12376#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012377 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012378#endif
12379#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012380 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012381#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012382#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012383 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012384#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012385#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012386 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012387#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012388};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012389static const char limits_name[] =
12390#ifdef RLIMIT_CPU
12391 "time(seconds)" "\0"
12392#endif
12393#ifdef RLIMIT_FSIZE
12394 "file(blocks)" "\0"
12395#endif
12396#ifdef RLIMIT_DATA
12397 "data(kb)" "\0"
12398#endif
12399#ifdef RLIMIT_STACK
12400 "stack(kb)" "\0"
12401#endif
12402#ifdef RLIMIT_CORE
12403 "coredump(blocks)" "\0"
12404#endif
12405#ifdef RLIMIT_RSS
12406 "memory(kb)" "\0"
12407#endif
12408#ifdef RLIMIT_MEMLOCK
12409 "locked memory(kb)" "\0"
12410#endif
12411#ifdef RLIMIT_NPROC
12412 "process" "\0"
12413#endif
12414#ifdef RLIMIT_NOFILE
12415 "nofiles" "\0"
12416#endif
12417#ifdef RLIMIT_AS
12418 "vmemory(kb)" "\0"
12419#endif
12420#ifdef RLIMIT_LOCKS
12421 "locks" "\0"
12422#endif
12423;
Eric Andersenc470f442003-07-28 09:56:35 +000012424
Glenn L McGrath76620622004-01-13 10:19:37 +000012425enum limtype { SOFT = 0x1, HARD = 0x2 };
12426
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012427static void
12428printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012429 const struct limits *l)
12430{
12431 rlim_t val;
12432
12433 val = limit->rlim_max;
12434 if (how & SOFT)
12435 val = limit->rlim_cur;
12436
12437 if (val == RLIM_INFINITY)
12438 out1fmt("unlimited\n");
12439 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012440 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012441 out1fmt("%lld\n", (long long) val);
12442 }
12443}
12444
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012445static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012446ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012447{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012448 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012449 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012450 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012451 const struct limits *l;
12452 int set, all = 0;
12453 int optc, what;
12454 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012455
12456 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012457 while ((optc = nextopt("HSa"
12458#ifdef RLIMIT_CPU
12459 "t"
12460#endif
12461#ifdef RLIMIT_FSIZE
12462 "f"
12463#endif
12464#ifdef RLIMIT_DATA
12465 "d"
12466#endif
12467#ifdef RLIMIT_STACK
12468 "s"
12469#endif
12470#ifdef RLIMIT_CORE
12471 "c"
12472#endif
12473#ifdef RLIMIT_RSS
12474 "m"
12475#endif
12476#ifdef RLIMIT_MEMLOCK
12477 "l"
12478#endif
12479#ifdef RLIMIT_NPROC
12480 "p"
12481#endif
12482#ifdef RLIMIT_NOFILE
12483 "n"
12484#endif
12485#ifdef RLIMIT_AS
12486 "v"
12487#endif
12488#ifdef RLIMIT_LOCKS
12489 "w"
12490#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012491 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012492 switch (optc) {
12493 case 'H':
12494 how = HARD;
12495 break;
12496 case 'S':
12497 how = SOFT;
12498 break;
12499 case 'a':
12500 all = 1;
12501 break;
12502 default:
12503 what = optc;
12504 }
12505
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012506 for (l = limits_tbl; l->option != what; l++)
12507 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012508
12509 set = *argptr ? 1 : 0;
12510 if (set) {
12511 char *p = *argptr;
12512
12513 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012514 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012515 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012516 val = RLIM_INFINITY;
12517 else {
12518 val = (rlim_t) 0;
12519
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012520 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012521 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012522 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012523 if (val < (rlim_t) 0)
12524 break;
12525 }
12526 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012527 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012528 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012529 }
12530 }
12531 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012532 const char *lname = limits_name;
12533 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012534 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012535 out1fmt("%-20s ", lname);
12536 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012537 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012538 }
12539 return 0;
12540 }
12541
12542 getrlimit(l->cmd, &limit);
12543 if (set) {
12544 if (how & HARD)
12545 limit.rlim_max = val;
12546 if (how & SOFT)
12547 limit.rlim_cur = val;
12548 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012549 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012550 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012551 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012552 }
12553 return 0;
12554}
12555
Eric Andersen90898442003-08-06 11:20:52 +000012556
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012557/* ============ Math support */
12558
Denis Vlasenko131ae172007-02-18 13:00:19 +000012559#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012560
12561/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12562
12563 Permission is hereby granted, free of charge, to any person obtaining
12564 a copy of this software and associated documentation files (the
12565 "Software"), to deal in the Software without restriction, including
12566 without limitation the rights to use, copy, modify, merge, publish,
12567 distribute, sublicense, and/or sell copies of the Software, and to
12568 permit persons to whom the Software is furnished to do so, subject to
12569 the following conditions:
12570
12571 The above copyright notice and this permission notice shall be
12572 included in all copies or substantial portions of the Software.
12573
12574 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12575 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12576 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12577 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12578 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12579 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12580 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12581*/
12582
12583/* This is my infix parser/evaluator. It is optimized for size, intended
12584 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012585 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012586 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012587 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012588 * be that which POSIX specifies for shells. */
12589
12590/* The code uses a simple two-stack algorithm. See
12591 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012592 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012593 * this is based (this code differs in that it applies operators immediately
12594 * to the stack instead of adding them to a queue to end up with an
12595 * expression). */
12596
12597/* To use the routine, call it with an expression string and error return
12598 * pointer */
12599
12600/*
12601 * Aug 24, 2001 Manuel Novoa III
12602 *
12603 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12604 *
12605 * 1) In arith_apply():
12606 * a) Cached values of *numptr and &(numptr[-1]).
12607 * b) Removed redundant test for zero denominator.
12608 *
12609 * 2) In arith():
12610 * a) Eliminated redundant code for processing operator tokens by moving
12611 * to a table-based implementation. Also folded handling of parens
12612 * into the table.
12613 * b) Combined all 3 loops which called arith_apply to reduce generated
12614 * code size at the cost of speed.
12615 *
12616 * 3) The following expressions were treated as valid by the original code:
12617 * 1() , 0! , 1 ( *3 ) .
12618 * These bugs have been fixed by internally enclosing the expression in
12619 * parens and then checking that all binary ops and right parens are
12620 * preceded by a valid expression (NUM_TOKEN).
12621 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012622 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012623 * ctype's isspace() if it is used by another busybox applet or if additional
12624 * whitespace chars should be considered. Look below the "#include"s for a
12625 * precompiler test.
12626 */
12627
12628/*
12629 * Aug 26, 2001 Manuel Novoa III
12630 *
12631 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12632 *
12633 * Merge in Aaron's comments previously posted to the busybox list,
12634 * modified slightly to take account of my changes to the code.
12635 *
12636 */
12637
12638/*
12639 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12640 *
12641 * - allow access to variable,
12642 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12643 * - realize assign syntax (VAR=expr, +=, *= etc)
12644 * - realize exponentiation (** operator)
12645 * - realize comma separated - expr, expr
12646 * - realise ++expr --expr expr++ expr--
12647 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012648 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012649 * - was restored loses XOR operator
12650 * - remove one goto label, added three ;-)
12651 * - protect $((num num)) as true zero expr (Manuel`s error)
12652 * - always use special isspace(), see comment from bash ;-)
12653 */
12654
Eric Andersen90898442003-08-06 11:20:52 +000012655#define arith_isspace(arithval) \
12656 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12657
Eric Andersen90898442003-08-06 11:20:52 +000012658typedef unsigned char operator;
12659
12660/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012661 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012662 * precedence. The ID portion is so that multiple operators can have the
12663 * same precedence, ensuring that the leftmost one is evaluated first.
12664 * Consider * and /. */
12665
12666#define tok_decl(prec,id) (((id)<<5)|(prec))
12667#define PREC(op) ((op) & 0x1F)
12668
12669#define TOK_LPAREN tok_decl(0,0)
12670
12671#define TOK_COMMA tok_decl(1,0)
12672
12673#define TOK_ASSIGN tok_decl(2,0)
12674#define TOK_AND_ASSIGN tok_decl(2,1)
12675#define TOK_OR_ASSIGN tok_decl(2,2)
12676#define TOK_XOR_ASSIGN tok_decl(2,3)
12677#define TOK_PLUS_ASSIGN tok_decl(2,4)
12678#define TOK_MINUS_ASSIGN tok_decl(2,5)
12679#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12680#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12681
12682#define TOK_MUL_ASSIGN tok_decl(3,0)
12683#define TOK_DIV_ASSIGN tok_decl(3,1)
12684#define TOK_REM_ASSIGN tok_decl(3,2)
12685
12686/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012687#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012688
12689/* conditional is right associativity too */
12690#define TOK_CONDITIONAL tok_decl(4,0)
12691#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12692
12693#define TOK_OR tok_decl(5,0)
12694
12695#define TOK_AND tok_decl(6,0)
12696
12697#define TOK_BOR tok_decl(7,0)
12698
12699#define TOK_BXOR tok_decl(8,0)
12700
12701#define TOK_BAND tok_decl(9,0)
12702
12703#define TOK_EQ tok_decl(10,0)
12704#define TOK_NE tok_decl(10,1)
12705
12706#define TOK_LT tok_decl(11,0)
12707#define TOK_GT tok_decl(11,1)
12708#define TOK_GE tok_decl(11,2)
12709#define TOK_LE tok_decl(11,3)
12710
12711#define TOK_LSHIFT tok_decl(12,0)
12712#define TOK_RSHIFT tok_decl(12,1)
12713
12714#define TOK_ADD tok_decl(13,0)
12715#define TOK_SUB tok_decl(13,1)
12716
12717#define TOK_MUL tok_decl(14,0)
12718#define TOK_DIV tok_decl(14,1)
12719#define TOK_REM tok_decl(14,2)
12720
12721/* exponent is right associativity */
12722#define TOK_EXPONENT tok_decl(15,1)
12723
12724/* For now unary operators. */
12725#define UNARYPREC 16
12726#define TOK_BNOT tok_decl(UNARYPREC,0)
12727#define TOK_NOT tok_decl(UNARYPREC,1)
12728
12729#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12730#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12731
12732#define PREC_PRE (UNARYPREC+2)
12733
12734#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12735#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12736
12737#define PREC_POST (UNARYPREC+3)
12738
12739#define TOK_POST_INC tok_decl(PREC_POST, 0)
12740#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12741
12742#define SPEC_PREC (UNARYPREC+4)
12743
12744#define TOK_NUM tok_decl(SPEC_PREC, 0)
12745#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12746
12747#define NUMPTR (*numstackptr)
12748
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012749static int
12750tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012751{
12752 operator prec = PREC(op);
12753
12754 convert_prec_is_assing(prec);
12755 return (prec == PREC(TOK_ASSIGN) ||
12756 prec == PREC_PRE || prec == PREC_POST);
12757}
12758
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012759static int
12760is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012761{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012762 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12763 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012764}
12765
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012766typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012767 arith_t val;
12768 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012769 char contidional_second_val_initialized;
12770 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012771 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012772} v_n_t;
12773
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012774typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012775 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012776 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012777} chk_var_recursive_looped_t;
12778
12779static chk_var_recursive_looped_t *prev_chk_var_recursive;
12780
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012781static int
12782arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012783{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012784 if (t->var) {
12785 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012786
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012787 if (p) {
12788 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012789
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012790 /* recursive try as expression */
12791 chk_var_recursive_looped_t *cur;
12792 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012793
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012794 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12795 if (strcmp(cur->var, t->var) == 0) {
12796 /* expression recursion loop detected */
12797 return -5;
12798 }
12799 }
12800 /* save current lookuped var name */
12801 cur = prev_chk_var_recursive;
12802 cur_save.var = t->var;
12803 cur_save.next = cur;
12804 prev_chk_var_recursive = &cur_save;
12805
12806 t->val = arith (p, &errcode);
12807 /* restore previous ptr after recursiving */
12808 prev_chk_var_recursive = cur;
12809 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012810 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012811 /* allow undefined var as 0 */
12812 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012813 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012814 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012815}
12816
12817/* "applying" a token means performing it on the top elements on the integer
12818 * stack. For a unary operator it will only change the top element, but a
12819 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012820static int
12821arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012822{
Eric Andersen90898442003-08-06 11:20:52 +000012823 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012824 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012825 int ret_arith_lookup_val;
12826
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012827 /* There is no operator that can work without arguments */
12828 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012829 numptr_m1 = NUMPTR - 1;
12830
12831 /* check operand is var with noninteger value */
12832 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012833 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012834 return ret_arith_lookup_val;
12835
12836 rez = numptr_m1->val;
12837 if (op == TOK_UMINUS)
12838 rez *= -1;
12839 else if (op == TOK_NOT)
12840 rez = !rez;
12841 else if (op == TOK_BNOT)
12842 rez = ~rez;
12843 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12844 rez++;
12845 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12846 rez--;
12847 else if (op != TOK_UPLUS) {
12848 /* Binary operators */
12849
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012850 /* check and binary operators need two arguments */
12851 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012852
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012853 /* ... and they pop one */
12854 --NUMPTR;
12855 numptr_val = rez;
12856 if (op == TOK_CONDITIONAL) {
12857 if (! numptr_m1->contidional_second_val_initialized) {
12858 /* protect $((expr1 ? expr2)) without ": expr" */
12859 goto err;
12860 }
12861 rez = numptr_m1->contidional_second_val;
12862 } else if (numptr_m1->contidional_second_val_initialized) {
12863 /* protect $((expr1 : expr2)) without "expr ? " */
12864 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012865 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012866 numptr_m1 = NUMPTR - 1;
12867 if (op != TOK_ASSIGN) {
12868 /* check operand is var with noninteger value for not '=' */
12869 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12870 if (ret_arith_lookup_val)
12871 return ret_arith_lookup_val;
12872 }
12873 if (op == TOK_CONDITIONAL) {
12874 numptr_m1->contidional_second_val = rez;
12875 }
12876 rez = numptr_m1->val;
12877 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012878 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012879 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012880 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012881 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012882 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012883 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012884 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012885 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012886 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012887 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012888 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012889 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012890 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012891 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012892 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012893 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012894 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012895 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012896 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012897 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012898 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012899 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012900 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012901 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012902 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012903 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012904 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012905 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012906 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012908 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012909 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012910 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012911 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012912 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012913 /* protect $((expr : expr)) without "expr ? " */
12914 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012915 }
12916 numptr_m1->contidional_second_val_initialized = op;
12917 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012918 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012919 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012920 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012921 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012922 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012923 return -3; /* exponent less than 0 */
12924 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012925 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012926
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012927 if (numptr_val)
12928 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012929 c *= rez;
12930 rez = c;
12931 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012932 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012933 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012934 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012935 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012936 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012937 rez %= numptr_val;
12938 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012939 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012940 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012941
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012942 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012943 /* Hmm, 1=2 ? */
12944 goto err;
12945 }
12946 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012947#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012948 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012949#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012950 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012951#endif
Eric Andersen90898442003-08-06 11:20:52 +000012952 setvar(numptr_m1->var, buf, 0);
12953 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012954 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012955 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012956 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012957 rez++;
12958 }
12959 numptr_m1->val = rez;
12960 /* protect geting var value, is number now */
12961 numptr_m1->var = NULL;
12962 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012963 err:
12964 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012965}
12966
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012967/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012968static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012969 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12970 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12971 '<','<', 0, TOK_LSHIFT,
12972 '>','>', 0, TOK_RSHIFT,
12973 '|','|', 0, TOK_OR,
12974 '&','&', 0, TOK_AND,
12975 '!','=', 0, TOK_NE,
12976 '<','=', 0, TOK_LE,
12977 '>','=', 0, TOK_GE,
12978 '=','=', 0, TOK_EQ,
12979 '|','=', 0, TOK_OR_ASSIGN,
12980 '&','=', 0, TOK_AND_ASSIGN,
12981 '*','=', 0, TOK_MUL_ASSIGN,
12982 '/','=', 0, TOK_DIV_ASSIGN,
12983 '%','=', 0, TOK_REM_ASSIGN,
12984 '+','=', 0, TOK_PLUS_ASSIGN,
12985 '-','=', 0, TOK_MINUS_ASSIGN,
12986 '-','-', 0, TOK_POST_DEC,
12987 '^','=', 0, TOK_XOR_ASSIGN,
12988 '+','+', 0, TOK_POST_INC,
12989 '*','*', 0, TOK_EXPONENT,
12990 '!', 0, TOK_NOT,
12991 '<', 0, TOK_LT,
12992 '>', 0, TOK_GT,
12993 '=', 0, TOK_ASSIGN,
12994 '|', 0, TOK_BOR,
12995 '&', 0, TOK_BAND,
12996 '*', 0, TOK_MUL,
12997 '/', 0, TOK_DIV,
12998 '%', 0, TOK_REM,
12999 '+', 0, TOK_ADD,
13000 '-', 0, TOK_SUB,
13001 '^', 0, TOK_BXOR,
13002 /* uniq */
13003 '~', 0, TOK_BNOT,
13004 ',', 0, TOK_COMMA,
13005 '?', 0, TOK_CONDITIONAL,
13006 ':', 0, TOK_CONDITIONAL_SEP,
13007 ')', 0, TOK_RPAREN,
13008 '(', 0, TOK_LPAREN,
13009 0
13010};
13011/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013012#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013013
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013014static arith_t
13015arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013016{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 char arithval; /* Current character under analysis */
13018 operator lasttok, op;
13019 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013020 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013021 const char *p = endexpression;
13022 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013023 v_n_t *numstack, *numstackptr;
13024 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013025
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013026 /* Stack of integers */
13027 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13028 * in any given correct or incorrect expression is left as an exercise to
13029 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013030 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013031 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013032 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013033
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013034 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13035 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013036
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013037 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013038 arithval = *expr;
13039 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013040 if (p == endexpression) {
13041 /* Null expression. */
13042 return 0;
13043 }
13044
13045 /* This is only reached after all tokens have been extracted from the
13046 * input stream. If there are still tokens on the operator stack, they
13047 * are to be applied in order. At the end, there should be a final
13048 * result on the integer stack */
13049
13050 if (expr != endexpression + 1) {
13051 /* If we haven't done so already, */
13052 /* append a closing right paren */
13053 expr = endexpression;
13054 /* and let the loop process it. */
13055 continue;
13056 }
13057 /* At this point, we're done with the expression. */
13058 if (numstackptr != numstack+1) {
13059 /* ... but if there isn't, it's bad */
13060 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013061 *perrcode = -1;
13062 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013063 }
13064 if (numstack->var) {
13065 /* expression is $((var)) only, lookup now */
13066 errcode = arith_lookup_val(numstack);
13067 }
13068 ret:
13069 *perrcode = errcode;
13070 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013071 }
13072
Eric Andersen90898442003-08-06 11:20:52 +000013073 /* Continue processing the expression. */
13074 if (arith_isspace(arithval)) {
13075 /* Skip whitespace */
13076 goto prologue;
13077 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013078 p = endofname(expr);
13079 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013080 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013081
13082 numstackptr->var = alloca(var_name_size);
13083 safe_strncpy(numstackptr->var, expr, var_name_size);
13084 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013085 num:
Eric Andersen90898442003-08-06 11:20:52 +000013086 numstackptr->contidional_second_val_initialized = 0;
13087 numstackptr++;
13088 lasttok = TOK_NUM;
13089 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013090 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013091 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013092 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013093#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013094 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013095#else
13096 numstackptr->val = strtol(expr, (char **) &expr, 0);
13097#endif
Eric Andersen90898442003-08-06 11:20:52 +000013098 goto num;
13099 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013100 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013101 const char *o;
13102
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013103 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013104 /* strange operator not found */
13105 goto err;
13106 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013107 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013108 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013109 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013110 /* found */
13111 expr = o - 1;
13112 break;
13113 }
13114 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013115 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013116 p++;
13117 /* skip zero delim */
13118 p++;
13119 }
13120 op = p[1];
13121
13122 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13124 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013125
13126 /* Plus and minus are binary (not unary) _only_ if the last
13127 * token was as number, or a right paren (which pretends to be
13128 * a number, since it evaluates to one). Think about it.
13129 * It makes sense. */
13130 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013131 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013132 case TOK_ADD:
13133 op = TOK_UPLUS;
13134 break;
13135 case TOK_SUB:
13136 op = TOK_UMINUS;
13137 break;
13138 case TOK_POST_INC:
13139 op = TOK_PRE_INC;
13140 break;
13141 case TOK_POST_DEC:
13142 op = TOK_PRE_DEC;
13143 break;
Eric Andersen90898442003-08-06 11:20:52 +000013144 }
13145 }
13146 /* We don't want a unary operator to cause recursive descent on the
13147 * stack, because there can be many in a row and it could cause an
13148 * operator to be evaluated before its argument is pushed onto the
13149 * integer stack. */
13150 /* But for binary operators, "apply" everything on the operator
13151 * stack until we find an operator with a lesser priority than the
13152 * one we have just extracted. */
13153 /* Left paren is given the lowest priority so it will never be
13154 * "applied" in this way.
13155 * if associativity is right and priority eq, applied also skip
13156 */
13157 prec = PREC(op);
13158 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13159 /* not left paren or unary */
13160 if (lasttok != TOK_NUM) {
13161 /* binary op must be preceded by a num */
13162 goto err;
13163 }
13164 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013165 if (op == TOK_RPAREN) {
13166 /* The algorithm employed here is simple: while we don't
13167 * hit an open paren nor the bottom of the stack, pop
13168 * tokens and apply them */
13169 if (stackptr[-1] == TOK_LPAREN) {
13170 --stackptr;
13171 /* Any operator directly after a */
13172 lasttok = TOK_NUM;
13173 /* close paren should consider itself binary */
13174 goto prologue;
13175 }
13176 } else {
13177 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013178
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013179 convert_prec_is_assing(prec);
13180 convert_prec_is_assing(prev_prec);
13181 if (prev_prec < prec)
13182 break;
13183 /* check right assoc */
13184 if (prev_prec == prec && is_right_associativity(prec))
13185 break;
13186 }
13187 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13188 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013189 }
13190 if (op == TOK_RPAREN) {
13191 goto err;
13192 }
13193 }
13194
13195 /* Push this operator to the stack and remember it. */
13196 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013197 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013198 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013199 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013200}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013201#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013202
13203
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013204/* ============ main() and helpers */
13205
13206/*
13207 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013208 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013209static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013210static void
13211exitshell(void)
13212{
13213 struct jmploc loc;
13214 char *p;
13215 int status;
13216
13217 status = exitstatus;
13218 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13219 if (setjmp(loc.loc)) {
13220 if (exception == EXEXIT)
13221/* dash bug: it just does _exit(exitstatus) here
13222 * but we have to do setjobctl(0) first!
13223 * (bug is still not fixed in dash-0.5.3 - if you run dash
13224 * under Midnight Commander, on exit from dash MC is backgrounded) */
13225 status = exitstatus;
13226 goto out;
13227 }
13228 exception_handler = &loc;
13229 p = trap[0];
13230 if (p) {
13231 trap[0] = NULL;
13232 evalstring(p, 0);
13233 }
13234 flush_stdout_stderr();
13235 out:
13236 setjobctl(0);
13237 _exit(status);
13238 /* NOTREACHED */
13239}
13240
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013241static void
13242init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013243{
13244 /* from input.c: */
13245 basepf.nextc = basepf.buf = basebuf;
13246
13247 /* from trap.c: */
13248 signal(SIGCHLD, SIG_DFL);
13249
13250 /* from var.c: */
13251 {
13252 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013253 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013254 const char *p;
13255 struct stat st1, st2;
13256
13257 initvar();
13258 for (envp = environ; envp && *envp; envp++) {
13259 if (strchr(*envp, '=')) {
13260 setvareq(*envp, VEXPORT|VTEXTFIXED);
13261 }
13262 }
13263
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013264 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013265 setvar("PPID", ppid, 0);
13266
13267 p = lookupvar("PWD");
13268 if (p)
13269 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13270 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13271 p = '\0';
13272 setpwd(p, 0);
13273 }
13274}
13275
13276/*
13277 * Process the shell command line arguments.
13278 */
13279static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013280procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013281{
13282 int i;
13283 const char *xminusc;
13284 char **xargv;
13285
13286 xargv = argv;
13287 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013288 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013289 xargv++;
13290 for (i = 0; i < NOPTS; i++)
13291 optlist[i] = 2;
13292 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013293 if (options(1)) {
13294 /* it already printed err message */
13295 raise_exception(EXERROR);
13296 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013297 xargv = argptr;
13298 xminusc = minusc;
13299 if (*xargv == NULL) {
13300 if (xminusc)
13301 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13302 sflag = 1;
13303 }
13304 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13305 iflag = 1;
13306 if (mflag == 2)
13307 mflag = iflag;
13308 for (i = 0; i < NOPTS; i++)
13309 if (optlist[i] == 2)
13310 optlist[i] = 0;
13311#if DEBUG == 2
13312 debug = 1;
13313#endif
13314 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13315 if (xminusc) {
13316 minusc = *xargv++;
13317 if (*xargv)
13318 goto setarg0;
13319 } else if (!sflag) {
13320 setinputfile(*xargv, 0);
13321 setarg0:
13322 arg0 = *xargv++;
13323 commandname = arg0;
13324 }
13325
13326 shellparam.p = xargv;
13327#if ENABLE_ASH_GETOPTS
13328 shellparam.optind = 1;
13329 shellparam.optoff = -1;
13330#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013331 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013332 while (*xargv) {
13333 shellparam.nparam++;
13334 xargv++;
13335 }
13336 optschanged();
13337}
13338
13339/*
13340 * Read /etc/profile or .profile.
13341 */
13342static void
13343read_profile(const char *name)
13344{
13345 int skip;
13346
13347 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13348 return;
13349 skip = cmdloop(0);
13350 popfile();
13351 if (skip)
13352 exitshell();
13353}
13354
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013355/*
13356 * This routine is called when an error or an interrupt occurs in an
13357 * interactive shell and control is returned to the main command loop.
13358 */
13359static void
13360reset(void)
13361{
13362 /* from eval.c: */
13363 evalskip = 0;
13364 loopnest = 0;
13365 /* from input.c: */
13366 parselleft = parsenleft = 0; /* clear input buffer */
13367 popallfiles();
13368 /* from parser.c: */
13369 tokpushback = 0;
13370 checkkwd = 0;
13371 /* from redir.c: */
13372 clearredir(0);
13373}
13374
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013375#if PROFILE
13376static short profile_buf[16384];
13377extern int etext();
13378#endif
13379
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013380/*
13381 * Main routine. We initialize things, parse the arguments, execute
13382 * profiles if we're a login shell, and then call cmdloop to execute
13383 * commands. The setjmp call sets up the location to jump to when an
13384 * exception occurs. When an exception occurs the variable "state"
13385 * is used to figure out how far we had gotten.
13386 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013387int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013388int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013389{
13390 char *shinit;
13391 volatile int state;
13392 struct jmploc jmploc;
13393 struct stackmark smark;
13394
Denis Vlasenko01631112007-12-16 17:20:38 +000013395 /* Initialize global data */
13396 INIT_G_misc();
13397 INIT_G_memstack();
13398 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013399#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013400 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013401#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013402 INIT_G_cmdtable();
13403
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013404#if PROFILE
13405 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13406#endif
13407
13408#if ENABLE_FEATURE_EDITING
13409 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13410#endif
13411 state = 0;
13412 if (setjmp(jmploc.loc)) {
13413 int e;
13414 int s;
13415
13416 reset();
13417
13418 e = exception;
13419 if (e == EXERROR)
13420 exitstatus = 2;
13421 s = state;
13422 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13423 exitshell();
13424
13425 if (e == EXINT) {
13426 outcslow('\n', stderr);
13427 }
13428 popstackmark(&smark);
13429 FORCE_INT_ON; /* enable interrupts */
13430 if (s == 1)
13431 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013432 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013433 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013434 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013435 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013436 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013437 }
13438 exception_handler = &jmploc;
13439#if DEBUG
13440 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013441 trace_puts("Shell args: ");
13442 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013443#endif
13444 rootpid = getpid();
13445
13446#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013447 /* Can use monotonic_ns() for better randomness but for now it is
13448 * not used anywhere else in busybox... so avoid bloat */
13449 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013450#endif
13451 init();
13452 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013453 procargs(argv);
13454
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013455#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13456 if (iflag) {
13457 const char *hp = lookupvar("HISTFILE");
13458
13459 if (hp == NULL) {
13460 hp = lookupvar("HOME");
13461 if (hp != NULL) {
13462 char *defhp = concat_path_file(hp, ".ash_history");
13463 setvar("HISTFILE", defhp, 0);
13464 free(defhp);
13465 }
13466 }
13467 }
13468#endif
13469 if (argv[0] && argv[0][0] == '-')
13470 isloginsh = 1;
13471 if (isloginsh) {
13472 state = 1;
13473 read_profile("/etc/profile");
13474 state1:
13475 state = 2;
13476 read_profile(".profile");
13477 }
13478 state2:
13479 state = 3;
13480 if (
13481#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013482 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013483#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013484 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013485 ) {
13486 shinit = lookupvar("ENV");
13487 if (shinit != NULL && *shinit != '\0') {
13488 read_profile(shinit);
13489 }
13490 }
13491 state3:
13492 state = 4;
13493 if (minusc)
13494 evalstring(minusc, 0);
13495
13496 if (sflag || minusc == NULL) {
13497#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013498 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013499 const char *hp = lookupvar("HISTFILE");
13500
13501 if (hp != NULL)
13502 line_input_state->hist_file = hp;
13503 }
13504#endif
13505 state4: /* XXX ??? - why isn't this before the "if" statement */
13506 cmdloop(1);
13507 }
13508#if PROFILE
13509 monitor(0);
13510#endif
13511#ifdef GPROF
13512 {
13513 extern void _mcleanup(void);
13514 _mcleanup();
13515 }
13516#endif
13517 exitshell();
13518 /* NOTREACHED */
13519}
13520
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013521#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013522const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013523int main(int argc, char **argv)
13524{
13525 return ash_main(argc, argv);
13526}
13527#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013528
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013529
Eric Andersendf82f612001-06-28 07:46:40 +000013530/*-
13531 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013532 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013533 *
13534 * This code is derived from software contributed to Berkeley by
13535 * Kenneth Almquist.
13536 *
13537 * Redistribution and use in source and binary forms, with or without
13538 * modification, are permitted provided that the following conditions
13539 * are met:
13540 * 1. Redistributions of source code must retain the above copyright
13541 * notice, this list of conditions and the following disclaimer.
13542 * 2. Redistributions in binary form must reproduce the above copyright
13543 * notice, this list of conditions and the following disclaimer in the
13544 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013545 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013546 * may be used to endorse or promote products derived from this software
13547 * without specific prior written permission.
13548 *
13549 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13550 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13551 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13552 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13553 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13554 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13555 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13556 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13557 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13558 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13559 * SUCH DAMAGE.
13560 */