blob: 4263644fcb176a8faf203406f3febece7682376a [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 {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000529 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000530 union node *assign;
531 union node *args;
532 union node *redirect;
533};
534
535struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000536 smallint type;
537 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000538 struct nodelist *cmdlist;
539};
540
541struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000542 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000543 union node *n;
544 union node *redirect;
545};
546
547struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000548 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000549 union node *ch1;
550 union node *ch2;
551};
552
553struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000554 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000555 union node *test;
556 union node *ifpart;
557 union node *elsepart;
558};
559
560struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000561 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000562 union node *args;
563 union node *body;
564 char *var;
565};
566
567struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000568 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000569 union node *expr;
570 union node *cases;
571};
572
573struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000574 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000575 union node *next;
576 union node *pattern;
577 union node *body;
578};
579
580struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000581 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000582 union node *next;
583 char *text;
584 struct nodelist *backquote;
585};
586
587struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000588 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000589 union node *next;
590 int fd;
591 union node *fname;
592 char *expfname;
593};
594
595struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000596 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000597 union node *next;
598 int fd;
599 int dupfd;
600 union node *vname;
601};
602
603struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000604 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000605 union node *next;
606 int fd;
607 union node *doc;
608};
609
610struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000611 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000612 union node *com;
613};
614
615union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000616 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000617 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 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000957 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958 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{
Denis Vlasenko01631112007-12-16 17:20:38 +00001659 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001660 char **ap, **ap1;
1661 ap = ap1 = param->p;
1662 while (*ap)
1663 free(*ap++);
1664 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001665 }
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))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003537 ash_msg_and_raise_error("can't 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:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004847 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004848 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004849 ash_msg_and_raise_error("can't 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{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004902 struct redirtab *sv;
4903 int i;
4904 int fd;
4905 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004906
Denis Vlasenko01631112007-12-16 17:20:38 +00004907 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004908 if (!redir) {
4909 return;
4910 }
4911 sv = NULL;
4912 INT_OFF;
4913 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004914 sv = ckmalloc(sizeof(*sv));
4915 sv->next = redirlist;
4916 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004917 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004918 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004919 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004920 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004921 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004922 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004923 fd = redir->nfile.fd;
4924 if ((redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD)
4925 && redir->ndup.dupfd == fd)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004926 continue; /* redirect from/to same file descriptor */
4927
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004928 newfd = openredirect(redir);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004929 if (fd == newfd) {
4930 /* Descriptor wasn't open before redirect.
4931 * Mark it for close in the future */
4932 if (sv && sv->renamed[fd] == EMPTY)
4933 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004934 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004935 }
4936 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004937 i = fcntl(fd, F_DUPFD, 10);
4938
4939 if (i == -1) {
4940 i = errno;
4941 if (i != EBADF) {
4942 close(newfd);
4943 errno = i;
4944 ash_msg_and_raise_error("%d: %m", fd);
4945 /* NOTREACHED */
4946 }
4947 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004948 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004949 close(fd);
4950 }
4951 } else {
4952 close(fd);
4953 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004954 dupredirect(redir, newfd);
4955 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004957 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004958 preverrout_fd = sv->renamed[2];
4959}
4960
4961/*
4962 * Undo the effects of the last redirection.
4963 */
4964static void
4965popredir(int drop)
4966{
4967 struct redirtab *rp;
4968 int i;
4969
Denis Vlasenko01631112007-12-16 17:20:38 +00004970 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004971 return;
4972 INT_OFF;
4973 rp = redirlist;
4974 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004975 if (rp->renamed[i] == CLOSED) {
4976 if (!drop)
4977 close(i);
4978 continue;
4979 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004980 if (rp->renamed[i] != EMPTY) {
4981 if (!drop) {
4982 close(i);
4983 copyfd(rp->renamed[i], i);
4984 }
4985 close(rp->renamed[i]);
4986 }
4987 }
4988 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004989 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004990 free(rp);
4991 INT_ON;
4992}
4993
4994/*
4995 * Undo all redirections. Called on error or interrupt.
4996 */
4997
4998/*
4999 * Discard all saved file descriptors.
5000 */
5001static void
5002clearredir(int drop)
5003{
5004 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005005 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005006 if (!redirlist)
5007 break;
5008 popredir(drop);
5009 }
5010}
5011
5012static int
5013redirectsafe(union node *redir, int flags)
5014{
5015 int err;
5016 volatile int saveint;
5017 struct jmploc *volatile savehandler = exception_handler;
5018 struct jmploc jmploc;
5019
5020 SAVE_INT(saveint);
5021 err = setjmp(jmploc.loc) * 2;
5022 if (!err) {
5023 exception_handler = &jmploc;
5024 redirect(redir, flags);
5025 }
5026 exception_handler = savehandler;
5027 if (err && exception != EXERROR)
5028 longjmp(exception_handler->loc, 1);
5029 RESTORE_INT(saveint);
5030 return err;
5031}
5032
5033
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005034/* ============ Routines to expand arguments to commands
5035 *
5036 * We have to deal with backquotes, shell variables, and file metacharacters.
5037 */
5038
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005039#if ENABLE_ASH_MATH_SUPPORT_64
5040typedef int64_t arith_t;
5041#define arith_t_type long long
5042#else
5043typedef long arith_t;
5044#define arith_t_type long
5045#endif
5046
5047#if ENABLE_ASH_MATH_SUPPORT
5048static arith_t dash_arith(const char *);
5049static arith_t arith(const char *expr, int *perrcode);
5050#endif
5051
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005052/*
5053 * expandarg flags
5054 */
5055#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5056#define EXP_TILDE 0x2 /* do normal tilde expansion */
5057#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5058#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5059#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5060#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5061#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5062#define EXP_WORD 0x80 /* expand word in parameter expansion */
5063#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5064/*
5065 * _rmescape() flags
5066 */
5067#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5068#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5069#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5070#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5071#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5072
5073/*
5074 * Structure specifying which parts of the string should be searched
5075 * for IFS characters.
5076 */
5077struct ifsregion {
5078 struct ifsregion *next; /* next region in list */
5079 int begoff; /* offset of start of region */
5080 int endoff; /* offset of end of region */
5081 int nulonly; /* search for nul bytes only */
5082};
5083
5084struct arglist {
5085 struct strlist *list;
5086 struct strlist **lastp;
5087};
5088
5089/* output of current string */
5090static char *expdest;
5091/* list of back quote expressions */
5092static struct nodelist *argbackq;
5093/* first struct in list of ifs regions */
5094static struct ifsregion ifsfirst;
5095/* last struct in list */
5096static struct ifsregion *ifslastp;
5097/* holds expanded arg list */
5098static struct arglist exparg;
5099
5100/*
5101 * Our own itoa().
5102 */
5103static int
5104cvtnum(arith_t num)
5105{
5106 int len;
5107
5108 expdest = makestrspace(32, expdest);
5109#if ENABLE_ASH_MATH_SUPPORT_64
5110 len = fmtstr(expdest, 32, "%lld", (long long) num);
5111#else
5112 len = fmtstr(expdest, 32, "%ld", num);
5113#endif
5114 STADJUST(len, expdest);
5115 return len;
5116}
5117
5118static size_t
5119esclen(const char *start, const char *p)
5120{
5121 size_t esc = 0;
5122
5123 while (p > start && *--p == CTLESC) {
5124 esc++;
5125 }
5126 return esc;
5127}
5128
5129/*
5130 * Remove any CTLESC characters from a string.
5131 */
5132static char *
5133_rmescapes(char *str, int flag)
5134{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005135 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005136
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005137 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005138 unsigned inquotes;
5139 int notescaped;
5140 int globbing;
5141
5142 p = strpbrk(str, qchars);
5143 if (!p) {
5144 return str;
5145 }
5146 q = p;
5147 r = str;
5148 if (flag & RMESCAPE_ALLOC) {
5149 size_t len = p - str;
5150 size_t fulllen = len + strlen(p) + 1;
5151
5152 if (flag & RMESCAPE_GROW) {
5153 r = makestrspace(fulllen, expdest);
5154 } else if (flag & RMESCAPE_HEAP) {
5155 r = ckmalloc(fulllen);
5156 } else {
5157 r = stalloc(fulllen);
5158 }
5159 q = r;
5160 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005161 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005162 }
5163 }
5164 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5165 globbing = flag & RMESCAPE_GLOB;
5166 notescaped = globbing;
5167 while (*p) {
5168 if (*p == CTLQUOTEMARK) {
5169 inquotes = ~inquotes;
5170 p++;
5171 notescaped = globbing;
5172 continue;
5173 }
5174 if (*p == '\\') {
5175 /* naked back slash */
5176 notescaped = 0;
5177 goto copy;
5178 }
5179 if (*p == CTLESC) {
5180 p++;
5181 if (notescaped && inquotes && *p != '/') {
5182 *q++ = '\\';
5183 }
5184 }
5185 notescaped = globbing;
5186 copy:
5187 *q++ = *p++;
5188 }
5189 *q = '\0';
5190 if (flag & RMESCAPE_GROW) {
5191 expdest = r;
5192 STADJUST(q - r + 1, expdest);
5193 }
5194 return r;
5195}
5196#define rmescapes(p) _rmescapes((p), 0)
5197
5198#define pmatch(a, b) !fnmatch((a), (b), 0)
5199
5200/*
5201 * Prepare a pattern for a expmeta (internal glob(3)) call.
5202 *
5203 * Returns an stalloced string.
5204 */
5205static char *
5206preglob(const char *pattern, int quoted, int flag)
5207{
5208 flag |= RMESCAPE_GLOB;
5209 if (quoted) {
5210 flag |= RMESCAPE_QUOTED;
5211 }
5212 return _rmescapes((char *)pattern, flag);
5213}
5214
5215/*
5216 * Put a string on the stack.
5217 */
5218static void
5219memtodest(const char *p, size_t len, int syntax, int quotes)
5220{
5221 char *q = expdest;
5222
5223 q = makestrspace(len * 2, q);
5224
5225 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005226 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005227 if (!c)
5228 continue;
5229 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5230 USTPUTC(CTLESC, q);
5231 USTPUTC(c, q);
5232 }
5233
5234 expdest = q;
5235}
5236
5237static void
5238strtodest(const char *p, int syntax, int quotes)
5239{
5240 memtodest(p, strlen(p), syntax, quotes);
5241}
5242
5243/*
5244 * Record the fact that we have to scan this region of the
5245 * string for IFS characters.
5246 */
5247static void
5248recordregion(int start, int end, int nulonly)
5249{
5250 struct ifsregion *ifsp;
5251
5252 if (ifslastp == NULL) {
5253 ifsp = &ifsfirst;
5254 } else {
5255 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005256 ifsp = ckzalloc(sizeof(*ifsp));
5257 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005258 ifslastp->next = ifsp;
5259 INT_ON;
5260 }
5261 ifslastp = ifsp;
5262 ifslastp->begoff = start;
5263 ifslastp->endoff = end;
5264 ifslastp->nulonly = nulonly;
5265}
5266
5267static void
5268removerecordregions(int endoff)
5269{
5270 if (ifslastp == NULL)
5271 return;
5272
5273 if (ifsfirst.endoff > endoff) {
5274 while (ifsfirst.next != NULL) {
5275 struct ifsregion *ifsp;
5276 INT_OFF;
5277 ifsp = ifsfirst.next->next;
5278 free(ifsfirst.next);
5279 ifsfirst.next = ifsp;
5280 INT_ON;
5281 }
5282 if (ifsfirst.begoff > endoff)
5283 ifslastp = NULL;
5284 else {
5285 ifslastp = &ifsfirst;
5286 ifsfirst.endoff = endoff;
5287 }
5288 return;
5289 }
5290
5291 ifslastp = &ifsfirst;
5292 while (ifslastp->next && ifslastp->next->begoff < endoff)
5293 ifslastp=ifslastp->next;
5294 while (ifslastp->next != NULL) {
5295 struct ifsregion *ifsp;
5296 INT_OFF;
5297 ifsp = ifslastp->next->next;
5298 free(ifslastp->next);
5299 ifslastp->next = ifsp;
5300 INT_ON;
5301 }
5302 if (ifslastp->endoff > endoff)
5303 ifslastp->endoff = endoff;
5304}
5305
5306static char *
5307exptilde(char *startp, char *p, int flag)
5308{
5309 char c;
5310 char *name;
5311 struct passwd *pw;
5312 const char *home;
5313 int quotes = flag & (EXP_FULL | EXP_CASE);
5314 int startloc;
5315
5316 name = p + 1;
5317
5318 while ((c = *++p) != '\0') {
5319 switch (c) {
5320 case CTLESC:
5321 return startp;
5322 case CTLQUOTEMARK:
5323 return startp;
5324 case ':':
5325 if (flag & EXP_VARTILDE)
5326 goto done;
5327 break;
5328 case '/':
5329 case CTLENDVAR:
5330 goto done;
5331 }
5332 }
5333 done:
5334 *p = '\0';
5335 if (*name == '\0') {
5336 home = lookupvar(homestr);
5337 } else {
5338 pw = getpwnam(name);
5339 if (pw == NULL)
5340 goto lose;
5341 home = pw->pw_dir;
5342 }
5343 if (!home || !*home)
5344 goto lose;
5345 *p = c;
5346 startloc = expdest - (char *)stackblock();
5347 strtodest(home, SQSYNTAX, quotes);
5348 recordregion(startloc, expdest - (char *)stackblock(), 0);
5349 return p;
5350 lose:
5351 *p = c;
5352 return startp;
5353}
5354
5355/*
5356 * Execute a command inside back quotes. If it's a builtin command, we
5357 * want to save its output in a block obtained from malloc. Otherwise
5358 * we fork off a subprocess and get the output of the command via a pipe.
5359 * Should be called with interrupts off.
5360 */
5361struct backcmd { /* result of evalbackcmd */
5362 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005363 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005364 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005365 struct job *jp; /* job structure for command */
5366};
5367
5368/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005369static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005370#define EV_EXIT 01 /* exit after evaluating tree */
5371static void evaltree(union node *, int);
5372
5373static void
5374evalbackcmd(union node *n, struct backcmd *result)
5375{
5376 int saveherefd;
5377
5378 result->fd = -1;
5379 result->buf = NULL;
5380 result->nleft = 0;
5381 result->jp = NULL;
5382 if (n == NULL) {
5383 goto out;
5384 }
5385
5386 saveherefd = herefd;
5387 herefd = -1;
5388
5389 {
5390 int pip[2];
5391 struct job *jp;
5392
5393 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005394 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005395 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005396 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5397 FORCE_INT_ON;
5398 close(pip[0]);
5399 if (pip[1] != 1) {
5400 close(1);
5401 copyfd(pip[1], 1);
5402 close(pip[1]);
5403 }
5404 eflag = 0;
5405 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5406 /* NOTREACHED */
5407 }
5408 close(pip[1]);
5409 result->fd = pip[0];
5410 result->jp = jp;
5411 }
5412 herefd = saveherefd;
5413 out:
5414 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5415 result->fd, result->buf, result->nleft, result->jp));
5416}
5417
5418/*
5419 * Expand stuff in backwards quotes.
5420 */
5421static void
5422expbackq(union node *cmd, int quoted, int quotes)
5423{
5424 struct backcmd in;
5425 int i;
5426 char buf[128];
5427 char *p;
5428 char *dest;
5429 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005430 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005431 struct stackmark smark;
5432
5433 INT_OFF;
5434 setstackmark(&smark);
5435 dest = expdest;
5436 startloc = dest - (char *)stackblock();
5437 grabstackstr(dest);
5438 evalbackcmd(cmd, &in);
5439 popstackmark(&smark);
5440
5441 p = in.buf;
5442 i = in.nleft;
5443 if (i == 0)
5444 goto read;
5445 for (;;) {
5446 memtodest(p, i, syntax, quotes);
5447 read:
5448 if (in.fd < 0)
5449 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005450 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005451 TRACE(("expbackq: read returns %d\n", i));
5452 if (i <= 0)
5453 break;
5454 p = buf;
5455 }
5456
Denis Vlasenko60818682007-09-28 22:07:23 +00005457 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005458 if (in.fd >= 0) {
5459 close(in.fd);
5460 back_exitstatus = waitforjob(in.jp);
5461 }
5462 INT_ON;
5463
5464 /* Eat all trailing newlines */
5465 dest = expdest;
5466 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5467 STUNPUTC(dest);
5468 expdest = dest;
5469
5470 if (quoted == 0)
5471 recordregion(startloc, dest - (char *)stackblock(), 0);
5472 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5473 (dest - (char *)stackblock()) - startloc,
5474 (dest - (char *)stackblock()) - startloc,
5475 stackblock() + startloc));
5476}
5477
5478#if ENABLE_ASH_MATH_SUPPORT
5479/*
5480 * Expand arithmetic expression. Backup to start of expression,
5481 * evaluate, place result in (backed up) result, adjust string position.
5482 */
5483static void
5484expari(int quotes)
5485{
5486 char *p, *start;
5487 int begoff;
5488 int flag;
5489 int len;
5490
5491 /* ifsfree(); */
5492
5493 /*
5494 * This routine is slightly over-complicated for
5495 * efficiency. Next we scan backwards looking for the
5496 * start of arithmetic.
5497 */
5498 start = stackblock();
5499 p = expdest - 1;
5500 *p = '\0';
5501 p--;
5502 do {
5503 int esc;
5504
5505 while (*p != CTLARI) {
5506 p--;
5507#if DEBUG
5508 if (p < start) {
5509 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5510 }
5511#endif
5512 }
5513
5514 esc = esclen(start, p);
5515 if (!(esc % 2)) {
5516 break;
5517 }
5518
5519 p -= esc + 1;
5520 } while (1);
5521
5522 begoff = p - start;
5523
5524 removerecordregions(begoff);
5525
5526 flag = p[1];
5527
5528 expdest = p;
5529
5530 if (quotes)
5531 rmescapes(p + 2);
5532
5533 len = cvtnum(dash_arith(p + 2));
5534
5535 if (flag != '"')
5536 recordregion(begoff, begoff + len, 0);
5537}
5538#endif
5539
5540/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005541static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542
5543/*
5544 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5545 * characters to allow for further processing. Otherwise treat
5546 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005547 *
5548 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5549 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5550 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005551 */
5552static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005553argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005554{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005555 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005556 '=',
5557 ':',
5558 CTLQUOTEMARK,
5559 CTLENDVAR,
5560 CTLESC,
5561 CTLVAR,
5562 CTLBACKQ,
5563 CTLBACKQ | CTLQUOTE,
5564#if ENABLE_ASH_MATH_SUPPORT
5565 CTLENDARI,
5566#endif
5567 0
5568 };
5569 const char *reject = spclchars;
5570 int c;
5571 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5572 int breakall = flag & EXP_WORD;
5573 int inquotes;
5574 size_t length;
5575 int startloc;
5576
5577 if (!(flag & EXP_VARTILDE)) {
5578 reject += 2;
5579 } else if (flag & EXP_VARTILDE2) {
5580 reject++;
5581 }
5582 inquotes = 0;
5583 length = 0;
5584 if (flag & EXP_TILDE) {
5585 char *q;
5586
5587 flag &= ~EXP_TILDE;
5588 tilde:
5589 q = p;
5590 if (*q == CTLESC && (flag & EXP_QWORD))
5591 q++;
5592 if (*q == '~')
5593 p = exptilde(p, q, flag);
5594 }
5595 start:
5596 startloc = expdest - (char *)stackblock();
5597 for (;;) {
5598 length += strcspn(p + length, reject);
5599 c = p[length];
5600 if (c && (!(c & 0x80)
5601#if ENABLE_ASH_MATH_SUPPORT
5602 || c == CTLENDARI
5603#endif
5604 )) {
5605 /* c == '=' || c == ':' || c == CTLENDARI */
5606 length++;
5607 }
5608 if (length > 0) {
5609 int newloc;
5610 expdest = stack_nputstr(p, length, expdest);
5611 newloc = expdest - (char *)stackblock();
5612 if (breakall && !inquotes && newloc > startloc) {
5613 recordregion(startloc, newloc, 0);
5614 }
5615 startloc = newloc;
5616 }
5617 p += length + 1;
5618 length = 0;
5619
5620 switch (c) {
5621 case '\0':
5622 goto breakloop;
5623 case '=':
5624 if (flag & EXP_VARTILDE2) {
5625 p--;
5626 continue;
5627 }
5628 flag |= EXP_VARTILDE2;
5629 reject++;
5630 /* fall through */
5631 case ':':
5632 /*
5633 * sort of a hack - expand tildes in variable
5634 * assignments (after the first '=' and after ':'s).
5635 */
5636 if (*--p == '~') {
5637 goto tilde;
5638 }
5639 continue;
5640 }
5641
5642 switch (c) {
5643 case CTLENDVAR: /* ??? */
5644 goto breakloop;
5645 case CTLQUOTEMARK:
5646 /* "$@" syntax adherence hack */
5647 if (
5648 !inquotes &&
5649 !memcmp(p, dolatstr, 4) &&
5650 (p[4] == CTLQUOTEMARK || (
5651 p[4] == CTLENDVAR &&
5652 p[5] == CTLQUOTEMARK
5653 ))
5654 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005655 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005656 goto start;
5657 }
5658 inquotes = !inquotes;
5659 addquote:
5660 if (quotes) {
5661 p--;
5662 length++;
5663 startloc++;
5664 }
5665 break;
5666 case CTLESC:
5667 startloc++;
5668 length++;
5669 goto addquote;
5670 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005671 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005672 goto start;
5673 case CTLBACKQ:
5674 c = 0;
5675 case CTLBACKQ|CTLQUOTE:
5676 expbackq(argbackq->n, c, quotes);
5677 argbackq = argbackq->next;
5678 goto start;
5679#if ENABLE_ASH_MATH_SUPPORT
5680 case CTLENDARI:
5681 p--;
5682 expari(quotes);
5683 goto start;
5684#endif
5685 }
5686 }
5687 breakloop:
5688 ;
5689}
5690
5691static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005692scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005693 int zero)
5694{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005695// This commented out code was added by James Simmons <jsimmons@infradead.org>
5696// as part of a larger change when he added support for ${var/a/b}.
5697// However, it broke # and % operators:
5698//
5699//var=ababcdcd
5700// ok bad
5701//echo ${var#ab} abcdcd abcdcd
5702//echo ${var##ab} abcdcd abcdcd
5703//echo ${var#a*b} abcdcd ababcdcd (!)
5704//echo ${var##a*b} cdcd cdcd
5705//echo ${var#?} babcdcd ababcdcd (!)
5706//echo ${var##?} babcdcd babcdcd
5707//echo ${var#*} ababcdcd babcdcd (!)
5708//echo ${var##*}
5709//echo ${var%cd} ababcd ababcd
5710//echo ${var%%cd} ababcd abab (!)
5711//echo ${var%c*d} ababcd ababcd
5712//echo ${var%%c*d} abab ababcdcd (!)
5713//echo ${var%?} ababcdc ababcdc
5714//echo ${var%%?} ababcdc ababcdcd (!)
5715//echo ${var%*} ababcdcd ababcdcd
5716//echo ${var%%*}
5717//
5718// Commenting it back out helped. Remove it completely if it really
5719// is not needed.
5720
5721 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722 char c;
5723
5724 loc = startp;
5725 loc2 = rmesc;
5726 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005727 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005728 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005729
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 c = *loc2;
5731 if (zero) {
5732 *loc2 = '\0';
5733 s = rmesc;
5734 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005735 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005736
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005737// // chop off end if its '*'
5738// full = strrchr(str, '*');
5739// if (full && full != str)
5740// match--;
5741//
5742// // If str starts with '*' replace with s.
5743// if ((*str == '*') && strlen(s) >= match) {
5744// full = xstrdup(s);
5745// strncpy(full+strlen(s)-match+1, str+1, match-1);
5746// } else
5747// full = xstrndup(str, match);
5748// match = strncmp(s, full, strlen(full));
5749// free(full);
5750//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005751 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005752 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005753 return loc;
5754 if (quotes && *loc == CTLESC)
5755 loc++;
5756 loc++;
5757 loc2++;
5758 } while (c);
5759 return 0;
5760}
5761
5762static char *
5763scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5764 int zero)
5765{
5766 int esc = 0;
5767 char *loc;
5768 char *loc2;
5769
5770 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5771 int match;
5772 char c = *loc2;
5773 const char *s = loc2;
5774 if (zero) {
5775 *loc2 = '\0';
5776 s = rmesc;
5777 }
5778 match = pmatch(str, s);
5779 *loc2 = c;
5780 if (match)
5781 return loc;
5782 loc--;
5783 if (quotes) {
5784 if (--esc < 0) {
5785 esc = esclen(startp, loc);
5786 }
5787 if (esc % 2) {
5788 esc--;
5789 loc--;
5790 }
5791 }
5792 }
5793 return 0;
5794}
5795
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005796static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005797static void
5798varunset(const char *end, const char *var, const char *umsg, int varflags)
5799{
5800 const char *msg;
5801 const char *tail;
5802
5803 tail = nullstr;
5804 msg = "parameter not set";
5805 if (umsg) {
5806 if (*end == CTLENDVAR) {
5807 if (varflags & VSNUL)
5808 tail = " or null";
5809 } else
5810 msg = umsg;
5811 }
5812 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5813}
5814
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005815#if ENABLE_ASH_BASH_COMPAT
5816static char *
5817parse_sub_pattern(char *arg, int inquotes)
5818{
5819 char *idx, *repl = NULL;
5820 unsigned char c;
5821
Denis Vlasenko2659c632008-06-14 06:04:59 +00005822 idx = arg;
5823 while (1) {
5824 c = *arg;
5825 if (!c)
5826 break;
5827 if (c == '/') {
5828 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005829 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005830 repl = idx + 1;
5831 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005832 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005833 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005834 *idx++ = c;
5835 if (!inquotes && c == '\\' && arg[1] == '\\')
5836 arg++; /* skip both \\, not just first one */
5837 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005838 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005839 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005840
5841 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005842}
5843#endif /* ENABLE_ASH_BASH_COMPAT */
5844
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005845static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005846subevalvar(char *p, char *str, int strloc, int subtype,
5847 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005848{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005849 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850 char *startp;
5851 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005852 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005853 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5854 USE_ASH_BASH_COMPAT(char null = '\0';)
5855 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5856 int saveherefd = herefd;
5857 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005858 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005859 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860
5861 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005862 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5863 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005864 STPUTC('\0', expdest);
5865 herefd = saveherefd;
5866 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005867 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868
5869 switch (subtype) {
5870 case VSASSIGN:
5871 setvar(str, startp, 0);
5872 amount = startp - expdest;
5873 STADJUST(amount, expdest);
5874 return startp;
5875
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005876#if ENABLE_ASH_BASH_COMPAT
5877 case VSSUBSTR:
5878 loc = str = stackblock() + strloc;
5879// TODO: number() instead? It does error checking...
5880 pos = atoi(loc);
5881 len = str - startp - 1;
5882
5883 /* *loc != '\0', guaranteed by parser */
5884 if (quotes) {
5885 char *ptr;
5886
5887 /* We must adjust the length by the number of escapes we find. */
5888 for (ptr = startp; ptr < (str - 1); ptr++) {
5889 if(*ptr == CTLESC) {
5890 len--;
5891 ptr++;
5892 }
5893 }
5894 }
5895 orig_len = len;
5896
5897 if (*loc++ == ':') {
5898// TODO: number() instead? It does error checking...
5899 len = atoi(loc);
5900 } else {
5901 len = orig_len;
5902 while (*loc && *loc != ':')
5903 loc++;
5904 if (*loc++ == ':')
5905// TODO: number() instead? It does error checking...
5906 len = atoi(loc);
5907 }
5908 if (pos >= orig_len) {
5909 pos = 0;
5910 len = 0;
5911 }
5912 if (len > (orig_len - pos))
5913 len = orig_len - pos;
5914
5915 for (str = startp; pos; str++, pos--) {
5916 if (quotes && *str == CTLESC)
5917 str++;
5918 }
5919 for (loc = startp; len; len--) {
5920 if (quotes && *str == CTLESC)
5921 *loc++ = *str++;
5922 *loc++ = *str++;
5923 }
5924 *loc = '\0';
5925 amount = loc - expdest;
5926 STADJUST(amount, expdest);
5927 return loc;
5928#endif
5929
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005930 case VSQUESTION:
5931 varunset(p, str, startp, varflags);
5932 /* NOTREACHED */
5933 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005934 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005935
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005936 /* We'll comeback here if we grow the stack while handling
5937 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5938 * stack will need rebasing, and we'll need to remove our work
5939 * areas each time
5940 */
5941 USE_ASH_BASH_COMPAT(restart:)
5942
5943 amount = expdest - ((char *)stackblock() + resetloc);
5944 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005945 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946
5947 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005948 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005949 if (quotes) {
5950 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5951 if (rmesc != startp) {
5952 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005953 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005954 }
5955 }
5956 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005957 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005959 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005960
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005961#if ENABLE_ASH_BASH_COMPAT
5962 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5963 char *idx, *end, *restart_detect;
5964
5965 if(!repl) {
5966 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5967 if (!repl)
5968 repl = &null;
5969 }
5970
5971 /* If there's no pattern to match, return the expansion unmolested */
5972 if (*str == '\0')
5973 return 0;
5974
5975 len = 0;
5976 idx = startp;
5977 end = str - 1;
5978 while (idx < end) {
5979 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5980 if (!loc) {
5981 /* No match, advance */
5982 restart_detect = stackblock();
5983 STPUTC(*idx, expdest);
5984 if (quotes && *idx == CTLESC) {
5985 idx++;
5986 len++;
5987 STPUTC(*idx, expdest);
5988 }
5989 if (stackblock() != restart_detect)
5990 goto restart;
5991 idx++;
5992 len++;
5993 rmesc++;
5994 continue;
5995 }
5996
5997 if (subtype == VSREPLACEALL) {
5998 while (idx < loc) {
5999 if (quotes && *idx == CTLESC)
6000 idx++;
6001 idx++;
6002 rmesc++;
6003 }
6004 } else
6005 idx = loc;
6006
6007 for (loc = repl; *loc; loc++) {
6008 restart_detect = stackblock();
6009 STPUTC(*loc, expdest);
6010 if (stackblock() != restart_detect)
6011 goto restart;
6012 len++;
6013 }
6014
6015 if (subtype == VSREPLACE) {
6016 while (*idx) {
6017 restart_detect = stackblock();
6018 STPUTC(*idx, expdest);
6019 if (stackblock() != restart_detect)
6020 goto restart;
6021 len++;
6022 idx++;
6023 }
6024 break;
6025 }
6026 }
6027
6028 /* We've put the replaced text into a buffer at workloc, now
6029 * move it to the right place and adjust the stack.
6030 */
6031 startp = stackblock() + startloc;
6032 STPUTC('\0', expdest);
6033 memmove(startp, stackblock() + workloc, len);
6034 startp[len++] = '\0';
6035 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6036 STADJUST(-amount, expdest);
6037 return startp;
6038 }
6039#endif /* ENABLE_ASH_BASH_COMPAT */
6040
6041 subtype -= VSTRIMRIGHT;
6042#if DEBUG
6043 if (subtype < 0 || subtype > 7)
6044 abort();
6045#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006046 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6047 zero = subtype >> 1;
6048 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6049 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6050
6051 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6052 if (loc) {
6053 if (zero) {
6054 memmove(startp, loc, str - loc);
6055 loc = startp + (str - loc) - 1;
6056 }
6057 *loc = '\0';
6058 amount = loc - expdest;
6059 STADJUST(amount, expdest);
6060 }
6061 return loc;
6062}
6063
6064/*
6065 * Add the value of a specialized variable to the stack string.
6066 */
6067static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006068varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006069{
6070 int num;
6071 char *p;
6072 int i;
6073 int sep = 0;
6074 int sepq = 0;
6075 ssize_t len = 0;
6076 char **ap;
6077 int syntax;
6078 int quoted = varflags & VSQUOTE;
6079 int subtype = varflags & VSTYPE;
6080 int quotes = flags & (EXP_FULL | EXP_CASE);
6081
6082 if (quoted && (flags & EXP_FULL))
6083 sep = 1 << CHAR_BIT;
6084
6085 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6086 switch (*name) {
6087 case '$':
6088 num = rootpid;
6089 goto numvar;
6090 case '?':
6091 num = exitstatus;
6092 goto numvar;
6093 case '#':
6094 num = shellparam.nparam;
6095 goto numvar;
6096 case '!':
6097 num = backgndpid;
6098 if (num == 0)
6099 return -1;
6100 numvar:
6101 len = cvtnum(num);
6102 break;
6103 case '-':
6104 p = makestrspace(NOPTS, expdest);
6105 for (i = NOPTS - 1; i >= 0; i--) {
6106 if (optlist[i]) {
6107 USTPUTC(optletters(i), p);
6108 len++;
6109 }
6110 }
6111 expdest = p;
6112 break;
6113 case '@':
6114 if (sep)
6115 goto param;
6116 /* fall through */
6117 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006118 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006119 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6120 sepq = 1;
6121 param:
6122 ap = shellparam.p;
6123 if (!ap)
6124 return -1;
6125 while ((p = *ap++)) {
6126 size_t partlen;
6127
6128 partlen = strlen(p);
6129 len += partlen;
6130
6131 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6132 memtodest(p, partlen, syntax, quotes);
6133
6134 if (*ap && sep) {
6135 char *q;
6136
6137 len++;
6138 if (subtype == VSPLUS || subtype == VSLENGTH) {
6139 continue;
6140 }
6141 q = expdest;
6142 if (sepq)
6143 STPUTC(CTLESC, q);
6144 STPUTC(sep, q);
6145 expdest = q;
6146 }
6147 }
6148 return len;
6149 case '0':
6150 case '1':
6151 case '2':
6152 case '3':
6153 case '4':
6154 case '5':
6155 case '6':
6156 case '7':
6157 case '8':
6158 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006159// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006160 num = atoi(name);
6161 if (num < 0 || num > shellparam.nparam)
6162 return -1;
6163 p = num ? shellparam.p[num - 1] : arg0;
6164 goto value;
6165 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006166 /* NB: name has form "VAR=..." */
6167
6168 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6169 * which should be considered before we check variables. */
6170 if (var_str_list) {
6171 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6172 p = NULL;
6173 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006174 char *str, *eq;
6175 str = var_str_list->text;
6176 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006177 if (!eq) /* stop at first non-assignment */
6178 break;
6179 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006180 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006181 && strncmp(str, name, name_len) == 0) {
6182 p = eq;
6183 /* goto value; - WRONG! */
6184 /* think "A=1 A=2 B=$A" */
6185 }
6186 var_str_list = var_str_list->next;
6187 } while (var_str_list);
6188 if (p)
6189 goto value;
6190 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006191 p = lookupvar(name);
6192 value:
6193 if (!p)
6194 return -1;
6195
6196 len = strlen(p);
6197 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6198 memtodest(p, len, syntax, quotes);
6199 return len;
6200 }
6201
6202 if (subtype == VSPLUS || subtype == VSLENGTH)
6203 STADJUST(-len, expdest);
6204 return len;
6205}
6206
6207/*
6208 * Expand a variable, and return a pointer to the next character in the
6209 * input string.
6210 */
6211static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006212evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006214 char varflags;
6215 char subtype;
6216 char quoted;
6217 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006218 char *var;
6219 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006220 int startloc;
6221 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006223 varflags = *p++;
6224 subtype = varflags & VSTYPE;
6225 quoted = varflags & VSQUOTE;
6226 var = p;
6227 easy = (!quoted || (*var == '@' && shellparam.nparam));
6228 startloc = expdest - (char *)stackblock();
6229 p = strchr(p, '=') + 1;
6230
6231 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006232 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006233 if (varflags & VSNUL)
6234 varlen--;
6235
6236 if (subtype == VSPLUS) {
6237 varlen = -1 - varlen;
6238 goto vsplus;
6239 }
6240
6241 if (subtype == VSMINUS) {
6242 vsplus:
6243 if (varlen < 0) {
6244 argstr(
6245 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006246 (quoted ? EXP_QWORD : EXP_WORD),
6247 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006248 );
6249 goto end;
6250 }
6251 if (easy)
6252 goto record;
6253 goto end;
6254 }
6255
6256 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6257 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006258 if (subevalvar(p, var, /* strloc: */ 0,
6259 subtype, startloc, varflags,
6260 /* quotes: */ 0,
6261 var_str_list)
6262 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263 varflags &= ~VSNUL;
6264 /*
6265 * Remove any recorded regions beyond
6266 * start of variable
6267 */
6268 removerecordregions(startloc);
6269 goto again;
6270 }
6271 goto end;
6272 }
6273 if (easy)
6274 goto record;
6275 goto end;
6276 }
6277
6278 if (varlen < 0 && uflag)
6279 varunset(p, var, 0, 0);
6280
6281 if (subtype == VSLENGTH) {
6282 cvtnum(varlen > 0 ? varlen : 0);
6283 goto record;
6284 }
6285
6286 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006287 if (easy)
6288 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006289 goto end;
6290 }
6291
6292#if DEBUG
6293 switch (subtype) {
6294 case VSTRIMLEFT:
6295 case VSTRIMLEFTMAX:
6296 case VSTRIMRIGHT:
6297 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006298#if ENABLE_ASH_BASH_COMPAT
6299 case VSSUBSTR:
6300 case VSREPLACE:
6301 case VSREPLACEALL:
6302#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006303 break;
6304 default:
6305 abort();
6306 }
6307#endif
6308
6309 if (varlen >= 0) {
6310 /*
6311 * Terminate the string and start recording the pattern
6312 * right after it
6313 */
6314 STPUTC('\0', expdest);
6315 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006316 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6317 startloc, varflags,
6318 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6319 var_str_list)
6320 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006321 int amount = expdest - (
6322 (char *)stackblock() + patloc - 1
6323 );
6324 STADJUST(-amount, expdest);
6325 }
6326 /* Remove any recorded regions beyond start of variable */
6327 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006328 record:
6329 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006330 }
6331
6332 end:
6333 if (subtype != VSNORMAL) { /* skip to end of alternative */
6334 int nesting = 1;
6335 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006336 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006337 if (c == CTLESC)
6338 p++;
6339 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6340 if (varlen >= 0)
6341 argbackq = argbackq->next;
6342 } else if (c == CTLVAR) {
6343 if ((*p++ & VSTYPE) != VSNORMAL)
6344 nesting++;
6345 } else if (c == CTLENDVAR) {
6346 if (--nesting == 0)
6347 break;
6348 }
6349 }
6350 }
6351 return p;
6352}
6353
6354/*
6355 * Break the argument string into pieces based upon IFS and add the
6356 * strings to the argument list. The regions of the string to be
6357 * searched for IFS characters have been stored by recordregion.
6358 */
6359static void
6360ifsbreakup(char *string, struct arglist *arglist)
6361{
6362 struct ifsregion *ifsp;
6363 struct strlist *sp;
6364 char *start;
6365 char *p;
6366 char *q;
6367 const char *ifs, *realifs;
6368 int ifsspc;
6369 int nulonly;
6370
6371 start = string;
6372 if (ifslastp != NULL) {
6373 ifsspc = 0;
6374 nulonly = 0;
6375 realifs = ifsset() ? ifsval() : defifs;
6376 ifsp = &ifsfirst;
6377 do {
6378 p = string + ifsp->begoff;
6379 nulonly = ifsp->nulonly;
6380 ifs = nulonly ? nullstr : realifs;
6381 ifsspc = 0;
6382 while (p < string + ifsp->endoff) {
6383 q = p;
6384 if (*p == CTLESC)
6385 p++;
6386 if (!strchr(ifs, *p)) {
6387 p++;
6388 continue;
6389 }
6390 if (!nulonly)
6391 ifsspc = (strchr(defifs, *p) != NULL);
6392 /* Ignore IFS whitespace at start */
6393 if (q == start && ifsspc) {
6394 p++;
6395 start = p;
6396 continue;
6397 }
6398 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006399 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006400 sp->text = start;
6401 *arglist->lastp = sp;
6402 arglist->lastp = &sp->next;
6403 p++;
6404 if (!nulonly) {
6405 for (;;) {
6406 if (p >= string + ifsp->endoff) {
6407 break;
6408 }
6409 q = p;
6410 if (*p == CTLESC)
6411 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006412 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006413 p = q;
6414 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006415 }
6416 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006417 if (ifsspc) {
6418 p++;
6419 ifsspc = 0;
6420 } else {
6421 p = q;
6422 break;
6423 }
6424 } else
6425 p++;
6426 }
6427 }
6428 start = p;
6429 } /* while */
6430 ifsp = ifsp->next;
6431 } while (ifsp != NULL);
6432 if (nulonly)
6433 goto add;
6434 }
6435
6436 if (!*start)
6437 return;
6438
6439 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006440 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 sp->text = start;
6442 *arglist->lastp = sp;
6443 arglist->lastp = &sp->next;
6444}
6445
6446static void
6447ifsfree(void)
6448{
6449 struct ifsregion *p;
6450
6451 INT_OFF;
6452 p = ifsfirst.next;
6453 do {
6454 struct ifsregion *ifsp;
6455 ifsp = p->next;
6456 free(p);
6457 p = ifsp;
6458 } while (p);
6459 ifslastp = NULL;
6460 ifsfirst.next = NULL;
6461 INT_ON;
6462}
6463
6464/*
6465 * Add a file name to the list.
6466 */
6467static void
6468addfname(const char *name)
6469{
6470 struct strlist *sp;
6471
Denis Vlasenko597906c2008-02-20 16:38:54 +00006472 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006473 sp->text = ststrdup(name);
6474 *exparg.lastp = sp;
6475 exparg.lastp = &sp->next;
6476}
6477
6478static char *expdir;
6479
6480/*
6481 * Do metacharacter (i.e. *, ?, [...]) expansion.
6482 */
6483static void
6484expmeta(char *enddir, char *name)
6485{
6486 char *p;
6487 const char *cp;
6488 char *start;
6489 char *endname;
6490 int metaflag;
6491 struct stat statb;
6492 DIR *dirp;
6493 struct dirent *dp;
6494 int atend;
6495 int matchdot;
6496
6497 metaflag = 0;
6498 start = name;
6499 for (p = name; *p; p++) {
6500 if (*p == '*' || *p == '?')
6501 metaflag = 1;
6502 else if (*p == '[') {
6503 char *q = p + 1;
6504 if (*q == '!')
6505 q++;
6506 for (;;) {
6507 if (*q == '\\')
6508 q++;
6509 if (*q == '/' || *q == '\0')
6510 break;
6511 if (*++q == ']') {
6512 metaflag = 1;
6513 break;
6514 }
6515 }
6516 } else if (*p == '\\')
6517 p++;
6518 else if (*p == '/') {
6519 if (metaflag)
6520 goto out;
6521 start = p + 1;
6522 }
6523 }
6524 out:
6525 if (metaflag == 0) { /* we've reached the end of the file name */
6526 if (enddir != expdir)
6527 metaflag++;
6528 p = name;
6529 do {
6530 if (*p == '\\')
6531 p++;
6532 *enddir++ = *p;
6533 } while (*p++);
6534 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6535 addfname(expdir);
6536 return;
6537 }
6538 endname = p;
6539 if (name < start) {
6540 p = name;
6541 do {
6542 if (*p == '\\')
6543 p++;
6544 *enddir++ = *p++;
6545 } while (p < start);
6546 }
6547 if (enddir == expdir) {
6548 cp = ".";
6549 } else if (enddir == expdir + 1 && *expdir == '/') {
6550 cp = "/";
6551 } else {
6552 cp = expdir;
6553 enddir[-1] = '\0';
6554 }
6555 dirp = opendir(cp);
6556 if (dirp == NULL)
6557 return;
6558 if (enddir != expdir)
6559 enddir[-1] = '/';
6560 if (*endname == 0) {
6561 atend = 1;
6562 } else {
6563 atend = 0;
6564 *endname++ = '\0';
6565 }
6566 matchdot = 0;
6567 p = start;
6568 if (*p == '\\')
6569 p++;
6570 if (*p == '.')
6571 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006572 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006573 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006574 continue;
6575 if (pmatch(start, dp->d_name)) {
6576 if (atend) {
6577 strcpy(enddir, dp->d_name);
6578 addfname(expdir);
6579 } else {
6580 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6581 continue;
6582 p[-1] = '/';
6583 expmeta(p, endname);
6584 }
6585 }
6586 }
6587 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006588 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589 endname[-1] = '/';
6590}
6591
6592static struct strlist *
6593msort(struct strlist *list, int len)
6594{
6595 struct strlist *p, *q = NULL;
6596 struct strlist **lpp;
6597 int half;
6598 int n;
6599
6600 if (len <= 1)
6601 return list;
6602 half = len >> 1;
6603 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006604 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006605 q = p;
6606 p = p->next;
6607 }
6608 q->next = NULL; /* terminate first half of list */
6609 q = msort(list, half); /* sort first half of list */
6610 p = msort(p, len - half); /* sort second half */
6611 lpp = &list;
6612 for (;;) {
6613#if ENABLE_LOCALE_SUPPORT
6614 if (strcoll(p->text, q->text) < 0)
6615#else
6616 if (strcmp(p->text, q->text) < 0)
6617#endif
6618 {
6619 *lpp = p;
6620 lpp = &p->next;
6621 p = *lpp;
6622 if (p == NULL) {
6623 *lpp = q;
6624 break;
6625 }
6626 } else {
6627 *lpp = q;
6628 lpp = &q->next;
6629 q = *lpp;
6630 if (q == NULL) {
6631 *lpp = p;
6632 break;
6633 }
6634 }
6635 }
6636 return list;
6637}
6638
6639/*
6640 * Sort the results of file name expansion. It calculates the number of
6641 * strings to sort and then calls msort (short for merge sort) to do the
6642 * work.
6643 */
6644static struct strlist *
6645expsort(struct strlist *str)
6646{
6647 int len;
6648 struct strlist *sp;
6649
6650 len = 0;
6651 for (sp = str; sp; sp = sp->next)
6652 len++;
6653 return msort(str, len);
6654}
6655
6656static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006657expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006658{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006659 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006660 '*', '?', '[', 0
6661 };
6662 /* TODO - EXP_REDIR */
6663
6664 while (str) {
6665 struct strlist **savelastp;
6666 struct strlist *sp;
6667 char *p;
6668
6669 if (fflag)
6670 goto nometa;
6671 if (!strpbrk(str->text, metachars))
6672 goto nometa;
6673 savelastp = exparg.lastp;
6674
6675 INT_OFF;
6676 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6677 {
6678 int i = strlen(str->text);
6679 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6680 }
6681
6682 expmeta(expdir, p);
6683 free(expdir);
6684 if (p != str->text)
6685 free(p);
6686 INT_ON;
6687 if (exparg.lastp == savelastp) {
6688 /*
6689 * no matches
6690 */
6691 nometa:
6692 *exparg.lastp = str;
6693 rmescapes(str->text);
6694 exparg.lastp = &str->next;
6695 } else {
6696 *exparg.lastp = NULL;
6697 *savelastp = sp = expsort(*savelastp);
6698 while (sp->next != NULL)
6699 sp = sp->next;
6700 exparg.lastp = &sp->next;
6701 }
6702 str = str->next;
6703 }
6704}
6705
6706/*
6707 * Perform variable substitution and command substitution on an argument,
6708 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6709 * perform splitting and file name expansion. When arglist is NULL, perform
6710 * here document expansion.
6711 */
6712static void
6713expandarg(union node *arg, struct arglist *arglist, int flag)
6714{
6715 struct strlist *sp;
6716 char *p;
6717
6718 argbackq = arg->narg.backquote;
6719 STARTSTACKSTR(expdest);
6720 ifsfirst.next = NULL;
6721 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006722 argstr(arg->narg.text, flag,
6723 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006724 p = _STPUTC('\0', expdest);
6725 expdest = p - 1;
6726 if (arglist == NULL) {
6727 return; /* here document expanded */
6728 }
6729 p = grabstackstr(p);
6730 exparg.lastp = &exparg.list;
6731 /*
6732 * TODO - EXP_REDIR
6733 */
6734 if (flag & EXP_FULL) {
6735 ifsbreakup(p, &exparg);
6736 *exparg.lastp = NULL;
6737 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006738 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006739 } else {
6740 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6741 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006742 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006743 sp->text = p;
6744 *exparg.lastp = sp;
6745 exparg.lastp = &sp->next;
6746 }
6747 if (ifsfirst.next)
6748 ifsfree();
6749 *exparg.lastp = NULL;
6750 if (exparg.list) {
6751 *arglist->lastp = exparg.list;
6752 arglist->lastp = exparg.lastp;
6753 }
6754}
6755
6756/*
6757 * Expand shell variables and backquotes inside a here document.
6758 */
6759static void
6760expandhere(union node *arg, int fd)
6761{
6762 herefd = fd;
6763 expandarg(arg, (struct arglist *)NULL, 0);
6764 full_write(fd, stackblock(), expdest - (char *)stackblock());
6765}
6766
6767/*
6768 * Returns true if the pattern matches the string.
6769 */
6770static int
6771patmatch(char *pattern, const char *string)
6772{
6773 return pmatch(preglob(pattern, 0, 0), string);
6774}
6775
6776/*
6777 * See if a pattern matches in a case statement.
6778 */
6779static int
6780casematch(union node *pattern, char *val)
6781{
6782 struct stackmark smark;
6783 int result;
6784
6785 setstackmark(&smark);
6786 argbackq = pattern->narg.backquote;
6787 STARTSTACKSTR(expdest);
6788 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006789 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6790 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006791 STACKSTRNUL(expdest);
6792 result = patmatch(stackblock(), val);
6793 popstackmark(&smark);
6794 return result;
6795}
6796
6797
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006798/* ============ find_command */
6799
6800struct builtincmd {
6801 const char *name;
6802 int (*builtin)(int, char **);
6803 /* unsigned flags; */
6804};
6805#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006806/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006807 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006808#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006809#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006810
6811struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006812 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006813 union param {
6814 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006815 /* index >= 0 for commands without path (slashes) */
6816 /* (TODO: what exactly does the value mean? PATH position?) */
6817 /* index == -1 for commands with slashes */
6818 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006819 const struct builtincmd *cmd;
6820 struct funcnode *func;
6821 } u;
6822};
6823/* values of cmdtype */
6824#define CMDUNKNOWN -1 /* no entry in table for command */
6825#define CMDNORMAL 0 /* command is an executable program */
6826#define CMDFUNCTION 1 /* command is a shell function */
6827#define CMDBUILTIN 2 /* command is a shell builtin */
6828
6829/* action to find_command() */
6830#define DO_ERR 0x01 /* prints errors */
6831#define DO_ABS 0x02 /* checks absolute paths */
6832#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6833#define DO_ALTPATH 0x08 /* using alternate path */
6834#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6835
6836static void find_command(char *, struct cmdentry *, int, const char *);
6837
6838
6839/* ============ Hashing commands */
6840
6841/*
6842 * When commands are first encountered, they are entered in a hash table.
6843 * This ensures that a full path search will not have to be done for them
6844 * on each invocation.
6845 *
6846 * We should investigate converting to a linear search, even though that
6847 * would make the command name "hash" a misnomer.
6848 */
6849
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006850struct tblentry {
6851 struct tblentry *next; /* next entry in hash chain */
6852 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006853 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006854 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006855 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006856};
6857
Denis Vlasenko01631112007-12-16 17:20:38 +00006858static struct tblentry **cmdtable;
6859#define INIT_G_cmdtable() do { \
6860 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6861} while (0)
6862
6863static int builtinloc = -1; /* index in path of %builtin, or -1 */
6864
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006865
6866static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006867tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006868{
6869 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006870
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006871#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006872 if (applet_no >= 0) {
6873 if (APPLET_IS_NOEXEC(applet_no))
6874 run_applet_no_and_exit(applet_no, argv);
6875 /* re-exec ourselves with the new arguments */
6876 execve(bb_busybox_exec_path, argv, envp);
6877 /* If they called chroot or otherwise made the binary no longer
6878 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006879 }
6880#endif
6881
6882 repeat:
6883#ifdef SYSV
6884 do {
6885 execve(cmd, argv, envp);
6886 } while (errno == EINTR);
6887#else
6888 execve(cmd, argv, envp);
6889#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006890 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006891 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006892 return;
6893 }
6894 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006895 char **ap;
6896 char **new;
6897
6898 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006899 continue;
6900 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006902 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006903 ap += 2;
6904 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006905 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006906 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006907 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006908 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006909 goto repeat;
6910 }
6911}
6912
6913/*
6914 * Exec a program. Never returns. If you change this routine, you may
6915 * have to change the find_command routine as well.
6916 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006917static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006918static void
6919shellexec(char **argv, const char *path, int idx)
6920{
6921 char *cmdname;
6922 int e;
6923 char **envp;
6924 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006925#if ENABLE_FEATURE_SH_STANDALONE
6926 int applet_no = -1;
6927#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006928
6929 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006930 envp = listvars(VEXPORT, VUNSET, 0);
6931 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006932#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006933 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006934#endif
6935 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006936 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006937 e = errno;
6938 } else {
6939 e = ENOENT;
6940 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6941 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006942 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006943 if (errno != ENOENT && errno != ENOTDIR)
6944 e = errno;
6945 }
6946 stunalloc(cmdname);
6947 }
6948 }
6949
6950 /* Map to POSIX errors */
6951 switch (e) {
6952 case EACCES:
6953 exerrno = 126;
6954 break;
6955 case ENOENT:
6956 exerrno = 127;
6957 break;
6958 default:
6959 exerrno = 2;
6960 break;
6961 }
6962 exitstatus = exerrno;
6963 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006964 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006965 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6966 /* NOTREACHED */
6967}
6968
6969static void
6970printentry(struct tblentry *cmdp)
6971{
6972 int idx;
6973 const char *path;
6974 char *name;
6975
6976 idx = cmdp->param.index;
6977 path = pathval();
6978 do {
6979 name = padvance(&path, cmdp->cmdname);
6980 stunalloc(name);
6981 } while (--idx >= 0);
6982 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6983}
6984
6985/*
6986 * Clear out command entries. The argument specifies the first entry in
6987 * PATH which has changed.
6988 */
6989static void
6990clearcmdentry(int firstchange)
6991{
6992 struct tblentry **tblp;
6993 struct tblentry **pp;
6994 struct tblentry *cmdp;
6995
6996 INT_OFF;
6997 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6998 pp = tblp;
6999 while ((cmdp = *pp) != NULL) {
7000 if ((cmdp->cmdtype == CMDNORMAL &&
7001 cmdp->param.index >= firstchange)
7002 || (cmdp->cmdtype == CMDBUILTIN &&
7003 builtinloc >= firstchange)
7004 ) {
7005 *pp = cmdp->next;
7006 free(cmdp);
7007 } else {
7008 pp = &cmdp->next;
7009 }
7010 }
7011 }
7012 INT_ON;
7013}
7014
7015/*
7016 * Locate a command in the command hash table. If "add" is nonzero,
7017 * add the command to the table if it is not already present. The
7018 * variable "lastcmdentry" is set to point to the address of the link
7019 * pointing to the entry, so that delete_cmd_entry can delete the
7020 * entry.
7021 *
7022 * Interrupts must be off if called with add != 0.
7023 */
7024static struct tblentry **lastcmdentry;
7025
7026static struct tblentry *
7027cmdlookup(const char *name, int add)
7028{
7029 unsigned int hashval;
7030 const char *p;
7031 struct tblentry *cmdp;
7032 struct tblentry **pp;
7033
7034 p = name;
7035 hashval = (unsigned char)*p << 4;
7036 while (*p)
7037 hashval += (unsigned char)*p++;
7038 hashval &= 0x7FFF;
7039 pp = &cmdtable[hashval % CMDTABLESIZE];
7040 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7041 if (strcmp(cmdp->cmdname, name) == 0)
7042 break;
7043 pp = &cmdp->next;
7044 }
7045 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007046 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7047 + strlen(name)
7048 /* + 1 - already done because
7049 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007050 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007051 cmdp->cmdtype = CMDUNKNOWN;
7052 strcpy(cmdp->cmdname, name);
7053 }
7054 lastcmdentry = pp;
7055 return cmdp;
7056}
7057
7058/*
7059 * Delete the command entry returned on the last lookup.
7060 */
7061static void
7062delete_cmd_entry(void)
7063{
7064 struct tblentry *cmdp;
7065
7066 INT_OFF;
7067 cmdp = *lastcmdentry;
7068 *lastcmdentry = cmdp->next;
7069 if (cmdp->cmdtype == CMDFUNCTION)
7070 freefunc(cmdp->param.func);
7071 free(cmdp);
7072 INT_ON;
7073}
7074
7075/*
7076 * Add a new command entry, replacing any existing command entry for
7077 * the same name - except special builtins.
7078 */
7079static void
7080addcmdentry(char *name, struct cmdentry *entry)
7081{
7082 struct tblentry *cmdp;
7083
7084 cmdp = cmdlookup(name, 1);
7085 if (cmdp->cmdtype == CMDFUNCTION) {
7086 freefunc(cmdp->param.func);
7087 }
7088 cmdp->cmdtype = entry->cmdtype;
7089 cmdp->param = entry->u;
7090 cmdp->rehash = 0;
7091}
7092
7093static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007094hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007095{
7096 struct tblentry **pp;
7097 struct tblentry *cmdp;
7098 int c;
7099 struct cmdentry entry;
7100 char *name;
7101
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007102 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007103 clearcmdentry(0);
7104 return 0;
7105 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007106
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007107 if (*argptr == NULL) {
7108 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7109 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7110 if (cmdp->cmdtype == CMDNORMAL)
7111 printentry(cmdp);
7112 }
7113 }
7114 return 0;
7115 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007116
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007117 c = 0;
7118 while ((name = *argptr) != NULL) {
7119 cmdp = cmdlookup(name, 0);
7120 if (cmdp != NULL
7121 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007122 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7123 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007124 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007125 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007126 find_command(name, &entry, DO_ERR, pathval());
7127 if (entry.cmdtype == CMDUNKNOWN)
7128 c = 1;
7129 argptr++;
7130 }
7131 return c;
7132}
7133
7134/*
7135 * Called when a cd is done. Marks all commands so the next time they
7136 * are executed they will be rehashed.
7137 */
7138static void
7139hashcd(void)
7140{
7141 struct tblentry **pp;
7142 struct tblentry *cmdp;
7143
7144 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7145 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007146 if (cmdp->cmdtype == CMDNORMAL
7147 || (cmdp->cmdtype == CMDBUILTIN
7148 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7149 && builtinloc > 0)
7150 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007151 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007152 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007153 }
7154 }
7155}
7156
7157/*
7158 * Fix command hash table when PATH changed.
7159 * Called before PATH is changed. The argument is the new value of PATH;
7160 * pathval() still returns the old value at this point.
7161 * Called with interrupts off.
7162 */
7163static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007164changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007165{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007166 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007167 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007168 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007169 int idx_bltin;
7170
7171 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007172 firstchange = 9999; /* assume no change */
7173 idx = 0;
7174 idx_bltin = -1;
7175 for (;;) {
7176 if (*old != *new) {
7177 firstchange = idx;
7178 if ((*old == '\0' && *new == ':')
7179 || (*old == ':' && *new == '\0'))
7180 firstchange++;
7181 old = new; /* ignore subsequent differences */
7182 }
7183 if (*new == '\0')
7184 break;
7185 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7186 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007187 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007188 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007189 new++, old++;
7190 }
7191 if (builtinloc < 0 && idx_bltin >= 0)
7192 builtinloc = idx_bltin; /* zap builtins */
7193 if (builtinloc >= 0 && idx_bltin < 0)
7194 firstchange = 0;
7195 clearcmdentry(firstchange);
7196 builtinloc = idx_bltin;
7197}
7198
7199#define TEOF 0
7200#define TNL 1
7201#define TREDIR 2
7202#define TWORD 3
7203#define TSEMI 4
7204#define TBACKGND 5
7205#define TAND 6
7206#define TOR 7
7207#define TPIPE 8
7208#define TLP 9
7209#define TRP 10
7210#define TENDCASE 11
7211#define TENDBQUOTE 12
7212#define TNOT 13
7213#define TCASE 14
7214#define TDO 15
7215#define TDONE 16
7216#define TELIF 17
7217#define TELSE 18
7218#define TESAC 19
7219#define TFI 20
7220#define TFOR 21
7221#define TIF 22
7222#define TIN 23
7223#define TTHEN 24
7224#define TUNTIL 25
7225#define TWHILE 26
7226#define TBEGIN 27
7227#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007228typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007229
7230/* first char is indicating which tokens mark the end of a list */
7231static const char *const tokname_array[] = {
7232 "\1end of file",
7233 "\0newline",
7234 "\0redirection",
7235 "\0word",
7236 "\0;",
7237 "\0&",
7238 "\0&&",
7239 "\0||",
7240 "\0|",
7241 "\0(",
7242 "\1)",
7243 "\1;;",
7244 "\1`",
7245#define KWDOFFSET 13
7246 /* the following are keywords */
7247 "\0!",
7248 "\0case",
7249 "\1do",
7250 "\1done",
7251 "\1elif",
7252 "\1else",
7253 "\1esac",
7254 "\1fi",
7255 "\0for",
7256 "\0if",
7257 "\0in",
7258 "\1then",
7259 "\0until",
7260 "\0while",
7261 "\0{",
7262 "\1}",
7263};
7264
7265static const char *
7266tokname(int tok)
7267{
7268 static char buf[16];
7269
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007270//try this:
7271//if (tok < TSEMI) return tokname_array[tok] + 1;
7272//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7273//return buf;
7274
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007275 if (tok >= TSEMI)
7276 buf[0] = '"';
7277 sprintf(buf + (tok >= TSEMI), "%s%c",
7278 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7279 return buf;
7280}
7281
7282/* Wrapper around strcmp for qsort/bsearch/... */
7283static int
7284pstrcmp(const void *a, const void *b)
7285{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007286 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287}
7288
7289static const char *const *
7290findkwd(const char *s)
7291{
7292 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007293 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7294 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007295}
7296
7297/*
7298 * Locate and print what a word is...
7299 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007300static int
7301describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302{
7303 struct cmdentry entry;
7304 struct tblentry *cmdp;
7305#if ENABLE_ASH_ALIAS
7306 const struct alias *ap;
7307#endif
7308 const char *path = pathval();
7309
7310 if (describe_command_verbose) {
7311 out1str(command);
7312 }
7313
7314 /* First look at the keywords */
7315 if (findkwd(command)) {
7316 out1str(describe_command_verbose ? " is a shell keyword" : command);
7317 goto out;
7318 }
7319
7320#if ENABLE_ASH_ALIAS
7321 /* Then look at the aliases */
7322 ap = lookupalias(command, 0);
7323 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007324 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007325 out1str("alias ");
7326 printalias(ap);
7327 return 0;
7328 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007329 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007330 goto out;
7331 }
7332#endif
7333 /* Then check if it is a tracked alias */
7334 cmdp = cmdlookup(command, 0);
7335 if (cmdp != NULL) {
7336 entry.cmdtype = cmdp->cmdtype;
7337 entry.u = cmdp->param;
7338 } else {
7339 /* Finally use brute force */
7340 find_command(command, &entry, DO_ABS, path);
7341 }
7342
7343 switch (entry.cmdtype) {
7344 case CMDNORMAL: {
7345 int j = entry.u.index;
7346 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007347 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007348 p = command;
7349 } else {
7350 do {
7351 p = padvance(&path, command);
7352 stunalloc(p);
7353 } while (--j >= 0);
7354 }
7355 if (describe_command_verbose) {
7356 out1fmt(" is%s %s",
7357 (cmdp ? " a tracked alias for" : nullstr), p
7358 );
7359 } else {
7360 out1str(p);
7361 }
7362 break;
7363 }
7364
7365 case CMDFUNCTION:
7366 if (describe_command_verbose) {
7367 out1str(" is a shell function");
7368 } else {
7369 out1str(command);
7370 }
7371 break;
7372
7373 case CMDBUILTIN:
7374 if (describe_command_verbose) {
7375 out1fmt(" is a %sshell builtin",
7376 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7377 "special " : nullstr
7378 );
7379 } else {
7380 out1str(command);
7381 }
7382 break;
7383
7384 default:
7385 if (describe_command_verbose) {
7386 out1str(": not found\n");
7387 }
7388 return 127;
7389 }
7390 out:
7391 outstr("\n", stdout);
7392 return 0;
7393}
7394
7395static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007396typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007397{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007398 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007399 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007400 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401
Denis Vlasenko46846e22007-05-20 13:08:31 +00007402 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007403 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007404 i++;
7405 verbose = 0;
7406 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007407 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007408 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409 }
7410 return err;
7411}
7412
7413#if ENABLE_ASH_CMDCMD
7414static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007415commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007416{
7417 int c;
7418 enum {
7419 VERIFY_BRIEF = 1,
7420 VERIFY_VERBOSE = 2,
7421 } verify = 0;
7422
7423 while ((c = nextopt("pvV")) != '\0')
7424 if (c == 'V')
7425 verify |= VERIFY_VERBOSE;
7426 else if (c == 'v')
7427 verify |= VERIFY_BRIEF;
7428#if DEBUG
7429 else if (c != 'p')
7430 abort();
7431#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007432 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7433 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007434 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007435 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436
7437 return 0;
7438}
7439#endif
7440
7441
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007442/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007443
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007444static int funcblocksize; /* size of structures in function */
7445static int funcstringsize; /* size of strings in node */
7446static void *funcblock; /* block to allocate function from */
7447static char *funcstring; /* block to allocate strings from */
7448
Eric Andersencb57d552001-06-28 07:25:16 +00007449/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007450#define EV_EXIT 01 /* exit after evaluating tree */
7451#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7452#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007453
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454static const short nodesize[26] = {
7455 SHELL_ALIGN(sizeof(struct ncmd)),
7456 SHELL_ALIGN(sizeof(struct npipe)),
7457 SHELL_ALIGN(sizeof(struct nredir)),
7458 SHELL_ALIGN(sizeof(struct nredir)),
7459 SHELL_ALIGN(sizeof(struct nredir)),
7460 SHELL_ALIGN(sizeof(struct nbinary)),
7461 SHELL_ALIGN(sizeof(struct nbinary)),
7462 SHELL_ALIGN(sizeof(struct nbinary)),
7463 SHELL_ALIGN(sizeof(struct nif)),
7464 SHELL_ALIGN(sizeof(struct nbinary)),
7465 SHELL_ALIGN(sizeof(struct nbinary)),
7466 SHELL_ALIGN(sizeof(struct nfor)),
7467 SHELL_ALIGN(sizeof(struct ncase)),
7468 SHELL_ALIGN(sizeof(struct nclist)),
7469 SHELL_ALIGN(sizeof(struct narg)),
7470 SHELL_ALIGN(sizeof(struct narg)),
7471 SHELL_ALIGN(sizeof(struct nfile)),
7472 SHELL_ALIGN(sizeof(struct nfile)),
7473 SHELL_ALIGN(sizeof(struct nfile)),
7474 SHELL_ALIGN(sizeof(struct nfile)),
7475 SHELL_ALIGN(sizeof(struct nfile)),
7476 SHELL_ALIGN(sizeof(struct ndup)),
7477 SHELL_ALIGN(sizeof(struct ndup)),
7478 SHELL_ALIGN(sizeof(struct nhere)),
7479 SHELL_ALIGN(sizeof(struct nhere)),
7480 SHELL_ALIGN(sizeof(struct nnot)),
7481};
7482
7483static void calcsize(union node *n);
7484
7485static void
7486sizenodelist(struct nodelist *lp)
7487{
7488 while (lp) {
7489 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7490 calcsize(lp->n);
7491 lp = lp->next;
7492 }
7493}
7494
7495static void
7496calcsize(union node *n)
7497{
7498 if (n == NULL)
7499 return;
7500 funcblocksize += nodesize[n->type];
7501 switch (n->type) {
7502 case NCMD:
7503 calcsize(n->ncmd.redirect);
7504 calcsize(n->ncmd.args);
7505 calcsize(n->ncmd.assign);
7506 break;
7507 case NPIPE:
7508 sizenodelist(n->npipe.cmdlist);
7509 break;
7510 case NREDIR:
7511 case NBACKGND:
7512 case NSUBSHELL:
7513 calcsize(n->nredir.redirect);
7514 calcsize(n->nredir.n);
7515 break;
7516 case NAND:
7517 case NOR:
7518 case NSEMI:
7519 case NWHILE:
7520 case NUNTIL:
7521 calcsize(n->nbinary.ch2);
7522 calcsize(n->nbinary.ch1);
7523 break;
7524 case NIF:
7525 calcsize(n->nif.elsepart);
7526 calcsize(n->nif.ifpart);
7527 calcsize(n->nif.test);
7528 break;
7529 case NFOR:
7530 funcstringsize += strlen(n->nfor.var) + 1;
7531 calcsize(n->nfor.body);
7532 calcsize(n->nfor.args);
7533 break;
7534 case NCASE:
7535 calcsize(n->ncase.cases);
7536 calcsize(n->ncase.expr);
7537 break;
7538 case NCLIST:
7539 calcsize(n->nclist.body);
7540 calcsize(n->nclist.pattern);
7541 calcsize(n->nclist.next);
7542 break;
7543 case NDEFUN:
7544 case NARG:
7545 sizenodelist(n->narg.backquote);
7546 funcstringsize += strlen(n->narg.text) + 1;
7547 calcsize(n->narg.next);
7548 break;
7549 case NTO:
7550 case NCLOBBER:
7551 case NFROM:
7552 case NFROMTO:
7553 case NAPPEND:
7554 calcsize(n->nfile.fname);
7555 calcsize(n->nfile.next);
7556 break;
7557 case NTOFD:
7558 case NFROMFD:
7559 calcsize(n->ndup.vname);
7560 calcsize(n->ndup.next);
7561 break;
7562 case NHERE:
7563 case NXHERE:
7564 calcsize(n->nhere.doc);
7565 calcsize(n->nhere.next);
7566 break;
7567 case NNOT:
7568 calcsize(n->nnot.com);
7569 break;
7570 };
7571}
7572
7573static char *
7574nodeckstrdup(char *s)
7575{
7576 char *rtn = funcstring;
7577
7578 strcpy(funcstring, s);
7579 funcstring += strlen(s) + 1;
7580 return rtn;
7581}
7582
7583static union node *copynode(union node *);
7584
7585static struct nodelist *
7586copynodelist(struct nodelist *lp)
7587{
7588 struct nodelist *start;
7589 struct nodelist **lpp;
7590
7591 lpp = &start;
7592 while (lp) {
7593 *lpp = funcblock;
7594 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7595 (*lpp)->n = copynode(lp->n);
7596 lp = lp->next;
7597 lpp = &(*lpp)->next;
7598 }
7599 *lpp = NULL;
7600 return start;
7601}
7602
7603static union node *
7604copynode(union node *n)
7605{
7606 union node *new;
7607
7608 if (n == NULL)
7609 return NULL;
7610 new = funcblock;
7611 funcblock = (char *) funcblock + nodesize[n->type];
7612
7613 switch (n->type) {
7614 case NCMD:
7615 new->ncmd.redirect = copynode(n->ncmd.redirect);
7616 new->ncmd.args = copynode(n->ncmd.args);
7617 new->ncmd.assign = copynode(n->ncmd.assign);
7618 break;
7619 case NPIPE:
7620 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007621 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007622 break;
7623 case NREDIR:
7624 case NBACKGND:
7625 case NSUBSHELL:
7626 new->nredir.redirect = copynode(n->nredir.redirect);
7627 new->nredir.n = copynode(n->nredir.n);
7628 break;
7629 case NAND:
7630 case NOR:
7631 case NSEMI:
7632 case NWHILE:
7633 case NUNTIL:
7634 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7635 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7636 break;
7637 case NIF:
7638 new->nif.elsepart = copynode(n->nif.elsepart);
7639 new->nif.ifpart = copynode(n->nif.ifpart);
7640 new->nif.test = copynode(n->nif.test);
7641 break;
7642 case NFOR:
7643 new->nfor.var = nodeckstrdup(n->nfor.var);
7644 new->nfor.body = copynode(n->nfor.body);
7645 new->nfor.args = copynode(n->nfor.args);
7646 break;
7647 case NCASE:
7648 new->ncase.cases = copynode(n->ncase.cases);
7649 new->ncase.expr = copynode(n->ncase.expr);
7650 break;
7651 case NCLIST:
7652 new->nclist.body = copynode(n->nclist.body);
7653 new->nclist.pattern = copynode(n->nclist.pattern);
7654 new->nclist.next = copynode(n->nclist.next);
7655 break;
7656 case NDEFUN:
7657 case NARG:
7658 new->narg.backquote = copynodelist(n->narg.backquote);
7659 new->narg.text = nodeckstrdup(n->narg.text);
7660 new->narg.next = copynode(n->narg.next);
7661 break;
7662 case NTO:
7663 case NCLOBBER:
7664 case NFROM:
7665 case NFROMTO:
7666 case NAPPEND:
7667 new->nfile.fname = copynode(n->nfile.fname);
7668 new->nfile.fd = n->nfile.fd;
7669 new->nfile.next = copynode(n->nfile.next);
7670 break;
7671 case NTOFD:
7672 case NFROMFD:
7673 new->ndup.vname = copynode(n->ndup.vname);
7674 new->ndup.dupfd = n->ndup.dupfd;
7675 new->ndup.fd = n->ndup.fd;
7676 new->ndup.next = copynode(n->ndup.next);
7677 break;
7678 case NHERE:
7679 case NXHERE:
7680 new->nhere.doc = copynode(n->nhere.doc);
7681 new->nhere.fd = n->nhere.fd;
7682 new->nhere.next = copynode(n->nhere.next);
7683 break;
7684 case NNOT:
7685 new->nnot.com = copynode(n->nnot.com);
7686 break;
7687 };
7688 new->type = n->type;
7689 return new;
7690}
7691
7692/*
7693 * Make a copy of a parse tree.
7694 */
7695static struct funcnode *
7696copyfunc(union node *n)
7697{
7698 struct funcnode *f;
7699 size_t blocksize;
7700
7701 funcblocksize = offsetof(struct funcnode, n);
7702 funcstringsize = 0;
7703 calcsize(n);
7704 blocksize = funcblocksize;
7705 f = ckmalloc(blocksize + funcstringsize);
7706 funcblock = (char *) f + offsetof(struct funcnode, n);
7707 funcstring = (char *) f + blocksize;
7708 copynode(n);
7709 f->count = 0;
7710 return f;
7711}
7712
7713/*
7714 * Define a shell function.
7715 */
7716static void
7717defun(char *name, union node *func)
7718{
7719 struct cmdentry entry;
7720
7721 INT_OFF;
7722 entry.cmdtype = CMDFUNCTION;
7723 entry.u.func = copyfunc(func);
7724 addcmdentry(name, &entry);
7725 INT_ON;
7726}
7727
7728static int evalskip; /* set if we are skipping commands */
7729/* reasons for skipping commands (see comment on breakcmd routine) */
7730#define SKIPBREAK (1 << 0)
7731#define SKIPCONT (1 << 1)
7732#define SKIPFUNC (1 << 2)
7733#define SKIPFILE (1 << 3)
7734#define SKIPEVAL (1 << 4)
7735static int skipcount; /* number of levels to skip */
7736static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007737static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007738
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007739/* forward decl way out to parsing code - dotrap needs it */
7740static int evalstring(char *s, int mask);
7741
7742/*
7743 * Called to execute a trap. Perhaps we should avoid entering new trap
7744 * handlers while we are executing a trap handler.
7745 */
7746static int
7747dotrap(void)
7748{
7749 char *p;
7750 char *q;
7751 int i;
7752 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007753 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007754
7755 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007756 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007757 xbarrier();
7758
7759 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7760 if (!*q)
7761 continue;
7762 *q = '\0';
7763
7764 p = trap[i + 1];
7765 if (!p)
7766 continue;
7767 skip = evalstring(p, SKIPEVAL);
7768 exitstatus = savestatus;
7769 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007770 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007771 }
7772
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007773 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007774}
7775
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007776/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007777static void evalloop(union node *, int);
7778static void evalfor(union node *, int);
7779static void evalcase(union node *, int);
7780static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007781static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007782static void evalpipe(union node *, int);
7783static void evalcommand(union node *, int);
7784static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007785static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007786
Eric Andersen62483552001-07-10 06:09:16 +00007787/*
Eric Andersenc470f442003-07-28 09:56:35 +00007788 * Evaluate a parse tree. The value is left in the global variable
7789 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007790 */
Eric Andersenc470f442003-07-28 09:56:35 +00007791static void
7792evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007793{
Eric Andersenc470f442003-07-28 09:56:35 +00007794 int checkexit = 0;
7795 void (*evalfn)(union node *, int);
7796 unsigned isor;
7797 int status;
7798 if (n == NULL) {
7799 TRACE(("evaltree(NULL) called\n"));
7800 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007801 }
Eric Andersenc470f442003-07-28 09:56:35 +00007802 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007803 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007804 switch (n->type) {
7805 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007806#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007807 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007808 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007809 break;
7810#endif
7811 case NNOT:
7812 evaltree(n->nnot.com, EV_TESTED);
7813 status = !exitstatus;
7814 goto setstatus;
7815 case NREDIR:
7816 expredir(n->nredir.redirect);
7817 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7818 if (!status) {
7819 evaltree(n->nredir.n, flags & EV_TESTED);
7820 status = exitstatus;
7821 }
7822 popredir(0);
7823 goto setstatus;
7824 case NCMD:
7825 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007826 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007827 if (eflag && !(flags & EV_TESTED))
7828 checkexit = ~0;
7829 goto calleval;
7830 case NFOR:
7831 evalfn = evalfor;
7832 goto calleval;
7833 case NWHILE:
7834 case NUNTIL:
7835 evalfn = evalloop;
7836 goto calleval;
7837 case NSUBSHELL:
7838 case NBACKGND:
7839 evalfn = evalsubshell;
7840 goto calleval;
7841 case NPIPE:
7842 evalfn = evalpipe;
7843 goto checkexit;
7844 case NCASE:
7845 evalfn = evalcase;
7846 goto calleval;
7847 case NAND:
7848 case NOR:
7849 case NSEMI:
7850#if NAND + 1 != NOR
7851#error NAND + 1 != NOR
7852#endif
7853#if NOR + 1 != NSEMI
7854#error NOR + 1 != NSEMI
7855#endif
7856 isor = n->type - NAND;
7857 evaltree(
7858 n->nbinary.ch1,
7859 (flags | ((isor >> 1) - 1)) & EV_TESTED
7860 );
7861 if (!exitstatus == isor)
7862 break;
7863 if (!evalskip) {
7864 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007865 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007866 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007867 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007868 evalfn(n, flags);
7869 break;
7870 }
7871 break;
7872 case NIF:
7873 evaltree(n->nif.test, EV_TESTED);
7874 if (evalskip)
7875 break;
7876 if (exitstatus == 0) {
7877 n = n->nif.ifpart;
7878 goto evaln;
7879 } else if (n->nif.elsepart) {
7880 n = n->nif.elsepart;
7881 goto evaln;
7882 }
7883 goto success;
7884 case NDEFUN:
7885 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007886 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007887 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007888 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007889 exitstatus = status;
7890 break;
7891 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007892 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007893 if ((checkexit & exitstatus))
7894 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007895 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007896 goto exexit;
7897
7898 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007899 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007900 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007901 }
Eric Andersen62483552001-07-10 06:09:16 +00007902}
7903
Eric Andersenc470f442003-07-28 09:56:35 +00007904#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7905static
7906#endif
7907void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7908
Eric Andersenc470f442003-07-28 09:56:35 +00007909static void
7910evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007911{
7912 int status;
7913
7914 loopnest++;
7915 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007916 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007917 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007918 int i;
7919
Eric Andersencb57d552001-06-28 07:25:16 +00007920 evaltree(n->nbinary.ch1, EV_TESTED);
7921 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007922 skipping:
7923 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007924 evalskip = 0;
7925 continue;
7926 }
7927 if (evalskip == SKIPBREAK && --skipcount <= 0)
7928 evalskip = 0;
7929 break;
7930 }
Eric Andersenc470f442003-07-28 09:56:35 +00007931 i = exitstatus;
7932 if (n->type != NWHILE)
7933 i = !i;
7934 if (i != 0)
7935 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007936 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007937 status = exitstatus;
7938 if (evalskip)
7939 goto skipping;
7940 }
7941 loopnest--;
7942 exitstatus = status;
7943}
7944
Eric Andersenc470f442003-07-28 09:56:35 +00007945static void
7946evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007947{
7948 struct arglist arglist;
7949 union node *argp;
7950 struct strlist *sp;
7951 struct stackmark smark;
7952
7953 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007954 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007955 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007956 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007957 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007958 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007959 if (evalskip)
7960 goto out;
7961 }
7962 *arglist.lastp = NULL;
7963
7964 exitstatus = 0;
7965 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007966 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007967 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007968 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007969 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007970 if (evalskip) {
7971 if (evalskip == SKIPCONT && --skipcount <= 0) {
7972 evalskip = 0;
7973 continue;
7974 }
7975 if (evalskip == SKIPBREAK && --skipcount <= 0)
7976 evalskip = 0;
7977 break;
7978 }
7979 }
7980 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007981 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007982 popstackmark(&smark);
7983}
7984
Eric Andersenc470f442003-07-28 09:56:35 +00007985static void
7986evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007987{
7988 union node *cp;
7989 union node *patp;
7990 struct arglist arglist;
7991 struct stackmark smark;
7992
7993 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007994 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007995 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007996 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007997 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007998 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7999 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008000 if (casematch(patp, arglist.list->text)) {
8001 if (evalskip == 0) {
8002 evaltree(cp->nclist.body, flags);
8003 }
8004 goto out;
8005 }
8006 }
8007 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008008 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008009 popstackmark(&smark);
8010}
8011
Eric Andersenc470f442003-07-28 09:56:35 +00008012/*
8013 * Kick off a subshell to evaluate a tree.
8014 */
Eric Andersenc470f442003-07-28 09:56:35 +00008015static void
8016evalsubshell(union node *n, int flags)
8017{
8018 struct job *jp;
8019 int backgnd = (n->type == NBACKGND);
8020 int status;
8021
8022 expredir(n->nredir.redirect);
8023 if (!backgnd && flags & EV_EXIT && !trap[0])
8024 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008025 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008026 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008027 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008028 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008029 flags |= EV_EXIT;
8030 if (backgnd)
8031 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008032 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008033 redirect(n->nredir.redirect, 0);
8034 evaltreenr(n->nredir.n, flags);
8035 /* never returns */
8036 }
8037 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008038 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008039 status = waitforjob(jp);
8040 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008041 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008042}
8043
Eric Andersenc470f442003-07-28 09:56:35 +00008044/*
8045 * Compute the names of the files in a redirection list.
8046 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008047static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008048static void
8049expredir(union node *n)
8050{
8051 union node *redir;
8052
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008053 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008054 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008055
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008056 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008057 fn.lastp = &fn.list;
8058 switch (redir->type) {
8059 case NFROMTO:
8060 case NFROM:
8061 case NTO:
8062 case NCLOBBER:
8063 case NAPPEND:
8064 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8065 redir->nfile.expfname = fn.list->text;
8066 break;
8067 case NFROMFD:
8068 case NTOFD:
8069 if (redir->ndup.vname) {
8070 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008071 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008072 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008073 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008074 }
8075 break;
8076 }
8077 }
8078}
8079
Eric Andersencb57d552001-06-28 07:25:16 +00008080/*
Eric Andersencb57d552001-06-28 07:25:16 +00008081 * Evaluate a pipeline. All the processes in the pipeline are children
8082 * of the process creating the pipeline. (This differs from some versions
8083 * of the shell, which make the last process in a pipeline the parent
8084 * of all the rest.)
8085 */
Eric Andersenc470f442003-07-28 09:56:35 +00008086static void
8087evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008088{
8089 struct job *jp;
8090 struct nodelist *lp;
8091 int pipelen;
8092 int prevfd;
8093 int pip[2];
8094
Eric Andersenc470f442003-07-28 09:56:35 +00008095 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008096 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008097 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008098 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008099 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008100 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008101 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008102 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008103 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008104 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008105 pip[1] = -1;
8106 if (lp->next) {
8107 if (pipe(pip) < 0) {
8108 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008109 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008110 }
8111 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008112 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008113 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008114 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008115 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008116 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008117 if (prevfd > 0) {
8118 dup2(prevfd, 0);
8119 close(prevfd);
8120 }
8121 if (pip[1] > 1) {
8122 dup2(pip[1], 1);
8123 close(pip[1]);
8124 }
Eric Andersenc470f442003-07-28 09:56:35 +00008125 evaltreenr(lp->n, flags);
8126 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008127 }
8128 if (prevfd >= 0)
8129 close(prevfd);
8130 prevfd = pip[0];
8131 close(pip[1]);
8132 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008133 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008134 exitstatus = waitforjob(jp);
8135 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008136 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008137 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008138}
8139
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008140/*
8141 * Controls whether the shell is interactive or not.
8142 */
8143static void
8144setinteractive(int on)
8145{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008146 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008147
8148 if (++on == is_interactive)
8149 return;
8150 is_interactive = on;
8151 setsignal(SIGINT);
8152 setsignal(SIGQUIT);
8153 setsignal(SIGTERM);
8154#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8155 if (is_interactive > 1) {
8156 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008157 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008158
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008159 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008160 out1fmt(
8161 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008162 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008163 "Enter 'help' for a list of built-in commands."
8164 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008165 bb_banner);
8166 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008167 }
8168 }
8169#endif
8170}
8171
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008172static void
8173optschanged(void)
8174{
8175#if DEBUG
8176 opentrace();
8177#endif
8178 setinteractive(iflag);
8179 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008180#if ENABLE_FEATURE_EDITING_VI
8181 if (viflag)
8182 line_input_state->flags |= VI_MODE;
8183 else
8184 line_input_state->flags &= ~VI_MODE;
8185#else
8186 viflag = 0; /* forcibly keep the option off */
8187#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008188}
8189
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008190static struct localvar *localvars;
8191
8192/*
8193 * Called after a function returns.
8194 * Interrupts must be off.
8195 */
8196static void
8197poplocalvars(void)
8198{
8199 struct localvar *lvp;
8200 struct var *vp;
8201
8202 while ((lvp = localvars) != NULL) {
8203 localvars = lvp->next;
8204 vp = lvp->vp;
8205 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8206 if (vp == NULL) { /* $- saved */
8207 memcpy(optlist, lvp->text, sizeof(optlist));
8208 free((char*)lvp->text);
8209 optschanged();
8210 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8211 unsetvar(vp->text);
8212 } else {
8213 if (vp->func)
8214 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8215 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8216 free((char*)vp->text);
8217 vp->flags = lvp->flags;
8218 vp->text = lvp->text;
8219 }
8220 free(lvp);
8221 }
8222}
8223
8224static int
8225evalfun(struct funcnode *func, int argc, char **argv, int flags)
8226{
8227 volatile struct shparam saveparam;
8228 struct localvar *volatile savelocalvars;
8229 struct jmploc *volatile savehandler;
8230 struct jmploc jmploc;
8231 int e;
8232
8233 saveparam = shellparam;
8234 savelocalvars = localvars;
8235 e = setjmp(jmploc.loc);
8236 if (e) {
8237 goto funcdone;
8238 }
8239 INT_OFF;
8240 savehandler = exception_handler;
8241 exception_handler = &jmploc;
8242 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008243 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008244 func->count++;
8245 funcnest++;
8246 INT_ON;
8247 shellparam.nparam = argc - 1;
8248 shellparam.p = argv + 1;
8249#if ENABLE_ASH_GETOPTS
8250 shellparam.optind = 1;
8251 shellparam.optoff = -1;
8252#endif
8253 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008254 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008255 INT_OFF;
8256 funcnest--;
8257 freefunc(func);
8258 poplocalvars();
8259 localvars = savelocalvars;
8260 freeparam(&shellparam);
8261 shellparam = saveparam;
8262 exception_handler = savehandler;
8263 INT_ON;
8264 evalskip &= ~SKIPFUNC;
8265 return e;
8266}
8267
Denis Vlasenko131ae172007-02-18 13:00:19 +00008268#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008269static char **
8270parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008271{
8272 char *cp, c;
8273
8274 for (;;) {
8275 cp = *++argv;
8276 if (!cp)
8277 return 0;
8278 if (*cp++ != '-')
8279 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008280 c = *cp++;
8281 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008282 break;
8283 if (c == '-' && !*cp) {
8284 argv++;
8285 break;
8286 }
8287 do {
8288 switch (c) {
8289 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008290 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008291 break;
8292 default:
8293 /* run 'typecmd' for other options */
8294 return 0;
8295 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008296 c = *cp++;
8297 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008298 }
8299 return argv;
8300}
8301#endif
8302
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008303/*
8304 * Make a variable a local variable. When a variable is made local, it's
8305 * value and flags are saved in a localvar structure. The saved values
8306 * will be restored when the shell function returns. We handle the name
8307 * "-" as a special case.
8308 */
8309static void
8310mklocal(char *name)
8311{
8312 struct localvar *lvp;
8313 struct var **vpp;
8314 struct var *vp;
8315
8316 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008317 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008318 if (LONE_DASH(name)) {
8319 char *p;
8320 p = ckmalloc(sizeof(optlist));
8321 lvp->text = memcpy(p, optlist, sizeof(optlist));
8322 vp = NULL;
8323 } else {
8324 char *eq;
8325
8326 vpp = hashvar(name);
8327 vp = *findvar(vpp, name);
8328 eq = strchr(name, '=');
8329 if (vp == NULL) {
8330 if (eq)
8331 setvareq(name, VSTRFIXED);
8332 else
8333 setvar(name, NULL, VSTRFIXED);
8334 vp = *vpp; /* the new variable */
8335 lvp->flags = VUNSET;
8336 } else {
8337 lvp->text = vp->text;
8338 lvp->flags = vp->flags;
8339 vp->flags |= VSTRFIXED|VTEXTFIXED;
8340 if (eq)
8341 setvareq(name, 0);
8342 }
8343 }
8344 lvp->vp = vp;
8345 lvp->next = localvars;
8346 localvars = lvp;
8347 INT_ON;
8348}
8349
8350/*
8351 * The "local" command.
8352 */
8353static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008354localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008355{
8356 char *name;
8357
8358 argv = argptr;
8359 while ((name = *argv++) != NULL) {
8360 mklocal(name);
8361 }
8362 return 0;
8363}
8364
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008365static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008366falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008367{
8368 return 1;
8369}
8370
8371static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008372truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008373{
8374 return 0;
8375}
8376
8377static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008378execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008379{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008380 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008381 iflag = 0; /* exit on error */
8382 mflag = 0;
8383 optschanged();
8384 shellexec(argv + 1, pathval(), 0);
8385 }
8386 return 0;
8387}
8388
8389/*
8390 * The return command.
8391 */
8392static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008393returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008394{
8395 /*
8396 * If called outside a function, do what ksh does;
8397 * skip the rest of the file.
8398 */
8399 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8400 return argv[1] ? number(argv[1]) : exitstatus;
8401}
8402
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008403/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008404static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008405static int dotcmd(int, char **);
8406static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008407static int exitcmd(int, char **);
8408static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008409#if ENABLE_ASH_GETOPTS
8410static int getoptscmd(int, char **);
8411#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008412#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008413static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008414#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008415#if ENABLE_ASH_MATH_SUPPORT
8416static int letcmd(int, char **);
8417#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008418static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008419static int setcmd(int, char **);
8420static int shiftcmd(int, char **);
8421static int timescmd(int, char **);
8422static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008423static int umaskcmd(int, char **);
8424static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008425static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008426
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008427#define BUILTIN_NOSPEC "0"
8428#define BUILTIN_SPECIAL "1"
8429#define BUILTIN_REGULAR "2"
8430#define BUILTIN_SPEC_REG "3"
8431#define BUILTIN_ASSIGN "4"
8432#define BUILTIN_SPEC_ASSG "5"
8433#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008434#define BUILTIN_SPEC_REG_ASSG "7"
8435
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008436/* We do not handle [[ expr ]] bashism bash-compatibly,
8437 * we make it a synonym of [ expr ].
8438 * Basically, word splitting and pathname expansion should NOT be performed
8439 * Examples:
8440 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8441 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8442 * Additional operators:
8443 * || and && should work as -o and -a
8444 * =~ regexp match
8445 * Apart from the above, [[ expr ]] should work as [ expr ]
8446 */
8447
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008448#define echocmd echo_main
8449#define printfcmd printf_main
8450#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008451
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008452/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008453static const struct builtincmd builtintab[] = {
8454 { BUILTIN_SPEC_REG ".", dotcmd },
8455 { BUILTIN_SPEC_REG ":", truecmd },
8456#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008457 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008458#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008459 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008460#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008461#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008462#if ENABLE_ASH_ALIAS
8463 { BUILTIN_REG_ASSG "alias", aliascmd },
8464#endif
8465#if JOBS
8466 { BUILTIN_REGULAR "bg", fg_bgcmd },
8467#endif
8468 { BUILTIN_SPEC_REG "break", breakcmd },
8469 { BUILTIN_REGULAR "cd", cdcmd },
8470 { BUILTIN_NOSPEC "chdir", cdcmd },
8471#if ENABLE_ASH_CMDCMD
8472 { BUILTIN_REGULAR "command", commandcmd },
8473#endif
8474 { BUILTIN_SPEC_REG "continue", breakcmd },
8475#if ENABLE_ASH_BUILTIN_ECHO
8476 { BUILTIN_REGULAR "echo", echocmd },
8477#endif
8478 { BUILTIN_SPEC_REG "eval", evalcmd },
8479 { BUILTIN_SPEC_REG "exec", execcmd },
8480 { BUILTIN_SPEC_REG "exit", exitcmd },
8481 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8482 { BUILTIN_REGULAR "false", falsecmd },
8483#if JOBS
8484 { BUILTIN_REGULAR "fg", fg_bgcmd },
8485#endif
8486#if ENABLE_ASH_GETOPTS
8487 { BUILTIN_REGULAR "getopts", getoptscmd },
8488#endif
8489 { BUILTIN_NOSPEC "hash", hashcmd },
8490#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8491 { BUILTIN_NOSPEC "help", helpcmd },
8492#endif
8493#if JOBS
8494 { BUILTIN_REGULAR "jobs", jobscmd },
8495 { BUILTIN_REGULAR "kill", killcmd },
8496#endif
8497#if ENABLE_ASH_MATH_SUPPORT
8498 { BUILTIN_NOSPEC "let", letcmd },
8499#endif
8500 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008501#if ENABLE_ASH_BUILTIN_PRINTF
8502 { BUILTIN_REGULAR "printf", printfcmd },
8503#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008504 { BUILTIN_NOSPEC "pwd", pwdcmd },
8505 { BUILTIN_REGULAR "read", readcmd },
8506 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8507 { BUILTIN_SPEC_REG "return", returncmd },
8508 { BUILTIN_SPEC_REG "set", setcmd },
8509 { BUILTIN_SPEC_REG "shift", shiftcmd },
8510 { BUILTIN_SPEC_REG "source", dotcmd },
8511#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008512 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008513#endif
8514 { BUILTIN_SPEC_REG "times", timescmd },
8515 { BUILTIN_SPEC_REG "trap", trapcmd },
8516 { BUILTIN_REGULAR "true", truecmd },
8517 { BUILTIN_NOSPEC "type", typecmd },
8518 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8519 { BUILTIN_REGULAR "umask", umaskcmd },
8520#if ENABLE_ASH_ALIAS
8521 { BUILTIN_REGULAR "unalias", unaliascmd },
8522#endif
8523 { BUILTIN_SPEC_REG "unset", unsetcmd },
8524 { BUILTIN_REGULAR "wait", waitcmd },
8525};
8526
Denis Vlasenko80591b02008-03-25 07:49:43 +00008527/* Should match the above table! */
8528#define COMMANDCMD (builtintab + \
8529 2 + \
8530 1 * ENABLE_ASH_BUILTIN_TEST + \
8531 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8532 1 * ENABLE_ASH_ALIAS + \
8533 1 * ENABLE_ASH_JOB_CONTROL + \
8534 3)
8535#define EXECCMD (builtintab + \
8536 2 + \
8537 1 * ENABLE_ASH_BUILTIN_TEST + \
8538 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8539 1 * ENABLE_ASH_ALIAS + \
8540 1 * ENABLE_ASH_JOB_CONTROL + \
8541 3 + \
8542 1 * ENABLE_ASH_CMDCMD + \
8543 1 + \
8544 ENABLE_ASH_BUILTIN_ECHO + \
8545 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008546
8547/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008548 * Search the table of builtin commands.
8549 */
8550static struct builtincmd *
8551find_builtin(const char *name)
8552{
8553 struct builtincmd *bp;
8554
8555 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008556 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008557 pstrcmp
8558 );
8559 return bp;
8560}
8561
8562/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008563 * Execute a simple command.
8564 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565static int
8566isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008567{
8568 const char *q = endofname(p);
8569 if (p == q)
8570 return 0;
8571 return *q == '=';
8572}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008573static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008574bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008575{
8576 /* Preserve exitstatus of a previous possible redirection
8577 * as POSIX mandates */
8578 return back_exitstatus;
8579}
Eric Andersenc470f442003-07-28 09:56:35 +00008580static void
8581evalcommand(union node *cmd, int flags)
8582{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008583 static const struct builtincmd null_bltin = {
8584 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008585 };
Eric Andersenc470f442003-07-28 09:56:35 +00008586 struct stackmark smark;
8587 union node *argp;
8588 struct arglist arglist;
8589 struct arglist varlist;
8590 char **argv;
8591 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008592 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008593 struct cmdentry cmdentry;
8594 struct job *jp;
8595 char *lastarg;
8596 const char *path;
8597 int spclbltin;
8598 int cmd_is_exec;
8599 int status;
8600 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008601 struct builtincmd *bcmd;
8602 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008603
8604 /* First expand the arguments. */
8605 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8606 setstackmark(&smark);
8607 back_exitstatus = 0;
8608
8609 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008610 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008611 varlist.lastp = &varlist.list;
8612 *varlist.lastp = NULL;
8613 arglist.lastp = &arglist.list;
8614 *arglist.lastp = NULL;
8615
8616 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008617 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008618 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8619 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8620 }
8621
Eric Andersenc470f442003-07-28 09:56:35 +00008622 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8623 struct strlist **spp;
8624
8625 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008626 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008627 expandarg(argp, &arglist, EXP_VARTILDE);
8628 else
8629 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8630
Eric Andersenc470f442003-07-28 09:56:35 +00008631 for (sp = *spp; sp; sp = sp->next)
8632 argc++;
8633 }
8634
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008635 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008636 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008637 TRACE(("evalcommand arg: %s\n", sp->text));
8638 *nargv++ = sp->text;
8639 }
8640 *nargv = NULL;
8641
8642 lastarg = NULL;
8643 if (iflag && funcnest == 0 && argc > 0)
8644 lastarg = nargv[-1];
8645
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008646 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008647 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008648 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008649
8650 path = vpath.text;
8651 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8652 struct strlist **spp;
8653 char *p;
8654
8655 spp = varlist.lastp;
8656 expandarg(argp, &varlist, EXP_VARTILDE);
8657
8658 /*
8659 * Modify the command lookup path, if a PATH= assignment
8660 * is present
8661 */
8662 p = (*spp)->text;
8663 if (varequal(p, path))
8664 path = p;
8665 }
8666
8667 /* Print the command if xflag is set. */
8668 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008669 int n;
8670 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008671
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008672 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008673 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008674
8675 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008676 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008677 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008678 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008679 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008680 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008681 p--;
8682 }
8683 }
8684 sp = arglist.list;
8685 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008686 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008687 }
8688
8689 cmd_is_exec = 0;
8690 spclbltin = -1;
8691
8692 /* Now locate the command. */
8693 if (argc) {
8694 const char *oldpath;
8695 int cmd_flag = DO_ERR;
8696
8697 path += 5;
8698 oldpath = path;
8699 for (;;) {
8700 find_command(argv[0], &cmdentry, cmd_flag, path);
8701 if (cmdentry.cmdtype == CMDUNKNOWN) {
8702 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008703 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008704 goto bail;
8705 }
8706
8707 /* implement bltin and command here */
8708 if (cmdentry.cmdtype != CMDBUILTIN)
8709 break;
8710 if (spclbltin < 0)
8711 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8712 if (cmdentry.u.cmd == EXECCMD)
8713 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008714#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008715 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008716 path = oldpath;
8717 nargv = parse_command_args(argv, &path);
8718 if (!nargv)
8719 break;
8720 argc -= nargv - argv;
8721 argv = nargv;
8722 cmd_flag |= DO_NOFUNC;
8723 } else
8724#endif
8725 break;
8726 }
8727 }
8728
8729 if (status) {
8730 /* We have a redirection error. */
8731 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008732 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008733 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008734 exitstatus = status;
8735 goto out;
8736 }
8737
8738 /* Execute the command. */
8739 switch (cmdentry.cmdtype) {
8740 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008741#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008742 {
8743 /* find_command() encodes applet_no as (-2 - applet_no) */
8744 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008745 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008746 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008747 /* run <applet>_main() */
8748 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008749 break;
8750 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008751 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008752#endif
8753
Eric Andersenc470f442003-07-28 09:56:35 +00008754 /* Fork off a child process if necessary. */
8755 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008756 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008757 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008758 if (forkshell(jp, cmd, FORK_FG) != 0) {
8759 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008760 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008761 break;
8762 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008763 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008764 }
8765 listsetvar(varlist.list, VEXPORT|VSTACK);
8766 shellexec(argv, path, cmdentry.u.index);
8767 /* NOTREACHED */
8768
8769 case CMDBUILTIN:
8770 cmdenviron = varlist.list;
8771 if (cmdenviron) {
8772 struct strlist *list = cmdenviron;
8773 int i = VNOSET;
8774 if (spclbltin > 0 || argc == 0) {
8775 i = 0;
8776 if (cmd_is_exec && argc > 1)
8777 i = VEXPORT;
8778 }
8779 listsetvar(list, i);
8780 }
8781 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8782 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008783 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008784 if (i == EXEXIT)
8785 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008786 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008787 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008788 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008789 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008790 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008791 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008792 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008793 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008794 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008795 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008796 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008797 }
8798 break;
8799
8800 case CMDFUNCTION:
8801 listsetvar(varlist.list, 0);
8802 if (evalfun(cmdentry.u.func, argc, argv, flags))
8803 goto raise;
8804 break;
8805 }
8806
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008807 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008808 popredir(cmd_is_exec);
8809 if (lastarg)
8810 /* dsl: I think this is intended to be used to support
8811 * '_' in 'vi' command mode during line editing...
8812 * However I implemented that within libedit itself.
8813 */
8814 setvar("_", lastarg, 0);
8815 popstackmark(&smark);
8816}
8817
8818static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008819evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8820{
Eric Andersenc470f442003-07-28 09:56:35 +00008821 char *volatile savecmdname;
8822 struct jmploc *volatile savehandler;
8823 struct jmploc jmploc;
8824 int i;
8825
8826 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008827 i = setjmp(jmploc.loc);
8828 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008829 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008830 savehandler = exception_handler;
8831 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008832 commandname = argv[0];
8833 argptr = argv + 1;
8834 optptr = NULL; /* initialize nextopt */
8835 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008836 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008837 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008838 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008839 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008840 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008841// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008842 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008843
8844 return i;
8845}
8846
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008847static int
8848goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008849{
8850 return !*endofname(p);
8851}
8852
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008853
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008854/*
8855 * Search for a command. This is called before we fork so that the
8856 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008857 * the child. The check for "goodname" is an overly conservative
8858 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008859 */
Eric Andersenc470f442003-07-28 09:56:35 +00008860static void
8861prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008862{
8863 struct cmdentry entry;
8864
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008865 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8866 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008867}
8868
Eric Andersencb57d552001-06-28 07:25:16 +00008869
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008870/* ============ Builtin commands
8871 *
8872 * Builtin commands whose functions are closely tied to evaluation
8873 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008874 */
8875
8876/*
Eric Andersencb57d552001-06-28 07:25:16 +00008877 * Handle break and continue commands. Break, continue, and return are
8878 * all handled by setting the evalskip flag. The evaluation routines
8879 * above all check this flag, and if it is set they start skipping
8880 * commands rather than executing them. The variable skipcount is
8881 * the number of loops to break/continue, or the number of function
8882 * levels to return. (The latter is always 1.) It should probably
8883 * be an error to break out of more loops than exist, but it isn't
8884 * in the standard shell so we don't make it one here.
8885 */
Eric Andersenc470f442003-07-28 09:56:35 +00008886static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008887breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008888{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008889 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008890
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008891 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008892 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008893 if (n > loopnest)
8894 n = loopnest;
8895 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008896 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008897 skipcount = n;
8898 }
8899 return 0;
8900}
8901
Eric Andersenc470f442003-07-28 09:56:35 +00008902
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008903/* ============ input.c
8904 *
Eric Andersen90898442003-08-06 11:20:52 +00008905 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008906 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008907
Eric Andersenc470f442003-07-28 09:56:35 +00008908#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008909
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008910enum {
8911 INPUT_PUSH_FILE = 1,
8912 INPUT_NOFILE_OK = 2,
8913};
Eric Andersencb57d552001-06-28 07:25:16 +00008914
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008915static int plinno = 1; /* input line number */
8916/* number of characters left in input buffer */
8917static int parsenleft; /* copy of parsefile->nleft */
8918static int parselleft; /* copy of parsefile->lleft */
8919/* next character in input buffer */
8920static char *parsenextc; /* copy of parsefile->nextc */
8921
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008922static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008923/* values of checkkwd variable */
8924#define CHKALIAS 0x1
8925#define CHKKWD 0x2
8926#define CHKNL 0x4
8927
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008928static void
8929popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008930{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008931 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008932
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008933 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008934#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008935 if (sp->ap) {
8936 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8937 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008938 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008939 if (sp->string != sp->ap->val) {
8940 free(sp->string);
8941 }
8942 sp->ap->flag &= ~ALIASINUSE;
8943 if (sp->ap->flag & ALIASDEAD) {
8944 unalias(sp->ap->name);
8945 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008946 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008947#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008948 parsenextc = sp->prevstring;
8949 parsenleft = sp->prevnleft;
8950/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008951 g_parsefile->strpush = sp->prev;
8952 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008953 free(sp);
8954 INT_ON;
8955}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008956
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008957static int
8958preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008959{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008960 int nr;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008961 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008962 parsenextc = buf;
8963
Denis Vlasenko38f63192007-01-22 09:03:07 +00008964#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008965 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008966 if (!iflag || g_parsefile->fd)
8967 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008968 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008969#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008970 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008971#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008972 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8973 if (nr == 0) {
8974 /* Ctrl+C pressed */
8975 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008976 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008977 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008978 raise(SIGINT);
8979 return 1;
8980 }
Eric Andersenc470f442003-07-28 09:56:35 +00008981 goto retry;
8982 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008983 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008984 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008985 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008986 }
Eric Andersencb57d552001-06-28 07:25:16 +00008987 }
8988#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00008989 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008990#endif
8991
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008992#if 0
8993/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008994 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008995 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008996 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008997 if (flags >= 0 && (flags & O_NONBLOCK)) {
8998 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008999 if (fcntl(0, F_SETFL, flags) >= 0) {
9000 out2str("sh: turning off NDELAY mode\n");
9001 goto retry;
9002 }
9003 }
9004 }
9005 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009006#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009007 return nr;
9008}
9009
9010/*
9011 * Refill the input buffer and return the next input character:
9012 *
9013 * 1) If a string was pushed back on the input, pop it;
9014 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9015 * from a string so we can't refill the buffer, return EOF.
9016 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9017 * 4) Process input up to the next newline, deleting nul characters.
9018 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009019static int
Eric Andersenc470f442003-07-28 09:56:35 +00009020preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009021{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009022 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009023 int more;
9024 char savec;
9025
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009026 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009027#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009028 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009029 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009030 return PEOA;
9031 }
Eric Andersen2870d962001-07-02 17:27:21 +00009032#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009033 popstring();
9034 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009035 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009036 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009037 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009038 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009039 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009040
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009041 more = parselleft;
9042 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009043 again:
9044 more = preadfd();
9045 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009046 parselleft = parsenleft = EOF_NLEFT;
9047 return PEOF;
9048 }
9049 }
9050
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009051 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009052
9053 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009054 for (;;) {
9055 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009056
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009057 more--;
9058 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009059
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009060 if (!c)
9061 memmove(q, q + 1, more);
9062 else {
9063 q++;
9064 if (c == '\n') {
9065 parsenleft = q - parsenextc - 1;
9066 break;
9067 }
Eric Andersencb57d552001-06-28 07:25:16 +00009068 }
9069
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009070 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009071 parsenleft = q - parsenextc - 1;
9072 if (parsenleft < 0)
9073 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009074 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009075 }
9076 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009077 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009078
9079 savec = *q;
9080 *q = '\0';
9081
9082 if (vflag) {
9083 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009084 }
9085
9086 *q = savec;
9087
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009088 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009089}
9090
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009091#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092static int
9093pgetc(void)
9094{
9095 return pgetc_as_macro();
9096}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009097
9098#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9099#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009100#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009101#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009102#endif
9103
9104/*
9105 * Same as pgetc(), but ignores PEOA.
9106 */
9107#if ENABLE_ASH_ALIAS
9108static int
9109pgetc2(void)
9110{
9111 int c;
9112
9113 do {
9114 c = pgetc_macro();
9115 } while (c == PEOA);
9116 return c;
9117}
9118#else
9119static int
9120pgetc2(void)
9121{
9122 return pgetc_macro();
9123}
9124#endif
9125
9126/*
9127 * Read a line from the script.
9128 */
9129static char *
9130pfgets(char *line, int len)
9131{
9132 char *p = line;
9133 int nleft = len;
9134 int c;
9135
9136 while (--nleft > 0) {
9137 c = pgetc2();
9138 if (c == PEOF) {
9139 if (p == line)
9140 return NULL;
9141 break;
9142 }
9143 *p++ = c;
9144 if (c == '\n')
9145 break;
9146 }
9147 *p = '\0';
9148 return line;
9149}
9150
Eric Andersenc470f442003-07-28 09:56:35 +00009151/*
9152 * Undo the last call to pgetc. Only one character may be pushed back.
9153 * PEOF may be pushed back.
9154 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009155static void
Eric Andersenc470f442003-07-28 09:56:35 +00009156pungetc(void)
9157{
9158 parsenleft++;
9159 parsenextc--;
9160}
Eric Andersencb57d552001-06-28 07:25:16 +00009161
9162/*
9163 * Push a string back onto the input at this current parsefile level.
9164 * We handle aliases this way.
9165 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009166#if !ENABLE_ASH_ALIAS
9167#define pushstring(s, ap) pushstring(s)
9168#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009169static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009170pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009171{
Eric Andersencb57d552001-06-28 07:25:16 +00009172 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009173 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009174
Eric Andersenc470f442003-07-28 09:56:35 +00009175 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009176 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009177/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009178 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009179 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009180 sp->prev = g_parsefile->strpush;
9181 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009182 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009183 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009184 sp->prevstring = parsenextc;
9185 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009186#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009187 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009188 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009189 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009190 sp->string = s;
9191 }
Eric Andersen2870d962001-07-02 17:27:21 +00009192#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009193 parsenextc = s;
9194 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009195 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009196}
9197
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009198/*
9199 * To handle the "." command, a stack of input files is used. Pushfile
9200 * adds a new entry to the stack and popfile restores the previous level.
9201 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009202static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009204{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205 struct parsefile *pf;
9206
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009207 g_parsefile->nleft = parsenleft;
9208 g_parsefile->lleft = parselleft;
9209 g_parsefile->nextc = parsenextc;
9210 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009211 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009212 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009213 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009214 /*pf->strpush = NULL; - ckzalloc did it */
9215 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009216 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009217}
9218
9219static void
9220popfile(void)
9221{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009222 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009223
Denis Vlasenkob012b102007-02-19 22:43:01 +00009224 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009225 if (pf->fd >= 0)
9226 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009227 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009228 while (pf->strpush)
9229 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009230 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009231 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009232 parsenleft = g_parsefile->nleft;
9233 parselleft = g_parsefile->lleft;
9234 parsenextc = g_parsefile->nextc;
9235 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009236 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009237}
9238
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009239/*
9240 * Return to top level.
9241 */
9242static void
9243popallfiles(void)
9244{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009245 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009246 popfile();
9247}
9248
9249/*
9250 * Close the file(s) that the shell is reading commands from. Called
9251 * after a fork is done.
9252 */
9253static void
9254closescript(void)
9255{
9256 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009257 if (g_parsefile->fd > 0) {
9258 close(g_parsefile->fd);
9259 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009260 }
9261}
9262
9263/*
9264 * Like setinputfile, but takes an open file descriptor. Call this with
9265 * interrupts off.
9266 */
9267static void
9268setinputfd(int fd, int push)
9269{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009270 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009271 if (push) {
9272 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009273 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009274 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009275 g_parsefile->fd = fd;
9276 if (g_parsefile->buf == NULL)
9277 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009278 parselleft = parsenleft = 0;
9279 plinno = 1;
9280}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009281
Eric Andersenc470f442003-07-28 09:56:35 +00009282/*
9283 * Set the input to take input from a file. If push is set, push the
9284 * old input onto the stack first.
9285 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009286static int
9287setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009288{
9289 int fd;
9290 int fd2;
9291
Denis Vlasenkob012b102007-02-19 22:43:01 +00009292 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009293 fd = open(fname, O_RDONLY);
9294 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009295 if (flags & INPUT_NOFILE_OK)
9296 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009297 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009298 }
Eric Andersenc470f442003-07-28 09:56:35 +00009299 if (fd < 10) {
9300 fd2 = copyfd(fd, 10);
9301 close(fd);
9302 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009303 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009304 fd = fd2;
9305 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009306 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009307 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009309 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009310}
9311
Eric Andersencb57d552001-06-28 07:25:16 +00009312/*
9313 * Like setinputfile, but takes input from a string.
9314 */
Eric Andersenc470f442003-07-28 09:56:35 +00009315static void
9316setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009317{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009318 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009319 pushfile();
9320 parsenextc = string;
9321 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009322 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009323 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009324 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009325}
9326
9327
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009328/* ============ mail.c
9329 *
9330 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009331 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009332
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009333#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009334
Eric Andersencb57d552001-06-28 07:25:16 +00009335#define MAXMBOXES 10
9336
Eric Andersenc470f442003-07-28 09:56:35 +00009337/* times of mailboxes */
9338static time_t mailtime[MAXMBOXES];
9339/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009340static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009341
Eric Andersencb57d552001-06-28 07:25:16 +00009342/*
Eric Andersenc470f442003-07-28 09:56:35 +00009343 * Print appropriate message(s) if mail has arrived.
9344 * If mail_var_path_changed is set,
9345 * then the value of MAIL has mail_var_path_changed,
9346 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009347 */
Eric Andersenc470f442003-07-28 09:56:35 +00009348static void
9349chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009350{
Eric Andersencb57d552001-06-28 07:25:16 +00009351 const char *mpath;
9352 char *p;
9353 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009354 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009355 struct stackmark smark;
9356 struct stat statb;
9357
Eric Andersencb57d552001-06-28 07:25:16 +00009358 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009359 mpath = mpathset() ? mpathval() : mailval();
9360 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009361 p = padvance(&mpath, nullstr);
9362 if (p == NULL)
9363 break;
9364 if (*p == '\0')
9365 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009366 for (q = p; *q; q++)
9367 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009368#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009369 if (q[-1] != '/')
9370 abort();
9371#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009372 q[-1] = '\0'; /* delete trailing '/' */
9373 if (stat(p, &statb) < 0) {
9374 *mtp = 0;
9375 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009376 }
Eric Andersenc470f442003-07-28 09:56:35 +00009377 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9378 fprintf(
9379 stderr, snlfmt,
9380 pathopt ? pathopt : "you have mail"
9381 );
9382 }
9383 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009384 }
Eric Andersenc470f442003-07-28 09:56:35 +00009385 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009386 popstackmark(&smark);
9387}
Eric Andersencb57d552001-06-28 07:25:16 +00009388
Eric Andersenc470f442003-07-28 09:56:35 +00009389static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009390changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009391{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009392 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009393}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009394
Denis Vlasenko131ae172007-02-18 13:00:19 +00009395#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009396
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009397
9398/* ============ ??? */
9399
Eric Andersencb57d552001-06-28 07:25:16 +00009400/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009401 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009402 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009403static void
9404setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009405{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009406 char **newparam;
9407 char **ap;
9408 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009409
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009410 for (nparam = 0; argv[nparam]; nparam++)
9411 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009412 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9413 while (*argv) {
9414 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009415 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009416 *ap = NULL;
9417 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009418 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009419 shellparam.nparam = nparam;
9420 shellparam.p = newparam;
9421#if ENABLE_ASH_GETOPTS
9422 shellparam.optind = 1;
9423 shellparam.optoff = -1;
9424#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009425}
9426
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009427/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009428 * Process shell options. The global variable argptr contains a pointer
9429 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009430 *
9431 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9432 * For a non-interactive shell, an error condition encountered
9433 * by a special built-in ... shall cause the shell to write a diagnostic message
9434 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009435 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009436 * ...
9437 * Utility syntax error (option or operand error) Shall exit
9438 * ...
9439 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9440 * we see that bash does not do that (set "finishes" with error code 1 instead,
9441 * and shell continues), and people rely on this behavior!
9442 * Testcase:
9443 * set -o barfoo 2>/dev/null
9444 * echo $?
9445 *
9446 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009447 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009448static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009449plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009450{
9451 int i;
9452
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009453 if (name) {
9454 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009455 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009456 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009457 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009458 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009459 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009460 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009461 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009462 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009463 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009464 if (val) {
9465 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9466 } else {
9467 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9468 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009469 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009470 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009471}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009472static void
9473setoption(int flag, int val)
9474{
9475 int i;
9476
9477 for (i = 0; i < NOPTS; i++) {
9478 if (optletters(i) == flag) {
9479 optlist[i] = val;
9480 return;
9481 }
9482 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009483 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009484 /* NOTREACHED */
9485}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009486static int
Eric Andersenc470f442003-07-28 09:56:35 +00009487options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009488{
9489 char *p;
9490 int val;
9491 int c;
9492
9493 if (cmdline)
9494 minusc = NULL;
9495 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009496 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009497 if (c != '-' && c != '+')
9498 break;
9499 argptr++;
9500 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009501 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009502 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009503 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009504 if (!cmdline) {
9505 /* "-" means turn off -x and -v */
9506 if (p[0] == '\0')
9507 xflag = vflag = 0;
9508 /* "--" means reset params */
9509 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009510 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009511 }
Eric Andersenc470f442003-07-28 09:56:35 +00009512 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009513 }
Eric Andersencb57d552001-06-28 07:25:16 +00009514 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009515 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009516 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009517 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009518 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009519 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009520 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009521 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009522 /* it already printed err message */
9523 return 1; /* error */
9524 }
Eric Andersencb57d552001-06-28 07:25:16 +00009525 if (*argptr)
9526 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009527 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9528 isloginsh = 1;
9529 /* bash does not accept +-login, we also won't */
9530 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009531 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009532 isloginsh = 1;
9533 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009534 } else {
9535 setoption(c, val);
9536 }
9537 }
9538 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009539 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009540}
9541
Eric Andersencb57d552001-06-28 07:25:16 +00009542/*
Eric Andersencb57d552001-06-28 07:25:16 +00009543 * The shift builtin command.
9544 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009545static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009546shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009547{
9548 int n;
9549 char **ap1, **ap2;
9550
9551 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009552 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009553 n = number(argv[1]);
9554 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009555 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009556 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009557 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009558 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009559 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009560 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009561 }
9562 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009563 while ((*ap2++ = *ap1++) != NULL)
9564 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009565#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009566 shellparam.optind = 1;
9567 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009568#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009569 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009570 return 0;
9571}
9572
Eric Andersencb57d552001-06-28 07:25:16 +00009573/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009574 * POSIX requires that 'set' (but not export or readonly) output the
9575 * variables in lexicographic order - by the locale's collating order (sigh).
9576 * Maybe we could keep them in an ordered balanced binary tree
9577 * instead of hashed lists.
9578 * For now just roll 'em through qsort for printing...
9579 */
9580static int
9581showvars(const char *sep_prefix, int on, int off)
9582{
9583 const char *sep;
9584 char **ep, **epend;
9585
9586 ep = listvars(on, off, &epend);
9587 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9588
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009589 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009590
9591 for (; ep < epend; ep++) {
9592 const char *p;
9593 const char *q;
9594
9595 p = strchrnul(*ep, '=');
9596 q = nullstr;
9597 if (*p)
9598 q = single_quote(++p);
9599 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9600 }
9601 return 0;
9602}
9603
9604/*
Eric Andersencb57d552001-06-28 07:25:16 +00009605 * The set command builtin.
9606 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009607static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009608setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009609{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009610 int retval;
9611
Denis Vlasenko68404f12008-03-17 09:00:54 +00009612 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009613 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009614 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009615 retval = 1;
9616 if (!options(0)) { /* if no parse error... */
9617 retval = 0;
9618 optschanged();
9619 if (*argptr != NULL) {
9620 setparam(argptr);
9621 }
Eric Andersencb57d552001-06-28 07:25:16 +00009622 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009623 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009624 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009625}
9626
Denis Vlasenko131ae172007-02-18 13:00:19 +00009627#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009628static void
9629change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009630{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009631 /* Galois LFSR parameter */
9632 /* Taps at 32 31 29 1: */
9633 enum { MASK = 0x8000000b };
9634 /* Another example - taps at 32 31 30 10: */
9635 /* MASK = 0x00400007 */
9636
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009637 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009638 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009639 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009640
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009641 /* LCG has period of 2^32 and alternating lowest bit */
9642 random_LCG = 1664525 * random_LCG + 1013904223;
9643 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9644 t = (random_galois_LFSR << 1);
9645 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9646 t ^= MASK;
9647 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009648 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009649 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009650 * for $RANDOM range. Combining with subtraction is
9651 * just for fun. + and ^ would work equally well. */
9652 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009653 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009654 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009655 vrandom.flags &= ~VNOFUNC;
9656 } else {
9657 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009658 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009659 }
Eric Andersenef02f822004-03-11 13:34:24 +00009660}
Eric Andersen16767e22004-03-16 05:14:10 +00009661#endif
9662
Denis Vlasenko131ae172007-02-18 13:00:19 +00009663#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009664static int
Eric Andersenc470f442003-07-28 09:56:35 +00009665getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009666{
9667 char *p, *q;
9668 char c = '?';
9669 int done = 0;
9670 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009671 char s[12];
9672 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009673
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009674 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009675 return 1;
9676 optnext = optfirst + *param_optind - 1;
9677
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009678 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009679 p = NULL;
9680 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009681 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009682 if (p == NULL || *p == '\0') {
9683 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009684 p = *optnext;
9685 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009686 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009687 p = NULL;
9688 done = 1;
9689 goto out;
9690 }
9691 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009692 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009693 goto atend;
9694 }
9695
9696 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009697 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009698 if (*q == '\0') {
9699 if (optstr[0] == ':') {
9700 s[0] = c;
9701 s[1] = '\0';
9702 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009703 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009704 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009705 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009706 }
9707 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009708 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009709 }
9710 if (*++q == ':')
9711 q++;
9712 }
9713
9714 if (*++q == ':') {
9715 if (*p == '\0' && (p = *optnext) == NULL) {
9716 if (optstr[0] == ':') {
9717 s[0] = c;
9718 s[1] = '\0';
9719 err |= setvarsafe("OPTARG", s, 0);
9720 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009721 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009722 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009723 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009724 c = '?';
9725 }
Eric Andersenc470f442003-07-28 09:56:35 +00009726 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009727 }
9728
9729 if (p == *optnext)
9730 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009731 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009732 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009733 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009734 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009735 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009736 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009737 *param_optind = optnext - optfirst + 1;
9738 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009739 err |= setvarsafe("OPTIND", s, VNOFUNC);
9740 s[0] = c;
9741 s[1] = '\0';
9742 err |= setvarsafe(optvar, s, 0);
9743 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009744 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009745 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009746 flush_stdout_stderr();
9747 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009748 }
9749 return done;
9750}
Eric Andersenc470f442003-07-28 09:56:35 +00009751
9752/*
9753 * The getopts builtin. Shellparam.optnext points to the next argument
9754 * to be processed. Shellparam.optptr points to the next character to
9755 * be processed in the current argument. If shellparam.optnext is NULL,
9756 * then it's the first time getopts has been called.
9757 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009758static int
Eric Andersenc470f442003-07-28 09:56:35 +00009759getoptscmd(int argc, char **argv)
9760{
9761 char **optbase;
9762
9763 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009764 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009765 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009766 optbase = shellparam.p;
9767 if (shellparam.optind > shellparam.nparam + 1) {
9768 shellparam.optind = 1;
9769 shellparam.optoff = -1;
9770 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009771 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009772 optbase = &argv[3];
9773 if (shellparam.optind > argc - 2) {
9774 shellparam.optind = 1;
9775 shellparam.optoff = -1;
9776 }
9777 }
9778
9779 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009780 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009781}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009782#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009783
Eric Andersencb57d552001-06-28 07:25:16 +00009784
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009785/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009786
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009787struct heredoc {
9788 struct heredoc *next; /* next here document in list */
9789 union node *here; /* redirection node */
9790 char *eofmark; /* string indicating end of input */
9791 smallint striptabs; /* if set, strip leading tabs */
9792};
9793
9794static smallint tokpushback; /* last token pushed back */
9795static smallint parsebackquote; /* nonzero if we are inside backquotes */
9796static smallint quoteflag; /* set if (part of) last token was quoted */
9797static token_id_t lasttoken; /* last token read (integer id Txxx) */
9798static struct heredoc *heredoclist; /* list of here documents to read */
9799static char *wordtext; /* text of last word returned by readtoken */
9800static struct nodelist *backquotelist;
9801static union node *redirnode;
9802static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009803/*
9804 * NEOF is returned by parsecmd when it encounters an end of file. It
9805 * must be distinct from NULL, so we use the address of a variable that
9806 * happens to be handy.
9807 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009808#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009809
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009810static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009811static void
9812raise_error_syntax(const char *msg)
9813{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009814 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009815 /* NOTREACHED */
9816}
9817
9818/*
9819 * Called when an unexpected token is read during the parse. The argument
9820 * is the token that is expected, or -1 if more than one type of token can
9821 * occur at this point.
9822 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009823static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009824static void
9825raise_error_unexpected_syntax(int token)
9826{
9827 char msg[64];
9828 int l;
9829
9830 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9831 if (token >= 0)
9832 sprintf(msg + l, " (expecting %s)", tokname(token));
9833 raise_error_syntax(msg);
9834 /* NOTREACHED */
9835}
Eric Andersencb57d552001-06-28 07:25:16 +00009836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009837#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009838
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009839/* parsing is heavily cross-recursive, need these forward decls */
9840static union node *andor(void);
9841static union node *pipeline(void);
9842static union node *parse_command(void);
9843static void parseheredoc(void);
9844static char peektoken(void);
9845static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009846
Eric Andersenc470f442003-07-28 09:56:35 +00009847static union node *
9848list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009849{
9850 union node *n1, *n2, *n3;
9851 int tok;
9852
Eric Andersenc470f442003-07-28 09:56:35 +00009853 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9854 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009855 return NULL;
9856 n1 = NULL;
9857 for (;;) {
9858 n2 = andor();
9859 tok = readtoken();
9860 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009861 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009862 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009863 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009864 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009865 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009866 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009867 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009868 n2 = n3;
9869 }
9870 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009871 }
9872 }
9873 if (n1 == NULL) {
9874 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009875 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009876 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009877 n3->type = NSEMI;
9878 n3->nbinary.ch1 = n1;
9879 n3->nbinary.ch2 = n2;
9880 n1 = n3;
9881 }
9882 switch (tok) {
9883 case TBACKGND:
9884 case TSEMI:
9885 tok = readtoken();
9886 /* fall through */
9887 case TNL:
9888 if (tok == TNL) {
9889 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009890 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009891 return n1;
9892 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009893 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009894 }
Eric Andersenc470f442003-07-28 09:56:35 +00009895 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009896 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009897 return n1;
9898 break;
9899 case TEOF:
9900 if (heredoclist)
9901 parseheredoc();
9902 else
Eric Andersenc470f442003-07-28 09:56:35 +00009903 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009904 return n1;
9905 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009906 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009907 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009908 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 return n1;
9910 }
9911 }
9912}
9913
Eric Andersenc470f442003-07-28 09:56:35 +00009914static union node *
9915andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009916{
Eric Andersencb57d552001-06-28 07:25:16 +00009917 union node *n1, *n2, *n3;
9918 int t;
9919
Eric Andersencb57d552001-06-28 07:25:16 +00009920 n1 = pipeline();
9921 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009922 t = readtoken();
9923 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009924 t = NAND;
9925 } else if (t == TOR) {
9926 t = NOR;
9927 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009928 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009929 return n1;
9930 }
Eric Andersenc470f442003-07-28 09:56:35 +00009931 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009932 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009933 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009934 n3->type = t;
9935 n3->nbinary.ch1 = n1;
9936 n3->nbinary.ch2 = n2;
9937 n1 = n3;
9938 }
9939}
9940
Eric Andersenc470f442003-07-28 09:56:35 +00009941static union node *
9942pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009943{
Eric Andersencb57d552001-06-28 07:25:16 +00009944 union node *n1, *n2, *pipenode;
9945 struct nodelist *lp, *prev;
9946 int negate;
9947
9948 negate = 0;
9949 TRACE(("pipeline: entered\n"));
9950 if (readtoken() == TNOT) {
9951 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009952 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009953 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009954 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009955 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009957 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009958 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009959 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009960 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009961 pipenode->npipe.cmdlist = lp;
9962 lp->n = n1;
9963 do {
9964 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009965 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009966 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009967 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009968 prev->next = lp;
9969 } while (readtoken() == TPIPE);
9970 lp->next = NULL;
9971 n1 = pipenode;
9972 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009973 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009974 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009975 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009976 n2->type = NNOT;
9977 n2->nnot.com = n1;
9978 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009979 }
9980 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009981}
9982
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009983static union node *
9984makename(void)
9985{
9986 union node *n;
9987
Denis Vlasenko597906c2008-02-20 16:38:54 +00009988 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009989 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009990 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009991 n->narg.text = wordtext;
9992 n->narg.backquote = backquotelist;
9993 return n;
9994}
9995
9996static void
9997fixredir(union node *n, const char *text, int err)
9998{
9999 TRACE(("Fix redir %s %d\n", text, err));
10000 if (!err)
10001 n->ndup.vname = NULL;
10002
10003 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010004 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010005 else if (LONE_DASH(text))
10006 n->ndup.dupfd = -1;
10007 else {
10008 if (err)
10009 raise_error_syntax("Bad fd number");
10010 n->ndup.vname = makename();
10011 }
10012}
10013
10014/*
10015 * Returns true if the text contains nothing to expand (no dollar signs
10016 * or backquotes).
10017 */
10018static int
10019noexpand(char *text)
10020{
10021 char *p;
10022 char c;
10023
10024 p = text;
10025 while ((c = *p++) != '\0') {
10026 if (c == CTLQUOTEMARK)
10027 continue;
10028 if (c == CTLESC)
10029 p++;
10030 else if (SIT(c, BASESYNTAX) == CCTL)
10031 return 0;
10032 }
10033 return 1;
10034}
10035
10036static void
10037parsefname(void)
10038{
10039 union node *n = redirnode;
10040
10041 if (readtoken() != TWORD)
10042 raise_error_unexpected_syntax(-1);
10043 if (n->type == NHERE) {
10044 struct heredoc *here = heredoc;
10045 struct heredoc *p;
10046 int i;
10047
10048 if (quoteflag == 0)
10049 n->type = NXHERE;
10050 TRACE(("Here document %d\n", n->type));
10051 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10052 raise_error_syntax("Illegal eof marker for << redirection");
10053 rmescapes(wordtext);
10054 here->eofmark = wordtext;
10055 here->next = NULL;
10056 if (heredoclist == NULL)
10057 heredoclist = here;
10058 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010059 for (p = heredoclist; p->next; p = p->next)
10060 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010061 p->next = here;
10062 }
10063 } else if (n->type == NTOFD || n->type == NFROMFD) {
10064 fixredir(n, wordtext, 0);
10065 } else {
10066 n->nfile.fname = makename();
10067 }
10068}
Eric Andersencb57d552001-06-28 07:25:16 +000010069
Eric Andersenc470f442003-07-28 09:56:35 +000010070static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010071simplecmd(void)
10072{
10073 union node *args, **app;
10074 union node *n = NULL;
10075 union node *vars, **vpp;
10076 union node **rpp, *redir;
10077 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010078#if ENABLE_ASH_BASH_COMPAT
10079 smallint double_brackets_flag = 0;
10080#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010081
10082 args = NULL;
10083 app = &args;
10084 vars = NULL;
10085 vpp = &vars;
10086 redir = NULL;
10087 rpp = &redir;
10088
10089 savecheckkwd = CHKALIAS;
10090 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010091 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010092 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010093 t = readtoken();
10094 switch (t) {
10095#if ENABLE_ASH_BASH_COMPAT
10096 case TAND: /* "&&" */
10097 case TOR: /* "||" */
10098 if (!double_brackets_flag) {
10099 tokpushback = 1;
10100 goto out;
10101 }
10102 wordtext = (char *) (t == TAND ? "-a" : "-o");
10103#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010104 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010105 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010106 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010107 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010108 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010109#if ENABLE_ASH_BASH_COMPAT
10110 if (strcmp("[[", wordtext) == 0)
10111 double_brackets_flag = 1;
10112 else if (strcmp("]]", wordtext) == 0)
10113 double_brackets_flag = 0;
10114#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010115 n->narg.backquote = backquotelist;
10116 if (savecheckkwd && isassignment(wordtext)) {
10117 *vpp = n;
10118 vpp = &n->narg.next;
10119 } else {
10120 *app = n;
10121 app = &n->narg.next;
10122 savecheckkwd = 0;
10123 }
10124 break;
10125 case TREDIR:
10126 *rpp = n = redirnode;
10127 rpp = &n->nfile.next;
10128 parsefname(); /* read name of redirection file */
10129 break;
10130 case TLP:
10131 if (args && app == &args->narg.next
10132 && !vars && !redir
10133 ) {
10134 struct builtincmd *bcmd;
10135 const char *name;
10136
10137 /* We have a function */
10138 if (readtoken() != TRP)
10139 raise_error_unexpected_syntax(TRP);
10140 name = n->narg.text;
10141 if (!goodname(name)
10142 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10143 ) {
10144 raise_error_syntax("Bad function name");
10145 }
10146 n->type = NDEFUN;
10147 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10148 n->narg.next = parse_command();
10149 return n;
10150 }
10151 /* fall through */
10152 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010153 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010154 goto out;
10155 }
10156 }
10157 out:
10158 *app = NULL;
10159 *vpp = NULL;
10160 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010161 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010162 n->type = NCMD;
10163 n->ncmd.args = args;
10164 n->ncmd.assign = vars;
10165 n->ncmd.redirect = redir;
10166 return n;
10167}
10168
10169static union node *
10170parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010171{
Eric Andersencb57d552001-06-28 07:25:16 +000010172 union node *n1, *n2;
10173 union node *ap, **app;
10174 union node *cp, **cpp;
10175 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010176 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010177 int t;
10178
10179 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010180 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010181
Eric Andersencb57d552001-06-28 07:25:16 +000010182 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010183 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010184 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010185 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010186 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010187 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010188 n1->type = NIF;
10189 n1->nif.test = list(0);
10190 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010191 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010192 n1->nif.ifpart = list(0);
10193 n2 = n1;
10194 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010195 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010196 n2 = n2->nif.elsepart;
10197 n2->type = NIF;
10198 n2->nif.test = list(0);
10199 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010200 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010201 n2->nif.ifpart = list(0);
10202 }
10203 if (lasttoken == TELSE)
10204 n2->nif.elsepart = list(0);
10205 else {
10206 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010207 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010208 }
Eric Andersenc470f442003-07-28 09:56:35 +000010209 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010210 break;
10211 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010212 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010213 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010214 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010215 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010216 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010217 got = readtoken();
10218 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010219 TRACE(("expecting DO got %s %s\n", tokname(got),
10220 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010221 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010222 }
10223 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010224 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010225 break;
10226 }
10227 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010228 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010229 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010230 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010231 n1->type = NFOR;
10232 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010233 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010234 if (readtoken() == TIN) {
10235 app = &ap;
10236 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010237 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010238 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010239 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010240 n2->narg.text = wordtext;
10241 n2->narg.backquote = backquotelist;
10242 *app = n2;
10243 app = &n2->narg.next;
10244 }
10245 *app = NULL;
10246 n1->nfor.args = ap;
10247 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010248 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010249 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010250 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010251 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010252 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010253 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010254 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010255 n1->nfor.args = n2;
10256 /*
10257 * Newline or semicolon here is optional (but note
10258 * that the original Bourne shell only allowed NL).
10259 */
10260 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010261 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010262 }
Eric Andersenc470f442003-07-28 09:56:35 +000010263 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010264 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010265 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010266 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010267 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010268 break;
10269 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010270 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010271 n1->type = NCASE;
10272 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010273 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010274 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010275 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010276 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010277 n2->narg.text = wordtext;
10278 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010279 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010280 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 } while (readtoken() == TNL);
10282 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010283 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010284 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010285 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010286 checkkwd = CHKNL | CHKKWD;
10287 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010288 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010289 if (lasttoken == TLP)
10290 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010291 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010292 cp->type = NCLIST;
10293 app = &cp->nclist.pattern;
10294 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010295 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010296 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010297 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010298 ap->narg.text = wordtext;
10299 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010300 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010301 break;
10302 app = &ap->narg.next;
10303 readtoken();
10304 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010305 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010306 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010307 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010308 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010309
Eric Andersenc470f442003-07-28 09:56:35 +000010310 cpp = &cp->nclist.next;
10311
10312 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010313 t = readtoken();
10314 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010315 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010316 raise_error_unexpected_syntax(TENDCASE);
10317 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010318 }
Eric Andersenc470f442003-07-28 09:56:35 +000010319 }
Eric Andersencb57d552001-06-28 07:25:16 +000010320 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010321 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010322 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010323 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010324 n1->type = NSUBSHELL;
10325 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010326 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010327 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010328 break;
10329 case TBEGIN:
10330 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010331 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010332 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010333 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010334 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010335 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010336 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010337 }
10338
Eric Andersenc470f442003-07-28 09:56:35 +000010339 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010340 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010341
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010342 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010343 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010344 checkkwd = CHKKWD | CHKALIAS;
10345 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010346 while (readtoken() == TREDIR) {
10347 *rpp = n2 = redirnode;
10348 rpp = &n2->nfile.next;
10349 parsefname();
10350 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010351 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010352 *rpp = NULL;
10353 if (redir) {
10354 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010355 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010356 n2->type = NREDIR;
10357 n2->nredir.n = n1;
10358 n1 = n2;
10359 }
10360 n1->nredir.redirect = redir;
10361 }
Eric Andersencb57d552001-06-28 07:25:16 +000010362 return n1;
10363}
10364
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010365#if ENABLE_ASH_BASH_COMPAT
10366static int decode_dollar_squote(void)
10367{
10368 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10369 int c, cnt;
10370 char *p;
10371 char buf[4];
10372
10373 c = pgetc();
10374 p = strchr(C_escapes, c);
10375 if (p) {
10376 buf[0] = c;
10377 p = buf;
10378 cnt = 3;
10379 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10380 do {
10381 c = pgetc();
10382 *++p = c;
10383 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10384 pungetc();
10385 } else if (c == 'x') { /* \xHH */
10386 do {
10387 c = pgetc();
10388 *++p = c;
10389 } while (isxdigit(c) && --cnt);
10390 pungetc();
10391 if (cnt == 3) { /* \x but next char is "bad" */
10392 c = 'x';
10393 goto unrecognized;
10394 }
10395 } else { /* simple seq like \\ or \t */
10396 p++;
10397 }
10398 *p = '\0';
10399 p = buf;
10400 c = bb_process_escape_sequence((void*)&p);
10401 } else { /* unrecognized "\z": print both chars unless ' or " */
10402 if (c != '\'' && c != '"') {
10403 unrecognized:
10404 c |= 0x100; /* "please encode \, then me" */
10405 }
10406 }
10407 return c;
10408}
10409#endif
10410
Eric Andersencb57d552001-06-28 07:25:16 +000010411/*
10412 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10413 * is not NULL, read a here document. In the latter case, eofmark is the
10414 * word which marks the end of the document and striptabs is true if
10415 * leading tabs should be stripped from the document. The argument firstc
10416 * is the first character of the input token or document.
10417 *
10418 * Because C does not have internal subroutines, I have simulated them
10419 * using goto's to implement the subroutine linkage. The following macros
10420 * will run code that appears at the end of readtoken1.
10421 */
Eric Andersen2870d962001-07-02 17:27:21 +000010422#define CHECKEND() {goto checkend; checkend_return:;}
10423#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10424#define PARSESUB() {goto parsesub; parsesub_return:;}
10425#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10426#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10427#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010428static int
Eric Andersenc470f442003-07-28 09:56:35 +000010429readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010430{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010431 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010432 int c = firstc;
10433 char *out;
10434 int len;
10435 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010436 struct nodelist *bqlist;
10437 smallint quotef;
10438 smallint dblquote;
10439 smallint oldstyle;
10440 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010441#if ENABLE_ASH_EXPAND_PRMT
10442 smallint pssyntax; /* we are expanding a prompt string */
10443#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010444 int varnest; /* levels of variables expansion */
10445 int arinest; /* levels of arithmetic expansion */
10446 int parenlevel; /* levels of parens in arithmetic */
10447 int dqvarnest; /* levels of variables expansion within double quotes */
10448
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010449 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10450
Eric Andersencb57d552001-06-28 07:25:16 +000010451#if __GNUC__
10452 /* Avoid longjmp clobbering */
10453 (void) &out;
10454 (void) &quotef;
10455 (void) &dblquote;
10456 (void) &varnest;
10457 (void) &arinest;
10458 (void) &parenlevel;
10459 (void) &dqvarnest;
10460 (void) &oldstyle;
10461 (void) &prevsyntax;
10462 (void) &syntax;
10463#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010464 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010465 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010466 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010467 oldstyle = 0;
10468 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010469#if ENABLE_ASH_EXPAND_PRMT
10470 pssyntax = (syntax == PSSYNTAX);
10471 if (pssyntax)
10472 syntax = DQSYNTAX;
10473#endif
10474 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010475 varnest = 0;
10476 arinest = 0;
10477 parenlevel = 0;
10478 dqvarnest = 0;
10479
10480 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010481 loop: { /* for each line, until end of word */
10482 CHECKEND(); /* set c to PEOF if at end of here document */
10483 for (;;) { /* until end of line or end of word */
10484 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010485 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010486 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010487 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010488 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010489 USTPUTC(c, out);
10490 plinno++;
10491 if (doprompt)
10492 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010493 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010494 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010495 case CWORD:
10496 USTPUTC(c, out);
10497 break;
10498 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010499 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010500 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010501#if ENABLE_ASH_BASH_COMPAT
10502 if (c == '\\' && bash_dollar_squote) {
10503 c = decode_dollar_squote();
10504 if (c & 0x100) {
10505 USTPUTC('\\', out);
10506 c = (unsigned char)c;
10507 }
10508 }
10509#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010510 USTPUTC(c, out);
10511 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010512 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010513 c = pgetc2();
10514 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010515 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010516 USTPUTC('\\', out);
10517 pungetc();
10518 } else if (c == '\n') {
10519 if (doprompt)
10520 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010521 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010522#if ENABLE_ASH_EXPAND_PRMT
10523 if (c == '$' && pssyntax) {
10524 USTPUTC(CTLESC, out);
10525 USTPUTC('\\', out);
10526 }
10527#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010528 if (dblquote && c != '\\'
10529 && c != '`' && c != '$'
10530 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010531 ) {
10532 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010533 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010534 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010535 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010536 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010537 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010538 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010539 }
10540 break;
10541 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010542 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010543 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010544 if (eofmark == NULL) {
10545 USTPUTC(CTLQUOTEMARK, out);
10546 }
Eric Andersencb57d552001-06-28 07:25:16 +000010547 break;
10548 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010549 syntax = DQSYNTAX;
10550 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010551 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010552 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010553 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010554 if (eofmark != NULL && arinest == 0
10555 && varnest == 0
10556 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010557 USTPUTC(c, out);
10558 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010559 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010560 syntax = BASESYNTAX;
10561 dblquote = 0;
10562 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010563 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010564 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010565 }
10566 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010567 case CVAR: /* '$' */
10568 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010569 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010570 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010571 if (varnest > 0) {
10572 varnest--;
10573 if (dqvarnest > 0) {
10574 dqvarnest--;
10575 }
10576 USTPUTC(CTLENDVAR, out);
10577 } else {
10578 USTPUTC(c, out);
10579 }
10580 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010581#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010582 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010583 parenlevel++;
10584 USTPUTC(c, out);
10585 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010586 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010587 if (parenlevel > 0) {
10588 USTPUTC(c, out);
10589 --parenlevel;
10590 } else {
10591 if (pgetc() == ')') {
10592 if (--arinest == 0) {
10593 USTPUTC(CTLENDARI, out);
10594 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010595 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010596 } else
10597 USTPUTC(')', out);
10598 } else {
10599 /*
10600 * unbalanced parens
10601 * (don't 2nd guess - no error)
10602 */
10603 pungetc();
10604 USTPUTC(')', out);
10605 }
10606 }
10607 break;
10608#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010609 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010610 PARSEBACKQOLD();
10611 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010612 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010613 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010614 case CIGN:
10615 break;
10616 default:
10617 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010618 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010619#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010620 if (c != PEOA)
10621#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010622 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010623
Eric Andersencb57d552001-06-28 07:25:16 +000010624 }
10625 c = pgetc_macro();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010626 } /* for(;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010627 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010628 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010629#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010630 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010631 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010632#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010633 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010634 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010635 if (varnest != 0) {
10636 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010637 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010638 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010639 }
10640 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010641 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010642 out = stackblock();
10643 if (eofmark == NULL) {
10644 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010645 && quotef == 0
10646 && len <= 2
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010647 && (*out == '\0' || isdigit(*out))
10648 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010649 PARSEREDIR();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010650 lasttoken = TREDIR;
10651 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010652 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010653 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010654 }
10655 quoteflag = quotef;
10656 backquotelist = bqlist;
10657 grabstackblock(len);
10658 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010659 lasttoken = TWORD;
10660 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010661/* end of readtoken routine */
10662
Eric Andersencb57d552001-06-28 07:25:16 +000010663/*
10664 * Check to see whether we are at the end of the here document. When this
10665 * is called, c is set to the first character of the next input line. If
10666 * we are at the end of the here document, this routine sets the c to PEOF.
10667 */
Eric Andersenc470f442003-07-28 09:56:35 +000010668checkend: {
10669 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010670#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010671 if (c == PEOA) {
10672 c = pgetc2();
10673 }
10674#endif
10675 if (striptabs) {
10676 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010677 c = pgetc2();
10678 }
Eric Andersenc470f442003-07-28 09:56:35 +000010679 }
10680 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010681 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010682 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010683
Eric Andersenc470f442003-07-28 09:56:35 +000010684 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010685 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10686 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010687 if (*p == '\n' && *q == '\0') {
10688 c = PEOF;
10689 plinno++;
10690 needprompt = doprompt;
10691 } else {
10692 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010693 }
10694 }
10695 }
10696 }
Eric Andersenc470f442003-07-28 09:56:35 +000010697 goto checkend_return;
10698}
Eric Andersencb57d552001-06-28 07:25:16 +000010699
Eric Andersencb57d552001-06-28 07:25:16 +000010700/*
10701 * Parse a redirection operator. The variable "out" points to a string
10702 * specifying the fd to be redirected. The variable "c" contains the
10703 * first character of the redirection operator.
10704 */
Eric Andersenc470f442003-07-28 09:56:35 +000010705parseredir: {
10706 char fd = *out;
10707 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010708
Denis Vlasenko597906c2008-02-20 16:38:54 +000010709 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010710 if (c == '>') {
10711 np->nfile.fd = 1;
10712 c = pgetc();
10713 if (c == '>')
10714 np->type = NAPPEND;
10715 else if (c == '|')
10716 np->type = NCLOBBER;
10717 else if (c == '&')
10718 np->type = NTOFD;
10719 else {
10720 np->type = NTO;
10721 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010722 }
Eric Andersenc470f442003-07-28 09:56:35 +000010723 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010724 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010725 c = pgetc();
10726 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010727 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010728 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010729 np = stzalloc(sizeof(struct nhere));
10730 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010731 }
10732 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010733 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010734 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010735 c = pgetc();
10736 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010737 heredoc->striptabs = 1;
10738 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010739 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010740 pungetc();
10741 }
10742 break;
10743
10744 case '&':
10745 np->type = NFROMFD;
10746 break;
10747
10748 case '>':
10749 np->type = NFROMTO;
10750 break;
10751
10752 default:
10753 np->type = NFROM;
10754 pungetc();
10755 break;
10756 }
Eric Andersencb57d552001-06-28 07:25:16 +000010757 }
Eric Andersenc470f442003-07-28 09:56:35 +000010758 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010759 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010760 redirnode = np;
10761 goto parseredir_return;
10762}
Eric Andersencb57d552001-06-28 07:25:16 +000010763
Eric Andersencb57d552001-06-28 07:25:16 +000010764/*
10765 * Parse a substitution. At this point, we have read the dollar sign
10766 * and nothing else.
10767 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010768
10769/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10770 * (assuming ascii char codes, as the original implementation did) */
10771#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010772 (((unsigned)(c) - 33 < 32) \
10773 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010774parsesub: {
10775 int subtype;
10776 int typeloc;
10777 int flags;
10778 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010779 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010780
Eric Andersenc470f442003-07-28 09:56:35 +000010781 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010782 if (c <= PEOA_OR_PEOF
10783 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010784 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010785#if ENABLE_ASH_BASH_COMPAT
10786 if (c == '\'')
10787 bash_dollar_squote = 1;
10788 else
10789#endif
10790 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010791 pungetc();
10792 } else if (c == '(') { /* $(command) or $((arith)) */
10793 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010794#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010795 PARSEARITH();
10796#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010797 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010798#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010799 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010800 pungetc();
10801 PARSEBACKQNEW();
10802 }
10803 } else {
10804 USTPUTC(CTLVAR, out);
10805 typeloc = out - (char *)stackblock();
10806 USTPUTC(VSNORMAL, out);
10807 subtype = VSNORMAL;
10808 if (c == '{') {
10809 c = pgetc();
10810 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010811 c = pgetc();
10812 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010813 c = '#';
10814 else
10815 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010816 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010817 subtype = 0;
10818 }
10819 if (c > PEOA_OR_PEOF && is_name(c)) {
10820 do {
10821 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010822 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010823 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010824 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010825 do {
10826 STPUTC(c, out);
10827 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010828 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010829 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010830 USTPUTC(c, out);
10831 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010832 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010833 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010834
Eric Andersenc470f442003-07-28 09:56:35 +000010835 STPUTC('=', out);
10836 flags = 0;
10837 if (subtype == 0) {
10838 switch (c) {
10839 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010840 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010841#if ENABLE_ASH_BASH_COMPAT
10842 if (c == ':' || c == '$' || isdigit(c)) {
10843 pungetc();
10844 subtype = VSSUBSTR;
10845 break;
10846 }
10847#endif
10848 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010849 /*FALLTHROUGH*/
10850 default:
10851 p = strchr(types, c);
10852 if (p == NULL)
10853 goto badsub;
10854 subtype = p - types + VSNORMAL;
10855 break;
10856 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010857 case '#': {
10858 int cc = c;
10859 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10860 c = pgetc();
10861 if (c == cc)
10862 subtype++;
10863 else
10864 pungetc();
10865 break;
10866 }
10867#if ENABLE_ASH_BASH_COMPAT
10868 case '/':
10869 subtype = VSREPLACE;
10870 c = pgetc();
10871 if (c == '/')
10872 subtype++; /* VSREPLACEALL */
10873 else
10874 pungetc();
10875 break;
10876#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010877 }
Eric Andersenc470f442003-07-28 09:56:35 +000010878 } else {
10879 pungetc();
10880 }
10881 if (dblquote || arinest)
10882 flags |= VSQUOTE;
10883 *((char *)stackblock() + typeloc) = subtype | flags;
10884 if (subtype != VSNORMAL) {
10885 varnest++;
10886 if (dblquote || arinest) {
10887 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010888 }
10889 }
10890 }
Eric Andersenc470f442003-07-28 09:56:35 +000010891 goto parsesub_return;
10892}
Eric Andersencb57d552001-06-28 07:25:16 +000010893
Eric Andersencb57d552001-06-28 07:25:16 +000010894/*
10895 * Called to parse command substitutions. Newstyle is set if the command
10896 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10897 * list of commands (passed by reference), and savelen is the number of
10898 * characters on the top of the stack which must be preserved.
10899 */
Eric Andersenc470f442003-07-28 09:56:35 +000010900parsebackq: {
10901 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010902 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010903 union node *n;
10904 char *volatile str;
10905 struct jmploc jmploc;
10906 struct jmploc *volatile savehandler;
10907 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010908 smallint saveprompt = 0;
10909
Eric Andersencb57d552001-06-28 07:25:16 +000010910#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010911 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010912#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010913 savepbq = parsebackquote;
10914 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010915 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010916 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010917 exception_handler = savehandler;
10918 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010919 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010920 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010921 str = NULL;
10922 savelen = out - (char *)stackblock();
10923 if (savelen > 0) {
10924 str = ckmalloc(savelen);
10925 memcpy(str, stackblock(), savelen);
10926 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010927 savehandler = exception_handler;
10928 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010929 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010930 if (oldstyle) {
10931 /* We must read until the closing backquote, giving special
10932 treatment to some slashes, and then push the string and
10933 reread it as input, interpreting it normally. */
10934 char *pout;
10935 int pc;
10936 size_t psavelen;
10937 char *pstr;
10938
10939
10940 STARTSTACKSTR(pout);
10941 for (;;) {
10942 if (needprompt) {
10943 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010944 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010945 pc = pgetc();
10946 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010947 case '`':
10948 goto done;
10949
10950 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010951 pc = pgetc();
10952 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010953 plinno++;
10954 if (doprompt)
10955 setprompt(2);
10956 /*
10957 * If eating a newline, avoid putting
10958 * the newline into the new character
10959 * stream (via the STPUTC after the
10960 * switch).
10961 */
10962 continue;
10963 }
10964 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010965 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010966 STPUTC('\\', pout);
10967 if (pc > PEOA_OR_PEOF) {
10968 break;
10969 }
10970 /* fall through */
10971
10972 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010973#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010974 case PEOA:
10975#endif
10976 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010977 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010978
10979 case '\n':
10980 plinno++;
10981 needprompt = doprompt;
10982 break;
10983
10984 default:
10985 break;
10986 }
10987 STPUTC(pc, pout);
10988 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010989 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010990 STPUTC('\0', pout);
10991 psavelen = pout - (char *)stackblock();
10992 if (psavelen > 0) {
10993 pstr = grabstackstr(pout);
10994 setinputstring(pstr);
10995 }
10996 }
10997 nlpp = &bqlist;
10998 while (*nlpp)
10999 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011000 *nlpp = stzalloc(sizeof(**nlpp));
11001 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011002 parsebackquote = oldstyle;
11003
11004 if (oldstyle) {
11005 saveprompt = doprompt;
11006 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011007 }
11008
Eric Andersenc470f442003-07-28 09:56:35 +000011009 n = list(2);
11010
11011 if (oldstyle)
11012 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011013 else if (readtoken() != TRP)
11014 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011015
11016 (*nlpp)->n = n;
11017 if (oldstyle) {
11018 /*
11019 * Start reading from old file again, ignoring any pushed back
11020 * tokens left from the backquote parsing
11021 */
11022 popfile();
11023 tokpushback = 0;
11024 }
11025 while (stackblocksize() <= savelen)
11026 growstackblock();
11027 STARTSTACKSTR(out);
11028 if (str) {
11029 memcpy(out, str, savelen);
11030 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011031 INT_OFF;
11032 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011033 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011034 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011035 }
11036 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011037 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011038 if (arinest || dblquote)
11039 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11040 else
11041 USTPUTC(CTLBACKQ, out);
11042 if (oldstyle)
11043 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011044 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011045}
11046
Denis Vlasenko131ae172007-02-18 13:00:19 +000011047#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011048/*
11049 * Parse an arithmetic expansion (indicate start of one and set state)
11050 */
Eric Andersenc470f442003-07-28 09:56:35 +000011051parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011052 if (++arinest == 1) {
11053 prevsyntax = syntax;
11054 syntax = ARISYNTAX;
11055 USTPUTC(CTLARI, out);
11056 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011057 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011058 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011059 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011060 } else {
11061 /*
11062 * we collapse embedded arithmetic expansion to
11063 * parenthesis, which should be equivalent
11064 */
11065 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011066 }
Eric Andersenc470f442003-07-28 09:56:35 +000011067 goto parsearith_return;
11068}
11069#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011070
Eric Andersenc470f442003-07-28 09:56:35 +000011071} /* end of readtoken */
11072
Eric Andersencb57d552001-06-28 07:25:16 +000011073/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011074 * Read the next input token.
11075 * If the token is a word, we set backquotelist to the list of cmds in
11076 * backquotes. We set quoteflag to true if any part of the word was
11077 * quoted.
11078 * If the token is TREDIR, then we set redirnode to a structure containing
11079 * the redirection.
11080 * In all cases, the variable startlinno is set to the number of the line
11081 * on which the token starts.
11082 *
11083 * [Change comment: here documents and internal procedures]
11084 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11085 * word parsing code into a separate routine. In this case, readtoken
11086 * doesn't need to have any internal procedures, but parseword does.
11087 * We could also make parseoperator in essence the main routine, and
11088 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011089 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011090#define NEW_xxreadtoken
11091#ifdef NEW_xxreadtoken
11092/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011093static const char xxreadtoken_chars[7] ALIGN1 = {
11094 '\n', '(', ')', '&', '|', ';', 0
11095};
Eric Andersencb57d552001-06-28 07:25:16 +000011096
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011097static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011098 TNL, TLP, TRP, /* only single occurrence allowed */
11099 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11100 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011101 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011102};
11103
11104#define xxreadtoken_doubles \
11105 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11106#define xxreadtoken_singles \
11107 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11108
11109static int
11110xxreadtoken(void)
11111{
11112 int c;
11113
11114 if (tokpushback) {
11115 tokpushback = 0;
11116 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011117 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011118 if (needprompt) {
11119 setprompt(2);
11120 }
11121 startlinno = plinno;
11122 for (;;) { /* until token or start of word found */
11123 c = pgetc_macro();
11124
11125 if ((c != ' ') && (c != '\t')
11126#if ENABLE_ASH_ALIAS
11127 && (c != PEOA)
11128#endif
11129 ) {
11130 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011131 while ((c = pgetc()) != '\n' && c != PEOF)
11132 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011133 pungetc();
11134 } else if (c == '\\') {
11135 if (pgetc() != '\n') {
11136 pungetc();
11137 goto READTOKEN1;
11138 }
11139 startlinno = ++plinno;
11140 if (doprompt)
11141 setprompt(2);
11142 } else {
11143 const char *p
11144 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11145
11146 if (c != PEOF) {
11147 if (c == '\n') {
11148 plinno++;
11149 needprompt = doprompt;
11150 }
11151
11152 p = strchr(xxreadtoken_chars, c);
11153 if (p == NULL) {
11154 READTOKEN1:
11155 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11156 }
11157
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011158 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011159 if (pgetc() == *p) { /* double occurrence? */
11160 p += xxreadtoken_doubles + 1;
11161 } else {
11162 pungetc();
11163 }
11164 }
11165 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011166 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11167 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011168 }
11169 }
11170 } /* for */
11171}
11172#else
11173#define RETURN(token) return lasttoken = token
11174static int
11175xxreadtoken(void)
11176{
11177 int c;
11178
11179 if (tokpushback) {
11180 tokpushback = 0;
11181 return lasttoken;
11182 }
11183 if (needprompt) {
11184 setprompt(2);
11185 }
11186 startlinno = plinno;
11187 for (;;) { /* until token or start of word found */
11188 c = pgetc_macro();
11189 switch (c) {
11190 case ' ': case '\t':
11191#if ENABLE_ASH_ALIAS
11192 case PEOA:
11193#endif
11194 continue;
11195 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011196 while ((c = pgetc()) != '\n' && c != PEOF)
11197 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011198 pungetc();
11199 continue;
11200 case '\\':
11201 if (pgetc() == '\n') {
11202 startlinno = ++plinno;
11203 if (doprompt)
11204 setprompt(2);
11205 continue;
11206 }
11207 pungetc();
11208 goto breakloop;
11209 case '\n':
11210 plinno++;
11211 needprompt = doprompt;
11212 RETURN(TNL);
11213 case PEOF:
11214 RETURN(TEOF);
11215 case '&':
11216 if (pgetc() == '&')
11217 RETURN(TAND);
11218 pungetc();
11219 RETURN(TBACKGND);
11220 case '|':
11221 if (pgetc() == '|')
11222 RETURN(TOR);
11223 pungetc();
11224 RETURN(TPIPE);
11225 case ';':
11226 if (pgetc() == ';')
11227 RETURN(TENDCASE);
11228 pungetc();
11229 RETURN(TSEMI);
11230 case '(':
11231 RETURN(TLP);
11232 case ')':
11233 RETURN(TRP);
11234 default:
11235 goto breakloop;
11236 }
11237 }
11238 breakloop:
11239 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11240#undef RETURN
11241}
11242#endif /* NEW_xxreadtoken */
11243
11244static int
11245readtoken(void)
11246{
11247 int t;
11248#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011249 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011250#endif
11251
11252#if ENABLE_ASH_ALIAS
11253 top:
11254#endif
11255
11256 t = xxreadtoken();
11257
11258 /*
11259 * eat newlines
11260 */
11261 if (checkkwd & CHKNL) {
11262 while (t == TNL) {
11263 parseheredoc();
11264 t = xxreadtoken();
11265 }
11266 }
11267
11268 if (t != TWORD || quoteflag) {
11269 goto out;
11270 }
11271
11272 /*
11273 * check for keywords
11274 */
11275 if (checkkwd & CHKKWD) {
11276 const char *const *pp;
11277
11278 pp = findkwd(wordtext);
11279 if (pp) {
11280 lasttoken = t = pp - tokname_array;
11281 TRACE(("keyword %s recognized\n", tokname(t)));
11282 goto out;
11283 }
11284 }
11285
11286 if (checkkwd & CHKALIAS) {
11287#if ENABLE_ASH_ALIAS
11288 struct alias *ap;
11289 ap = lookupalias(wordtext, 1);
11290 if (ap != NULL) {
11291 if (*ap->val) {
11292 pushstring(ap->val, ap);
11293 }
11294 goto top;
11295 }
11296#endif
11297 }
11298 out:
11299 checkkwd = 0;
11300#if DEBUG
11301 if (!alreadyseen)
11302 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11303 else
11304 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11305#endif
11306 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011307}
11308
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011309static char
11310peektoken(void)
11311{
11312 int t;
11313
11314 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011315 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011316 return tokname_array[t][0];
11317}
Eric Andersencb57d552001-06-28 07:25:16 +000011318
11319/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011320 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11321 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011322 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011323static union node *
11324parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011325{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011326 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011327
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011328 tokpushback = 0;
11329 doprompt = interact;
11330 if (doprompt)
11331 setprompt(doprompt);
11332 needprompt = 0;
11333 t = readtoken();
11334 if (t == TEOF)
11335 return NEOF;
11336 if (t == TNL)
11337 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011338 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011339 return list(1);
11340}
11341
11342/*
11343 * Input any here documents.
11344 */
11345static void
11346parseheredoc(void)
11347{
11348 struct heredoc *here;
11349 union node *n;
11350
11351 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011352 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011353
11354 while (here) {
11355 if (needprompt) {
11356 setprompt(2);
11357 }
11358 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11359 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011360 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011361 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011362 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011363 n->narg.text = wordtext;
11364 n->narg.backquote = backquotelist;
11365 here->here->nhere.doc = n;
11366 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011367 }
Eric Andersencb57d552001-06-28 07:25:16 +000011368}
11369
11370
11371/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011372 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011373 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011374#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011375static const char *
11376expandstr(const char *ps)
11377{
11378 union node n;
11379
11380 /* XXX Fix (char *) cast. */
11381 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011382 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011383 popfile();
11384
11385 n.narg.type = NARG;
11386 n.narg.next = NULL;
11387 n.narg.text = wordtext;
11388 n.narg.backquote = backquotelist;
11389
11390 expandarg(&n, NULL, 0);
11391 return stackblock();
11392}
11393#endif
11394
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011395/*
11396 * Execute a command or commands contained in a string.
11397 */
11398static int
11399evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011400{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011401 union node *n;
11402 struct stackmark smark;
11403 int skip;
11404
11405 setinputstring(s);
11406 setstackmark(&smark);
11407
11408 skip = 0;
11409 while ((n = parsecmd(0)) != NEOF) {
11410 evaltree(n, 0);
11411 popstackmark(&smark);
11412 skip = evalskip;
11413 if (skip)
11414 break;
11415 }
11416 popfile();
11417
11418 skip &= mask;
11419 evalskip = skip;
11420 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011421}
11422
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011423/*
11424 * The eval command.
11425 */
11426static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011427evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011428{
11429 char *p;
11430 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011431
Denis Vlasenko68404f12008-03-17 09:00:54 +000011432 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011433 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011434 argv += 2;
11435 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011436 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011437 for (;;) {
11438 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011439 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011440 if (p == NULL)
11441 break;
11442 STPUTC(' ', concat);
11443 }
11444 STPUTC('\0', concat);
11445 p = grabstackstr(concat);
11446 }
11447 evalstring(p, ~SKIPEVAL);
11448
11449 }
11450 return exitstatus;
11451}
11452
11453/*
11454 * Read and execute commands. "Top" is nonzero for the top level command
11455 * loop; it turns on prompting if the shell is interactive.
11456 */
11457static int
11458cmdloop(int top)
11459{
11460 union node *n;
11461 struct stackmark smark;
11462 int inter;
11463 int numeof = 0;
11464
11465 TRACE(("cmdloop(%d) called\n", top));
11466 for (;;) {
11467 int skip;
11468
11469 setstackmark(&smark);
11470#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011471 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011472 showjobs(stderr, SHOW_CHANGED);
11473#endif
11474 inter = 0;
11475 if (iflag && top) {
11476 inter++;
11477#if ENABLE_ASH_MAIL
11478 chkmail();
11479#endif
11480 }
11481 n = parsecmd(inter);
11482 /* showtree(n); DEBUG */
11483 if (n == NEOF) {
11484 if (!top || numeof >= 50)
11485 break;
11486 if (!stoppedjobs()) {
11487 if (!Iflag)
11488 break;
11489 out2str("\nUse \"exit\" to leave shell.\n");
11490 }
11491 numeof++;
11492 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011493 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11494 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011495 numeof = 0;
11496 evaltree(n, 0);
11497 }
11498 popstackmark(&smark);
11499 skip = evalskip;
11500
11501 if (skip) {
11502 evalskip = 0;
11503 return skip & SKIPEVAL;
11504 }
11505 }
11506 return 0;
11507}
11508
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011509/*
11510 * Take commands from a file. To be compatible we should do a path
11511 * search for the file, which is necessary to find sub-commands.
11512 */
11513static char *
11514find_dot_file(char *name)
11515{
11516 char *fullname;
11517 const char *path = pathval();
11518 struct stat statb;
11519
11520 /* don't try this for absolute or relative paths */
11521 if (strchr(name, '/'))
11522 return name;
11523
11524 while ((fullname = padvance(&path, name)) != NULL) {
11525 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11526 /*
11527 * Don't bother freeing here, since it will
11528 * be freed by the caller.
11529 */
11530 return fullname;
11531 }
11532 stunalloc(fullname);
11533 }
11534
11535 /* not found in the PATH */
11536 ash_msg_and_raise_error("%s: not found", name);
11537 /* NOTREACHED */
11538}
11539
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011540static int
11541dotcmd(int argc, char **argv)
11542{
11543 struct strlist *sp;
11544 volatile struct shparam saveparam;
11545 int status = 0;
11546
11547 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011548 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011549
Denis Vlasenko68404f12008-03-17 09:00:54 +000011550 if (argv[1]) { /* That's what SVR2 does */
11551 char *fullname = find_dot_file(argv[1]);
11552 argv += 2;
11553 argc -= 2;
11554 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011555 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011556 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011557 shellparam.nparam = argc;
11558 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011559 };
11560
11561 setinputfile(fullname, INPUT_PUSH_FILE);
11562 commandname = fullname;
11563 cmdloop(0);
11564 popfile();
11565
Denis Vlasenko68404f12008-03-17 09:00:54 +000011566 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011567 freeparam(&shellparam);
11568 shellparam = saveparam;
11569 };
11570 status = exitstatus;
11571 }
11572 return status;
11573}
11574
11575static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011576exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011577{
11578 if (stoppedjobs())
11579 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011580 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011581 exitstatus = number(argv[1]);
11582 raise_exception(EXEXIT);
11583 /* NOTREACHED */
11584}
11585
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011586/*
11587 * Read a file containing shell functions.
11588 */
11589static void
11590readcmdfile(char *name)
11591{
11592 setinputfile(name, INPUT_PUSH_FILE);
11593 cmdloop(0);
11594 popfile();
11595}
11596
11597
Denis Vlasenkocc571512007-02-23 21:10:35 +000011598/* ============ find_command inplementation */
11599
11600/*
11601 * Resolve a command name. If you change this routine, you may have to
11602 * change the shellexec routine as well.
11603 */
11604static void
11605find_command(char *name, struct cmdentry *entry, int act, const char *path)
11606{
11607 struct tblentry *cmdp;
11608 int idx;
11609 int prev;
11610 char *fullname;
11611 struct stat statb;
11612 int e;
11613 int updatetbl;
11614 struct builtincmd *bcmd;
11615
11616 /* If name contains a slash, don't use PATH or hash table */
11617 if (strchr(name, '/') != NULL) {
11618 entry->u.index = -1;
11619 if (act & DO_ABS) {
11620 while (stat(name, &statb) < 0) {
11621#ifdef SYSV
11622 if (errno == EINTR)
11623 continue;
11624#endif
11625 entry->cmdtype = CMDUNKNOWN;
11626 return;
11627 }
11628 }
11629 entry->cmdtype = CMDNORMAL;
11630 return;
11631 }
11632
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011633/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011634
11635 updatetbl = (path == pathval());
11636 if (!updatetbl) {
11637 act |= DO_ALTPATH;
11638 if (strstr(path, "%builtin") != NULL)
11639 act |= DO_ALTBLTIN;
11640 }
11641
11642 /* If name is in the table, check answer will be ok */
11643 cmdp = cmdlookup(name, 0);
11644 if (cmdp != NULL) {
11645 int bit;
11646
11647 switch (cmdp->cmdtype) {
11648 default:
11649#if DEBUG
11650 abort();
11651#endif
11652 case CMDNORMAL:
11653 bit = DO_ALTPATH;
11654 break;
11655 case CMDFUNCTION:
11656 bit = DO_NOFUNC;
11657 break;
11658 case CMDBUILTIN:
11659 bit = DO_ALTBLTIN;
11660 break;
11661 }
11662 if (act & bit) {
11663 updatetbl = 0;
11664 cmdp = NULL;
11665 } else if (cmdp->rehash == 0)
11666 /* if not invalidated by cd, we're done */
11667 goto success;
11668 }
11669
11670 /* If %builtin not in path, check for builtin next */
11671 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011672 if (bcmd) {
11673 if (IS_BUILTIN_REGULAR(bcmd))
11674 goto builtin_success;
11675 if (act & DO_ALTPATH) {
11676 if (!(act & DO_ALTBLTIN))
11677 goto builtin_success;
11678 } else if (builtinloc <= 0) {
11679 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011680 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011681 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011682
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011683#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011684 {
11685 int applet_no = find_applet_by_name(name);
11686 if (applet_no >= 0) {
11687 entry->cmdtype = CMDNORMAL;
11688 entry->u.index = -2 - applet_no;
11689 return;
11690 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011691 }
11692#endif
11693
Denis Vlasenkocc571512007-02-23 21:10:35 +000011694 /* We have to search path. */
11695 prev = -1; /* where to start */
11696 if (cmdp && cmdp->rehash) { /* doing a rehash */
11697 if (cmdp->cmdtype == CMDBUILTIN)
11698 prev = builtinloc;
11699 else
11700 prev = cmdp->param.index;
11701 }
11702
11703 e = ENOENT;
11704 idx = -1;
11705 loop:
11706 while ((fullname = padvance(&path, name)) != NULL) {
11707 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011708 /* NB: code below will still use fullname
11709 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011710 idx++;
11711 if (pathopt) {
11712 if (prefix(pathopt, "builtin")) {
11713 if (bcmd)
11714 goto builtin_success;
11715 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011716 }
11717 if ((act & DO_NOFUNC)
11718 || !prefix(pathopt, "func")
11719 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011720 continue;
11721 }
11722 }
11723 /* if rehash, don't redo absolute path names */
11724 if (fullname[0] == '/' && idx <= prev) {
11725 if (idx < prev)
11726 continue;
11727 TRACE(("searchexec \"%s\": no change\n", name));
11728 goto success;
11729 }
11730 while (stat(fullname, &statb) < 0) {
11731#ifdef SYSV
11732 if (errno == EINTR)
11733 continue;
11734#endif
11735 if (errno != ENOENT && errno != ENOTDIR)
11736 e = errno;
11737 goto loop;
11738 }
11739 e = EACCES; /* if we fail, this will be the error */
11740 if (!S_ISREG(statb.st_mode))
11741 continue;
11742 if (pathopt) { /* this is a %func directory */
11743 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011744 /* NB: stalloc will return space pointed by fullname
11745 * (because we don't have any intervening allocations
11746 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011747 readcmdfile(fullname);
11748 cmdp = cmdlookup(name, 0);
11749 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11750 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11751 stunalloc(fullname);
11752 goto success;
11753 }
11754 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11755 if (!updatetbl) {
11756 entry->cmdtype = CMDNORMAL;
11757 entry->u.index = idx;
11758 return;
11759 }
11760 INT_OFF;
11761 cmdp = cmdlookup(name, 1);
11762 cmdp->cmdtype = CMDNORMAL;
11763 cmdp->param.index = idx;
11764 INT_ON;
11765 goto success;
11766 }
11767
11768 /* We failed. If there was an entry for this command, delete it */
11769 if (cmdp && updatetbl)
11770 delete_cmd_entry();
11771 if (act & DO_ERR)
11772 ash_msg("%s: %s", name, errmsg(e, "not found"));
11773 entry->cmdtype = CMDUNKNOWN;
11774 return;
11775
11776 builtin_success:
11777 if (!updatetbl) {
11778 entry->cmdtype = CMDBUILTIN;
11779 entry->u.cmd = bcmd;
11780 return;
11781 }
11782 INT_OFF;
11783 cmdp = cmdlookup(name, 1);
11784 cmdp->cmdtype = CMDBUILTIN;
11785 cmdp->param.cmd = bcmd;
11786 INT_ON;
11787 success:
11788 cmdp->rehash = 0;
11789 entry->cmdtype = cmdp->cmdtype;
11790 entry->u = cmdp->param;
11791}
11792
11793
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011794/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011795
Eric Andersencb57d552001-06-28 07:25:16 +000011796/*
Eric Andersencb57d552001-06-28 07:25:16 +000011797 * The trap builtin.
11798 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011799static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011800trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011801{
11802 char *action;
11803 char **ap;
11804 int signo;
11805
Eric Andersenc470f442003-07-28 09:56:35 +000011806 nextopt(nullstr);
11807 ap = argptr;
11808 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011809 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011810 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011811 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011812
Rob Landleyc9c1a412006-07-12 19:17:55 +000011813 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011814 out1fmt("trap -- %s %s\n",
11815 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011816 }
11817 }
11818 return 0;
11819 }
Eric Andersenc470f442003-07-28 09:56:35 +000011820 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011821 action = NULL;
11822 else
11823 action = *ap++;
11824 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011825 signo = get_signum(*ap);
11826 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011827 ash_msg_and_raise_error("%s: bad trap", *ap);
11828 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011829 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011830 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011831 action = NULL;
11832 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011833 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011834 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011835 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011836 trap[signo] = action;
11837 if (signo != 0)
11838 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011839 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011840 ap++;
11841 }
11842 return 0;
11843}
11844
Eric Andersenc470f442003-07-28 09:56:35 +000011845
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011846/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011847
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011848#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011849/*
11850 * Lists available builtins
11851 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011852static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011853helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011854{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011855 unsigned col;
11856 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011857
11858 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011859 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011860 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011861 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011862 if (col > 60) {
11863 out1fmt("\n");
11864 col = 0;
11865 }
11866 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011867#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011868 {
11869 const char *a = applet_names;
11870 while (*a) {
11871 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11872 if (col > 60) {
11873 out1fmt("\n");
11874 col = 0;
11875 }
11876 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011877 }
11878 }
11879#endif
11880 out1fmt("\n\n");
11881 return EXIT_SUCCESS;
11882}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011883#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011884
Eric Andersencb57d552001-06-28 07:25:16 +000011885/*
Eric Andersencb57d552001-06-28 07:25:16 +000011886 * The export and readonly commands.
11887 */
Eric Andersenc470f442003-07-28 09:56:35 +000011888static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011889exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011890{
11891 struct var *vp;
11892 char *name;
11893 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011894 char **aptr;
11895 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011896
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011897 if (nextopt("p") != 'p') {
11898 aptr = argptr;
11899 name = *aptr;
11900 if (name) {
11901 do {
11902 p = strchr(name, '=');
11903 if (p != NULL) {
11904 p++;
11905 } else {
11906 vp = *findvar(hashvar(name), name);
11907 if (vp) {
11908 vp->flags |= flag;
11909 continue;
11910 }
Eric Andersencb57d552001-06-28 07:25:16 +000011911 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011912 setvar(name, p, flag);
11913 } while ((name = *++aptr) != NULL);
11914 return 0;
11915 }
Eric Andersencb57d552001-06-28 07:25:16 +000011916 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011917 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011918 return 0;
11919}
11920
Eric Andersencb57d552001-06-28 07:25:16 +000011921/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011922 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011923 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011924static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011925unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011926{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011927 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011928
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011929 cmdp = cmdlookup(name, 0);
11930 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11931 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011932}
11933
Eric Andersencb57d552001-06-28 07:25:16 +000011934/*
Eric Andersencb57d552001-06-28 07:25:16 +000011935 * The unset builtin command. We unset the function before we unset the
11936 * variable to allow a function to be unset when there is a readonly variable
11937 * with the same name.
11938 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011939static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011940unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011941{
11942 char **ap;
11943 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011944 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011945 int ret = 0;
11946
11947 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011948 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011949 }
Eric Andersencb57d552001-06-28 07:25:16 +000011950
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011951 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011952 if (flag != 'f') {
11953 i = unsetvar(*ap);
11954 ret |= i;
11955 if (!(i & 2))
11956 continue;
11957 }
11958 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011959 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011960 }
Eric Andersenc470f442003-07-28 09:56:35 +000011961 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011962}
11963
11964
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011965/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011966
Eric Andersenc470f442003-07-28 09:56:35 +000011967#include <sys/times.h>
11968
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011969static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011970 ' ', offsetof(struct tms, tms_utime),
11971 '\n', offsetof(struct tms, tms_stime),
11972 ' ', offsetof(struct tms, tms_cutime),
11973 '\n', offsetof(struct tms, tms_cstime),
11974 0
11975};
Eric Andersencb57d552001-06-28 07:25:16 +000011976
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011977static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011978timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011979{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011980 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011981 const unsigned char *p;
11982 struct tms buf;
11983
11984 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011985 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011986
11987 p = timescmd_str;
11988 do {
11989 t = *(clock_t *)(((char *) &buf) + p[1]);
11990 s = t / clk_tck;
11991 out1fmt("%ldm%ld.%.3lds%c",
11992 s/60, s%60,
11993 ((t - s * clk_tck) * 1000) / clk_tck,
11994 p[0]);
11995 } while (*(p += 2));
11996
Eric Andersencb57d552001-06-28 07:25:16 +000011997 return 0;
11998}
11999
Denis Vlasenko131ae172007-02-18 13:00:19 +000012000#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012001static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012002dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012003{
Eric Andersened9ecf72004-06-22 08:29:45 +000012004 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012005 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012006
Denis Vlasenkob012b102007-02-19 22:43:01 +000012007 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012008 result = arith(s, &errcode);
12009 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012010 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012011 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012012 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012013 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012014 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012015 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012016 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012017 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012018 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012019
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012020 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012021}
Eric Andersenc470f442003-07-28 09:56:35 +000012022
Eric Andersenc470f442003-07-28 09:56:35 +000012023/*
Eric Andersen90898442003-08-06 11:20:52 +000012024 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12025 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12026 *
12027 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012028 */
12029static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012030letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012031{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012032 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012033
Denis Vlasenko68404f12008-03-17 09:00:54 +000012034 argv++;
12035 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012036 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012037 do {
12038 i = dash_arith(*argv);
12039 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012040
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012041 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012042}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012043#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012044
Eric Andersenc470f442003-07-28 09:56:35 +000012045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012046/* ============ miscbltin.c
12047 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012048 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012049 */
12050
12051#undef rflag
12052
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012053#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012054typedef enum __rlimit_resource rlim_t;
12055#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012056
Eric Andersenc470f442003-07-28 09:56:35 +000012057/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012058 * The read builtin. Options:
12059 * -r Do not interpret '\' specially
12060 * -s Turn off echo (tty only)
12061 * -n NCHARS Read NCHARS max
12062 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12063 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12064 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012065 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012066 * TODO: bash also has:
12067 * -a ARRAY Read into array[0],[1],etc
12068 * -d DELIM End on DELIM char, not newline
12069 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012070 */
Eric Andersenc470f442003-07-28 09:56:35 +000012071static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012072readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012073{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012074 static const char *const arg_REPLY[] = { "REPLY", NULL };
12075
Eric Andersenc470f442003-07-28 09:56:35 +000012076 char **ap;
12077 int backslash;
12078 char c;
12079 int rflag;
12080 char *prompt;
12081 const char *ifs;
12082 char *p;
12083 int startword;
12084 int status;
12085 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012086 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012087#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012088 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012089 int silent = 0;
12090 struct termios tty, old_tty;
12091#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012092#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012093 unsigned end_ms = 0;
12094 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012095#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012096
12097 rflag = 0;
12098 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012099 while ((i = nextopt("p:u:r"
12100 USE_ASH_READ_TIMEOUT("t:")
12101 USE_ASH_READ_NCHARS("n:s")
12102 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012103 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012104 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012105 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012106 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012107#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012108 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012109 nchars = bb_strtou(optionarg, NULL, 10);
12110 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012111 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012112 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012113 break;
12114 case 's':
12115 silent = 1;
12116 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012117#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012118#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012119 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012120 timeout = bb_strtou(optionarg, NULL, 10);
12121 if (errno || timeout > UINT_MAX / 2048)
12122 ash_msg_and_raise_error("invalid timeout");
12123 timeout *= 1000;
12124#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012125 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012126 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012127 /* EINVAL means number is ok, but not terminated by NUL */
12128 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012129 char *p2;
12130 if (*++p) {
12131 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012132 ts.tv_usec = bb_strtou(p, &p2, 10);
12133 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012134 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012135 scale = p2 - p;
12136 /* normalize to usec */
12137 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012138 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012139 while (scale++ < 6)
12140 ts.tv_usec *= 10;
12141 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012142 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012143 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012144 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012145 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012146 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012147 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012148#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012149 break;
12150#endif
12151 case 'r':
12152 rflag = 1;
12153 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012154 case 'u':
12155 fd = bb_strtou(optionarg, NULL, 10);
12156 if (fd < 0 || errno)
12157 ash_msg_and_raise_error("invalid file descriptor");
12158 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012159 default:
12160 break;
12161 }
Eric Andersenc470f442003-07-28 09:56:35 +000012162 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012163 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012164 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012165 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012166 ap = argptr;
12167 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012168 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012169 ifs = bltinlookup("IFS");
12170 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012171 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012172#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012173 tcgetattr(fd, &tty);
12174 old_tty = tty;
12175 if (nchars || silent) {
12176 if (nchars) {
12177 tty.c_lflag &= ~ICANON;
12178 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012179 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012180 if (silent) {
12181 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12182 }
12183 /* if tcgetattr failed, tcsetattr will fail too.
12184 * Ignoring, it's harmless. */
12185 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012186 }
12187#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012188
Eric Andersenc470f442003-07-28 09:56:35 +000012189 status = 0;
12190 startword = 1;
12191 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012192#if ENABLE_ASH_READ_TIMEOUT
12193 if (timeout) /* NB: ensuring end_ms is nonzero */
12194 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12195#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012196 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012197 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012198#if ENABLE_ASH_READ_TIMEOUT
12199 if (end_ms) {
12200 struct pollfd pfd[1];
12201 pfd[0].fd = fd;
12202 pfd[0].events = POLLIN;
12203 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12204 if ((int)timeout <= 0 /* already late? */
12205 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12206 ) { /* timed out! */
12207#if ENABLE_ASH_READ_NCHARS
12208 tcsetattr(fd, TCSANOW, &old_tty);
12209#endif
12210 return 1;
12211 }
12212 }
12213#endif
12214 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012215 status = 1;
12216 break;
12217 }
12218 if (c == '\0')
12219 continue;
12220 if (backslash) {
12221 backslash = 0;
12222 if (c != '\n')
12223 goto put;
12224 continue;
12225 }
12226 if (!rflag && c == '\\') {
12227 backslash++;
12228 continue;
12229 }
12230 if (c == '\n')
12231 break;
12232 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12233 continue;
12234 }
12235 startword = 0;
12236 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12237 STACKSTRNUL(p);
12238 setvar(*ap, stackblock(), 0);
12239 ap++;
12240 startword = 1;
12241 STARTSTACKSTR(p);
12242 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012243 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012244 STPUTC(c, p);
12245 }
12246 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012247/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012248#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012249 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012250#else
12251 while (1);
12252#endif
12253
12254#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012255 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012256#endif
12257
Eric Andersenc470f442003-07-28 09:56:35 +000012258 STACKSTRNUL(p);
12259 /* Remove trailing blanks */
12260 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12261 *p = '\0';
12262 setvar(*ap, stackblock(), 0);
12263 while (*++ap != NULL)
12264 setvar(*ap, nullstr, 0);
12265 return status;
12266}
12267
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012268static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012269umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012270{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012271 static const char permuser[3] ALIGN1 = "ugo";
12272 static const char permmode[3] ALIGN1 = "rwx";
12273 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012274 S_IRUSR, S_IWUSR, S_IXUSR,
12275 S_IRGRP, S_IWGRP, S_IXGRP,
12276 S_IROTH, S_IWOTH, S_IXOTH
12277 };
12278
12279 char *ap;
12280 mode_t mask;
12281 int i;
12282 int symbolic_mode = 0;
12283
12284 while (nextopt("S") != '\0') {
12285 symbolic_mode = 1;
12286 }
12287
Denis Vlasenkob012b102007-02-19 22:43:01 +000012288 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012289 mask = umask(0);
12290 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012291 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012292
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012293 ap = *argptr;
12294 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012295 if (symbolic_mode) {
12296 char buf[18];
12297 char *p = buf;
12298
12299 for (i = 0; i < 3; i++) {
12300 int j;
12301
12302 *p++ = permuser[i];
12303 *p++ = '=';
12304 for (j = 0; j < 3; j++) {
12305 if ((mask & permmask[3 * i + j]) == 0) {
12306 *p++ = permmode[j];
12307 }
12308 }
12309 *p++ = ',';
12310 }
12311 *--p = 0;
12312 puts(buf);
12313 } else {
12314 out1fmt("%.4o\n", mask);
12315 }
12316 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012317 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012318 mask = 0;
12319 do {
12320 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012321 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012322 mask = (mask << 3) + (*ap - '0');
12323 } while (*++ap != '\0');
12324 umask(mask);
12325 } else {
12326 mask = ~mask & 0777;
12327 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012328 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012329 }
12330 umask(~mask & 0777);
12331 }
12332 }
12333 return 0;
12334}
12335
12336/*
12337 * ulimit builtin
12338 *
12339 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12340 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12341 * ash by J.T. Conklin.
12342 *
12343 * Public domain.
12344 */
12345
12346struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012347 uint8_t cmd; /* RLIMIT_xxx fit into it */
12348 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012349 char option;
12350};
12351
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012352static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012353#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012354 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012355#endif
12356#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012357 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012358#endif
12359#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012360 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012361#endif
12362#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012363 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012364#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012365#ifdef RLIMIT_CORE
12366 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012367#endif
12368#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012369 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012370#endif
12371#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012372 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012373#endif
12374#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012375 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012376#endif
12377#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012378 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012379#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012380#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012381 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012382#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012383#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012384 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012385#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012386};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012387static const char limits_name[] =
12388#ifdef RLIMIT_CPU
12389 "time(seconds)" "\0"
12390#endif
12391#ifdef RLIMIT_FSIZE
12392 "file(blocks)" "\0"
12393#endif
12394#ifdef RLIMIT_DATA
12395 "data(kb)" "\0"
12396#endif
12397#ifdef RLIMIT_STACK
12398 "stack(kb)" "\0"
12399#endif
12400#ifdef RLIMIT_CORE
12401 "coredump(blocks)" "\0"
12402#endif
12403#ifdef RLIMIT_RSS
12404 "memory(kb)" "\0"
12405#endif
12406#ifdef RLIMIT_MEMLOCK
12407 "locked memory(kb)" "\0"
12408#endif
12409#ifdef RLIMIT_NPROC
12410 "process" "\0"
12411#endif
12412#ifdef RLIMIT_NOFILE
12413 "nofiles" "\0"
12414#endif
12415#ifdef RLIMIT_AS
12416 "vmemory(kb)" "\0"
12417#endif
12418#ifdef RLIMIT_LOCKS
12419 "locks" "\0"
12420#endif
12421;
Eric Andersenc470f442003-07-28 09:56:35 +000012422
Glenn L McGrath76620622004-01-13 10:19:37 +000012423enum limtype { SOFT = 0x1, HARD = 0x2 };
12424
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012425static void
12426printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012427 const struct limits *l)
12428{
12429 rlim_t val;
12430
12431 val = limit->rlim_max;
12432 if (how & SOFT)
12433 val = limit->rlim_cur;
12434
12435 if (val == RLIM_INFINITY)
12436 out1fmt("unlimited\n");
12437 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012438 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012439 out1fmt("%lld\n", (long long) val);
12440 }
12441}
12442
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012443static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012444ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012445{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012446 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012447 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012448 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012449 const struct limits *l;
12450 int set, all = 0;
12451 int optc, what;
12452 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012453
12454 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012455 while ((optc = nextopt("HSa"
12456#ifdef RLIMIT_CPU
12457 "t"
12458#endif
12459#ifdef RLIMIT_FSIZE
12460 "f"
12461#endif
12462#ifdef RLIMIT_DATA
12463 "d"
12464#endif
12465#ifdef RLIMIT_STACK
12466 "s"
12467#endif
12468#ifdef RLIMIT_CORE
12469 "c"
12470#endif
12471#ifdef RLIMIT_RSS
12472 "m"
12473#endif
12474#ifdef RLIMIT_MEMLOCK
12475 "l"
12476#endif
12477#ifdef RLIMIT_NPROC
12478 "p"
12479#endif
12480#ifdef RLIMIT_NOFILE
12481 "n"
12482#endif
12483#ifdef RLIMIT_AS
12484 "v"
12485#endif
12486#ifdef RLIMIT_LOCKS
12487 "w"
12488#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012489 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012490 switch (optc) {
12491 case 'H':
12492 how = HARD;
12493 break;
12494 case 'S':
12495 how = SOFT;
12496 break;
12497 case 'a':
12498 all = 1;
12499 break;
12500 default:
12501 what = optc;
12502 }
12503
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012504 for (l = limits_tbl; l->option != what; l++)
12505 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012506
12507 set = *argptr ? 1 : 0;
12508 if (set) {
12509 char *p = *argptr;
12510
12511 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012512 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012513 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012514 val = RLIM_INFINITY;
12515 else {
12516 val = (rlim_t) 0;
12517
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012518 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012519 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012520 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012521 if (val < (rlim_t) 0)
12522 break;
12523 }
12524 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012525 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012526 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012527 }
12528 }
12529 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012530 const char *lname = limits_name;
12531 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012532 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012533 out1fmt("%-20s ", lname);
12534 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012535 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012536 }
12537 return 0;
12538 }
12539
12540 getrlimit(l->cmd, &limit);
12541 if (set) {
12542 if (how & HARD)
12543 limit.rlim_max = val;
12544 if (how & SOFT)
12545 limit.rlim_cur = val;
12546 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012547 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012548 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012549 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012550 }
12551 return 0;
12552}
12553
Eric Andersen90898442003-08-06 11:20:52 +000012554
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012555/* ============ Math support */
12556
Denis Vlasenko131ae172007-02-18 13:00:19 +000012557#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012558
12559/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12560
12561 Permission is hereby granted, free of charge, to any person obtaining
12562 a copy of this software and associated documentation files (the
12563 "Software"), to deal in the Software without restriction, including
12564 without limitation the rights to use, copy, modify, merge, publish,
12565 distribute, sublicense, and/or sell copies of the Software, and to
12566 permit persons to whom the Software is furnished to do so, subject to
12567 the following conditions:
12568
12569 The above copyright notice and this permission notice shall be
12570 included in all copies or substantial portions of the Software.
12571
12572 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12573 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12574 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12575 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12576 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12577 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12578 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12579*/
12580
12581/* This is my infix parser/evaluator. It is optimized for size, intended
12582 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012583 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012584 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012585 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012586 * be that which POSIX specifies for shells. */
12587
12588/* The code uses a simple two-stack algorithm. See
12589 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012590 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012591 * this is based (this code differs in that it applies operators immediately
12592 * to the stack instead of adding them to a queue to end up with an
12593 * expression). */
12594
12595/* To use the routine, call it with an expression string and error return
12596 * pointer */
12597
12598/*
12599 * Aug 24, 2001 Manuel Novoa III
12600 *
12601 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12602 *
12603 * 1) In arith_apply():
12604 * a) Cached values of *numptr and &(numptr[-1]).
12605 * b) Removed redundant test for zero denominator.
12606 *
12607 * 2) In arith():
12608 * a) Eliminated redundant code for processing operator tokens by moving
12609 * to a table-based implementation. Also folded handling of parens
12610 * into the table.
12611 * b) Combined all 3 loops which called arith_apply to reduce generated
12612 * code size at the cost of speed.
12613 *
12614 * 3) The following expressions were treated as valid by the original code:
12615 * 1() , 0! , 1 ( *3 ) .
12616 * These bugs have been fixed by internally enclosing the expression in
12617 * parens and then checking that all binary ops and right parens are
12618 * preceded by a valid expression (NUM_TOKEN).
12619 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012620 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012621 * ctype's isspace() if it is used by another busybox applet or if additional
12622 * whitespace chars should be considered. Look below the "#include"s for a
12623 * precompiler test.
12624 */
12625
12626/*
12627 * Aug 26, 2001 Manuel Novoa III
12628 *
12629 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12630 *
12631 * Merge in Aaron's comments previously posted to the busybox list,
12632 * modified slightly to take account of my changes to the code.
12633 *
12634 */
12635
12636/*
12637 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12638 *
12639 * - allow access to variable,
12640 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12641 * - realize assign syntax (VAR=expr, +=, *= etc)
12642 * - realize exponentiation (** operator)
12643 * - realize comma separated - expr, expr
12644 * - realise ++expr --expr expr++ expr--
12645 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012646 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012647 * - was restored loses XOR operator
12648 * - remove one goto label, added three ;-)
12649 * - protect $((num num)) as true zero expr (Manuel`s error)
12650 * - always use special isspace(), see comment from bash ;-)
12651 */
12652
Eric Andersen90898442003-08-06 11:20:52 +000012653#define arith_isspace(arithval) \
12654 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12655
Eric Andersen90898442003-08-06 11:20:52 +000012656typedef unsigned char operator;
12657
12658/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012659 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012660 * precedence. The ID portion is so that multiple operators can have the
12661 * same precedence, ensuring that the leftmost one is evaluated first.
12662 * Consider * and /. */
12663
12664#define tok_decl(prec,id) (((id)<<5)|(prec))
12665#define PREC(op) ((op) & 0x1F)
12666
12667#define TOK_LPAREN tok_decl(0,0)
12668
12669#define TOK_COMMA tok_decl(1,0)
12670
12671#define TOK_ASSIGN tok_decl(2,0)
12672#define TOK_AND_ASSIGN tok_decl(2,1)
12673#define TOK_OR_ASSIGN tok_decl(2,2)
12674#define TOK_XOR_ASSIGN tok_decl(2,3)
12675#define TOK_PLUS_ASSIGN tok_decl(2,4)
12676#define TOK_MINUS_ASSIGN tok_decl(2,5)
12677#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12678#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12679
12680#define TOK_MUL_ASSIGN tok_decl(3,0)
12681#define TOK_DIV_ASSIGN tok_decl(3,1)
12682#define TOK_REM_ASSIGN tok_decl(3,2)
12683
12684/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012685#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012686
12687/* conditional is right associativity too */
12688#define TOK_CONDITIONAL tok_decl(4,0)
12689#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12690
12691#define TOK_OR tok_decl(5,0)
12692
12693#define TOK_AND tok_decl(6,0)
12694
12695#define TOK_BOR tok_decl(7,0)
12696
12697#define TOK_BXOR tok_decl(8,0)
12698
12699#define TOK_BAND tok_decl(9,0)
12700
12701#define TOK_EQ tok_decl(10,0)
12702#define TOK_NE tok_decl(10,1)
12703
12704#define TOK_LT tok_decl(11,0)
12705#define TOK_GT tok_decl(11,1)
12706#define TOK_GE tok_decl(11,2)
12707#define TOK_LE tok_decl(11,3)
12708
12709#define TOK_LSHIFT tok_decl(12,0)
12710#define TOK_RSHIFT tok_decl(12,1)
12711
12712#define TOK_ADD tok_decl(13,0)
12713#define TOK_SUB tok_decl(13,1)
12714
12715#define TOK_MUL tok_decl(14,0)
12716#define TOK_DIV tok_decl(14,1)
12717#define TOK_REM tok_decl(14,2)
12718
12719/* exponent is right associativity */
12720#define TOK_EXPONENT tok_decl(15,1)
12721
12722/* For now unary operators. */
12723#define UNARYPREC 16
12724#define TOK_BNOT tok_decl(UNARYPREC,0)
12725#define TOK_NOT tok_decl(UNARYPREC,1)
12726
12727#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12728#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12729
12730#define PREC_PRE (UNARYPREC+2)
12731
12732#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12733#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12734
12735#define PREC_POST (UNARYPREC+3)
12736
12737#define TOK_POST_INC tok_decl(PREC_POST, 0)
12738#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12739
12740#define SPEC_PREC (UNARYPREC+4)
12741
12742#define TOK_NUM tok_decl(SPEC_PREC, 0)
12743#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12744
12745#define NUMPTR (*numstackptr)
12746
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012747static int
12748tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012749{
12750 operator prec = PREC(op);
12751
12752 convert_prec_is_assing(prec);
12753 return (prec == PREC(TOK_ASSIGN) ||
12754 prec == PREC_PRE || prec == PREC_POST);
12755}
12756
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012757static int
12758is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012759{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012760 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12761 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012762}
12763
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012764typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012765 arith_t val;
12766 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012767 char contidional_second_val_initialized;
12768 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012769 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012770} v_n_t;
12771
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012772typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012773 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012774 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012775} chk_var_recursive_looped_t;
12776
12777static chk_var_recursive_looped_t *prev_chk_var_recursive;
12778
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012779static int
12780arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012781{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012782 if (t->var) {
12783 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012784
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012785 if (p) {
12786 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012787
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012788 /* recursive try as expression */
12789 chk_var_recursive_looped_t *cur;
12790 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012791
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012792 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12793 if (strcmp(cur->var, t->var) == 0) {
12794 /* expression recursion loop detected */
12795 return -5;
12796 }
12797 }
12798 /* save current lookuped var name */
12799 cur = prev_chk_var_recursive;
12800 cur_save.var = t->var;
12801 cur_save.next = cur;
12802 prev_chk_var_recursive = &cur_save;
12803
12804 t->val = arith (p, &errcode);
12805 /* restore previous ptr after recursiving */
12806 prev_chk_var_recursive = cur;
12807 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012808 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012809 /* allow undefined var as 0 */
12810 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012811 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012812 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012813}
12814
12815/* "applying" a token means performing it on the top elements on the integer
12816 * stack. For a unary operator it will only change the top element, but a
12817 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012818static int
12819arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012820{
Eric Andersen90898442003-08-06 11:20:52 +000012821 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012822 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012823 int ret_arith_lookup_val;
12824
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012825 /* There is no operator that can work without arguments */
12826 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012827 numptr_m1 = NUMPTR - 1;
12828
12829 /* check operand is var with noninteger value */
12830 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012831 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012832 return ret_arith_lookup_val;
12833
12834 rez = numptr_m1->val;
12835 if (op == TOK_UMINUS)
12836 rez *= -1;
12837 else if (op == TOK_NOT)
12838 rez = !rez;
12839 else if (op == TOK_BNOT)
12840 rez = ~rez;
12841 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12842 rez++;
12843 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12844 rez--;
12845 else if (op != TOK_UPLUS) {
12846 /* Binary operators */
12847
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012848 /* check and binary operators need two arguments */
12849 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012850
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012851 /* ... and they pop one */
12852 --NUMPTR;
12853 numptr_val = rez;
12854 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012855 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012856 /* protect $((expr1 ? expr2)) without ": expr" */
12857 goto err;
12858 }
12859 rez = numptr_m1->contidional_second_val;
12860 } else if (numptr_m1->contidional_second_val_initialized) {
12861 /* protect $((expr1 : expr2)) without "expr ? " */
12862 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012863 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012864 numptr_m1 = NUMPTR - 1;
12865 if (op != TOK_ASSIGN) {
12866 /* check operand is var with noninteger value for not '=' */
12867 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12868 if (ret_arith_lookup_val)
12869 return ret_arith_lookup_val;
12870 }
12871 if (op == TOK_CONDITIONAL) {
12872 numptr_m1->contidional_second_val = rez;
12873 }
12874 rez = numptr_m1->val;
12875 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012876 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012877 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012878 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012879 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012880 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012881 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012882 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012883 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012884 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012885 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012886 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012887 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012888 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012889 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012890 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012891 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012892 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012893 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012894 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012895 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012896 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012897 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012898 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012899 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012900 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012901 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012902 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012903 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012904 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012905 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012906 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012908 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012909 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012910 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012911 /* protect $((expr : expr)) without "expr ? " */
12912 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012913 }
12914 numptr_m1->contidional_second_val_initialized = op;
12915 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012916 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012917 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012918 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012919 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012920 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012921 return -3; /* exponent less than 0 */
12922 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012923 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012924
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012925 if (numptr_val)
12926 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012927 c *= rez;
12928 rez = c;
12929 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012930 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012931 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012932 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012933 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012934 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012935 rez %= numptr_val;
12936 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012937 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012938 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012939
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012940 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012941 /* Hmm, 1=2 ? */
12942 goto err;
12943 }
12944 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012945#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012946 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012947#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012948 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012949#endif
Eric Andersen90898442003-08-06 11:20:52 +000012950 setvar(numptr_m1->var, buf, 0);
12951 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012952 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012953 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012954 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012955 rez++;
12956 }
12957 numptr_m1->val = rez;
12958 /* protect geting var value, is number now */
12959 numptr_m1->var = NULL;
12960 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012961 err:
12962 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012963}
12964
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012965/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012966static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012967 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12968 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12969 '<','<', 0, TOK_LSHIFT,
12970 '>','>', 0, TOK_RSHIFT,
12971 '|','|', 0, TOK_OR,
12972 '&','&', 0, TOK_AND,
12973 '!','=', 0, TOK_NE,
12974 '<','=', 0, TOK_LE,
12975 '>','=', 0, TOK_GE,
12976 '=','=', 0, TOK_EQ,
12977 '|','=', 0, TOK_OR_ASSIGN,
12978 '&','=', 0, TOK_AND_ASSIGN,
12979 '*','=', 0, TOK_MUL_ASSIGN,
12980 '/','=', 0, TOK_DIV_ASSIGN,
12981 '%','=', 0, TOK_REM_ASSIGN,
12982 '+','=', 0, TOK_PLUS_ASSIGN,
12983 '-','=', 0, TOK_MINUS_ASSIGN,
12984 '-','-', 0, TOK_POST_DEC,
12985 '^','=', 0, TOK_XOR_ASSIGN,
12986 '+','+', 0, TOK_POST_INC,
12987 '*','*', 0, TOK_EXPONENT,
12988 '!', 0, TOK_NOT,
12989 '<', 0, TOK_LT,
12990 '>', 0, TOK_GT,
12991 '=', 0, TOK_ASSIGN,
12992 '|', 0, TOK_BOR,
12993 '&', 0, TOK_BAND,
12994 '*', 0, TOK_MUL,
12995 '/', 0, TOK_DIV,
12996 '%', 0, TOK_REM,
12997 '+', 0, TOK_ADD,
12998 '-', 0, TOK_SUB,
12999 '^', 0, TOK_BXOR,
13000 /* uniq */
13001 '~', 0, TOK_BNOT,
13002 ',', 0, TOK_COMMA,
13003 '?', 0, TOK_CONDITIONAL,
13004 ':', 0, TOK_CONDITIONAL_SEP,
13005 ')', 0, TOK_RPAREN,
13006 '(', 0, TOK_LPAREN,
13007 0
13008};
13009/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013010#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013011
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013012static arith_t
13013arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013014{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013015 char arithval; /* Current character under analysis */
13016 operator lasttok, op;
13017 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013018 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013019 const char *p = endexpression;
13020 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013021 v_n_t *numstack, *numstackptr;
13022 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013023
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013024 /* Stack of integers */
13025 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13026 * in any given correct or incorrect expression is left as an exercise to
13027 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013028 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013029 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013030 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013031
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013032 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13033 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013034
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013035 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013036 arithval = *expr;
13037 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013038 if (p == endexpression) {
13039 /* Null expression. */
13040 return 0;
13041 }
13042
13043 /* This is only reached after all tokens have been extracted from the
13044 * input stream. If there are still tokens on the operator stack, they
13045 * are to be applied in order. At the end, there should be a final
13046 * result on the integer stack */
13047
13048 if (expr != endexpression + 1) {
13049 /* If we haven't done so already, */
13050 /* append a closing right paren */
13051 expr = endexpression;
13052 /* and let the loop process it. */
13053 continue;
13054 }
13055 /* At this point, we're done with the expression. */
13056 if (numstackptr != numstack+1) {
13057 /* ... but if there isn't, it's bad */
13058 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013059 *perrcode = -1;
13060 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013061 }
13062 if (numstack->var) {
13063 /* expression is $((var)) only, lookup now */
13064 errcode = arith_lookup_val(numstack);
13065 }
13066 ret:
13067 *perrcode = errcode;
13068 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013069 }
13070
Eric Andersen90898442003-08-06 11:20:52 +000013071 /* Continue processing the expression. */
13072 if (arith_isspace(arithval)) {
13073 /* Skip whitespace */
13074 goto prologue;
13075 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013076 p = endofname(expr);
13077 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013078 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013079
13080 numstackptr->var = alloca(var_name_size);
13081 safe_strncpy(numstackptr->var, expr, var_name_size);
13082 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013083 num:
Eric Andersen90898442003-08-06 11:20:52 +000013084 numstackptr->contidional_second_val_initialized = 0;
13085 numstackptr++;
13086 lasttok = TOK_NUM;
13087 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013088 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013089 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013090 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013091#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013092 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013093#else
13094 numstackptr->val = strtol(expr, (char **) &expr, 0);
13095#endif
Eric Andersen90898442003-08-06 11:20:52 +000013096 goto num;
13097 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013098 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013099 const char *o;
13100
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013101 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013102 /* strange operator not found */
13103 goto err;
13104 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013105 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013106 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013107 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013108 /* found */
13109 expr = o - 1;
13110 break;
13111 }
13112 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013113 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013114 p++;
13115 /* skip zero delim */
13116 p++;
13117 }
13118 op = p[1];
13119
13120 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013121 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13122 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013123
13124 /* Plus and minus are binary (not unary) _only_ if the last
13125 * token was as number, or a right paren (which pretends to be
13126 * a number, since it evaluates to one). Think about it.
13127 * It makes sense. */
13128 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013129 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013130 case TOK_ADD:
13131 op = TOK_UPLUS;
13132 break;
13133 case TOK_SUB:
13134 op = TOK_UMINUS;
13135 break;
13136 case TOK_POST_INC:
13137 op = TOK_PRE_INC;
13138 break;
13139 case TOK_POST_DEC:
13140 op = TOK_PRE_DEC;
13141 break;
Eric Andersen90898442003-08-06 11:20:52 +000013142 }
13143 }
13144 /* We don't want a unary operator to cause recursive descent on the
13145 * stack, because there can be many in a row and it could cause an
13146 * operator to be evaluated before its argument is pushed onto the
13147 * integer stack. */
13148 /* But for binary operators, "apply" everything on the operator
13149 * stack until we find an operator with a lesser priority than the
13150 * one we have just extracted. */
13151 /* Left paren is given the lowest priority so it will never be
13152 * "applied" in this way.
13153 * if associativity is right and priority eq, applied also skip
13154 */
13155 prec = PREC(op);
13156 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13157 /* not left paren or unary */
13158 if (lasttok != TOK_NUM) {
13159 /* binary op must be preceded by a num */
13160 goto err;
13161 }
13162 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013163 if (op == TOK_RPAREN) {
13164 /* The algorithm employed here is simple: while we don't
13165 * hit an open paren nor the bottom of the stack, pop
13166 * tokens and apply them */
13167 if (stackptr[-1] == TOK_LPAREN) {
13168 --stackptr;
13169 /* Any operator directly after a */
13170 lasttok = TOK_NUM;
13171 /* close paren should consider itself binary */
13172 goto prologue;
13173 }
13174 } else {
13175 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013176
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013177 convert_prec_is_assing(prec);
13178 convert_prec_is_assing(prev_prec);
13179 if (prev_prec < prec)
13180 break;
13181 /* check right assoc */
13182 if (prev_prec == prec && is_right_associativity(prec))
13183 break;
13184 }
13185 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13186 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013187 }
13188 if (op == TOK_RPAREN) {
13189 goto err;
13190 }
13191 }
13192
13193 /* Push this operator to the stack and remember it. */
13194 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013195 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013196 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013197 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013198}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013199#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013200
13201
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013202/* ============ main() and helpers */
13203
13204/*
13205 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013206 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013207static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013208static void
13209exitshell(void)
13210{
13211 struct jmploc loc;
13212 char *p;
13213 int status;
13214
13215 status = exitstatus;
13216 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13217 if (setjmp(loc.loc)) {
13218 if (exception == EXEXIT)
13219/* dash bug: it just does _exit(exitstatus) here
13220 * but we have to do setjobctl(0) first!
13221 * (bug is still not fixed in dash-0.5.3 - if you run dash
13222 * under Midnight Commander, on exit from dash MC is backgrounded) */
13223 status = exitstatus;
13224 goto out;
13225 }
13226 exception_handler = &loc;
13227 p = trap[0];
13228 if (p) {
13229 trap[0] = NULL;
13230 evalstring(p, 0);
13231 }
13232 flush_stdout_stderr();
13233 out:
13234 setjobctl(0);
13235 _exit(status);
13236 /* NOTREACHED */
13237}
13238
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013239static void
13240init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013241{
13242 /* from input.c: */
13243 basepf.nextc = basepf.buf = basebuf;
13244
13245 /* from trap.c: */
13246 signal(SIGCHLD, SIG_DFL);
13247
13248 /* from var.c: */
13249 {
13250 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013251 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013252 const char *p;
13253 struct stat st1, st2;
13254
13255 initvar();
13256 for (envp = environ; envp && *envp; envp++) {
13257 if (strchr(*envp, '=')) {
13258 setvareq(*envp, VEXPORT|VTEXTFIXED);
13259 }
13260 }
13261
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013262 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013263 setvar("PPID", ppid, 0);
13264
13265 p = lookupvar("PWD");
13266 if (p)
13267 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13268 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13269 p = '\0';
13270 setpwd(p, 0);
13271 }
13272}
13273
13274/*
13275 * Process the shell command line arguments.
13276 */
13277static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013278procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013279{
13280 int i;
13281 const char *xminusc;
13282 char **xargv;
13283
13284 xargv = argv;
13285 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013286 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013287 xargv++;
13288 for (i = 0; i < NOPTS; i++)
13289 optlist[i] = 2;
13290 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013291 if (options(1)) {
13292 /* it already printed err message */
13293 raise_exception(EXERROR);
13294 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013295 xargv = argptr;
13296 xminusc = minusc;
13297 if (*xargv == NULL) {
13298 if (xminusc)
13299 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13300 sflag = 1;
13301 }
13302 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13303 iflag = 1;
13304 if (mflag == 2)
13305 mflag = iflag;
13306 for (i = 0; i < NOPTS; i++)
13307 if (optlist[i] == 2)
13308 optlist[i] = 0;
13309#if DEBUG == 2
13310 debug = 1;
13311#endif
13312 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13313 if (xminusc) {
13314 minusc = *xargv++;
13315 if (*xargv)
13316 goto setarg0;
13317 } else if (!sflag) {
13318 setinputfile(*xargv, 0);
13319 setarg0:
13320 arg0 = *xargv++;
13321 commandname = arg0;
13322 }
13323
13324 shellparam.p = xargv;
13325#if ENABLE_ASH_GETOPTS
13326 shellparam.optind = 1;
13327 shellparam.optoff = -1;
13328#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013329 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013330 while (*xargv) {
13331 shellparam.nparam++;
13332 xargv++;
13333 }
13334 optschanged();
13335}
13336
13337/*
13338 * Read /etc/profile or .profile.
13339 */
13340static void
13341read_profile(const char *name)
13342{
13343 int skip;
13344
13345 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13346 return;
13347 skip = cmdloop(0);
13348 popfile();
13349 if (skip)
13350 exitshell();
13351}
13352
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013353/*
13354 * This routine is called when an error or an interrupt occurs in an
13355 * interactive shell and control is returned to the main command loop.
13356 */
13357static void
13358reset(void)
13359{
13360 /* from eval.c: */
13361 evalskip = 0;
13362 loopnest = 0;
13363 /* from input.c: */
13364 parselleft = parsenleft = 0; /* clear input buffer */
13365 popallfiles();
13366 /* from parser.c: */
13367 tokpushback = 0;
13368 checkkwd = 0;
13369 /* from redir.c: */
13370 clearredir(0);
13371}
13372
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013373#if PROFILE
13374static short profile_buf[16384];
13375extern int etext();
13376#endif
13377
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013378/*
13379 * Main routine. We initialize things, parse the arguments, execute
13380 * profiles if we're a login shell, and then call cmdloop to execute
13381 * commands. The setjmp call sets up the location to jump to when an
13382 * exception occurs. When an exception occurs the variable "state"
13383 * is used to figure out how far we had gotten.
13384 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013385int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013386int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013387{
13388 char *shinit;
13389 volatile int state;
13390 struct jmploc jmploc;
13391 struct stackmark smark;
13392
Denis Vlasenko01631112007-12-16 17:20:38 +000013393 /* Initialize global data */
13394 INIT_G_misc();
13395 INIT_G_memstack();
13396 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013397#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013398 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013399#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013400 INIT_G_cmdtable();
13401
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013402#if PROFILE
13403 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13404#endif
13405
13406#if ENABLE_FEATURE_EDITING
13407 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13408#endif
13409 state = 0;
13410 if (setjmp(jmploc.loc)) {
13411 int e;
13412 int s;
13413
13414 reset();
13415
13416 e = exception;
13417 if (e == EXERROR)
13418 exitstatus = 2;
13419 s = state;
13420 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13421 exitshell();
13422
13423 if (e == EXINT) {
13424 outcslow('\n', stderr);
13425 }
13426 popstackmark(&smark);
13427 FORCE_INT_ON; /* enable interrupts */
13428 if (s == 1)
13429 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013430 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013431 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013432 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013433 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013434 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013435 }
13436 exception_handler = &jmploc;
13437#if DEBUG
13438 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013439 trace_puts("Shell args: ");
13440 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013441#endif
13442 rootpid = getpid();
13443
13444#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013445 /* Can use monotonic_ns() for better randomness but for now it is
13446 * not used anywhere else in busybox... so avoid bloat */
13447 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013448#endif
13449 init();
13450 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013451 procargs(argv);
13452
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013453#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13454 if (iflag) {
13455 const char *hp = lookupvar("HISTFILE");
13456
13457 if (hp == NULL) {
13458 hp = lookupvar("HOME");
13459 if (hp != NULL) {
13460 char *defhp = concat_path_file(hp, ".ash_history");
13461 setvar("HISTFILE", defhp, 0);
13462 free(defhp);
13463 }
13464 }
13465 }
13466#endif
13467 if (argv[0] && argv[0][0] == '-')
13468 isloginsh = 1;
13469 if (isloginsh) {
13470 state = 1;
13471 read_profile("/etc/profile");
13472 state1:
13473 state = 2;
13474 read_profile(".profile");
13475 }
13476 state2:
13477 state = 3;
13478 if (
13479#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013480 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013481#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013482 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013483 ) {
13484 shinit = lookupvar("ENV");
13485 if (shinit != NULL && *shinit != '\0') {
13486 read_profile(shinit);
13487 }
13488 }
13489 state3:
13490 state = 4;
13491 if (minusc)
13492 evalstring(minusc, 0);
13493
13494 if (sflag || minusc == NULL) {
13495#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013496 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013497 const char *hp = lookupvar("HISTFILE");
13498
13499 if (hp != NULL)
13500 line_input_state->hist_file = hp;
13501 }
13502#endif
13503 state4: /* XXX ??? - why isn't this before the "if" statement */
13504 cmdloop(1);
13505 }
13506#if PROFILE
13507 monitor(0);
13508#endif
13509#ifdef GPROF
13510 {
13511 extern void _mcleanup(void);
13512 _mcleanup();
13513 }
13514#endif
13515 exitshell();
13516 /* NOTREACHED */
13517}
13518
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013519#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013520const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013521int main(int argc, char **argv)
13522{
13523 return ash_main(argc, argv);
13524}
13525#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013526
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013527
Eric Andersendf82f612001-06-28 07:46:40 +000013528/*-
13529 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013530 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013531 *
13532 * This code is derived from software contributed to Berkeley by
13533 * Kenneth Almquist.
13534 *
13535 * Redistribution and use in source and binary forms, with or without
13536 * modification, are permitted provided that the following conditions
13537 * are met:
13538 * 1. Redistributions of source code must retain the above copyright
13539 * notice, this list of conditions and the following disclaimer.
13540 * 2. Redistributions in binary form must reproduce the above copyright
13541 * notice, this list of conditions and the following disclaimer in the
13542 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013543 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013544 * may be used to endorse or promote products derived from this software
13545 * without specific prior written permission.
13546 *
13547 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13548 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13549 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13550 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13551 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13552 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13553 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13554 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13555 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13556 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13557 * SUCH DAMAGE.
13558 */