blob: 9eff1b38e4e2230576a3c66ca95a1b258bec94c4 [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 *
Eric Andersencb57d552001-06-28 07:25:16 +000011 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000014 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000015 *
Eric Andersen81fe1232003-07-29 06:38:40 +000016 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Eric Andersen90898442003-08-06 11:20:52 +000020 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
Eric Andersenef02f822004-03-11 13:34:24 +000023 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000025 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000026 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000027 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
Eric Andersen90898442003-08-06 11:20:52 +000030 */
31
Eric Andersen90898442003-08-06 11:20:52 +000032/*
Eric Andersenc470f442003-07-28 09:56:35 +000033 * The follow should be set to reflect the type of system you have:
34 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V.
36 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
37 * define DEBUG=2 to compile in and turn on debugging.
38 *
39 * When debugging is on, debugging info will be written to ./trace and
40 * a quit signal will generate a core dump.
41 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000042#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000043#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000044
45#define IFS_BROKEN
46
47#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048
Denis Vlasenkob012b102007-02-19 22:43:01 +000049#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000050#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#define _GNU_SOURCE
52#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000053#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000054
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000055#include "busybox.h" /* for applet_names */
Denis Vlasenko61befda2008-11-25 01:36:03 +000056//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
57//#include "applet_tables.h" doesn't work
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko61befda2008-11-25 01:36:03 +000061
62#if defined SINGLE_APPLET_MAIN
63/* STANDALONE does not make sense, and won't compile */
64#undef CONFIG_FEATURE_SH_STANDALONE
65#undef ENABLE_FEATURE_SH_STANDALONE
66#undef USE_FEATURE_SH_STANDALONE
67#undef SKIP_FEATURE_SH_STANDALONE(...)
68#define ENABLE_FEATURE_SH_STANDALONE 0
69#define USE_FEATURE_SH_STANDALONE(...)
70#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000071#endif
72
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000073#ifndef PIPE_BUF
74#define PIPE_BUF 4096 /* amount of buffering in a pipe */
75#endif
76
Denis Vlasenkob012b102007-02-19 22:43:01 +000077#if defined(__uClinux__)
78#error "Do not even bother, ash will not run on uClinux"
79#endif
80
Denis Vlasenkob012b102007-02-19 22:43:01 +000081
Denis Vlasenko01631112007-12-16 17:20:38 +000082/* ============ Hash table sizes. Configurable. */
83
84#define VTABSIZE 39
85#define ATABSIZE 39
86#define CMDTABLESIZE 31 /* should be prime */
87
88
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000089/* ============ Misc helpers */
90
91#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
92
93/* C99 say: "char" declaration may be signed or unsigned default */
94#define signed_char2int(sc) ((int)((signed char)sc))
95
96
Denis Vlasenkob012b102007-02-19 22:43:01 +000097/* ============ Shell options */
98
99static const char *const optletters_optnames[] = {
100 "e" "errexit",
101 "f" "noglob",
102 "I" "ignoreeof",
103 "i" "interactive",
104 "m" "monitor",
105 "n" "noexec",
106 "s" "stdin",
107 "x" "xtrace",
108 "v" "verbose",
109 "C" "noclobber",
110 "a" "allexport",
111 "b" "notify",
112 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000115 ,"\0" "nolog"
116 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000117#endif
118};
119
120#define optletters(n) optletters_optnames[(n)][0]
121#define optnames(n) (&optletters_optnames[(n)][1])
122
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000123enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000124
Eric Andersenc470f442003-07-28 09:56:35 +0000125
Denis Vlasenkob012b102007-02-19 22:43:01 +0000126/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000127
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000128static const char homestr[] ALIGN1 = "HOME";
129static const char snlfmt[] ALIGN1 = "%s\n";
130static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000131
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000132/*
Eric Andersenc470f442003-07-28 09:56:35 +0000133 * We enclose jmp_buf in a structure so that we can declare pointers to
134 * jump locations. The global variable handler contains the location to
135 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000136 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000137 * exception handlers, the user should save the value of handler on entry
138 * to an inner scope, set handler to point to a jmploc structure for the
139 * inner scope, and restore handler on exit from the scope.
140 */
Eric Andersenc470f442003-07-28 09:56:35 +0000141struct jmploc {
142 jmp_buf loc;
143};
Denis Vlasenko01631112007-12-16 17:20:38 +0000144
145struct globals_misc {
146 /* pid of main shell */
147 int rootpid;
148 /* shell level: 0 for the main shell, 1 for its children, and so on */
149 int shlvl;
150#define rootshell (!shlvl)
151 char *minusc; /* argument to -c option */
152
153 char *curdir; // = nullstr; /* current working directory */
154 char *physdir; // = nullstr; /* physical working directory */
155
156 char *arg0; /* value of $0 */
157
158 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000159
160// disabled by vda: cannot understand how it was supposed to work -
161// cannot fix bugs. That's why you have to explain your non-trivial designs!
162// /* do we generate EXSIG events */
163// int exsig; /* counter */
164 volatile int suppressint; /* counter */
165 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
166 /* last pending signal */
167 volatile /*sig_atomic_t*/ smallint pendingsig;
168 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000169 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000170#define EXINT 0 /* SIGINT received */
171#define EXERROR 1 /* a generic error */
172#define EXSHELLPROC 2 /* execute a shell procedure */
173#define EXEXEC 3 /* command execution failed */
174#define EXEXIT 4 /* exit the shell */
175#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000176
Denis Vlasenko01631112007-12-16 17:20:38 +0000177 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000178 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000179
180 char optlist[NOPTS];
181#define eflag optlist[0]
182#define fflag optlist[1]
183#define Iflag optlist[2]
184#define iflag optlist[3]
185#define mflag optlist[4]
186#define nflag optlist[5]
187#define sflag optlist[6]
188#define xflag optlist[7]
189#define vflag optlist[8]
190#define Cflag optlist[9]
191#define aflag optlist[10]
192#define bflag optlist[11]
193#define uflag optlist[12]
194#define viflag optlist[13]
195#if DEBUG
196#define nolog optlist[14]
197#define debug optlist[15]
198#endif
199
200 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /*
202 * Sigmode records the current value of the signal handlers for the various
203 * modes. A value of zero means that the current handler is not known.
204 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
205 */
206 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000207#define S_DFL 1 /* default signal handling (SIG_DFL) */
208#define S_CATCH 2 /* signal is caught */
209#define S_IGN 3 /* signal is ignored (SIG_IGN) */
210#define S_HARD_IGN 4 /* signal is ignored permenantly */
211#define S_RESET 5 /* temporary - to reset a hard ignored sig */
212
Denis Vlasenko01631112007-12-16 17:20:38 +0000213 /* indicates specified signal received */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +0000214 char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000215 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000216
217 /* Rarely referenced stuff */
218#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000219 /* Random number generators */
220 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
221 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000222#endif
223 pid_t backgndpid; /* pid of last background process */
224 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000225};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000226extern struct globals_misc *const ash_ptr_to_globals_misc;
227#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000228#define rootpid (G_misc.rootpid )
229#define shlvl (G_misc.shlvl )
230#define minusc (G_misc.minusc )
231#define curdir (G_misc.curdir )
232#define physdir (G_misc.physdir )
233#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000234#define exception_handler (G_misc.exception_handler)
235#define exception (G_misc.exception )
236#define suppressint (G_misc.suppressint )
237#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000238//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000239#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000240#define isloginsh (G_misc.isloginsh )
241#define nullstr (G_misc.nullstr )
242#define optlist (G_misc.optlist )
243#define sigmode (G_misc.sigmode )
244#define gotsig (G_misc.gotsig )
245#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000246#define random_galois_LFSR (G_misc.random_galois_LFSR)
247#define random_LCG (G_misc.random_LCG )
248#define backgndpid (G_misc.backgndpid )
249#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000250#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000251 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
252 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000253 curdir = nullstr; \
254 physdir = nullstr; \
255} while (0)
256
257
Denis Vlasenko559691a2008-10-05 18:39:31 +0000258/* ============ Utility functions */
259static int isdigit_str9(const char *str)
260{
261 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
262 while (--maxlen && isdigit(*str))
263 str++;
264 return (*str == '\0');
265}
Denis Vlasenko01631112007-12-16 17:20:38 +0000266
Denis Vlasenko559691a2008-10-05 18:39:31 +0000267
268/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000269/*
Eric Andersen2870d962001-07-02 17:27:21 +0000270 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000271 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000272 * much more efficient and portable. (But hacking the kernel is so much
273 * more fun than worrying about efficiency and portability. :-))
274 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000275#define INT_OFF do { \
276 suppressint++; \
277 xbarrier(); \
278} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000279
280/*
281 * Called to raise an exception. Since C doesn't include exceptions, we
282 * just do a longjmp to the exception handler. The type of exception is
283 * stored in the global variable "exception".
284 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000285static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286static void
287raise_exception(int e)
288{
289#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000290 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000291 abort();
292#endif
293 INT_OFF;
294 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000295 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000296}
297
298/*
299 * Called from trap.c when a SIGINT is received. (If the user specifies
300 * that SIGINT is to be trapped or ignored using the trap builtin, then
301 * this routine is not called.) Suppressint is nonzero when interrupts
302 * are held using the INT_OFF macro. (The test for iflag is just
303 * defensive programming.)
304 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000305static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000306static void
307raise_interrupt(void)
308{
309 int i;
310
311 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000312 /* Signal is not automatically unmasked after it is raised,
313 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000314 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000315 /* pendingsig = 0; - now done in onsig() */
316
Denis Vlasenkob012b102007-02-19 22:43:01 +0000317 i = EXSIG;
318 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
319 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000320 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000321 signal(SIGINT, SIG_DFL);
322 raise(SIGINT);
323 }
324 i = EXINT;
325 }
326 raise_exception(i);
327 /* NOTREACHED */
328}
329
330#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000331static void
332int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000333{
334 if (--suppressint == 0 && intpending) {
335 raise_interrupt();
336 }
337}
338#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000339static void
340force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000341{
342 suppressint = 0;
343 if (intpending)
344 raise_interrupt();
345}
346#define FORCE_INT_ON force_int_on()
347#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000348#define INT_ON do { \
349 xbarrier(); \
350 if (--suppressint == 0 && intpending) \
351 raise_interrupt(); \
352} while (0)
353#define FORCE_INT_ON do { \
354 xbarrier(); \
355 suppressint = 0; \
356 if (intpending) \
357 raise_interrupt(); \
358} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000359#endif /* ASH_OPTIMIZE_FOR_SIZE */
360
361#define SAVE_INT(v) ((v) = suppressint)
362
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000363#define RESTORE_INT(v) do { \
364 xbarrier(); \
365 suppressint = (v); \
366 if (suppressint == 0 && intpending) \
367 raise_interrupt(); \
368} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000369
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000370/*
371 * Ignore a signal. Only one usage site - in forkchild()
372 */
373static void
374ignoresig(int signo)
375{
376 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
377 signal(signo, SIG_IGN);
378 }
379 sigmode[signo - 1] = S_HARD_IGN;
380}
381
382/*
383 * Signal handler. Only one usage site - in setsignal()
384 */
385static void
386onsig(int signo)
387{
388 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000389 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000390
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000391 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000392 if (!suppressint) {
393 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000394 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000395 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000396 intpending = 1;
397 }
398}
399
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000400
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000401/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000402
Eric Andersenc470f442003-07-28 09:56:35 +0000403static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000404outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000405{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000406 INT_OFF;
407 fputs(p, file);
408 INT_ON;
409}
410
411static void
412flush_stdout_stderr(void)
413{
414 INT_OFF;
415 fflush(stdout);
416 fflush(stderr);
417 INT_ON;
418}
419
420static void
421flush_stderr(void)
422{
423 INT_OFF;
424 fflush(stderr);
425 INT_ON;
426}
427
428static void
429outcslow(int c, FILE *dest)
430{
431 INT_OFF;
432 putc(c, dest);
433 fflush(dest);
434 INT_ON;
435}
436
437static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
438static int
439out1fmt(const char *fmt, ...)
440{
441 va_list ap;
442 int r;
443
444 INT_OFF;
445 va_start(ap, fmt);
446 r = vprintf(fmt, ap);
447 va_end(ap);
448 INT_ON;
449 return r;
450}
451
452static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
453static int
454fmtstr(char *outbuf, size_t length, const char *fmt, ...)
455{
456 va_list ap;
457 int ret;
458
459 va_start(ap, fmt);
460 INT_OFF;
461 ret = vsnprintf(outbuf, length, fmt, ap);
462 va_end(ap);
463 INT_ON;
464 return ret;
465}
466
467static void
468out1str(const char *p)
469{
470 outstr(p, stdout);
471}
472
473static void
474out2str(const char *p)
475{
476 outstr(p, stderr);
477 flush_stderr();
478}
479
480
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000481/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000482
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000483/* control characters in argument strings */
484#define CTLESC '\201' /* escape next character */
485#define CTLVAR '\202' /* variable defn */
486#define CTLENDVAR '\203'
487#define CTLBACKQ '\204'
488#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
489/* CTLBACKQ | CTLQUOTE == '\205' */
490#define CTLARI '\206' /* arithmetic expression */
491#define CTLENDARI '\207'
492#define CTLQUOTEMARK '\210'
493
494/* variable substitution byte (follows CTLVAR) */
495#define VSTYPE 0x0f /* type of variable substitution */
496#define VSNUL 0x10 /* colon--treat the empty string as unset */
497#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
498
499/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000500#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
501#define VSMINUS 0x2 /* ${var-text} */
502#define VSPLUS 0x3 /* ${var+text} */
503#define VSQUESTION 0x4 /* ${var?message} */
504#define VSASSIGN 0x5 /* ${var=text} */
505#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
506#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
507#define VSTRIMLEFT 0x8 /* ${var#pattern} */
508#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
509#define VSLENGTH 0xa /* ${#var} */
510#if ENABLE_ASH_BASH_COMPAT
511#define VSSUBSTR 0xc /* ${var:position:length} */
512#define VSREPLACE 0xd /* ${var/pattern/replacement} */
513#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
514#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000515
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000516static const char dolatstr[] ALIGN1 = {
517 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
518};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000519
Denis Vlasenko559691a2008-10-05 18:39:31 +0000520#define NCMD 0
521#define NPIPE 1
522#define NREDIR 2
523#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000524#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000525#define NAND 5
526#define NOR 6
527#define NSEMI 7
528#define NIF 8
529#define NWHILE 9
530#define NUNTIL 10
531#define NFOR 11
532#define NCASE 12
533#define NCLIST 13
534#define NDEFUN 14
535#define NARG 15
536#define NTO 16
537#if ENABLE_ASH_BASH_COMPAT
538#define NTO2 17
539#endif
540#define NCLOBBER 18
541#define NFROM 19
542#define NFROMTO 20
543#define NAPPEND 21
544#define NTOFD 22
545#define NFROMFD 23
546#define NHERE 24
547#define NXHERE 25
548#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000549#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000550
551union node;
552
553struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000554 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000555 union node *assign;
556 union node *args;
557 union node *redirect;
558};
559
560struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000561 smallint type;
562 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000563 struct nodelist *cmdlist;
564};
565
566struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000567 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000568 union node *n;
569 union node *redirect;
570};
571
572struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000573 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000574 union node *ch1;
575 union node *ch2;
576};
577
578struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000579 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000580 union node *test;
581 union node *ifpart;
582 union node *elsepart;
583};
584
585struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000586 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000587 union node *args;
588 union node *body;
589 char *var;
590};
591
592struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000593 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000594 union node *expr;
595 union node *cases;
596};
597
598struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000599 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000600 union node *next;
601 union node *pattern;
602 union node *body;
603};
604
605struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000606 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607 union node *next;
608 char *text;
609 struct nodelist *backquote;
610};
611
Denis Vlasenko559691a2008-10-05 18:39:31 +0000612/* nfile and ndup layout must match!
613 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
614 * that it is actually NTO2 (>&file), and change its type.
615 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000616struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000617 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618 union node *next;
619 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621 union node *fname;
622 char *expfname;
623};
624
625struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000626 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627 union node *next;
628 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000629 int dupfd;
630 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000631 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000632};
633
634struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000635 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000636 union node *next;
637 int fd;
638 union node *doc;
639};
640
641struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000642 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000643 union node *com;
644};
645
646union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000647 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000648 struct ncmd ncmd;
649 struct npipe npipe;
650 struct nredir nredir;
651 struct nbinary nbinary;
652 struct nif nif;
653 struct nfor nfor;
654 struct ncase ncase;
655 struct nclist nclist;
656 struct narg narg;
657 struct nfile nfile;
658 struct ndup ndup;
659 struct nhere nhere;
660 struct nnot nnot;
661};
662
663struct nodelist {
664 struct nodelist *next;
665 union node *n;
666};
667
668struct funcnode {
669 int count;
670 union node n;
671};
672
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000673/*
674 * Free a parse tree.
675 */
676static void
677freefunc(struct funcnode *f)
678{
679 if (f && --f->count < 0)
680 free(f);
681}
682
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000683
684/* ============ Debugging output */
685
686#if DEBUG
687
688static FILE *tracefile;
689
690static void
691trace_printf(const char *fmt, ...)
692{
693 va_list va;
694
695 if (debug != 1)
696 return;
697 va_start(va, fmt);
698 vfprintf(tracefile, fmt, va);
699 va_end(va);
700}
701
702static void
703trace_vprintf(const char *fmt, va_list va)
704{
705 if (debug != 1)
706 return;
707 vfprintf(tracefile, fmt, va);
708}
709
710static void
711trace_puts(const char *s)
712{
713 if (debug != 1)
714 return;
715 fputs(s, tracefile);
716}
717
718static void
719trace_puts_quoted(char *s)
720{
721 char *p;
722 char c;
723
724 if (debug != 1)
725 return;
726 putc('"', tracefile);
727 for (p = s; *p; p++) {
728 switch (*p) {
729 case '\n': c = 'n'; goto backslash;
730 case '\t': c = 't'; goto backslash;
731 case '\r': c = 'r'; goto backslash;
732 case '"': c = '"'; goto backslash;
733 case '\\': c = '\\'; goto backslash;
734 case CTLESC: c = 'e'; goto backslash;
735 case CTLVAR: c = 'v'; goto backslash;
736 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
737 case CTLBACKQ: c = 'q'; goto backslash;
738 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
739 backslash:
740 putc('\\', tracefile);
741 putc(c, tracefile);
742 break;
743 default:
744 if (*p >= ' ' && *p <= '~')
745 putc(*p, tracefile);
746 else {
747 putc('\\', tracefile);
748 putc(*p >> 6 & 03, tracefile);
749 putc(*p >> 3 & 07, tracefile);
750 putc(*p & 07, tracefile);
751 }
752 break;
753 }
754 }
755 putc('"', tracefile);
756}
757
758static void
759trace_puts_args(char **ap)
760{
761 if (debug != 1)
762 return;
763 if (!*ap)
764 return;
765 while (1) {
766 trace_puts_quoted(*ap);
767 if (!*++ap) {
768 putc('\n', tracefile);
769 break;
770 }
771 putc(' ', tracefile);
772 }
773}
774
775static void
776opentrace(void)
777{
778 char s[100];
779#ifdef O_APPEND
780 int flags;
781#endif
782
783 if (debug != 1) {
784 if (tracefile)
785 fflush(tracefile);
786 /* leave open because libedit might be using it */
787 return;
788 }
789 strcpy(s, "./trace");
790 if (tracefile) {
791 if (!freopen(s, "a", tracefile)) {
792 fprintf(stderr, "Can't re-open %s\n", s);
793 debug = 0;
794 return;
795 }
796 } else {
797 tracefile = fopen(s, "a");
798 if (tracefile == NULL) {
799 fprintf(stderr, "Can't open %s\n", s);
800 debug = 0;
801 return;
802 }
803 }
804#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000805 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000806 if (flags >= 0)
807 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
808#endif
809 setlinebuf(tracefile);
810 fputs("\nTracing started.\n", tracefile);
811}
812
813static void
814indent(int amount, char *pfx, FILE *fp)
815{
816 int i;
817
818 for (i = 0; i < amount; i++) {
819 if (pfx && i == amount - 1)
820 fputs(pfx, fp);
821 putc('\t', fp);
822 }
823}
824
825/* little circular references here... */
826static void shtree(union node *n, int ind, char *pfx, FILE *fp);
827
828static void
829sharg(union node *arg, FILE *fp)
830{
831 char *p;
832 struct nodelist *bqlist;
833 int subtype;
834
835 if (arg->type != NARG) {
836 out1fmt("<node type %d>\n", arg->type);
837 abort();
838 }
839 bqlist = arg->narg.backquote;
840 for (p = arg->narg.text; *p; p++) {
841 switch (*p) {
842 case CTLESC:
843 putc(*++p, fp);
844 break;
845 case CTLVAR:
846 putc('$', fp);
847 putc('{', fp);
848 subtype = *++p;
849 if (subtype == VSLENGTH)
850 putc('#', fp);
851
852 while (*p != '=')
853 putc(*p++, fp);
854
855 if (subtype & VSNUL)
856 putc(':', fp);
857
858 switch (subtype & VSTYPE) {
859 case VSNORMAL:
860 putc('}', fp);
861 break;
862 case VSMINUS:
863 putc('-', fp);
864 break;
865 case VSPLUS:
866 putc('+', fp);
867 break;
868 case VSQUESTION:
869 putc('?', fp);
870 break;
871 case VSASSIGN:
872 putc('=', fp);
873 break;
874 case VSTRIMLEFT:
875 putc('#', fp);
876 break;
877 case VSTRIMLEFTMAX:
878 putc('#', fp);
879 putc('#', fp);
880 break;
881 case VSTRIMRIGHT:
882 putc('%', fp);
883 break;
884 case VSTRIMRIGHTMAX:
885 putc('%', fp);
886 putc('%', fp);
887 break;
888 case VSLENGTH:
889 break;
890 default:
891 out1fmt("<subtype %d>", subtype);
892 }
893 break;
894 case CTLENDVAR:
895 putc('}', fp);
896 break;
897 case CTLBACKQ:
898 case CTLBACKQ|CTLQUOTE:
899 putc('$', fp);
900 putc('(', fp);
901 shtree(bqlist->n, -1, NULL, fp);
902 putc(')', fp);
903 break;
904 default:
905 putc(*p, fp);
906 break;
907 }
908 }
909}
910
911static void
912shcmd(union node *cmd, FILE *fp)
913{
914 union node *np;
915 int first;
916 const char *s;
917 int dftfd;
918
919 first = 1;
920 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000921 if (!first)
922 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000923 sharg(np, fp);
924 first = 0;
925 }
926 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000927 if (!first)
928 putc(' ', fp);
929 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000930 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000931 case NTO: s = ">>"+1; dftfd = 1; break;
932 case NCLOBBER: s = ">|"; dftfd = 1; break;
933 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000934#if ENABLE_ASH_BASH_COMPAT
935 case NTO2:
936#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000937 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000938 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000939 case NFROMFD: s = "<&"; break;
940 case NFROMTO: s = "<>"; break;
941 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000942 }
943 if (np->nfile.fd != dftfd)
944 fprintf(fp, "%d", np->nfile.fd);
945 fputs(s, fp);
946 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
947 fprintf(fp, "%d", np->ndup.dupfd);
948 } else {
949 sharg(np->nfile.fname, fp);
950 }
951 first = 0;
952 }
953}
954
955static void
956shtree(union node *n, int ind, char *pfx, FILE *fp)
957{
958 struct nodelist *lp;
959 const char *s;
960
961 if (n == NULL)
962 return;
963
964 indent(ind, pfx, fp);
965 switch (n->type) {
966 case NSEMI:
967 s = "; ";
968 goto binop;
969 case NAND:
970 s = " && ";
971 goto binop;
972 case NOR:
973 s = " || ";
974 binop:
975 shtree(n->nbinary.ch1, ind, NULL, fp);
976 /* if (ind < 0) */
977 fputs(s, fp);
978 shtree(n->nbinary.ch2, ind, NULL, fp);
979 break;
980 case NCMD:
981 shcmd(n, fp);
982 if (ind >= 0)
983 putc('\n', fp);
984 break;
985 case NPIPE:
986 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
987 shcmd(lp->n, fp);
988 if (lp->next)
989 fputs(" | ", fp);
990 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000991 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000992 fputs(" &", fp);
993 if (ind >= 0)
994 putc('\n', fp);
995 break;
996 default:
997 fprintf(fp, "<node type %d>", n->type);
998 if (ind >= 0)
999 putc('\n', fp);
1000 break;
1001 }
1002}
1003
1004static void
1005showtree(union node *n)
1006{
1007 trace_puts("showtree called\n");
1008 shtree(n, 1, NULL, stdout);
1009}
1010
1011#define TRACE(param) trace_printf param
1012#define TRACEV(param) trace_vprintf param
1013
1014#else
1015
1016#define TRACE(param)
1017#define TRACEV(param)
1018
1019#endif /* DEBUG */
1020
1021
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001022/* ============ Parser data */
1023
1024/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001025 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1026 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001027struct strlist {
1028 struct strlist *next;
1029 char *text;
1030};
1031
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001032struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001033
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034struct strpush {
1035 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001036 char *prev_string;
1037 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001038#if ENABLE_ASH_ALIAS
1039 struct alias *ap; /* if push was associated with an alias */
1040#endif
1041 char *string; /* remember the string since it may change */
1042};
1043
1044struct parsefile {
1045 struct parsefile *prev; /* preceding file on stack */
1046 int linno; /* current line */
1047 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001048 int left_in_line; /* number of chars left in this line */
1049 int left_in_buffer; /* number of chars left in this buffer past the line */
1050 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001051 char *buf; /* input buffer */
1052 struct strpush *strpush; /* for pushing strings at this level */
1053 struct strpush basestrpush; /* so pushing one is fast */
1054};
1055
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001056static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001057static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001058static int startlinno; /* line # where last token started */
1059static char *commandname; /* currently executing command */
1060static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001061static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001062
1063
1064/* ============ Message printing */
1065
1066static void
1067ash_vmsg(const char *msg, va_list ap)
1068{
1069 fprintf(stderr, "%s: ", arg0);
1070 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001071 if (strcmp(arg0, commandname))
1072 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001073 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001074 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001075 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001076 vfprintf(stderr, msg, ap);
1077 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001078}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001079
1080/*
1081 * Exverror is called to raise the error exception. If the second argument
1082 * is not NULL then error prints an error message using printf style
1083 * formatting. It then raises the error exception.
1084 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001085static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001086static void
1087ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001088{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001089#if DEBUG
1090 if (msg) {
1091 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1092 TRACEV((msg, ap));
1093 TRACE(("\") pid=%d\n", getpid()));
1094 } else
1095 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1096 if (msg)
1097#endif
1098 ash_vmsg(msg, ap);
1099
1100 flush_stdout_stderr();
1101 raise_exception(cond);
1102 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001103}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001104
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001105static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001106static void
1107ash_msg_and_raise_error(const char *msg, ...)
1108{
1109 va_list ap;
1110
1111 va_start(ap, msg);
1112 ash_vmsg_and_raise(EXERROR, msg, ap);
1113 /* NOTREACHED */
1114 va_end(ap);
1115}
1116
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001117static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001118static void
1119ash_msg_and_raise(int cond, const char *msg, ...)
1120{
1121 va_list ap;
1122
1123 va_start(ap, msg);
1124 ash_vmsg_and_raise(cond, msg, ap);
1125 /* NOTREACHED */
1126 va_end(ap);
1127}
1128
1129/*
1130 * error/warning routines for external builtins
1131 */
1132static void
1133ash_msg(const char *fmt, ...)
1134{
1135 va_list ap;
1136
1137 va_start(ap, fmt);
1138 ash_vmsg(fmt, ap);
1139 va_end(ap);
1140}
1141
1142/*
1143 * Return a string describing an error. The returned string may be a
1144 * pointer to a static buffer that will be overwritten on the next call.
1145 * Action describes the operation that got the error.
1146 */
1147static const char *
1148errmsg(int e, const char *em)
1149{
1150 if (e == ENOENT || e == ENOTDIR) {
1151 return em;
1152 }
1153 return strerror(e);
1154}
1155
1156
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001157/* ============ Memory allocation */
1158
1159/*
1160 * It appears that grabstackstr() will barf with such alignments
1161 * because stalloc() will return a string allocated in a new stackblock.
1162 */
1163#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1164enum {
1165 /* Most machines require the value returned from malloc to be aligned
1166 * in some way. The following macro will get this right
1167 * on many machines. */
1168 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1169 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001170 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001171};
1172
1173struct stack_block {
1174 struct stack_block *prev;
1175 char space[MINSIZE];
1176};
1177
1178struct stackmark {
1179 struct stack_block *stackp;
1180 char *stacknxt;
1181 size_t stacknleft;
1182 struct stackmark *marknext;
1183};
1184
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001185
Denis Vlasenko01631112007-12-16 17:20:38 +00001186struct globals_memstack {
1187 struct stack_block *g_stackp; // = &stackbase;
1188 struct stackmark *markp;
1189 char *g_stacknxt; // = stackbase.space;
1190 char *sstrend; // = stackbase.space + MINSIZE;
1191 size_t g_stacknleft; // = MINSIZE;
1192 int herefd; // = -1;
1193 struct stack_block stackbase;
1194};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001195extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1196#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001197#define g_stackp (G_memstack.g_stackp )
1198#define markp (G_memstack.markp )
1199#define g_stacknxt (G_memstack.g_stacknxt )
1200#define sstrend (G_memstack.sstrend )
1201#define g_stacknleft (G_memstack.g_stacknleft)
1202#define herefd (G_memstack.herefd )
1203#define stackbase (G_memstack.stackbase )
1204#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001205 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1206 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001207 g_stackp = &stackbase; \
1208 g_stacknxt = stackbase.space; \
1209 g_stacknleft = MINSIZE; \
1210 sstrend = stackbase.space + MINSIZE; \
1211 herefd = -1; \
1212} while (0)
1213
1214#define stackblock() ((void *)g_stacknxt)
1215#define stackblocksize() g_stacknleft
1216
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001217
1218static void *
1219ckrealloc(void * p, size_t nbytes)
1220{
1221 p = realloc(p, nbytes);
1222 if (!p)
1223 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1224 return p;
1225}
1226
1227static void *
1228ckmalloc(size_t nbytes)
1229{
1230 return ckrealloc(NULL, nbytes);
1231}
1232
Denis Vlasenko597906c2008-02-20 16:38:54 +00001233static void *
1234ckzalloc(size_t nbytes)
1235{
1236 return memset(ckmalloc(nbytes), 0, nbytes);
1237}
1238
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001239/*
1240 * Make a copy of a string in safe storage.
1241 */
1242static char *
1243ckstrdup(const char *s)
1244{
1245 char *p = strdup(s);
1246 if (!p)
1247 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1248 return p;
1249}
1250
1251/*
1252 * Parse trees for commands are allocated in lifo order, so we use a stack
1253 * to make this more efficient, and also to avoid all sorts of exception
1254 * handling code to handle interrupts in the middle of a parse.
1255 *
1256 * The size 504 was chosen because the Ultrix malloc handles that size
1257 * well.
1258 */
1259static void *
1260stalloc(size_t nbytes)
1261{
1262 char *p;
1263 size_t aligned;
1264
1265 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001266 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001267 size_t len;
1268 size_t blocksize;
1269 struct stack_block *sp;
1270
1271 blocksize = aligned;
1272 if (blocksize < MINSIZE)
1273 blocksize = MINSIZE;
1274 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1275 if (len < blocksize)
1276 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1277 INT_OFF;
1278 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001279 sp->prev = g_stackp;
1280 g_stacknxt = sp->space;
1281 g_stacknleft = blocksize;
1282 sstrend = g_stacknxt + blocksize;
1283 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001284 INT_ON;
1285 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001286 p = g_stacknxt;
1287 g_stacknxt += aligned;
1288 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001289 return p;
1290}
1291
Denis Vlasenko597906c2008-02-20 16:38:54 +00001292static void *
1293stzalloc(size_t nbytes)
1294{
1295 return memset(stalloc(nbytes), 0, nbytes);
1296}
1297
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001298static void
1299stunalloc(void *p)
1300{
1301#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001302 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001303 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001304 abort();
1305 }
1306#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001307 g_stacknleft += g_stacknxt - (char *)p;
1308 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001309}
1310
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001311/*
1312 * Like strdup but works with the ash stack.
1313 */
1314static char *
1315ststrdup(const char *p)
1316{
1317 size_t len = strlen(p) + 1;
1318 return memcpy(stalloc(len), p, len);
1319}
1320
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321static void
1322setstackmark(struct stackmark *mark)
1323{
Denis Vlasenko01631112007-12-16 17:20:38 +00001324 mark->stackp = g_stackp;
1325 mark->stacknxt = g_stacknxt;
1326 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001327 mark->marknext = markp;
1328 markp = mark;
1329}
1330
1331static void
1332popstackmark(struct stackmark *mark)
1333{
1334 struct stack_block *sp;
1335
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001336 if (!mark->stackp)
1337 return;
1338
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001339 INT_OFF;
1340 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001341 while (g_stackp != mark->stackp) {
1342 sp = g_stackp;
1343 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001344 free(sp);
1345 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001346 g_stacknxt = mark->stacknxt;
1347 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348 sstrend = mark->stacknxt + mark->stacknleft;
1349 INT_ON;
1350}
1351
1352/*
1353 * When the parser reads in a string, it wants to stick the string on the
1354 * stack and only adjust the stack pointer when it knows how big the
1355 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1356 * of space on top of the stack and stackblocklen returns the length of
1357 * this block. Growstackblock will grow this space by at least one byte,
1358 * possibly moving it (like realloc). Grabstackblock actually allocates the
1359 * part of the block that has been used.
1360 */
1361static void
1362growstackblock(void)
1363{
1364 size_t newlen;
1365
Denis Vlasenko01631112007-12-16 17:20:38 +00001366 newlen = g_stacknleft * 2;
1367 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001368 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1369 if (newlen < 128)
1370 newlen += 128;
1371
Denis Vlasenko01631112007-12-16 17:20:38 +00001372 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001373 struct stack_block *oldstackp;
1374 struct stackmark *xmark;
1375 struct stack_block *sp;
1376 struct stack_block *prevstackp;
1377 size_t grosslen;
1378
1379 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001380 oldstackp = g_stackp;
1381 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001382 prevstackp = sp->prev;
1383 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1384 sp = ckrealloc(sp, grosslen);
1385 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001386 g_stackp = sp;
1387 g_stacknxt = sp->space;
1388 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001389 sstrend = sp->space + newlen;
1390
1391 /*
1392 * Stack marks pointing to the start of the old block
1393 * must be relocated to point to the new block
1394 */
1395 xmark = markp;
1396 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001397 xmark->stackp = g_stackp;
1398 xmark->stacknxt = g_stacknxt;
1399 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001400 xmark = xmark->marknext;
1401 }
1402 INT_ON;
1403 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001404 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001405 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001406 char *p = stalloc(newlen);
1407
1408 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001409 g_stacknxt = memcpy(p, oldspace, oldlen);
1410 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001411 }
1412}
1413
1414static void
1415grabstackblock(size_t len)
1416{
1417 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001418 g_stacknxt += len;
1419 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001420}
1421
1422/*
1423 * The following routines are somewhat easier to use than the above.
1424 * The user declares a variable of type STACKSTR, which may be declared
1425 * to be a register. The macro STARTSTACKSTR initializes things. Then
1426 * the user uses the macro STPUTC to add characters to the string. In
1427 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1428 * grown as necessary. When the user is done, she can just leave the
1429 * string there and refer to it using stackblock(). Or she can allocate
1430 * the space for it using grabstackstr(). If it is necessary to allow
1431 * someone else to use the stack temporarily and then continue to grow
1432 * the string, the user should use grabstack to allocate the space, and
1433 * then call ungrabstr(p) to return to the previous mode of operation.
1434 *
1435 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1436 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1437 * is space for at least one character.
1438 */
1439static void *
1440growstackstr(void)
1441{
1442 size_t len = stackblocksize();
1443 if (herefd >= 0 && len >= 1024) {
1444 full_write(herefd, stackblock(), len);
1445 return stackblock();
1446 }
1447 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001448 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001449}
1450
1451/*
1452 * Called from CHECKSTRSPACE.
1453 */
1454static char *
1455makestrspace(size_t newlen, char *p)
1456{
Denis Vlasenko01631112007-12-16 17:20:38 +00001457 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001458 size_t size = stackblocksize();
1459
1460 for (;;) {
1461 size_t nleft;
1462
1463 size = stackblocksize();
1464 nleft = size - len;
1465 if (nleft >= newlen)
1466 break;
1467 growstackblock();
1468 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001469 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001470}
1471
1472static char *
1473stack_nputstr(const char *s, size_t n, char *p)
1474{
1475 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001476 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001477 return p;
1478}
1479
1480static char *
1481stack_putstr(const char *s, char *p)
1482{
1483 return stack_nputstr(s, strlen(s), p);
1484}
1485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001486static char *
1487_STPUTC(int c, char *p)
1488{
1489 if (p == sstrend)
1490 p = growstackstr();
1491 *p++ = c;
1492 return p;
1493}
1494
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001495#define STARTSTACKSTR(p) ((p) = stackblock())
1496#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001497#define CHECKSTRSPACE(n, p) do { \
1498 char *q = (p); \
1499 size_t l = (n); \
1500 size_t m = sstrend - q; \
1501 if (l > m) \
1502 (p) = makestrspace(l, q); \
1503} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001504#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001505#define STACKSTRNUL(p) do { \
1506 if ((p) == sstrend) \
1507 (p) = growstackstr(); \
1508 *(p) = '\0'; \
1509} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001510#define STUNPUTC(p) (--(p))
1511#define STTOPC(p) ((p)[-1])
1512#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001513
1514#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001515#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001516#define stackstrend() ((void *)sstrend)
1517
1518
1519/* ============ String helpers */
1520
1521/*
1522 * prefix -- see if pfx is a prefix of string.
1523 */
1524static char *
1525prefix(const char *string, const char *pfx)
1526{
1527 while (*pfx) {
1528 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001529 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001530 }
1531 return (char *) string;
1532}
1533
1534/*
1535 * Check for a valid number. This should be elsewhere.
1536 */
1537static int
1538is_number(const char *p)
1539{
1540 do {
1541 if (!isdigit(*p))
1542 return 0;
1543 } while (*++p != '\0');
1544 return 1;
1545}
1546
1547/*
1548 * Convert a string of digits to an integer, printing an error message on
1549 * failure.
1550 */
1551static int
1552number(const char *s)
1553{
1554 if (!is_number(s))
1555 ash_msg_and_raise_error(illnum, s);
1556 return atoi(s);
1557}
1558
1559/*
1560 * Produce a possibly single quoted string suitable as input to the shell.
1561 * The return string is allocated on the stack.
1562 */
1563static char *
1564single_quote(const char *s)
1565{
1566 char *p;
1567
1568 STARTSTACKSTR(p);
1569
1570 do {
1571 char *q;
1572 size_t len;
1573
1574 len = strchrnul(s, '\'') - s;
1575
1576 q = p = makestrspace(len + 3, p);
1577
1578 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001579 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001580 *q++ = '\'';
1581 s += len;
1582
1583 STADJUST(q - p, p);
1584
1585 len = strspn(s, "'");
1586 if (!len)
1587 break;
1588
1589 q = p = makestrspace(len + 3, p);
1590
1591 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001592 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001593 *q++ = '"';
1594 s += len;
1595
1596 STADJUST(q - p, p);
1597 } while (*s);
1598
1599 USTPUTC(0, p);
1600
1601 return stackblock();
1602}
1603
1604
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001605/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001606
1607static char **argptr; /* argument list for builtin commands */
1608static char *optionarg; /* set by nextopt (like getopt) */
1609static char *optptr; /* used by nextopt */
1610
1611/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001612 * XXX - should get rid of. Have all builtins use getopt(3).
1613 * The library getopt must have the BSD extension static variable
1614 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001615 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001616 * Standard option processing (a la getopt) for builtin routines.
1617 * The only argument that is passed to nextopt is the option string;
1618 * the other arguments are unnecessary. It returns the character,
1619 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620 */
1621static int
1622nextopt(const char *optstring)
1623{
1624 char *p;
1625 const char *q;
1626 char c;
1627
1628 p = optptr;
1629 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001630 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001631 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001632 if (p == NULL)
1633 return '\0';
1634 if (*p != '-')
1635 return '\0';
1636 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001637 return '\0';
1638 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001639 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001640 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001641 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001642 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001643 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001644 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001645 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001646 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001647 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001648 if (*++q == ':')
1649 q++;
1650 }
1651 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001652 if (*p == '\0') {
1653 p = *argptr++;
1654 if (p == NULL)
1655 ash_msg_and_raise_error("no arg for -%c option", c);
1656 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001657 optionarg = p;
1658 p = NULL;
1659 }
1660 optptr = p;
1661 return c;
1662}
1663
1664
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001665/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001666
Denis Vlasenko01631112007-12-16 17:20:38 +00001667/*
1668 * The parsefile structure pointed to by the global variable parsefile
1669 * contains information about the current file being read.
1670 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001671struct shparam {
1672 int nparam; /* # of positional parameters (without $0) */
1673#if ENABLE_ASH_GETOPTS
1674 int optind; /* next parameter to be processed by getopts */
1675 int optoff; /* used by getopts */
1676#endif
1677 unsigned char malloced; /* if parameter list dynamically allocated */
1678 char **p; /* parameter list */
1679};
1680
1681/*
1682 * Free the list of positional parameters.
1683 */
1684static void
1685freeparam(volatile struct shparam *param)
1686{
Denis Vlasenko01631112007-12-16 17:20:38 +00001687 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001688 char **ap, **ap1;
1689 ap = ap1 = param->p;
1690 while (*ap)
1691 free(*ap++);
1692 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001693 }
1694}
1695
1696#if ENABLE_ASH_GETOPTS
1697static void getoptsreset(const char *value);
1698#endif
1699
1700struct var {
1701 struct var *next; /* next entry in hash list */
1702 int flags; /* flags are defined above */
1703 const char *text; /* name=value */
1704 void (*func)(const char *); /* function to be called when */
1705 /* the variable gets set/unset */
1706};
1707
1708struct localvar {
1709 struct localvar *next; /* next local variable in list */
1710 struct var *vp; /* the variable that was made local */
1711 int flags; /* saved flags */
1712 const char *text; /* saved text */
1713};
1714
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715/* flags */
1716#define VEXPORT 0x01 /* variable is exported */
1717#define VREADONLY 0x02 /* variable cannot be modified */
1718#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1719#define VTEXTFIXED 0x08 /* text is statically allocated */
1720#define VSTACK 0x10 /* text is allocated on the stack */
1721#define VUNSET 0x20 /* the variable is not set */
1722#define VNOFUNC 0x40 /* don't call the callback function */
1723#define VNOSET 0x80 /* do not set variable - just readonly test */
1724#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001725#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001726# define VDYNAMIC 0x200 /* dynamic variable */
1727#else
1728# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729#endif
1730
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001732static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#define defifs (defifsvar + 4)
1734#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001735static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001736#endif
1737
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001738
Denis Vlasenko01631112007-12-16 17:20:38 +00001739/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001740#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001741static void
1742change_lc_all(const char *value)
1743{
1744 if (value && *value != '\0')
1745 setlocale(LC_ALL, value);
1746}
1747static void
1748change_lc_ctype(const char *value)
1749{
1750 if (value && *value != '\0')
1751 setlocale(LC_CTYPE, value);
1752}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001753#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754#if ENABLE_ASH_MAIL
1755static void chkmail(void);
1756static void changemail(const char *);
1757#endif
1758static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759#if ENABLE_ASH_RANDOM_SUPPORT
1760static void change_random(const char *);
1761#endif
1762
Denis Vlasenko01631112007-12-16 17:20:38 +00001763static const struct {
1764 int flags;
1765 const char *text;
1766 void (*func)(const char *);
1767} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001768#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001769 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001773#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001774 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1775 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001777 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1778 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1779 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1780 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001782 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783#endif
1784#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001785 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786#endif
1787#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001788 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1789 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790#endif
1791#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001792 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#endif
1794};
1795
Denis Vlasenko0b769642008-07-24 07:54:57 +00001796struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001797
1798struct globals_var {
1799 struct shparam shellparam; /* $@ current positional parameters */
1800 struct redirtab *redirlist;
1801 int g_nullredirs;
1802 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1803 struct var *vartab[VTABSIZE];
1804 struct var varinit[ARRAY_SIZE(varinit_data)];
1805};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001806extern struct globals_var *const ash_ptr_to_globals_var;
1807#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001808#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001809//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001810#define g_nullredirs (G_var.g_nullredirs )
1811#define preverrout_fd (G_var.preverrout_fd)
1812#define vartab (G_var.vartab )
1813#define varinit (G_var.varinit )
1814#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001815 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001816 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1817 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001818 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1819 varinit[i].flags = varinit_data[i].flags; \
1820 varinit[i].text = varinit_data[i].text; \
1821 varinit[i].func = varinit_data[i].func; \
1822 } \
1823} while (0)
1824
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001825#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001826#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001827# define vmail (&vifs)[1]
1828# define vmpath (&vmail)[1]
1829# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001831# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001832#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001833#define vps1 (&vpath)[1]
1834#define vps2 (&vps1)[1]
1835#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001837# define voptind (&vps4)[1]
1838# if ENABLE_ASH_RANDOM_SUPPORT
1839# define vrandom (&voptind)[1]
1840# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001841#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001842# if ENABLE_ASH_RANDOM_SUPPORT
1843# define vrandom (&vps4)[1]
1844# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001846
1847/*
1848 * The following macros access the values of the above variables.
1849 * They have to skip over the name. They return the null string
1850 * for unset variables.
1851 */
1852#define ifsval() (vifs.text + 4)
1853#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001854#if ENABLE_ASH_MAIL
1855# define mailval() (vmail.text + 5)
1856# define mpathval() (vmpath.text + 9)
1857# define mpathset() ((vmpath.flags & VUNSET) == 0)
1858#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001859#define pathval() (vpath.text + 5)
1860#define ps1val() (vps1.text + 4)
1861#define ps2val() (vps2.text + 4)
1862#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001863#if ENABLE_ASH_GETOPTS
1864# define optindval() (voptind.text + 7)
1865#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001866
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001867
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001868#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1869#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1870
Denis Vlasenko01631112007-12-16 17:20:38 +00001871#if ENABLE_ASH_GETOPTS
1872static void
1873getoptsreset(const char *value)
1874{
1875 shellparam.optind = number(value);
1876 shellparam.optoff = -1;
1877}
1878#endif
1879
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001880/*
1881 * Return of a legal variable name (a letter or underscore followed by zero or
1882 * more letters, underscores, and digits).
1883 */
1884static char *
1885endofname(const char *name)
1886{
1887 char *p;
1888
1889 p = (char *) name;
1890 if (!is_name(*p))
1891 return p;
1892 while (*++p) {
1893 if (!is_in_name(*p))
1894 break;
1895 }
1896 return p;
1897}
1898
1899/*
1900 * Compares two strings up to the first = or '\0'. The first
1901 * string must be terminated by '='; the second may be terminated by
1902 * either '=' or '\0'.
1903 */
1904static int
1905varcmp(const char *p, const char *q)
1906{
1907 int c, d;
1908
1909 while ((c = *p) == (d = *q)) {
1910 if (!c || c == '=')
1911 goto out;
1912 p++;
1913 q++;
1914 }
1915 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001916 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001917 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001918 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001919 out:
1920 return c - d;
1921}
1922
1923static int
1924varequal(const char *a, const char *b)
1925{
1926 return !varcmp(a, b);
1927}
1928
1929/*
1930 * Find the appropriate entry in the hash table from the name.
1931 */
1932static struct var **
1933hashvar(const char *p)
1934{
1935 unsigned hashval;
1936
1937 hashval = ((unsigned char) *p) << 4;
1938 while (*p && *p != '=')
1939 hashval += (unsigned char) *p++;
1940 return &vartab[hashval % VTABSIZE];
1941}
1942
1943static int
1944vpcmp(const void *a, const void *b)
1945{
1946 return varcmp(*(const char **)a, *(const char **)b);
1947}
1948
1949/*
1950 * This routine initializes the builtin variables.
1951 */
1952static void
1953initvar(void)
1954{
1955 struct var *vp;
1956 struct var *end;
1957 struct var **vpp;
1958
1959 /*
1960 * PS1 depends on uid
1961 */
1962#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1963 vps1.text = "PS1=\\w \\$ ";
1964#else
1965 if (!geteuid())
1966 vps1.text = "PS1=# ";
1967#endif
1968 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001969 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970 do {
1971 vpp = hashvar(vp->text);
1972 vp->next = *vpp;
1973 *vpp = vp;
1974 } while (++vp < end);
1975}
1976
1977static struct var **
1978findvar(struct var **vpp, const char *name)
1979{
1980 for (; *vpp; vpp = &(*vpp)->next) {
1981 if (varequal((*vpp)->text, name)) {
1982 break;
1983 }
1984 }
1985 return vpp;
1986}
1987
1988/*
1989 * Find the value of a variable. Returns NULL if not set.
1990 */
1991static char *
1992lookupvar(const char *name)
1993{
1994 struct var *v;
1995
1996 v = *findvar(hashvar(name), name);
1997 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001998#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001999 /*
2000 * Dynamic variables are implemented roughly the same way they are
2001 * in bash. Namely, they're "special" so long as they aren't unset.
2002 * As soon as they're unset, they're no longer dynamic, and dynamic
2003 * lookup will no longer happen at that point. -- PFM.
2004 */
2005 if ((v->flags & VDYNAMIC))
2006 (*v->func)(NULL);
2007#endif
2008 if (!(v->flags & VUNSET))
2009 return strchrnul(v->text, '=') + 1;
2010 }
2011 return NULL;
2012}
2013
2014/*
2015 * Search the environment of a builtin command.
2016 */
2017static char *
2018bltinlookup(const char *name)
2019{
2020 struct strlist *sp;
2021
2022 for (sp = cmdenviron; sp; sp = sp->next) {
2023 if (varequal(sp->text, name))
2024 return strchrnul(sp->text, '=') + 1;
2025 }
2026 return lookupvar(name);
2027}
2028
2029/*
2030 * Same as setvar except that the variable and value are passed in
2031 * the first argument as name=value. Since the first argument will
2032 * be actually stored in the table, it should not be a string that
2033 * will go away.
2034 * Called with interrupts off.
2035 */
2036static void
2037setvareq(char *s, int flags)
2038{
2039 struct var *vp, **vpp;
2040
2041 vpp = hashvar(s);
2042 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2043 vp = *findvar(vpp, s);
2044 if (vp) {
2045 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2046 const char *n;
2047
2048 if (flags & VNOSAVE)
2049 free(s);
2050 n = vp->text;
2051 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2052 }
2053
2054 if (flags & VNOSET)
2055 return;
2056
2057 if (vp->func && (flags & VNOFUNC) == 0)
2058 (*vp->func)(strchrnul(s, '=') + 1);
2059
2060 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2061 free((char*)vp->text);
2062
2063 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2064 } else {
2065 if (flags & VNOSET)
2066 return;
2067 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002068 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002069 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002070 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002071 *vpp = vp;
2072 }
2073 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2074 s = ckstrdup(s);
2075 vp->text = s;
2076 vp->flags = flags;
2077}
2078
2079/*
2080 * Set the value of a variable. The flags argument is ored with the
2081 * flags of the variable. If val is NULL, the variable is unset.
2082 */
2083static void
2084setvar(const char *name, const char *val, int flags)
2085{
2086 char *p, *q;
2087 size_t namelen;
2088 char *nameeq;
2089 size_t vallen;
2090
2091 q = endofname(name);
2092 p = strchrnul(q, '=');
2093 namelen = p - name;
2094 if (!namelen || p != q)
2095 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2096 vallen = 0;
2097 if (val == NULL) {
2098 flags |= VUNSET;
2099 } else {
2100 vallen = strlen(val);
2101 }
2102 INT_OFF;
2103 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002104 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002105 if (val) {
2106 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002107 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002108 }
2109 *p = '\0';
2110 setvareq(nameeq, flags | VNOSAVE);
2111 INT_ON;
2112}
2113
2114#if ENABLE_ASH_GETOPTS
2115/*
2116 * Safe version of setvar, returns 1 on success 0 on failure.
2117 */
2118static int
2119setvarsafe(const char *name, const char *val, int flags)
2120{
2121 int err;
2122 volatile int saveint;
2123 struct jmploc *volatile savehandler = exception_handler;
2124 struct jmploc jmploc;
2125
2126 SAVE_INT(saveint);
2127 if (setjmp(jmploc.loc))
2128 err = 1;
2129 else {
2130 exception_handler = &jmploc;
2131 setvar(name, val, flags);
2132 err = 0;
2133 }
2134 exception_handler = savehandler;
2135 RESTORE_INT(saveint);
2136 return err;
2137}
2138#endif
2139
2140/*
2141 * Unset the specified variable.
2142 */
2143static int
2144unsetvar(const char *s)
2145{
2146 struct var **vpp;
2147 struct var *vp;
2148 int retval;
2149
2150 vpp = findvar(hashvar(s), s);
2151 vp = *vpp;
2152 retval = 2;
2153 if (vp) {
2154 int flags = vp->flags;
2155
2156 retval = 1;
2157 if (flags & VREADONLY)
2158 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002159#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002160 vp->flags &= ~VDYNAMIC;
2161#endif
2162 if (flags & VUNSET)
2163 goto ok;
2164 if ((flags & VSTRFIXED) == 0) {
2165 INT_OFF;
2166 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2167 free((char*)vp->text);
2168 *vpp = vp->next;
2169 free(vp);
2170 INT_ON;
2171 } else {
2172 setvar(s, 0, 0);
2173 vp->flags &= ~VEXPORT;
2174 }
2175 ok:
2176 retval = 0;
2177 }
2178 out:
2179 return retval;
2180}
2181
2182/*
2183 * Process a linked list of variable assignments.
2184 */
2185static void
2186listsetvar(struct strlist *list_set_var, int flags)
2187{
2188 struct strlist *lp = list_set_var;
2189
2190 if (!lp)
2191 return;
2192 INT_OFF;
2193 do {
2194 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002195 lp = lp->next;
2196 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002197 INT_ON;
2198}
2199
2200/*
2201 * Generate a list of variables satisfying the given conditions.
2202 */
2203static char **
2204listvars(int on, int off, char ***end)
2205{
2206 struct var **vpp;
2207 struct var *vp;
2208 char **ep;
2209 int mask;
2210
2211 STARTSTACKSTR(ep);
2212 vpp = vartab;
2213 mask = on | off;
2214 do {
2215 for (vp = *vpp; vp; vp = vp->next) {
2216 if ((vp->flags & mask) == on) {
2217 if (ep == stackstrend())
2218 ep = growstackstr();
2219 *ep++ = (char *) vp->text;
2220 }
2221 }
2222 } while (++vpp < vartab + VTABSIZE);
2223 if (ep == stackstrend())
2224 ep = growstackstr();
2225 if (end)
2226 *end = ep;
2227 *ep++ = NULL;
2228 return grabstackstr(ep);
2229}
2230
2231
2232/* ============ Path search helper
2233 *
2234 * The variable path (passed by reference) should be set to the start
2235 * of the path before the first call; padvance will update
2236 * this value as it proceeds. Successive calls to padvance will return
2237 * the possible path expansions in sequence. If an option (indicated by
2238 * a percent sign) appears in the path entry then the global variable
2239 * pathopt will be set to point to it; otherwise pathopt will be set to
2240 * NULL.
2241 */
2242static const char *pathopt; /* set by padvance */
2243
2244static char *
2245padvance(const char **path, const char *name)
2246{
2247 const char *p;
2248 char *q;
2249 const char *start;
2250 size_t len;
2251
2252 if (*path == NULL)
2253 return NULL;
2254 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002255 for (p = start; *p && *p != ':' && *p != '%'; p++)
2256 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002257 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2258 while (stackblocksize() < len)
2259 growstackblock();
2260 q = stackblock();
2261 if (p != start) {
2262 memcpy(q, start, p - start);
2263 q += p - start;
2264 *q++ = '/';
2265 }
2266 strcpy(q, name);
2267 pathopt = NULL;
2268 if (*p == '%') {
2269 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002270 while (*p && *p != ':')
2271 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002272 }
2273 if (*p == ':')
2274 *path = p + 1;
2275 else
2276 *path = NULL;
2277 return stalloc(len);
2278}
2279
2280
2281/* ============ Prompt */
2282
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002283static smallint doprompt; /* if set, prompt the user */
2284static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285
2286#if ENABLE_FEATURE_EDITING
2287static line_input_t *line_input_state;
2288static const char *cmdedit_prompt;
2289static void
2290putprompt(const char *s)
2291{
2292 if (ENABLE_ASH_EXPAND_PRMT) {
2293 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002294 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002295 return;
2296 }
2297 cmdedit_prompt = s;
2298}
2299#else
2300static void
2301putprompt(const char *s)
2302{
2303 out2str(s);
2304}
2305#endif
2306
2307#if ENABLE_ASH_EXPAND_PRMT
2308/* expandstr() needs parsing machinery, so it is far away ahead... */
2309static const char *expandstr(const char *ps);
2310#else
2311#define expandstr(s) s
2312#endif
2313
2314static void
2315setprompt(int whichprompt)
2316{
2317 const char *prompt;
2318#if ENABLE_ASH_EXPAND_PRMT
2319 struct stackmark smark;
2320#endif
2321
2322 needprompt = 0;
2323
2324 switch (whichprompt) {
2325 case 1:
2326 prompt = ps1val();
2327 break;
2328 case 2:
2329 prompt = ps2val();
2330 break;
2331 default: /* 0 */
2332 prompt = nullstr;
2333 }
2334#if ENABLE_ASH_EXPAND_PRMT
2335 setstackmark(&smark);
2336 stalloc(stackblocksize());
2337#endif
2338 putprompt(expandstr(prompt));
2339#if ENABLE_ASH_EXPAND_PRMT
2340 popstackmark(&smark);
2341#endif
2342}
2343
2344
2345/* ============ The cd and pwd commands */
2346
2347#define CD_PHYSICAL 1
2348#define CD_PRINT 2
2349
2350static int docd(const char *, int);
2351
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002352static int
2353cdopt(void)
2354{
2355 int flags = 0;
2356 int i, j;
2357
2358 j = 'L';
2359 while ((i = nextopt("LP"))) {
2360 if (i != j) {
2361 flags ^= CD_PHYSICAL;
2362 j = i;
2363 }
2364 }
2365
2366 return flags;
2367}
2368
2369/*
2370 * Update curdir (the name of the current directory) in response to a
2371 * cd command.
2372 */
2373static const char *
2374updatepwd(const char *dir)
2375{
2376 char *new;
2377 char *p;
2378 char *cdcomppath;
2379 const char *lim;
2380
2381 cdcomppath = ststrdup(dir);
2382 STARTSTACKSTR(new);
2383 if (*dir != '/') {
2384 if (curdir == nullstr)
2385 return 0;
2386 new = stack_putstr(curdir, new);
2387 }
2388 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002389 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390 if (*dir != '/') {
2391 if (new[-1] != '/')
2392 USTPUTC('/', new);
2393 if (new > lim && *lim == '/')
2394 lim++;
2395 } else {
2396 USTPUTC('/', new);
2397 cdcomppath++;
2398 if (dir[1] == '/' && dir[2] != '/') {
2399 USTPUTC('/', new);
2400 cdcomppath++;
2401 lim++;
2402 }
2403 }
2404 p = strtok(cdcomppath, "/");
2405 while (p) {
2406 switch (*p) {
2407 case '.':
2408 if (p[1] == '.' && p[2] == '\0') {
2409 while (new > lim) {
2410 STUNPUTC(new);
2411 if (new[-1] == '/')
2412 break;
2413 }
2414 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002415 }
2416 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002417 break;
2418 /* fall through */
2419 default:
2420 new = stack_putstr(p, new);
2421 USTPUTC('/', new);
2422 }
2423 p = strtok(0, "/");
2424 }
2425 if (new > lim)
2426 STUNPUTC(new);
2427 *new = 0;
2428 return stackblock();
2429}
2430
2431/*
2432 * Find out what the current directory is. If we already know the current
2433 * directory, this routine returns immediately.
2434 */
2435static char *
2436getpwd(void)
2437{
Denis Vlasenko01631112007-12-16 17:20:38 +00002438 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002439 return dir ? dir : nullstr;
2440}
2441
2442static void
2443setpwd(const char *val, int setold)
2444{
2445 char *oldcur, *dir;
2446
2447 oldcur = dir = curdir;
2448
2449 if (setold) {
2450 setvar("OLDPWD", oldcur, VEXPORT);
2451 }
2452 INT_OFF;
2453 if (physdir != nullstr) {
2454 if (physdir != oldcur)
2455 free(physdir);
2456 physdir = nullstr;
2457 }
2458 if (oldcur == val || !val) {
2459 char *s = getpwd();
2460 physdir = s;
2461 if (!val)
2462 dir = s;
2463 } else
2464 dir = ckstrdup(val);
2465 if (oldcur != dir && oldcur != nullstr) {
2466 free(oldcur);
2467 }
2468 curdir = dir;
2469 INT_ON;
2470 setvar("PWD", dir, VEXPORT);
2471}
2472
2473static void hashcd(void);
2474
2475/*
2476 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2477 * know that the current directory has changed.
2478 */
2479static int
2480docd(const char *dest, int flags)
2481{
2482 const char *dir = 0;
2483 int err;
2484
2485 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2486
2487 INT_OFF;
2488 if (!(flags & CD_PHYSICAL)) {
2489 dir = updatepwd(dest);
2490 if (dir)
2491 dest = dir;
2492 }
2493 err = chdir(dest);
2494 if (err)
2495 goto out;
2496 setpwd(dir, 1);
2497 hashcd();
2498 out:
2499 INT_ON;
2500 return err;
2501}
2502
2503static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002504cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002505{
2506 const char *dest;
2507 const char *path;
2508 const char *p;
2509 char c;
2510 struct stat statb;
2511 int flags;
2512
2513 flags = cdopt();
2514 dest = *argptr;
2515 if (!dest)
2516 dest = bltinlookup(homestr);
2517 else if (LONE_DASH(dest)) {
2518 dest = bltinlookup("OLDPWD");
2519 flags |= CD_PRINT;
2520 }
2521 if (!dest)
2522 dest = nullstr;
2523 if (*dest == '/')
2524 goto step7;
2525 if (*dest == '.') {
2526 c = dest[1];
2527 dotdot:
2528 switch (c) {
2529 case '\0':
2530 case '/':
2531 goto step6;
2532 case '.':
2533 c = dest[2];
2534 if (c != '.')
2535 goto dotdot;
2536 }
2537 }
2538 if (!*dest)
2539 dest = ".";
2540 path = bltinlookup("CDPATH");
2541 if (!path) {
2542 step6:
2543 step7:
2544 p = dest;
2545 goto docd;
2546 }
2547 do {
2548 c = *path;
2549 p = padvance(&path, dest);
2550 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2551 if (c && c != ':')
2552 flags |= CD_PRINT;
2553 docd:
2554 if (!docd(p, flags))
2555 goto out;
2556 break;
2557 }
2558 } while (path);
2559 ash_msg_and_raise_error("can't cd to %s", dest);
2560 /* NOTREACHED */
2561 out:
2562 if (flags & CD_PRINT)
2563 out1fmt(snlfmt, curdir);
2564 return 0;
2565}
2566
2567static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002568pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002569{
2570 int flags;
2571 const char *dir = curdir;
2572
2573 flags = cdopt();
2574 if (flags) {
2575 if (physdir == nullstr)
2576 setpwd(dir, 0);
2577 dir = physdir;
2578 }
2579 out1fmt(snlfmt, dir);
2580 return 0;
2581}
2582
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002583
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002584/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002585
Denis Vlasenko834dee72008-10-07 09:18:30 +00002586
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002587#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002588/* buffer for top level input file */
2589#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002590
Eric Andersenc470f442003-07-28 09:56:35 +00002591/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002592#define CWORD 0 /* character is nothing special */
2593#define CNL 1 /* newline character */
2594#define CBACK 2 /* a backslash character */
2595#define CSQUOTE 3 /* single quote */
2596#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002597#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002598#define CBQUOTE 6 /* backwards single quote */
2599#define CVAR 7 /* a dollar sign */
2600#define CENDVAR 8 /* a '}' character */
2601#define CLP 9 /* a left paren in arithmetic */
2602#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002603#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002604#define CCTL 12 /* like CWORD, except it must be escaped */
2605#define CSPCL 13 /* these terminate a word */
2606#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002607
Denis Vlasenko131ae172007-02-18 13:00:19 +00002608#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002609#define SYNBASE 130
2610#define PEOF -130
2611#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002612#define PEOA_OR_PEOF PEOA
2613#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002614#define SYNBASE 129
2615#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002616#define PEOA_OR_PEOF PEOF
2617#endif
2618
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002619/* number syntax index */
2620#define BASESYNTAX 0 /* not in quotes */
2621#define DQSYNTAX 1 /* in double quotes */
2622#define SQSYNTAX 2 /* in single quotes */
2623#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002624#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002625
Denis Vlasenko131ae172007-02-18 13:00:19 +00002626#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002627#define USE_SIT_FUNCTION
2628#endif
2629
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002631static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002632#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002633 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002634#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002635 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2636 { CNL, CNL, CNL, CNL }, /* 2, \n */
2637 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2638 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2639 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2640 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2641 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2642 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2643 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2644 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2645 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002646#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002647 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2648 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2649 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002650#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002651};
Eric Andersenc470f442003-07-28 09:56:35 +00002652#else
2653static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002654#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002655 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002656#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002657 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2658 { CNL, CNL, CNL }, /* 2, \n */
2659 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2660 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2661 { CVAR, CVAR, CWORD }, /* 5, $ */
2662 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2663 { CSPCL, CWORD, CWORD }, /* 7, ( */
2664 { CSPCL, CWORD, CWORD }, /* 8, ) */
2665 { CBACK, CBACK, CCTL }, /* 9, \ */
2666 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2667 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002668#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002669 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2670 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2671 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002672#endif
2673};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002674#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002675
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002676#ifdef USE_SIT_FUNCTION
2677
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002678static int
2679SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002680{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002681 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002682#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002683 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002684 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2685 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2686 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2687 11, 3 /* "}~" */
2688 };
2689#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002690 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002691 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2692 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2693 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2694 10, 2 /* "}~" */
2695 };
2696#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002697 const char *s;
2698 int indx;
2699
Eric Andersenc470f442003-07-28 09:56:35 +00002700 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002701 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002702#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002703 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002704 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002705 else
2706#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002707
2708 if ((unsigned char)c >= (unsigned char)(CTLESC)
2709 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2710 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002711 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002712 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002713 s = strchrnul(spec_symbls, c);
2714 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002715 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002716 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002717 }
2718 return S_I_T[indx][syntax];
2719}
2720
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002721#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002722
Denis Vlasenko131ae172007-02-18 13:00:19 +00002723#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002724#define CSPCL_CIGN_CIGN_CIGN 0
2725#define CSPCL_CWORD_CWORD_CWORD 1
2726#define CNL_CNL_CNL_CNL 2
2727#define CWORD_CCTL_CCTL_CWORD 3
2728#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2729#define CVAR_CVAR_CWORD_CVAR 5
2730#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2731#define CSPCL_CWORD_CWORD_CLP 7
2732#define CSPCL_CWORD_CWORD_CRP 8
2733#define CBACK_CBACK_CCTL_CBACK 9
2734#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2735#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2736#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2737#define CWORD_CWORD_CWORD_CWORD 13
2738#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002739#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002740#define CSPCL_CWORD_CWORD_CWORD 0
2741#define CNL_CNL_CNL_CNL 1
2742#define CWORD_CCTL_CCTL_CWORD 2
2743#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2744#define CVAR_CVAR_CWORD_CVAR 4
2745#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2746#define CSPCL_CWORD_CWORD_CLP 6
2747#define CSPCL_CWORD_CWORD_CRP 7
2748#define CBACK_CBACK_CCTL_CBACK 8
2749#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2750#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2751#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2752#define CWORD_CWORD_CWORD_CWORD 12
2753#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002754#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002755
2756static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002757 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002758 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002759#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002760 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2761#endif
2762 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2764 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2765 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2766 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2767 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2768 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2769 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2770 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002771 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2900 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2901 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2913 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2914 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2915 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2916 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2917 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2918 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2919 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2920 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2921 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2922 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2923 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002924 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002925 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2927 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002929 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002930 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2931 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2932 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2933 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2935 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2937 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2949 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2950 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2951 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2953 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2954 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2982 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2983 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2984 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2987 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3005 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3006 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3007 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3008 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3009 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3010 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3011 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3012 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3013 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3014 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3015 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3016 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3017 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003018};
3019
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003020#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3021
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003022#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003023
Eric Andersen2870d962001-07-02 17:27:21 +00003024
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003025/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003026
Denis Vlasenko131ae172007-02-18 13:00:19 +00003027#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003028
3029#define ALIASINUSE 1
3030#define ALIASDEAD 2
3031
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003032struct alias {
3033 struct alias *next;
3034 char *name;
3035 char *val;
3036 int flag;
3037};
3038
Denis Vlasenko01631112007-12-16 17:20:38 +00003039
3040static struct alias **atab; // [ATABSIZE];
3041#define INIT_G_alias() do { \
3042 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3043} while (0)
3044
Eric Andersen2870d962001-07-02 17:27:21 +00003045
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003046static struct alias **
3047__lookupalias(const char *name) {
3048 unsigned int hashval;
3049 struct alias **app;
3050 const char *p;
3051 unsigned int ch;
3052
3053 p = name;
3054
3055 ch = (unsigned char)*p;
3056 hashval = ch << 4;
3057 while (ch) {
3058 hashval += ch;
3059 ch = (unsigned char)*++p;
3060 }
3061 app = &atab[hashval % ATABSIZE];
3062
3063 for (; *app; app = &(*app)->next) {
3064 if (strcmp(name, (*app)->name) == 0) {
3065 break;
3066 }
3067 }
3068
3069 return app;
3070}
3071
3072static struct alias *
3073lookupalias(const char *name, int check)
3074{
3075 struct alias *ap = *__lookupalias(name);
3076
3077 if (check && ap && (ap->flag & ALIASINUSE))
3078 return NULL;
3079 return ap;
3080}
3081
3082static struct alias *
3083freealias(struct alias *ap)
3084{
3085 struct alias *next;
3086
3087 if (ap->flag & ALIASINUSE) {
3088 ap->flag |= ALIASDEAD;
3089 return ap;
3090 }
3091
3092 next = ap->next;
3093 free(ap->name);
3094 free(ap->val);
3095 free(ap);
3096 return next;
3097}
Eric Andersencb57d552001-06-28 07:25:16 +00003098
Eric Andersenc470f442003-07-28 09:56:35 +00003099static void
3100setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003101{
3102 struct alias *ap, **app;
3103
3104 app = __lookupalias(name);
3105 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003106 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003107 if (ap) {
3108 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003109 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003110 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003111 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003112 ap->flag &= ~ALIASDEAD;
3113 } else {
3114 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003115 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003116 ap->name = ckstrdup(name);
3117 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003118 /*ap->flag = 0; - ckzalloc did it */
3119 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003120 *app = ap;
3121 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003122 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003123}
3124
Eric Andersenc470f442003-07-28 09:56:35 +00003125static int
3126unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003127{
Eric Andersencb57d552001-06-28 07:25:16 +00003128 struct alias **app;
3129
3130 app = __lookupalias(name);
3131
3132 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003133 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003134 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003135 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003136 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003137 }
3138
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003139 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003140}
3141
Eric Andersenc470f442003-07-28 09:56:35 +00003142static void
3143rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003144{
Eric Andersencb57d552001-06-28 07:25:16 +00003145 struct alias *ap, **app;
3146 int i;
3147
Denis Vlasenkob012b102007-02-19 22:43:01 +00003148 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003149 for (i = 0; i < ATABSIZE; i++) {
3150 app = &atab[i];
3151 for (ap = *app; ap; ap = *app) {
3152 *app = freealias(*app);
3153 if (ap == *app) {
3154 app = &ap->next;
3155 }
3156 }
3157 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003158 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003159}
3160
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003161static void
3162printalias(const struct alias *ap)
3163{
3164 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3165}
3166
Eric Andersencb57d552001-06-28 07:25:16 +00003167/*
3168 * TODO - sort output
3169 */
Eric Andersenc470f442003-07-28 09:56:35 +00003170static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003171aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003172{
3173 char *n, *v;
3174 int ret = 0;
3175 struct alias *ap;
3176
Denis Vlasenko68404f12008-03-17 09:00:54 +00003177 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003178 int i;
3179
Denis Vlasenko68404f12008-03-17 09:00:54 +00003180 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003181 for (ap = atab[i]; ap; ap = ap->next) {
3182 printalias(ap);
3183 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003184 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003185 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003186 }
3187 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003188 v = strchr(n+1, '=');
3189 if (v == NULL) { /* n+1: funny ksh stuff */
3190 ap = *__lookupalias(n);
3191 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003192 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003193 ret = 1;
3194 } else
3195 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003196 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003197 *v++ = '\0';
3198 setalias(n, v);
3199 }
3200 }
3201
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003202 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003203}
3204
Eric Andersenc470f442003-07-28 09:56:35 +00003205static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003206unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003207{
3208 int i;
3209
3210 while ((i = nextopt("a")) != '\0') {
3211 if (i == 'a') {
3212 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003213 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003214 }
3215 }
3216 for (i = 0; *argptr; argptr++) {
3217 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003218 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003219 i = 1;
3220 }
3221 }
3222
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003223 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003224}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003225
Denis Vlasenko131ae172007-02-18 13:00:19 +00003226#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003227
Eric Andersenc470f442003-07-28 09:56:35 +00003228
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003229/* ============ jobs.c */
3230
3231/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3232#define FORK_FG 0
3233#define FORK_BG 1
3234#define FORK_NOJOB 2
3235
3236/* mode flags for showjob(s) */
3237#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3238#define SHOW_PID 0x04 /* include process pid */
3239#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3240
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003241/*
3242 * A job structure contains information about a job. A job is either a
3243 * single process or a set of processes contained in a pipeline. In the
3244 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3245 * array of pids.
3246 */
3247
3248struct procstat {
3249 pid_t pid; /* process id */
3250 int status; /* last process status from wait() */
3251 char *cmd; /* text of command being run */
3252};
3253
3254struct job {
3255 struct procstat ps0; /* status of process */
3256 struct procstat *ps; /* status or processes when more than one */
3257#if JOBS
3258 int stopstatus; /* status of a stopped job */
3259#endif
3260 uint32_t
3261 nprocs: 16, /* number of processes */
3262 state: 8,
3263#define JOBRUNNING 0 /* at least one proc running */
3264#define JOBSTOPPED 1 /* all procs are stopped */
3265#define JOBDONE 2 /* all procs are completed */
3266#if JOBS
3267 sigint: 1, /* job was killed by SIGINT */
3268 jobctl: 1, /* job running under job control */
3269#endif
3270 waited: 1, /* true if this entry has been waited for */
3271 used: 1, /* true if this entry is in used */
3272 changed: 1; /* true if status has changed */
3273 struct job *prev_job; /* previous job */
3274};
3275
Denis Vlasenko68404f12008-03-17 09:00:54 +00003276static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003277#if !JOBS
3278#define forkshell(job, node, mode) forkshell(job, mode)
3279#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003280static int forkshell(struct job *, union node *, int);
3281static int waitforjob(struct job *);
3282
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003283#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003284enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003285#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003286#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003287static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003288static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003289#endif
3290
3291/*
3292 * Set the signal handler for the specified signal. The routine figures
3293 * out what it should be set to.
3294 */
3295static void
3296setsignal(int signo)
3297{
3298 int action;
3299 char *t, tsig;
3300 struct sigaction act;
3301
3302 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003303 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003304 if (t == NULL)
3305 action = S_DFL;
3306 else if (*t != '\0')
3307 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003308 if (rootshell && action == S_DFL) {
3309 switch (signo) {
3310 case SIGINT:
3311 if (iflag || minusc || sflag == 0)
3312 action = S_CATCH;
3313 break;
3314 case SIGQUIT:
3315#if DEBUG
3316 if (debug)
3317 break;
3318#endif
3319 /* FALLTHROUGH */
3320 case SIGTERM:
3321 if (iflag)
3322 action = S_IGN;
3323 break;
3324#if JOBS
3325 case SIGTSTP:
3326 case SIGTTOU:
3327 if (mflag)
3328 action = S_IGN;
3329 break;
3330#endif
3331 }
3332 }
3333
3334 t = &sigmode[signo - 1];
3335 tsig = *t;
3336 if (tsig == 0) {
3337 /*
3338 * current setting unknown
3339 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003340 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 /*
3342 * Pretend it worked; maybe we should give a warning
3343 * here, but other shells don't. We don't alter
3344 * sigmode, so that we retry every time.
3345 */
3346 return;
3347 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003348 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003349 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003350 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003351 if (mflag
3352 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3353 ) {
3354 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003355 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356 }
3357 }
3358 if (tsig == S_HARD_IGN || tsig == action)
3359 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003360 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361 switch (action) {
3362 case S_CATCH:
3363 act.sa_handler = onsig;
3364 break;
3365 case S_IGN:
3366 act.sa_handler = SIG_IGN;
3367 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003368 }
3369 *t = action;
3370 act.sa_flags = 0;
3371 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003372 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373}
3374
3375/* mode flags for set_curjob */
3376#define CUR_DELETE 2
3377#define CUR_RUNNING 1
3378#define CUR_STOPPED 0
3379
3380/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003381#define DOWAIT_NONBLOCK WNOHANG
3382#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003383
3384#if JOBS
3385/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003386static int initialpgrp; //references:2
3387static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003388#endif
3389/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003390static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003391/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003392static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003393/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003394static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003395/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003396static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003397
3398static void
3399set_curjob(struct job *jp, unsigned mode)
3400{
3401 struct job *jp1;
3402 struct job **jpp, **curp;
3403
3404 /* first remove from list */
3405 jpp = curp = &curjob;
3406 do {
3407 jp1 = *jpp;
3408 if (jp1 == jp)
3409 break;
3410 jpp = &jp1->prev_job;
3411 } while (1);
3412 *jpp = jp1->prev_job;
3413
3414 /* Then re-insert in correct position */
3415 jpp = curp;
3416 switch (mode) {
3417 default:
3418#if DEBUG
3419 abort();
3420#endif
3421 case CUR_DELETE:
3422 /* job being deleted */
3423 break;
3424 case CUR_RUNNING:
3425 /* newly created job or backgrounded job,
3426 put after all stopped jobs. */
3427 do {
3428 jp1 = *jpp;
3429#if JOBS
3430 if (!jp1 || jp1->state != JOBSTOPPED)
3431#endif
3432 break;
3433 jpp = &jp1->prev_job;
3434 } while (1);
3435 /* FALLTHROUGH */
3436#if JOBS
3437 case CUR_STOPPED:
3438#endif
3439 /* newly stopped job - becomes curjob */
3440 jp->prev_job = *jpp;
3441 *jpp = jp;
3442 break;
3443 }
3444}
3445
3446#if JOBS || DEBUG
3447static int
3448jobno(const struct job *jp)
3449{
3450 return jp - jobtab + 1;
3451}
3452#endif
3453
3454/*
3455 * Convert a job name to a job structure.
3456 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003457#if !JOBS
3458#define getjob(name, getctl) getjob(name)
3459#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003460static struct job *
3461getjob(const char *name, int getctl)
3462{
3463 struct job *jp;
3464 struct job *found;
3465 const char *err_msg = "No such job: %s";
3466 unsigned num;
3467 int c;
3468 const char *p;
3469 char *(*match)(const char *, const char *);
3470
3471 jp = curjob;
3472 p = name;
3473 if (!p)
3474 goto currentjob;
3475
3476 if (*p != '%')
3477 goto err;
3478
3479 c = *++p;
3480 if (!c)
3481 goto currentjob;
3482
3483 if (!p[1]) {
3484 if (c == '+' || c == '%') {
3485 currentjob:
3486 err_msg = "No current job";
3487 goto check;
3488 }
3489 if (c == '-') {
3490 if (jp)
3491 jp = jp->prev_job;
3492 err_msg = "No previous job";
3493 check:
3494 if (!jp)
3495 goto err;
3496 goto gotit;
3497 }
3498 }
3499
3500 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003501// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003502 num = atoi(p);
3503 if (num < njobs) {
3504 jp = jobtab + num - 1;
3505 if (jp->used)
3506 goto gotit;
3507 goto err;
3508 }
3509 }
3510
3511 match = prefix;
3512 if (*p == '?') {
3513 match = strstr;
3514 p++;
3515 }
3516
3517 found = 0;
3518 while (1) {
3519 if (!jp)
3520 goto err;
3521 if (match(jp->ps[0].cmd, p)) {
3522 if (found)
3523 goto err;
3524 found = jp;
3525 err_msg = "%s: ambiguous";
3526 }
3527 jp = jp->prev_job;
3528 }
3529
3530 gotit:
3531#if JOBS
3532 err_msg = "job %s not created under job control";
3533 if (getctl && jp->jobctl == 0)
3534 goto err;
3535#endif
3536 return jp;
3537 err:
3538 ash_msg_and_raise_error(err_msg, name);
3539}
3540
3541/*
3542 * Mark a job structure as unused.
3543 */
3544static void
3545freejob(struct job *jp)
3546{
3547 struct procstat *ps;
3548 int i;
3549
3550 INT_OFF;
3551 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3552 if (ps->cmd != nullstr)
3553 free(ps->cmd);
3554 }
3555 if (jp->ps != &jp->ps0)
3556 free(jp->ps);
3557 jp->used = 0;
3558 set_curjob(jp, CUR_DELETE);
3559 INT_ON;
3560}
3561
3562#if JOBS
3563static void
3564xtcsetpgrp(int fd, pid_t pgrp)
3565{
3566 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003567 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003568}
3569
3570/*
3571 * Turn job control on and off.
3572 *
3573 * Note: This code assumes that the third arg to ioctl is a character
3574 * pointer, which is true on Berkeley systems but not System V. Since
3575 * System V doesn't have job control yet, this isn't a problem now.
3576 *
3577 * Called with interrupts off.
3578 */
3579static void
3580setjobctl(int on)
3581{
3582 int fd;
3583 int pgrp;
3584
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003585 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003586 return;
3587 if (on) {
3588 int ofd;
3589 ofd = fd = open(_PATH_TTY, O_RDWR);
3590 if (fd < 0) {
3591 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3592 * That sometimes helps to acquire controlling tty.
3593 * Obviously, a workaround for bugs when someone
3594 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003595 fd = 2;
3596 while (!isatty(fd))
3597 if (--fd < 0)
3598 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003599 }
3600 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003601 if (ofd >= 0)
3602 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003603 if (fd < 0)
3604 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003605 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003606 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607 do { /* while we are in the background */
3608 pgrp = tcgetpgrp(fd);
3609 if (pgrp < 0) {
3610 out:
3611 ash_msg("can't access tty; job control turned off");
3612 mflag = on = 0;
3613 goto close;
3614 }
3615 if (pgrp == getpgrp())
3616 break;
3617 killpg(0, SIGTTIN);
3618 } while (1);
3619 initialpgrp = pgrp;
3620
3621 setsignal(SIGTSTP);
3622 setsignal(SIGTTOU);
3623 setsignal(SIGTTIN);
3624 pgrp = rootpid;
3625 setpgid(0, pgrp);
3626 xtcsetpgrp(fd, pgrp);
3627 } else {
3628 /* turning job control off */
3629 fd = ttyfd;
3630 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003631 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003632 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003633 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634 setpgid(0, pgrp);
3635 setsignal(SIGTSTP);
3636 setsignal(SIGTTOU);
3637 setsignal(SIGTTIN);
3638 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003639 if (fd >= 0)
3640 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003641 fd = -1;
3642 }
3643 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003644 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003645}
3646
3647static int
3648killcmd(int argc, char **argv)
3649{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003650 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003651 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003652 do {
3653 if (argv[i][0] == '%') {
3654 struct job *jp = getjob(argv[i], 0);
3655 unsigned pid = jp->ps[0].pid;
3656 /* Enough space for ' -NNN<nul>' */
3657 argv[i] = alloca(sizeof(int)*3 + 3);
3658 /* kill_main has matching code to expect
3659 * leading space. Needed to not confuse
3660 * negative pids with "kill -SIGNAL_NO" syntax */
3661 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003662 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003663 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003664 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003665 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003666}
3667
3668static void
3669showpipe(struct job *jp, FILE *out)
3670{
3671 struct procstat *sp;
3672 struct procstat *spend;
3673
3674 spend = jp->ps + jp->nprocs;
3675 for (sp = jp->ps + 1; sp < spend; sp++)
3676 fprintf(out, " | %s", sp->cmd);
3677 outcslow('\n', out);
3678 flush_stdout_stderr();
3679}
3680
3681
3682static int
3683restartjob(struct job *jp, int mode)
3684{
3685 struct procstat *ps;
3686 int i;
3687 int status;
3688 pid_t pgid;
3689
3690 INT_OFF;
3691 if (jp->state == JOBDONE)
3692 goto out;
3693 jp->state = JOBRUNNING;
3694 pgid = jp->ps->pid;
3695 if (mode == FORK_FG)
3696 xtcsetpgrp(ttyfd, pgid);
3697 killpg(pgid, SIGCONT);
3698 ps = jp->ps;
3699 i = jp->nprocs;
3700 do {
3701 if (WIFSTOPPED(ps->status)) {
3702 ps->status = -1;
3703 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003704 ps++;
3705 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003706 out:
3707 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3708 INT_ON;
3709 return status;
3710}
3711
3712static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003713fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003714{
3715 struct job *jp;
3716 FILE *out;
3717 int mode;
3718 int retval;
3719
3720 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3721 nextopt(nullstr);
3722 argv = argptr;
3723 out = stdout;
3724 do {
3725 jp = getjob(*argv, 1);
3726 if (mode == FORK_BG) {
3727 set_curjob(jp, CUR_RUNNING);
3728 fprintf(out, "[%d] ", jobno(jp));
3729 }
3730 outstr(jp->ps->cmd, out);
3731 showpipe(jp, out);
3732 retval = restartjob(jp, mode);
3733 } while (*argv && *++argv);
3734 return retval;
3735}
3736#endif
3737
3738static int
3739sprint_status(char *s, int status, int sigonly)
3740{
3741 int col;
3742 int st;
3743
3744 col = 0;
3745 if (!WIFEXITED(status)) {
3746#if JOBS
3747 if (WIFSTOPPED(status))
3748 st = WSTOPSIG(status);
3749 else
3750#endif
3751 st = WTERMSIG(status);
3752 if (sigonly) {
3753 if (st == SIGINT || st == SIGPIPE)
3754 goto out;
3755#if JOBS
3756 if (WIFSTOPPED(status))
3757 goto out;
3758#endif
3759 }
3760 st &= 0x7f;
3761 col = fmtstr(s, 32, strsignal(st));
3762 if (WCOREDUMP(status)) {
3763 col += fmtstr(s + col, 16, " (core dumped)");
3764 }
3765 } else if (!sigonly) {
3766 st = WEXITSTATUS(status);
3767 if (st)
3768 col = fmtstr(s, 16, "Done(%d)", st);
3769 else
3770 col = fmtstr(s, 16, "Done");
3771 }
3772 out:
3773 return col;
3774}
3775
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003777dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003778{
3779 int pid;
3780 int status;
3781 struct job *jp;
3782 struct job *thisjob;
3783 int state;
3784
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003785 TRACE(("dowait(0x%x) called\n", wait_flags));
3786
3787 /* Do a wait system call. If job control is compiled in, we accept
3788 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3789 * NB: _not_ safe_waitpid, we need to detect EINTR */
3790 pid = waitpid(-1, &status,
3791 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3792 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
3793
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003794 if (pid <= 0) {
3795 /* If we were doing blocking wait and (probably) got EINTR,
3796 * check for pending sigs received while waiting.
3797 * (NB: can be moved into callers if needed) */
3798 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3799 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003801 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802 INT_OFF;
3803 thisjob = NULL;
3804 for (jp = curjob; jp; jp = jp->prev_job) {
3805 struct procstat *sp;
3806 struct procstat *spend;
3807 if (jp->state == JOBDONE)
3808 continue;
3809 state = JOBDONE;
3810 spend = jp->ps + jp->nprocs;
3811 sp = jp->ps;
3812 do {
3813 if (sp->pid == pid) {
3814 TRACE(("Job %d: changing status of proc %d "
3815 "from 0x%x to 0x%x\n",
3816 jobno(jp), pid, sp->status, status));
3817 sp->status = status;
3818 thisjob = jp;
3819 }
3820 if (sp->status == -1)
3821 state = JOBRUNNING;
3822#if JOBS
3823 if (state == JOBRUNNING)
3824 continue;
3825 if (WIFSTOPPED(sp->status)) {
3826 jp->stopstatus = sp->status;
3827 state = JOBSTOPPED;
3828 }
3829#endif
3830 } while (++sp < spend);
3831 if (thisjob)
3832 goto gotjob;
3833 }
3834#if JOBS
3835 if (!WIFSTOPPED(status))
3836#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003837 jobless--;
3838 goto out;
3839
3840 gotjob:
3841 if (state != JOBRUNNING) {
3842 thisjob->changed = 1;
3843
3844 if (thisjob->state != state) {
3845 TRACE(("Job %d: changing state from %d to %d\n",
3846 jobno(thisjob), thisjob->state, state));
3847 thisjob->state = state;
3848#if JOBS
3849 if (state == JOBSTOPPED) {
3850 set_curjob(thisjob, CUR_STOPPED);
3851 }
3852#endif
3853 }
3854 }
3855
3856 out:
3857 INT_ON;
3858
3859 if (thisjob && thisjob == job) {
3860 char s[48 + 1];
3861 int len;
3862
3863 len = sprint_status(s, status, 1);
3864 if (len) {
3865 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003866 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003867 out2str(s);
3868 }
3869 }
3870 return pid;
3871}
3872
3873#if JOBS
3874static void
3875showjob(FILE *out, struct job *jp, int mode)
3876{
3877 struct procstat *ps;
3878 struct procstat *psend;
3879 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003880 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003881 char s[80];
3882
3883 ps = jp->ps;
3884
3885 if (mode & SHOW_PGID) {
3886 /* just output process (group) id of pipeline */
3887 fprintf(out, "%d\n", ps->pid);
3888 return;
3889 }
3890
3891 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003892 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003893
3894 if (jp == curjob)
3895 s[col - 2] = '+';
3896 else if (curjob && jp == curjob->prev_job)
3897 s[col - 2] = '-';
3898
3899 if (mode & SHOW_PID)
3900 col += fmtstr(s + col, 16, "%d ", ps->pid);
3901
3902 psend = ps + jp->nprocs;
3903
3904 if (jp->state == JOBRUNNING) {
3905 strcpy(s + col, "Running");
3906 col += sizeof("Running") - 1;
3907 } else {
3908 int status = psend[-1].status;
3909 if (jp->state == JOBSTOPPED)
3910 status = jp->stopstatus;
3911 col += sprint_status(s + col, status, 0);
3912 }
3913
3914 goto start;
3915
3916 do {
3917 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003918 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003919 start:
3920 fprintf(out, "%s%*c%s",
3921 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3922 );
3923 if (!(mode & SHOW_PID)) {
3924 showpipe(jp, out);
3925 break;
3926 }
3927 if (++ps == psend) {
3928 outcslow('\n', out);
3929 break;
3930 }
3931 } while (1);
3932
3933 jp->changed = 0;
3934
3935 if (jp->state == JOBDONE) {
3936 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3937 freejob(jp);
3938 }
3939}
3940
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003941/*
3942 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3943 * statuses have changed since the last call to showjobs.
3944 */
3945static void
3946showjobs(FILE *out, int mode)
3947{
3948 struct job *jp;
3949
3950 TRACE(("showjobs(%x) called\n", mode));
3951
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003952 /* If not even one job changed, there is nothing to do */
3953 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003954 continue;
3955
3956 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003957 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003958 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003959 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003960 }
3961}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003962
3963static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003964jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003965{
3966 int mode, m;
3967
3968 mode = 0;
3969 while ((m = nextopt("lp"))) {
3970 if (m == 'l')
3971 mode = SHOW_PID;
3972 else
3973 mode = SHOW_PGID;
3974 }
3975
3976 argv = argptr;
3977 if (*argv) {
3978 do
3979 showjob(stdout, getjob(*argv,0), mode);
3980 while (*++argv);
3981 } else
3982 showjobs(stdout, mode);
3983
3984 return 0;
3985}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003986#endif /* JOBS */
3987
3988static int
3989getstatus(struct job *job)
3990{
3991 int status;
3992 int retval;
3993
3994 status = job->ps[job->nprocs - 1].status;
3995 retval = WEXITSTATUS(status);
3996 if (!WIFEXITED(status)) {
3997#if JOBS
3998 retval = WSTOPSIG(status);
3999 if (!WIFSTOPPED(status))
4000#endif
4001 {
4002 /* XXX: limits number of signals */
4003 retval = WTERMSIG(status);
4004#if JOBS
4005 if (retval == SIGINT)
4006 job->sigint = 1;
4007#endif
4008 }
4009 retval += 128;
4010 }
4011 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4012 jobno(job), job->nprocs, status, retval));
4013 return retval;
4014}
4015
4016static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004017waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004018{
4019 struct job *job;
4020 int retval;
4021 struct job *jp;
4022
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004023// exsig++;
4024// xbarrier();
4025 if (pendingsig)
4026 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004027
4028 nextopt(nullstr);
4029 retval = 0;
4030
4031 argv = argptr;
4032 if (!*argv) {
4033 /* wait for all jobs */
4034 for (;;) {
4035 jp = curjob;
4036 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004037 if (!jp) /* no running procs */
4038 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004039 if (jp->state == JOBRUNNING)
4040 break;
4041 jp->waited = 1;
4042 jp = jp->prev_job;
4043 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004044 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004045 }
4046 }
4047
4048 retval = 127;
4049 do {
4050 if (**argv != '%') {
4051 pid_t pid = number(*argv);
4052 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004053 while (1) {
4054 if (!job)
4055 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004056 if (job->ps[job->nprocs - 1].pid == pid)
4057 break;
4058 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004059 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004060 } else
4061 job = getjob(*argv, 0);
4062 /* loop until process terminated or stopped */
4063 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004064 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065 job->waited = 1;
4066 retval = getstatus(job);
4067 repeat:
4068 ;
4069 } while (*++argv);
4070
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004071 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004072 return retval;
4073}
4074
4075static struct job *
4076growjobtab(void)
4077{
4078 size_t len;
4079 ptrdiff_t offset;
4080 struct job *jp, *jq;
4081
4082 len = njobs * sizeof(*jp);
4083 jq = jobtab;
4084 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4085
4086 offset = (char *)jp - (char *)jq;
4087 if (offset) {
4088 /* Relocate pointers */
4089 size_t l = len;
4090
4091 jq = (struct job *)((char *)jq + l);
4092 while (l) {
4093 l -= sizeof(*jp);
4094 jq--;
4095#define joff(p) ((struct job *)((char *)(p) + l))
4096#define jmove(p) (p) = (void *)((char *)(p) + offset)
4097 if (joff(jp)->ps == &jq->ps0)
4098 jmove(joff(jp)->ps);
4099 if (joff(jp)->prev_job)
4100 jmove(joff(jp)->prev_job);
4101 }
4102 if (curjob)
4103 jmove(curjob);
4104#undef joff
4105#undef jmove
4106 }
4107
4108 njobs += 4;
4109 jobtab = jp;
4110 jp = (struct job *)((char *)jp + len);
4111 jq = jp + 3;
4112 do {
4113 jq->used = 0;
4114 } while (--jq >= jp);
4115 return jp;
4116}
4117
4118/*
4119 * Return a new job structure.
4120 * Called with interrupts off.
4121 */
4122static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004123makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124{
4125 int i;
4126 struct job *jp;
4127
4128 for (i = njobs, jp = jobtab; ; jp++) {
4129 if (--i < 0) {
4130 jp = growjobtab();
4131 break;
4132 }
4133 if (jp->used == 0)
4134 break;
4135 if (jp->state != JOBDONE || !jp->waited)
4136 continue;
4137#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004138 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004139 continue;
4140#endif
4141 freejob(jp);
4142 break;
4143 }
4144 memset(jp, 0, sizeof(*jp));
4145#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004146 /* jp->jobctl is a bitfield.
4147 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004148 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004149 jp->jobctl = 1;
4150#endif
4151 jp->prev_job = curjob;
4152 curjob = jp;
4153 jp->used = 1;
4154 jp->ps = &jp->ps0;
4155 if (nprocs > 1) {
4156 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4157 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004158 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004159 jobno(jp)));
4160 return jp;
4161}
4162
4163#if JOBS
4164/*
4165 * Return a string identifying a command (to be printed by the
4166 * jobs command).
4167 */
4168static char *cmdnextc;
4169
4170static void
4171cmdputs(const char *s)
4172{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004173 static const char vstype[VSTYPE + 1][3] = {
4174 "", "}", "-", "+", "?", "=",
4175 "%", "%%", "#", "##"
4176 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4177 };
4178
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179 const char *p, *str;
4180 char c, cc[2] = " ";
4181 char *nextc;
4182 int subtype = 0;
4183 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184
4185 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4186 p = s;
4187 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004188 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004189 switch (c) {
4190 case CTLESC:
4191 c = *p++;
4192 break;
4193 case CTLVAR:
4194 subtype = *p++;
4195 if ((subtype & VSTYPE) == VSLENGTH)
4196 str = "${#";
4197 else
4198 str = "${";
4199 if (!(subtype & VSQUOTE) == !(quoted & 1))
4200 goto dostr;
4201 quoted ^= 1;
4202 c = '"';
4203 break;
4204 case CTLENDVAR:
4205 str = "\"}" + !(quoted & 1);
4206 quoted >>= 1;
4207 subtype = 0;
4208 goto dostr;
4209 case CTLBACKQ:
4210 str = "$(...)";
4211 goto dostr;
4212 case CTLBACKQ+CTLQUOTE:
4213 str = "\"$(...)\"";
4214 goto dostr;
4215#if ENABLE_ASH_MATH_SUPPORT
4216 case CTLARI:
4217 str = "$((";
4218 goto dostr;
4219 case CTLENDARI:
4220 str = "))";
4221 goto dostr;
4222#endif
4223 case CTLQUOTEMARK:
4224 quoted ^= 1;
4225 c = '"';
4226 break;
4227 case '=':
4228 if (subtype == 0)
4229 break;
4230 if ((subtype & VSTYPE) != VSNORMAL)
4231 quoted <<= 1;
4232 str = vstype[subtype & VSTYPE];
4233 if (subtype & VSNUL)
4234 c = ':';
4235 else
4236 goto checkstr;
4237 break;
4238 case '\'':
4239 case '\\':
4240 case '"':
4241 case '$':
4242 /* These can only happen inside quotes */
4243 cc[0] = c;
4244 str = cc;
4245 c = '\\';
4246 break;
4247 default:
4248 break;
4249 }
4250 USTPUTC(c, nextc);
4251 checkstr:
4252 if (!str)
4253 continue;
4254 dostr:
4255 while ((c = *str++)) {
4256 USTPUTC(c, nextc);
4257 }
4258 }
4259 if (quoted & 1) {
4260 USTPUTC('"', nextc);
4261 }
4262 *nextc = 0;
4263 cmdnextc = nextc;
4264}
4265
4266/* cmdtxt() and cmdlist() call each other */
4267static void cmdtxt(union node *n);
4268
4269static void
4270cmdlist(union node *np, int sep)
4271{
4272 for (; np; np = np->narg.next) {
4273 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004274 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004275 cmdtxt(np);
4276 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004277 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004278 }
4279}
4280
4281static void
4282cmdtxt(union node *n)
4283{
4284 union node *np;
4285 struct nodelist *lp;
4286 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004287
4288 if (!n)
4289 return;
4290 switch (n->type) {
4291 default:
4292#if DEBUG
4293 abort();
4294#endif
4295 case NPIPE:
4296 lp = n->npipe.cmdlist;
4297 for (;;) {
4298 cmdtxt(lp->n);
4299 lp = lp->next;
4300 if (!lp)
4301 break;
4302 cmdputs(" | ");
4303 }
4304 break;
4305 case NSEMI:
4306 p = "; ";
4307 goto binop;
4308 case NAND:
4309 p = " && ";
4310 goto binop;
4311 case NOR:
4312 p = " || ";
4313 binop:
4314 cmdtxt(n->nbinary.ch1);
4315 cmdputs(p);
4316 n = n->nbinary.ch2;
4317 goto donode;
4318 case NREDIR:
4319 case NBACKGND:
4320 n = n->nredir.n;
4321 goto donode;
4322 case NNOT:
4323 cmdputs("!");
4324 n = n->nnot.com;
4325 donode:
4326 cmdtxt(n);
4327 break;
4328 case NIF:
4329 cmdputs("if ");
4330 cmdtxt(n->nif.test);
4331 cmdputs("; then ");
4332 n = n->nif.ifpart;
4333 if (n->nif.elsepart) {
4334 cmdtxt(n);
4335 cmdputs("; else ");
4336 n = n->nif.elsepart;
4337 }
4338 p = "; fi";
4339 goto dotail;
4340 case NSUBSHELL:
4341 cmdputs("(");
4342 n = n->nredir.n;
4343 p = ")";
4344 goto dotail;
4345 case NWHILE:
4346 p = "while ";
4347 goto until;
4348 case NUNTIL:
4349 p = "until ";
4350 until:
4351 cmdputs(p);
4352 cmdtxt(n->nbinary.ch1);
4353 n = n->nbinary.ch2;
4354 p = "; done";
4355 dodo:
4356 cmdputs("; do ");
4357 dotail:
4358 cmdtxt(n);
4359 goto dotail2;
4360 case NFOR:
4361 cmdputs("for ");
4362 cmdputs(n->nfor.var);
4363 cmdputs(" in ");
4364 cmdlist(n->nfor.args, 1);
4365 n = n->nfor.body;
4366 p = "; done";
4367 goto dodo;
4368 case NDEFUN:
4369 cmdputs(n->narg.text);
4370 p = "() { ... }";
4371 goto dotail2;
4372 case NCMD:
4373 cmdlist(n->ncmd.args, 1);
4374 cmdlist(n->ncmd.redirect, 0);
4375 break;
4376 case NARG:
4377 p = n->narg.text;
4378 dotail2:
4379 cmdputs(p);
4380 break;
4381 case NHERE:
4382 case NXHERE:
4383 p = "<<...";
4384 goto dotail2;
4385 case NCASE:
4386 cmdputs("case ");
4387 cmdputs(n->ncase.expr->narg.text);
4388 cmdputs(" in ");
4389 for (np = n->ncase.cases; np; np = np->nclist.next) {
4390 cmdtxt(np->nclist.pattern);
4391 cmdputs(") ");
4392 cmdtxt(np->nclist.body);
4393 cmdputs(";; ");
4394 }
4395 p = "esac";
4396 goto dotail2;
4397 case NTO:
4398 p = ">";
4399 goto redir;
4400 case NCLOBBER:
4401 p = ">|";
4402 goto redir;
4403 case NAPPEND:
4404 p = ">>";
4405 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004406#if ENABLE_ASH_BASH_COMPAT
4407 case NTO2:
4408#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004409 case NTOFD:
4410 p = ">&";
4411 goto redir;
4412 case NFROM:
4413 p = "<";
4414 goto redir;
4415 case NFROMFD:
4416 p = "<&";
4417 goto redir;
4418 case NFROMTO:
4419 p = "<>";
4420 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004421 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004422 cmdputs(p);
4423 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004424 cmdputs(utoa(n->ndup.dupfd));
4425 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004426 }
4427 n = n->nfile.fname;
4428 goto donode;
4429 }
4430}
4431
4432static char *
4433commandtext(union node *n)
4434{
4435 char *name;
4436
4437 STARTSTACKSTR(cmdnextc);
4438 cmdtxt(n);
4439 name = stackblock();
4440 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4441 name, cmdnextc, cmdnextc));
4442 return ckstrdup(name);
4443}
4444#endif /* JOBS */
4445
4446/*
4447 * Fork off a subshell. If we are doing job control, give the subshell its
4448 * own process group. Jp is a job structure that the job is to be added to.
4449 * N is the command that will be evaluated by the child. Both jp and n may
4450 * be NULL. The mode parameter can be one of the following:
4451 * FORK_FG - Fork off a foreground process.
4452 * FORK_BG - Fork off a background process.
4453 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4454 * process group even if job control is on.
4455 *
4456 * When job control is turned off, background processes have their standard
4457 * input redirected to /dev/null (except for the second and later processes
4458 * in a pipeline).
4459 *
4460 * Called with interrupts off.
4461 */
4462/*
4463 * Clear traps on a fork.
4464 */
4465static void
4466clear_traps(void)
4467{
4468 char **tp;
4469
4470 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004471 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004472 INT_OFF;
4473 free(*tp);
4474 *tp = NULL;
4475 if (tp != &trap[0])
4476 setsignal(tp - trap);
4477 INT_ON;
4478 }
4479 }
4480}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004481
4482/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004483static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004484
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004485/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004486static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004487forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488{
4489 int oldlvl;
4490
4491 TRACE(("Child shell %d\n", getpid()));
4492 oldlvl = shlvl;
4493 shlvl++;
4494
4495 closescript();
4496 clear_traps();
4497#if JOBS
4498 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004499 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4501 pid_t pgrp;
4502
4503 if (jp->nprocs == 0)
4504 pgrp = getpid();
4505 else
4506 pgrp = jp->ps[0].pid;
4507 /* This can fail because we are doing it in the parent also */
4508 (void)setpgid(0, pgrp);
4509 if (mode == FORK_FG)
4510 xtcsetpgrp(ttyfd, pgrp);
4511 setsignal(SIGTSTP);
4512 setsignal(SIGTTOU);
4513 } else
4514#endif
4515 if (mode == FORK_BG) {
4516 ignoresig(SIGINT);
4517 ignoresig(SIGQUIT);
4518 if (jp->nprocs == 0) {
4519 close(0);
4520 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004521 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004522 }
4523 }
4524 if (!oldlvl && iflag) {
4525 setsignal(SIGINT);
4526 setsignal(SIGQUIT);
4527 setsignal(SIGTERM);
4528 }
4529 for (jp = curjob; jp; jp = jp->prev_job)
4530 freejob(jp);
4531 jobless = 0;
4532}
4533
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004534/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004535#if !JOBS
4536#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4537#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538static void
4539forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4540{
4541 TRACE(("In parent shell: child = %d\n", pid));
4542 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004543 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4544 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545 jobless++;
4546 return;
4547 }
4548#if JOBS
4549 if (mode != FORK_NOJOB && jp->jobctl) {
4550 int pgrp;
4551
4552 if (jp->nprocs == 0)
4553 pgrp = pid;
4554 else
4555 pgrp = jp->ps[0].pid;
4556 /* This can fail because we are doing it in the child also */
4557 setpgid(pid, pgrp);
4558 }
4559#endif
4560 if (mode == FORK_BG) {
4561 backgndpid = pid; /* set $! */
4562 set_curjob(jp, CUR_RUNNING);
4563 }
4564 if (jp) {
4565 struct procstat *ps = &jp->ps[jp->nprocs++];
4566 ps->pid = pid;
4567 ps->status = -1;
4568 ps->cmd = nullstr;
4569#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004570 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004571 ps->cmd = commandtext(n);
4572#endif
4573 }
4574}
4575
4576static int
4577forkshell(struct job *jp, union node *n, int mode)
4578{
4579 int pid;
4580
4581 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4582 pid = fork();
4583 if (pid < 0) {
4584 TRACE(("Fork failed, errno=%d", errno));
4585 if (jp)
4586 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004587 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004588 }
4589 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004590 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004591 else
4592 forkparent(jp, n, mode, pid);
4593 return pid;
4594}
4595
4596/*
4597 * Wait for job to finish.
4598 *
4599 * Under job control we have the problem that while a child process is
4600 * running interrupts generated by the user are sent to the child but not
4601 * to the shell. This means that an infinite loop started by an inter-
4602 * active user may be hard to kill. With job control turned off, an
4603 * interactive user may place an interactive program inside a loop. If
4604 * the interactive program catches interrupts, the user doesn't want
4605 * these interrupts to also abort the loop. The approach we take here
4606 * is to have the shell ignore interrupt signals while waiting for a
4607 * foreground process to terminate, and then send itself an interrupt
4608 * signal if the child process was terminated by an interrupt signal.
4609 * Unfortunately, some programs want to do a bit of cleanup and then
4610 * exit on interrupt; unless these processes terminate themselves by
4611 * sending a signal to themselves (instead of calling exit) they will
4612 * confuse this approach.
4613 *
4614 * Called with interrupts off.
4615 */
4616static int
4617waitforjob(struct job *jp)
4618{
4619 int st;
4620
4621 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4622 while (jp->state == JOBRUNNING) {
4623 dowait(DOWAIT_BLOCK, jp);
4624 }
4625 st = getstatus(jp);
4626#if JOBS
4627 if (jp->jobctl) {
4628 xtcsetpgrp(ttyfd, rootpid);
4629 /*
4630 * This is truly gross.
4631 * If we're doing job control, then we did a TIOCSPGRP which
4632 * caused us (the shell) to no longer be in the controlling
4633 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4634 * intuit from the subprocess exit status whether a SIGINT
4635 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4636 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004637 if (jp->sigint) /* TODO: do the same with all signals */
4638 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004639 }
4640 if (jp->state == JOBDONE)
4641#endif
4642 freejob(jp);
4643 return st;
4644}
4645
4646/*
4647 * return 1 if there are stopped jobs, otherwise 0
4648 */
4649static int
4650stoppedjobs(void)
4651{
4652 struct job *jp;
4653 int retval;
4654
4655 retval = 0;
4656 if (job_warning)
4657 goto out;
4658 jp = curjob;
4659 if (jp && jp->state == JOBSTOPPED) {
4660 out2str("You have stopped jobs.\n");
4661 job_warning = 2;
4662 retval++;
4663 }
4664 out:
4665 return retval;
4666}
4667
4668
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004669/* ============ redir.c
4670 *
4671 * Code for dealing with input/output redirection.
4672 */
4673
4674#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004675#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004676
4677/*
4678 * Open a file in noclobber mode.
4679 * The code was copied from bash.
4680 */
4681static int
4682noclobberopen(const char *fname)
4683{
4684 int r, fd;
4685 struct stat finfo, finfo2;
4686
4687 /*
4688 * If the file exists and is a regular file, return an error
4689 * immediately.
4690 */
4691 r = stat(fname, &finfo);
4692 if (r == 0 && S_ISREG(finfo.st_mode)) {
4693 errno = EEXIST;
4694 return -1;
4695 }
4696
4697 /*
4698 * If the file was not present (r != 0), make sure we open it
4699 * exclusively so that if it is created before we open it, our open
4700 * will fail. Make sure that we do not truncate an existing file.
4701 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4702 * file was not a regular file, we leave O_EXCL off.
4703 */
4704 if (r != 0)
4705 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4706 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4707
4708 /* If the open failed, return the file descriptor right away. */
4709 if (fd < 0)
4710 return fd;
4711
4712 /*
4713 * OK, the open succeeded, but the file may have been changed from a
4714 * non-regular file to a regular file between the stat and the open.
4715 * We are assuming that the O_EXCL open handles the case where FILENAME
4716 * did not exist and is symlinked to an existing file between the stat
4717 * and open.
4718 */
4719
4720 /*
4721 * If we can open it and fstat the file descriptor, and neither check
4722 * revealed that it was a regular file, and the file has not been
4723 * replaced, return the file descriptor.
4724 */
4725 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4726 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4727 return fd;
4728
4729 /* The file has been replaced. badness. */
4730 close(fd);
4731 errno = EEXIST;
4732 return -1;
4733}
4734
4735/*
4736 * Handle here documents. Normally we fork off a process to write the
4737 * data to a pipe. If the document is short, we can stuff the data in
4738 * the pipe without forking.
4739 */
4740/* openhere needs this forward reference */
4741static void expandhere(union node *arg, int fd);
4742static int
4743openhere(union node *redir)
4744{
4745 int pip[2];
4746 size_t len = 0;
4747
4748 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004749 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004750 if (redir->type == NHERE) {
4751 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004752 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004753 full_write(pip[1], redir->nhere.doc->narg.text, len);
4754 goto out;
4755 }
4756 }
4757 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004758 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004759 close(pip[0]);
4760 signal(SIGINT, SIG_IGN);
4761 signal(SIGQUIT, SIG_IGN);
4762 signal(SIGHUP, SIG_IGN);
4763#ifdef SIGTSTP
4764 signal(SIGTSTP, SIG_IGN);
4765#endif
4766 signal(SIGPIPE, SIG_DFL);
4767 if (redir->type == NHERE)
4768 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004769 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004770 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004771 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772 }
4773 out:
4774 close(pip[1]);
4775 return pip[0];
4776}
4777
4778static int
4779openredirect(union node *redir)
4780{
4781 char *fname;
4782 int f;
4783
4784 switch (redir->nfile.type) {
4785 case NFROM:
4786 fname = redir->nfile.expfname;
4787 f = open(fname, O_RDONLY);
4788 if (f < 0)
4789 goto eopen;
4790 break;
4791 case NFROMTO:
4792 fname = redir->nfile.expfname;
4793 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4794 if (f < 0)
4795 goto ecreate;
4796 break;
4797 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004798#if ENABLE_ASH_BASH_COMPAT
4799 case NTO2:
4800#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004801 /* Take care of noclobber mode. */
4802 if (Cflag) {
4803 fname = redir->nfile.expfname;
4804 f = noclobberopen(fname);
4805 if (f < 0)
4806 goto ecreate;
4807 break;
4808 }
4809 /* FALLTHROUGH */
4810 case NCLOBBER:
4811 fname = redir->nfile.expfname;
4812 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4813 if (f < 0)
4814 goto ecreate;
4815 break;
4816 case NAPPEND:
4817 fname = redir->nfile.expfname;
4818 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4819 if (f < 0)
4820 goto ecreate;
4821 break;
4822 default:
4823#if DEBUG
4824 abort();
4825#endif
4826 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004827/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004828// case NTOFD:
4829// case NFROMFD:
4830// f = -1;
4831// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004832 case NHERE:
4833 case NXHERE:
4834 f = openhere(redir);
4835 break;
4836 }
4837
4838 return f;
4839 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004840 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004841 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004842 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004843}
4844
4845/*
4846 * Copy a file descriptor to be >= to. Returns -1
4847 * if the source file descriptor is closed, EMPTY if there are no unused
4848 * file descriptors left.
4849 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004850/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4851 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004852enum {
4853 COPYFD_EXACT = (int)~(INT_MAX),
4854 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4855};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004856static int
4857copyfd(int from, int to)
4858{
4859 int newfd;
4860
Denis Vlasenko5a867312008-07-24 19:46:38 +00004861 if (to & COPYFD_EXACT) {
4862 to &= ~COPYFD_EXACT;
4863 /*if (from != to)*/
4864 newfd = dup2(from, to);
4865 } else {
4866 newfd = fcntl(from, F_DUPFD, to);
4867 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004868 if (newfd < 0) {
4869 if (errno == EMFILE)
4870 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004871 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004872 ash_msg_and_raise_error("%d: %m", from);
4873 }
4874 return newfd;
4875}
4876
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004877/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004878struct two_fd_t {
4879 int orig, copy;
4880};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004881struct redirtab {
4882 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004883 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004884 int pair_count;
4885 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004886};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004887#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004888
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004889static int need_to_remember(struct redirtab *rp, int fd)
4890{
4891 int i;
4892
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004893 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004894 return 0;
4895
4896 for (i = 0; i < rp->pair_count; i++) {
4897 if (rp->two_fd[i].orig == fd) {
4898 /* already remembered */
4899 return 0;
4900 }
4901 }
4902 return 1;
4903}
4904
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004905/* "hidden" fd is a fd used to read scripts, or a copy of such */
4906static int is_hidden_fd(struct redirtab *rp, int fd)
4907{
4908 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004909 struct parsefile *pf;
4910
4911 if (fd == -1)
4912 return 0;
4913 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004914 while (pf) {
4915 if (fd == pf->fd) {
4916 return 1;
4917 }
4918 pf = pf->prev;
4919 }
4920 if (!rp)
4921 return 0;
4922 fd |= COPYFD_RESTORE;
4923 for (i = 0; i < rp->pair_count; i++) {
4924 if (rp->two_fd[i].copy == fd) {
4925 return 1;
4926 }
4927 }
4928 return 0;
4929}
4930
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004931/*
4932 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4933 * old file descriptors are stashed away so that the redirection can be
4934 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4935 * standard output, and the standard error if it becomes a duplicate of
4936 * stdout, is saved in memory.
4937 */
4938/* flags passed to redirect */
4939#define REDIR_PUSH 01 /* save previous values of file descriptors */
4940#define REDIR_SAVEFD2 03 /* set preverrout */
4941static void
4942redirect(union node *redir, int flags)
4943{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004944 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004945 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004946 int i;
4947 int fd;
4948 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004949 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004950
Denis Vlasenko01631112007-12-16 17:20:38 +00004951 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004952 if (!redir) {
4953 return;
4954 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004955
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004957 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004958 INT_OFF;
4959 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004960 union node *tmp = redir;
4961 do {
4962 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004963#if ENABLE_ASH_BASH_COMPAT
4964 if (redir->nfile.type == NTO2)
4965 sv_pos++;
4966#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004967 tmp = tmp->nfile.next;
4968 } while (tmp);
4969 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004970 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004971 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004972 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004973 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004974 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004975 while (sv_pos > 0) {
4976 sv_pos--;
4977 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
4978 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004979 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004980
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004982 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004983 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004984 int right_fd = redir->ndup.dupfd;
4985 /* redirect from/to same file descriptor? */
4986 if (right_fd == fd)
4987 continue;
4988 /* echo >&10 and 10 is a fd opened to the sh script? */
4989 if (is_hidden_fd(sv, right_fd)) {
4990 errno = EBADF; /* as if it is closed */
4991 ash_msg_and_raise_error("%d: %m", right_fd);
4992 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004993 newfd = -1;
4994 } else {
4995 newfd = openredirect(redir); /* always >= 0 */
4996 if (fd == newfd) {
4997 /* Descriptor wasn't open before redirect.
4998 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004999 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005000 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005001 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005002 continue;
5003 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005004 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005005#if ENABLE_ASH_BASH_COMPAT
5006 redirect_more:
5007#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005008 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005009 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005010 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005011/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5012 * are closed in popredir() in the child, preventing them from leaking
5013 * into child. (popredir() also cleans up the mess in case of failures)
5014 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005015 if (i == -1) {
5016 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005017 if (i != EBADF) {
5018 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005019 if (newfd >= 0)
5020 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005021 errno = i;
5022 ash_msg_and_raise_error("%d: %m", fd);
5023 /* NOTREACHED */
5024 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005025 /* EBADF: it is not open - good, remember to close it */
5026 remember_to_close:
5027 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005028 } else { /* fd is open, save its copy */
5029 /* "exec fd>&-" should not close fds
5030 * which point to script file(s).
5031 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005032 if (is_hidden_fd(sv, fd))
5033 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005034 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005035 if (fd == 2)
5036 copied_fd2 = i;
5037 sv->two_fd[sv_pos].orig = fd;
5038 sv->two_fd[sv_pos].copy = i;
5039 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005040 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005041 if (newfd < 0) {
5042 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005043 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005044 close(fd);
5045 } else {
5046 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005047 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005048 } else if (fd != newfd) { /* move newfd to fd */
5049 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005050#if ENABLE_ASH_BASH_COMPAT
5051 if (!(redir->nfile.type == NTO2 && fd == 2))
5052#endif
5053 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005054 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005055#if ENABLE_ASH_BASH_COMPAT
5056 if (redir->nfile.type == NTO2 && fd == 1) {
5057 /* We already redirected it to fd 1, now copy it to 2 */
5058 newfd = 1;
5059 fd = 2;
5060 goto redirect_more;
5061 }
5062#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005063 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005064
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005066 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5067 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005068}
5069
5070/*
5071 * Undo the effects of the last redirection.
5072 */
5073static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005074popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005075{
5076 struct redirtab *rp;
5077 int i;
5078
Denis Vlasenko01631112007-12-16 17:20:38 +00005079 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005080 return;
5081 INT_OFF;
5082 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005083 for (i = 0; i < rp->pair_count; i++) {
5084 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005085 int copy = rp->two_fd[i].copy;
5086 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005087 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005088 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005089 continue;
5090 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005091 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005092 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005093 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005094 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005095 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005096 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005097 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005098 }
5099 }
5100 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005101 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005102 free(rp);
5103 INT_ON;
5104}
5105
5106/*
5107 * Undo all redirections. Called on error or interrupt.
5108 */
5109
5110/*
5111 * Discard all saved file descriptors.
5112 */
5113static void
5114clearredir(int drop)
5115{
5116 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005117 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 if (!redirlist)
5119 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005120 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 }
5122}
5123
5124static int
5125redirectsafe(union node *redir, int flags)
5126{
5127 int err;
5128 volatile int saveint;
5129 struct jmploc *volatile savehandler = exception_handler;
5130 struct jmploc jmploc;
5131
5132 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005133 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5134 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005135 if (!err) {
5136 exception_handler = &jmploc;
5137 redirect(redir, flags);
5138 }
5139 exception_handler = savehandler;
5140 if (err && exception != EXERROR)
5141 longjmp(exception_handler->loc, 1);
5142 RESTORE_INT(saveint);
5143 return err;
5144}
5145
5146
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005147/* ============ Routines to expand arguments to commands
5148 *
5149 * We have to deal with backquotes, shell variables, and file metacharacters.
5150 */
5151
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005152#if ENABLE_ASH_MATH_SUPPORT_64
5153typedef int64_t arith_t;
5154#define arith_t_type long long
5155#else
5156typedef long arith_t;
5157#define arith_t_type long
5158#endif
5159
5160#if ENABLE_ASH_MATH_SUPPORT
5161static arith_t dash_arith(const char *);
5162static arith_t arith(const char *expr, int *perrcode);
5163#endif
5164
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005165/*
5166 * expandarg flags
5167 */
5168#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5169#define EXP_TILDE 0x2 /* do normal tilde expansion */
5170#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5171#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5172#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5173#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5174#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5175#define EXP_WORD 0x80 /* expand word in parameter expansion */
5176#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5177/*
5178 * _rmescape() flags
5179 */
5180#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5181#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5182#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5183#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5184#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5185
5186/*
5187 * Structure specifying which parts of the string should be searched
5188 * for IFS characters.
5189 */
5190struct ifsregion {
5191 struct ifsregion *next; /* next region in list */
5192 int begoff; /* offset of start of region */
5193 int endoff; /* offset of end of region */
5194 int nulonly; /* search for nul bytes only */
5195};
5196
5197struct arglist {
5198 struct strlist *list;
5199 struct strlist **lastp;
5200};
5201
5202/* output of current string */
5203static char *expdest;
5204/* list of back quote expressions */
5205static struct nodelist *argbackq;
5206/* first struct in list of ifs regions */
5207static struct ifsregion ifsfirst;
5208/* last struct in list */
5209static struct ifsregion *ifslastp;
5210/* holds expanded arg list */
5211static struct arglist exparg;
5212
5213/*
5214 * Our own itoa().
5215 */
5216static int
5217cvtnum(arith_t num)
5218{
5219 int len;
5220
5221 expdest = makestrspace(32, expdest);
5222#if ENABLE_ASH_MATH_SUPPORT_64
5223 len = fmtstr(expdest, 32, "%lld", (long long) num);
5224#else
5225 len = fmtstr(expdest, 32, "%ld", num);
5226#endif
5227 STADJUST(len, expdest);
5228 return len;
5229}
5230
5231static size_t
5232esclen(const char *start, const char *p)
5233{
5234 size_t esc = 0;
5235
5236 while (p > start && *--p == CTLESC) {
5237 esc++;
5238 }
5239 return esc;
5240}
5241
5242/*
5243 * Remove any CTLESC characters from a string.
5244 */
5245static char *
5246_rmescapes(char *str, int flag)
5247{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005248 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005249
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005250 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005251 unsigned inquotes;
5252 int notescaped;
5253 int globbing;
5254
5255 p = strpbrk(str, qchars);
5256 if (!p) {
5257 return str;
5258 }
5259 q = p;
5260 r = str;
5261 if (flag & RMESCAPE_ALLOC) {
5262 size_t len = p - str;
5263 size_t fulllen = len + strlen(p) + 1;
5264
5265 if (flag & RMESCAPE_GROW) {
5266 r = makestrspace(fulllen, expdest);
5267 } else if (flag & RMESCAPE_HEAP) {
5268 r = ckmalloc(fulllen);
5269 } else {
5270 r = stalloc(fulllen);
5271 }
5272 q = r;
5273 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005274 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005275 }
5276 }
5277 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5278 globbing = flag & RMESCAPE_GLOB;
5279 notescaped = globbing;
5280 while (*p) {
5281 if (*p == CTLQUOTEMARK) {
5282 inquotes = ~inquotes;
5283 p++;
5284 notescaped = globbing;
5285 continue;
5286 }
5287 if (*p == '\\') {
5288 /* naked back slash */
5289 notescaped = 0;
5290 goto copy;
5291 }
5292 if (*p == CTLESC) {
5293 p++;
5294 if (notescaped && inquotes && *p != '/') {
5295 *q++ = '\\';
5296 }
5297 }
5298 notescaped = globbing;
5299 copy:
5300 *q++ = *p++;
5301 }
5302 *q = '\0';
5303 if (flag & RMESCAPE_GROW) {
5304 expdest = r;
5305 STADJUST(q - r + 1, expdest);
5306 }
5307 return r;
5308}
5309#define rmescapes(p) _rmescapes((p), 0)
5310
5311#define pmatch(a, b) !fnmatch((a), (b), 0)
5312
5313/*
5314 * Prepare a pattern for a expmeta (internal glob(3)) call.
5315 *
5316 * Returns an stalloced string.
5317 */
5318static char *
5319preglob(const char *pattern, int quoted, int flag)
5320{
5321 flag |= RMESCAPE_GLOB;
5322 if (quoted) {
5323 flag |= RMESCAPE_QUOTED;
5324 }
5325 return _rmescapes((char *)pattern, flag);
5326}
5327
5328/*
5329 * Put a string on the stack.
5330 */
5331static void
5332memtodest(const char *p, size_t len, int syntax, int quotes)
5333{
5334 char *q = expdest;
5335
5336 q = makestrspace(len * 2, q);
5337
5338 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005339 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005340 if (!c)
5341 continue;
5342 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5343 USTPUTC(CTLESC, q);
5344 USTPUTC(c, q);
5345 }
5346
5347 expdest = q;
5348}
5349
5350static void
5351strtodest(const char *p, int syntax, int quotes)
5352{
5353 memtodest(p, strlen(p), syntax, quotes);
5354}
5355
5356/*
5357 * Record the fact that we have to scan this region of the
5358 * string for IFS characters.
5359 */
5360static void
5361recordregion(int start, int end, int nulonly)
5362{
5363 struct ifsregion *ifsp;
5364
5365 if (ifslastp == NULL) {
5366 ifsp = &ifsfirst;
5367 } else {
5368 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005369 ifsp = ckzalloc(sizeof(*ifsp));
5370 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005371 ifslastp->next = ifsp;
5372 INT_ON;
5373 }
5374 ifslastp = ifsp;
5375 ifslastp->begoff = start;
5376 ifslastp->endoff = end;
5377 ifslastp->nulonly = nulonly;
5378}
5379
5380static void
5381removerecordregions(int endoff)
5382{
5383 if (ifslastp == NULL)
5384 return;
5385
5386 if (ifsfirst.endoff > endoff) {
5387 while (ifsfirst.next != NULL) {
5388 struct ifsregion *ifsp;
5389 INT_OFF;
5390 ifsp = ifsfirst.next->next;
5391 free(ifsfirst.next);
5392 ifsfirst.next = ifsp;
5393 INT_ON;
5394 }
5395 if (ifsfirst.begoff > endoff)
5396 ifslastp = NULL;
5397 else {
5398 ifslastp = &ifsfirst;
5399 ifsfirst.endoff = endoff;
5400 }
5401 return;
5402 }
5403
5404 ifslastp = &ifsfirst;
5405 while (ifslastp->next && ifslastp->next->begoff < endoff)
5406 ifslastp=ifslastp->next;
5407 while (ifslastp->next != NULL) {
5408 struct ifsregion *ifsp;
5409 INT_OFF;
5410 ifsp = ifslastp->next->next;
5411 free(ifslastp->next);
5412 ifslastp->next = ifsp;
5413 INT_ON;
5414 }
5415 if (ifslastp->endoff > endoff)
5416 ifslastp->endoff = endoff;
5417}
5418
5419static char *
5420exptilde(char *startp, char *p, int flag)
5421{
5422 char c;
5423 char *name;
5424 struct passwd *pw;
5425 const char *home;
5426 int quotes = flag & (EXP_FULL | EXP_CASE);
5427 int startloc;
5428
5429 name = p + 1;
5430
5431 while ((c = *++p) != '\0') {
5432 switch (c) {
5433 case CTLESC:
5434 return startp;
5435 case CTLQUOTEMARK:
5436 return startp;
5437 case ':':
5438 if (flag & EXP_VARTILDE)
5439 goto done;
5440 break;
5441 case '/':
5442 case CTLENDVAR:
5443 goto done;
5444 }
5445 }
5446 done:
5447 *p = '\0';
5448 if (*name == '\0') {
5449 home = lookupvar(homestr);
5450 } else {
5451 pw = getpwnam(name);
5452 if (pw == NULL)
5453 goto lose;
5454 home = pw->pw_dir;
5455 }
5456 if (!home || !*home)
5457 goto lose;
5458 *p = c;
5459 startloc = expdest - (char *)stackblock();
5460 strtodest(home, SQSYNTAX, quotes);
5461 recordregion(startloc, expdest - (char *)stackblock(), 0);
5462 return p;
5463 lose:
5464 *p = c;
5465 return startp;
5466}
5467
5468/*
5469 * Execute a command inside back quotes. If it's a builtin command, we
5470 * want to save its output in a block obtained from malloc. Otherwise
5471 * we fork off a subprocess and get the output of the command via a pipe.
5472 * Should be called with interrupts off.
5473 */
5474struct backcmd { /* result of evalbackcmd */
5475 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005476 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005477 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005478 struct job *jp; /* job structure for command */
5479};
5480
5481/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005482static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005483#define EV_EXIT 01 /* exit after evaluating tree */
5484static void evaltree(union node *, int);
5485
5486static void
5487evalbackcmd(union node *n, struct backcmd *result)
5488{
5489 int saveherefd;
5490
5491 result->fd = -1;
5492 result->buf = NULL;
5493 result->nleft = 0;
5494 result->jp = NULL;
5495 if (n == NULL) {
5496 goto out;
5497 }
5498
5499 saveherefd = herefd;
5500 herefd = -1;
5501
5502 {
5503 int pip[2];
5504 struct job *jp;
5505
5506 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005507 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005508 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005509 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5510 FORCE_INT_ON;
5511 close(pip[0]);
5512 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005513 /*close(1);*/
5514 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005515 close(pip[1]);
5516 }
5517 eflag = 0;
5518 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5519 /* NOTREACHED */
5520 }
5521 close(pip[1]);
5522 result->fd = pip[0];
5523 result->jp = jp;
5524 }
5525 herefd = saveherefd;
5526 out:
5527 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5528 result->fd, result->buf, result->nleft, result->jp));
5529}
5530
5531/*
5532 * Expand stuff in backwards quotes.
5533 */
5534static void
5535expbackq(union node *cmd, int quoted, int quotes)
5536{
5537 struct backcmd in;
5538 int i;
5539 char buf[128];
5540 char *p;
5541 char *dest;
5542 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005543 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005544 struct stackmark smark;
5545
5546 INT_OFF;
5547 setstackmark(&smark);
5548 dest = expdest;
5549 startloc = dest - (char *)stackblock();
5550 grabstackstr(dest);
5551 evalbackcmd(cmd, &in);
5552 popstackmark(&smark);
5553
5554 p = in.buf;
5555 i = in.nleft;
5556 if (i == 0)
5557 goto read;
5558 for (;;) {
5559 memtodest(p, i, syntax, quotes);
5560 read:
5561 if (in.fd < 0)
5562 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005563 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005564 TRACE(("expbackq: read returns %d\n", i));
5565 if (i <= 0)
5566 break;
5567 p = buf;
5568 }
5569
Denis Vlasenko60818682007-09-28 22:07:23 +00005570 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005571 if (in.fd >= 0) {
5572 close(in.fd);
5573 back_exitstatus = waitforjob(in.jp);
5574 }
5575 INT_ON;
5576
5577 /* Eat all trailing newlines */
5578 dest = expdest;
5579 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5580 STUNPUTC(dest);
5581 expdest = dest;
5582
5583 if (quoted == 0)
5584 recordregion(startloc, dest - (char *)stackblock(), 0);
5585 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5586 (dest - (char *)stackblock()) - startloc,
5587 (dest - (char *)stackblock()) - startloc,
5588 stackblock() + startloc));
5589}
5590
5591#if ENABLE_ASH_MATH_SUPPORT
5592/*
5593 * Expand arithmetic expression. Backup to start of expression,
5594 * evaluate, place result in (backed up) result, adjust string position.
5595 */
5596static void
5597expari(int quotes)
5598{
5599 char *p, *start;
5600 int begoff;
5601 int flag;
5602 int len;
5603
5604 /* ifsfree(); */
5605
5606 /*
5607 * This routine is slightly over-complicated for
5608 * efficiency. Next we scan backwards looking for the
5609 * start of arithmetic.
5610 */
5611 start = stackblock();
5612 p = expdest - 1;
5613 *p = '\0';
5614 p--;
5615 do {
5616 int esc;
5617
5618 while (*p != CTLARI) {
5619 p--;
5620#if DEBUG
5621 if (p < start) {
5622 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5623 }
5624#endif
5625 }
5626
5627 esc = esclen(start, p);
5628 if (!(esc % 2)) {
5629 break;
5630 }
5631
5632 p -= esc + 1;
5633 } while (1);
5634
5635 begoff = p - start;
5636
5637 removerecordregions(begoff);
5638
5639 flag = p[1];
5640
5641 expdest = p;
5642
5643 if (quotes)
5644 rmescapes(p + 2);
5645
5646 len = cvtnum(dash_arith(p + 2));
5647
5648 if (flag != '"')
5649 recordregion(begoff, begoff + len, 0);
5650}
5651#endif
5652
5653/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005654static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005655
5656/*
5657 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5658 * characters to allow for further processing. Otherwise treat
5659 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005660 *
5661 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5662 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5663 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005664 */
5665static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005666argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005667{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005668 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005669 '=',
5670 ':',
5671 CTLQUOTEMARK,
5672 CTLENDVAR,
5673 CTLESC,
5674 CTLVAR,
5675 CTLBACKQ,
5676 CTLBACKQ | CTLQUOTE,
5677#if ENABLE_ASH_MATH_SUPPORT
5678 CTLENDARI,
5679#endif
5680 0
5681 };
5682 const char *reject = spclchars;
5683 int c;
5684 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5685 int breakall = flag & EXP_WORD;
5686 int inquotes;
5687 size_t length;
5688 int startloc;
5689
5690 if (!(flag & EXP_VARTILDE)) {
5691 reject += 2;
5692 } else if (flag & EXP_VARTILDE2) {
5693 reject++;
5694 }
5695 inquotes = 0;
5696 length = 0;
5697 if (flag & EXP_TILDE) {
5698 char *q;
5699
5700 flag &= ~EXP_TILDE;
5701 tilde:
5702 q = p;
5703 if (*q == CTLESC && (flag & EXP_QWORD))
5704 q++;
5705 if (*q == '~')
5706 p = exptilde(p, q, flag);
5707 }
5708 start:
5709 startloc = expdest - (char *)stackblock();
5710 for (;;) {
5711 length += strcspn(p + length, reject);
5712 c = p[length];
5713 if (c && (!(c & 0x80)
5714#if ENABLE_ASH_MATH_SUPPORT
5715 || c == CTLENDARI
5716#endif
5717 )) {
5718 /* c == '=' || c == ':' || c == CTLENDARI */
5719 length++;
5720 }
5721 if (length > 0) {
5722 int newloc;
5723 expdest = stack_nputstr(p, length, expdest);
5724 newloc = expdest - (char *)stackblock();
5725 if (breakall && !inquotes && newloc > startloc) {
5726 recordregion(startloc, newloc, 0);
5727 }
5728 startloc = newloc;
5729 }
5730 p += length + 1;
5731 length = 0;
5732
5733 switch (c) {
5734 case '\0':
5735 goto breakloop;
5736 case '=':
5737 if (flag & EXP_VARTILDE2) {
5738 p--;
5739 continue;
5740 }
5741 flag |= EXP_VARTILDE2;
5742 reject++;
5743 /* fall through */
5744 case ':':
5745 /*
5746 * sort of a hack - expand tildes in variable
5747 * assignments (after the first '=' and after ':'s).
5748 */
5749 if (*--p == '~') {
5750 goto tilde;
5751 }
5752 continue;
5753 }
5754
5755 switch (c) {
5756 case CTLENDVAR: /* ??? */
5757 goto breakloop;
5758 case CTLQUOTEMARK:
5759 /* "$@" syntax adherence hack */
5760 if (
5761 !inquotes &&
5762 !memcmp(p, dolatstr, 4) &&
5763 (p[4] == CTLQUOTEMARK || (
5764 p[4] == CTLENDVAR &&
5765 p[5] == CTLQUOTEMARK
5766 ))
5767 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005768 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005769 goto start;
5770 }
5771 inquotes = !inquotes;
5772 addquote:
5773 if (quotes) {
5774 p--;
5775 length++;
5776 startloc++;
5777 }
5778 break;
5779 case CTLESC:
5780 startloc++;
5781 length++;
5782 goto addquote;
5783 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005784 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005785 goto start;
5786 case CTLBACKQ:
5787 c = 0;
5788 case CTLBACKQ|CTLQUOTE:
5789 expbackq(argbackq->n, c, quotes);
5790 argbackq = argbackq->next;
5791 goto start;
5792#if ENABLE_ASH_MATH_SUPPORT
5793 case CTLENDARI:
5794 p--;
5795 expari(quotes);
5796 goto start;
5797#endif
5798 }
5799 }
5800 breakloop:
5801 ;
5802}
5803
5804static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005805scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005806 int zero)
5807{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005808// This commented out code was added by James Simmons <jsimmons@infradead.org>
5809// as part of a larger change when he added support for ${var/a/b}.
5810// However, it broke # and % operators:
5811//
5812//var=ababcdcd
5813// ok bad
5814//echo ${var#ab} abcdcd abcdcd
5815//echo ${var##ab} abcdcd abcdcd
5816//echo ${var#a*b} abcdcd ababcdcd (!)
5817//echo ${var##a*b} cdcd cdcd
5818//echo ${var#?} babcdcd ababcdcd (!)
5819//echo ${var##?} babcdcd babcdcd
5820//echo ${var#*} ababcdcd babcdcd (!)
5821//echo ${var##*}
5822//echo ${var%cd} ababcd ababcd
5823//echo ${var%%cd} ababcd abab (!)
5824//echo ${var%c*d} ababcd ababcd
5825//echo ${var%%c*d} abab ababcdcd (!)
5826//echo ${var%?} ababcdc ababcdc
5827//echo ${var%%?} ababcdc ababcdcd (!)
5828//echo ${var%*} ababcdcd ababcdcd
5829//echo ${var%%*}
5830//
5831// Commenting it back out helped. Remove it completely if it really
5832// is not needed.
5833
5834 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005835 char c;
5836
5837 loc = startp;
5838 loc2 = rmesc;
5839 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005840 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005841 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005842
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005843 c = *loc2;
5844 if (zero) {
5845 *loc2 = '\0';
5846 s = rmesc;
5847 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005848 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005849
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005850// // chop off end if its '*'
5851// full = strrchr(str, '*');
5852// if (full && full != str)
5853// match--;
5854//
5855// // If str starts with '*' replace with s.
5856// if ((*str == '*') && strlen(s) >= match) {
5857// full = xstrdup(s);
5858// strncpy(full+strlen(s)-match+1, str+1, match-1);
5859// } else
5860// full = xstrndup(str, match);
5861// match = strncmp(s, full, strlen(full));
5862// free(full);
5863//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005864 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005865 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 return loc;
5867 if (quotes && *loc == CTLESC)
5868 loc++;
5869 loc++;
5870 loc2++;
5871 } while (c);
5872 return 0;
5873}
5874
5875static char *
5876scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5877 int zero)
5878{
5879 int esc = 0;
5880 char *loc;
5881 char *loc2;
5882
5883 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5884 int match;
5885 char c = *loc2;
5886 const char *s = loc2;
5887 if (zero) {
5888 *loc2 = '\0';
5889 s = rmesc;
5890 }
5891 match = pmatch(str, s);
5892 *loc2 = c;
5893 if (match)
5894 return loc;
5895 loc--;
5896 if (quotes) {
5897 if (--esc < 0) {
5898 esc = esclen(startp, loc);
5899 }
5900 if (esc % 2) {
5901 esc--;
5902 loc--;
5903 }
5904 }
5905 }
5906 return 0;
5907}
5908
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005909static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005910static void
5911varunset(const char *end, const char *var, const char *umsg, int varflags)
5912{
5913 const char *msg;
5914 const char *tail;
5915
5916 tail = nullstr;
5917 msg = "parameter not set";
5918 if (umsg) {
5919 if (*end == CTLENDVAR) {
5920 if (varflags & VSNUL)
5921 tail = " or null";
5922 } else
5923 msg = umsg;
5924 }
5925 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5926}
5927
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005928#if ENABLE_ASH_BASH_COMPAT
5929static char *
5930parse_sub_pattern(char *arg, int inquotes)
5931{
5932 char *idx, *repl = NULL;
5933 unsigned char c;
5934
Denis Vlasenko2659c632008-06-14 06:04:59 +00005935 idx = arg;
5936 while (1) {
5937 c = *arg;
5938 if (!c)
5939 break;
5940 if (c == '/') {
5941 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005942 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005943 repl = idx + 1;
5944 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005945 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005946 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005947 *idx++ = c;
5948 if (!inquotes && c == '\\' && arg[1] == '\\')
5949 arg++; /* skip both \\, not just first one */
5950 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005951 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005952 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953
5954 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005955}
5956#endif /* ENABLE_ASH_BASH_COMPAT */
5957
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005959subevalvar(char *p, char *str, int strloc, int subtype,
5960 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005961{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005962 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963 char *startp;
5964 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005965 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005966 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5967 USE_ASH_BASH_COMPAT(char null = '\0';)
5968 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5969 int saveherefd = herefd;
5970 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005972 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005973
5974 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005975 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5976 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977 STPUTC('\0', expdest);
5978 herefd = saveherefd;
5979 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005980 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981
5982 switch (subtype) {
5983 case VSASSIGN:
5984 setvar(str, startp, 0);
5985 amount = startp - expdest;
5986 STADJUST(amount, expdest);
5987 return startp;
5988
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005989#if ENABLE_ASH_BASH_COMPAT
5990 case VSSUBSTR:
5991 loc = str = stackblock() + strloc;
5992// TODO: number() instead? It does error checking...
5993 pos = atoi(loc);
5994 len = str - startp - 1;
5995
5996 /* *loc != '\0', guaranteed by parser */
5997 if (quotes) {
5998 char *ptr;
5999
6000 /* We must adjust the length by the number of escapes we find. */
6001 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006002 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006003 len--;
6004 ptr++;
6005 }
6006 }
6007 }
6008 orig_len = len;
6009
6010 if (*loc++ == ':') {
6011// TODO: number() instead? It does error checking...
6012 len = atoi(loc);
6013 } else {
6014 len = orig_len;
6015 while (*loc && *loc != ':')
6016 loc++;
6017 if (*loc++ == ':')
6018// TODO: number() instead? It does error checking...
6019 len = atoi(loc);
6020 }
6021 if (pos >= orig_len) {
6022 pos = 0;
6023 len = 0;
6024 }
6025 if (len > (orig_len - pos))
6026 len = orig_len - pos;
6027
6028 for (str = startp; pos; str++, pos--) {
6029 if (quotes && *str == CTLESC)
6030 str++;
6031 }
6032 for (loc = startp; len; len--) {
6033 if (quotes && *str == CTLESC)
6034 *loc++ = *str++;
6035 *loc++ = *str++;
6036 }
6037 *loc = '\0';
6038 amount = loc - expdest;
6039 STADJUST(amount, expdest);
6040 return loc;
6041#endif
6042
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 case VSQUESTION:
6044 varunset(p, str, startp, varflags);
6045 /* NOTREACHED */
6046 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006047 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006049 /* We'll comeback here if we grow the stack while handling
6050 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6051 * stack will need rebasing, and we'll need to remove our work
6052 * areas each time
6053 */
6054 USE_ASH_BASH_COMPAT(restart:)
6055
6056 amount = expdest - ((char *)stackblock() + resetloc);
6057 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006058 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006059
6060 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006061 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006062 if (quotes) {
6063 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6064 if (rmesc != startp) {
6065 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006066 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006067 }
6068 }
6069 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006070 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006072 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006073
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006074#if ENABLE_ASH_BASH_COMPAT
6075 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6076 char *idx, *end, *restart_detect;
6077
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006078 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006079 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6080 if (!repl)
6081 repl = &null;
6082 }
6083
6084 /* If there's no pattern to match, return the expansion unmolested */
6085 if (*str == '\0')
6086 return 0;
6087
6088 len = 0;
6089 idx = startp;
6090 end = str - 1;
6091 while (idx < end) {
6092 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6093 if (!loc) {
6094 /* No match, advance */
6095 restart_detect = stackblock();
6096 STPUTC(*idx, expdest);
6097 if (quotes && *idx == CTLESC) {
6098 idx++;
6099 len++;
6100 STPUTC(*idx, expdest);
6101 }
6102 if (stackblock() != restart_detect)
6103 goto restart;
6104 idx++;
6105 len++;
6106 rmesc++;
6107 continue;
6108 }
6109
6110 if (subtype == VSREPLACEALL) {
6111 while (idx < loc) {
6112 if (quotes && *idx == CTLESC)
6113 idx++;
6114 idx++;
6115 rmesc++;
6116 }
6117 } else
6118 idx = loc;
6119
6120 for (loc = repl; *loc; loc++) {
6121 restart_detect = stackblock();
6122 STPUTC(*loc, expdest);
6123 if (stackblock() != restart_detect)
6124 goto restart;
6125 len++;
6126 }
6127
6128 if (subtype == VSREPLACE) {
6129 while (*idx) {
6130 restart_detect = stackblock();
6131 STPUTC(*idx, expdest);
6132 if (stackblock() != restart_detect)
6133 goto restart;
6134 len++;
6135 idx++;
6136 }
6137 break;
6138 }
6139 }
6140
6141 /* We've put the replaced text into a buffer at workloc, now
6142 * move it to the right place and adjust the stack.
6143 */
6144 startp = stackblock() + startloc;
6145 STPUTC('\0', expdest);
6146 memmove(startp, stackblock() + workloc, len);
6147 startp[len++] = '\0';
6148 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6149 STADJUST(-amount, expdest);
6150 return startp;
6151 }
6152#endif /* ENABLE_ASH_BASH_COMPAT */
6153
6154 subtype -= VSTRIMRIGHT;
6155#if DEBUG
6156 if (subtype < 0 || subtype > 7)
6157 abort();
6158#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006159 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6160 zero = subtype >> 1;
6161 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6162 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6163
6164 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6165 if (loc) {
6166 if (zero) {
6167 memmove(startp, loc, str - loc);
6168 loc = startp + (str - loc) - 1;
6169 }
6170 *loc = '\0';
6171 amount = loc - expdest;
6172 STADJUST(amount, expdest);
6173 }
6174 return loc;
6175}
6176
6177/*
6178 * Add the value of a specialized variable to the stack string.
6179 */
6180static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006181varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182{
6183 int num;
6184 char *p;
6185 int i;
6186 int sep = 0;
6187 int sepq = 0;
6188 ssize_t len = 0;
6189 char **ap;
6190 int syntax;
6191 int quoted = varflags & VSQUOTE;
6192 int subtype = varflags & VSTYPE;
6193 int quotes = flags & (EXP_FULL | EXP_CASE);
6194
6195 if (quoted && (flags & EXP_FULL))
6196 sep = 1 << CHAR_BIT;
6197
6198 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6199 switch (*name) {
6200 case '$':
6201 num = rootpid;
6202 goto numvar;
6203 case '?':
6204 num = exitstatus;
6205 goto numvar;
6206 case '#':
6207 num = shellparam.nparam;
6208 goto numvar;
6209 case '!':
6210 num = backgndpid;
6211 if (num == 0)
6212 return -1;
6213 numvar:
6214 len = cvtnum(num);
6215 break;
6216 case '-':
6217 p = makestrspace(NOPTS, expdest);
6218 for (i = NOPTS - 1; i >= 0; i--) {
6219 if (optlist[i]) {
6220 USTPUTC(optletters(i), p);
6221 len++;
6222 }
6223 }
6224 expdest = p;
6225 break;
6226 case '@':
6227 if (sep)
6228 goto param;
6229 /* fall through */
6230 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006231 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006232 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6233 sepq = 1;
6234 param:
6235 ap = shellparam.p;
6236 if (!ap)
6237 return -1;
6238 while ((p = *ap++)) {
6239 size_t partlen;
6240
6241 partlen = strlen(p);
6242 len += partlen;
6243
6244 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6245 memtodest(p, partlen, syntax, quotes);
6246
6247 if (*ap && sep) {
6248 char *q;
6249
6250 len++;
6251 if (subtype == VSPLUS || subtype == VSLENGTH) {
6252 continue;
6253 }
6254 q = expdest;
6255 if (sepq)
6256 STPUTC(CTLESC, q);
6257 STPUTC(sep, q);
6258 expdest = q;
6259 }
6260 }
6261 return len;
6262 case '0':
6263 case '1':
6264 case '2':
6265 case '3':
6266 case '4':
6267 case '5':
6268 case '6':
6269 case '7':
6270 case '8':
6271 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006272// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273 num = atoi(name);
6274 if (num < 0 || num > shellparam.nparam)
6275 return -1;
6276 p = num ? shellparam.p[num - 1] : arg0;
6277 goto value;
6278 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006279 /* NB: name has form "VAR=..." */
6280
6281 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6282 * which should be considered before we check variables. */
6283 if (var_str_list) {
6284 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6285 p = NULL;
6286 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006287 char *str, *eq;
6288 str = var_str_list->text;
6289 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006290 if (!eq) /* stop at first non-assignment */
6291 break;
6292 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006293 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006294 && strncmp(str, name, name_len) == 0) {
6295 p = eq;
6296 /* goto value; - WRONG! */
6297 /* think "A=1 A=2 B=$A" */
6298 }
6299 var_str_list = var_str_list->next;
6300 } while (var_str_list);
6301 if (p)
6302 goto value;
6303 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006304 p = lookupvar(name);
6305 value:
6306 if (!p)
6307 return -1;
6308
6309 len = strlen(p);
6310 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6311 memtodest(p, len, syntax, quotes);
6312 return len;
6313 }
6314
6315 if (subtype == VSPLUS || subtype == VSLENGTH)
6316 STADJUST(-len, expdest);
6317 return len;
6318}
6319
6320/*
6321 * Expand a variable, and return a pointer to the next character in the
6322 * input string.
6323 */
6324static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006325evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006327 char varflags;
6328 char subtype;
6329 char quoted;
6330 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006331 char *var;
6332 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006333 int startloc;
6334 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006335
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 varflags = *p++;
6337 subtype = varflags & VSTYPE;
6338 quoted = varflags & VSQUOTE;
6339 var = p;
6340 easy = (!quoted || (*var == '@' && shellparam.nparam));
6341 startloc = expdest - (char *)stackblock();
6342 p = strchr(p, '=') + 1;
6343
6344 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006345 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006346 if (varflags & VSNUL)
6347 varlen--;
6348
6349 if (subtype == VSPLUS) {
6350 varlen = -1 - varlen;
6351 goto vsplus;
6352 }
6353
6354 if (subtype == VSMINUS) {
6355 vsplus:
6356 if (varlen < 0) {
6357 argstr(
6358 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006359 (quoted ? EXP_QWORD : EXP_WORD),
6360 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006361 );
6362 goto end;
6363 }
6364 if (easy)
6365 goto record;
6366 goto end;
6367 }
6368
6369 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6370 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006371 if (subevalvar(p, var, /* strloc: */ 0,
6372 subtype, startloc, varflags,
6373 /* quotes: */ 0,
6374 var_str_list)
6375 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006376 varflags &= ~VSNUL;
6377 /*
6378 * Remove any recorded regions beyond
6379 * start of variable
6380 */
6381 removerecordregions(startloc);
6382 goto again;
6383 }
6384 goto end;
6385 }
6386 if (easy)
6387 goto record;
6388 goto end;
6389 }
6390
6391 if (varlen < 0 && uflag)
6392 varunset(p, var, 0, 0);
6393
6394 if (subtype == VSLENGTH) {
6395 cvtnum(varlen > 0 ? varlen : 0);
6396 goto record;
6397 }
6398
6399 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006400 if (easy)
6401 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006402 goto end;
6403 }
6404
6405#if DEBUG
6406 switch (subtype) {
6407 case VSTRIMLEFT:
6408 case VSTRIMLEFTMAX:
6409 case VSTRIMRIGHT:
6410 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006411#if ENABLE_ASH_BASH_COMPAT
6412 case VSSUBSTR:
6413 case VSREPLACE:
6414 case VSREPLACEALL:
6415#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006416 break;
6417 default:
6418 abort();
6419 }
6420#endif
6421
6422 if (varlen >= 0) {
6423 /*
6424 * Terminate the string and start recording the pattern
6425 * right after it
6426 */
6427 STPUTC('\0', expdest);
6428 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006429 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6430 startloc, varflags,
6431 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6432 var_str_list)
6433 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006434 int amount = expdest - (
6435 (char *)stackblock() + patloc - 1
6436 );
6437 STADJUST(-amount, expdest);
6438 }
6439 /* Remove any recorded regions beyond start of variable */
6440 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006441 record:
6442 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 }
6444
6445 end:
6446 if (subtype != VSNORMAL) { /* skip to end of alternative */
6447 int nesting = 1;
6448 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006449 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006450 if (c == CTLESC)
6451 p++;
6452 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6453 if (varlen >= 0)
6454 argbackq = argbackq->next;
6455 } else if (c == CTLVAR) {
6456 if ((*p++ & VSTYPE) != VSNORMAL)
6457 nesting++;
6458 } else if (c == CTLENDVAR) {
6459 if (--nesting == 0)
6460 break;
6461 }
6462 }
6463 }
6464 return p;
6465}
6466
6467/*
6468 * Break the argument string into pieces based upon IFS and add the
6469 * strings to the argument list. The regions of the string to be
6470 * searched for IFS characters have been stored by recordregion.
6471 */
6472static void
6473ifsbreakup(char *string, struct arglist *arglist)
6474{
6475 struct ifsregion *ifsp;
6476 struct strlist *sp;
6477 char *start;
6478 char *p;
6479 char *q;
6480 const char *ifs, *realifs;
6481 int ifsspc;
6482 int nulonly;
6483
6484 start = string;
6485 if (ifslastp != NULL) {
6486 ifsspc = 0;
6487 nulonly = 0;
6488 realifs = ifsset() ? ifsval() : defifs;
6489 ifsp = &ifsfirst;
6490 do {
6491 p = string + ifsp->begoff;
6492 nulonly = ifsp->nulonly;
6493 ifs = nulonly ? nullstr : realifs;
6494 ifsspc = 0;
6495 while (p < string + ifsp->endoff) {
6496 q = p;
6497 if (*p == CTLESC)
6498 p++;
6499 if (!strchr(ifs, *p)) {
6500 p++;
6501 continue;
6502 }
6503 if (!nulonly)
6504 ifsspc = (strchr(defifs, *p) != NULL);
6505 /* Ignore IFS whitespace at start */
6506 if (q == start && ifsspc) {
6507 p++;
6508 start = p;
6509 continue;
6510 }
6511 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006512 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006513 sp->text = start;
6514 *arglist->lastp = sp;
6515 arglist->lastp = &sp->next;
6516 p++;
6517 if (!nulonly) {
6518 for (;;) {
6519 if (p >= string + ifsp->endoff) {
6520 break;
6521 }
6522 q = p;
6523 if (*p == CTLESC)
6524 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006525 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006526 p = q;
6527 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006528 }
6529 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006530 if (ifsspc) {
6531 p++;
6532 ifsspc = 0;
6533 } else {
6534 p = q;
6535 break;
6536 }
6537 } else
6538 p++;
6539 }
6540 }
6541 start = p;
6542 } /* while */
6543 ifsp = ifsp->next;
6544 } while (ifsp != NULL);
6545 if (nulonly)
6546 goto add;
6547 }
6548
6549 if (!*start)
6550 return;
6551
6552 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006553 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554 sp->text = start;
6555 *arglist->lastp = sp;
6556 arglist->lastp = &sp->next;
6557}
6558
6559static void
6560ifsfree(void)
6561{
6562 struct ifsregion *p;
6563
6564 INT_OFF;
6565 p = ifsfirst.next;
6566 do {
6567 struct ifsregion *ifsp;
6568 ifsp = p->next;
6569 free(p);
6570 p = ifsp;
6571 } while (p);
6572 ifslastp = NULL;
6573 ifsfirst.next = NULL;
6574 INT_ON;
6575}
6576
6577/*
6578 * Add a file name to the list.
6579 */
6580static void
6581addfname(const char *name)
6582{
6583 struct strlist *sp;
6584
Denis Vlasenko597906c2008-02-20 16:38:54 +00006585 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006586 sp->text = ststrdup(name);
6587 *exparg.lastp = sp;
6588 exparg.lastp = &sp->next;
6589}
6590
6591static char *expdir;
6592
6593/*
6594 * Do metacharacter (i.e. *, ?, [...]) expansion.
6595 */
6596static void
6597expmeta(char *enddir, char *name)
6598{
6599 char *p;
6600 const char *cp;
6601 char *start;
6602 char *endname;
6603 int metaflag;
6604 struct stat statb;
6605 DIR *dirp;
6606 struct dirent *dp;
6607 int atend;
6608 int matchdot;
6609
6610 metaflag = 0;
6611 start = name;
6612 for (p = name; *p; p++) {
6613 if (*p == '*' || *p == '?')
6614 metaflag = 1;
6615 else if (*p == '[') {
6616 char *q = p + 1;
6617 if (*q == '!')
6618 q++;
6619 for (;;) {
6620 if (*q == '\\')
6621 q++;
6622 if (*q == '/' || *q == '\0')
6623 break;
6624 if (*++q == ']') {
6625 metaflag = 1;
6626 break;
6627 }
6628 }
6629 } else if (*p == '\\')
6630 p++;
6631 else if (*p == '/') {
6632 if (metaflag)
6633 goto out;
6634 start = p + 1;
6635 }
6636 }
6637 out:
6638 if (metaflag == 0) { /* we've reached the end of the file name */
6639 if (enddir != expdir)
6640 metaflag++;
6641 p = name;
6642 do {
6643 if (*p == '\\')
6644 p++;
6645 *enddir++ = *p;
6646 } while (*p++);
6647 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6648 addfname(expdir);
6649 return;
6650 }
6651 endname = p;
6652 if (name < start) {
6653 p = name;
6654 do {
6655 if (*p == '\\')
6656 p++;
6657 *enddir++ = *p++;
6658 } while (p < start);
6659 }
6660 if (enddir == expdir) {
6661 cp = ".";
6662 } else if (enddir == expdir + 1 && *expdir == '/') {
6663 cp = "/";
6664 } else {
6665 cp = expdir;
6666 enddir[-1] = '\0';
6667 }
6668 dirp = opendir(cp);
6669 if (dirp == NULL)
6670 return;
6671 if (enddir != expdir)
6672 enddir[-1] = '/';
6673 if (*endname == 0) {
6674 atend = 1;
6675 } else {
6676 atend = 0;
6677 *endname++ = '\0';
6678 }
6679 matchdot = 0;
6680 p = start;
6681 if (*p == '\\')
6682 p++;
6683 if (*p == '.')
6684 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006685 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006686 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006687 continue;
6688 if (pmatch(start, dp->d_name)) {
6689 if (atend) {
6690 strcpy(enddir, dp->d_name);
6691 addfname(expdir);
6692 } else {
6693 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6694 continue;
6695 p[-1] = '/';
6696 expmeta(p, endname);
6697 }
6698 }
6699 }
6700 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006701 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006702 endname[-1] = '/';
6703}
6704
6705static struct strlist *
6706msort(struct strlist *list, int len)
6707{
6708 struct strlist *p, *q = NULL;
6709 struct strlist **lpp;
6710 int half;
6711 int n;
6712
6713 if (len <= 1)
6714 return list;
6715 half = len >> 1;
6716 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006717 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006718 q = p;
6719 p = p->next;
6720 }
6721 q->next = NULL; /* terminate first half of list */
6722 q = msort(list, half); /* sort first half of list */
6723 p = msort(p, len - half); /* sort second half */
6724 lpp = &list;
6725 for (;;) {
6726#if ENABLE_LOCALE_SUPPORT
6727 if (strcoll(p->text, q->text) < 0)
6728#else
6729 if (strcmp(p->text, q->text) < 0)
6730#endif
6731 {
6732 *lpp = p;
6733 lpp = &p->next;
6734 p = *lpp;
6735 if (p == NULL) {
6736 *lpp = q;
6737 break;
6738 }
6739 } else {
6740 *lpp = q;
6741 lpp = &q->next;
6742 q = *lpp;
6743 if (q == NULL) {
6744 *lpp = p;
6745 break;
6746 }
6747 }
6748 }
6749 return list;
6750}
6751
6752/*
6753 * Sort the results of file name expansion. It calculates the number of
6754 * strings to sort and then calls msort (short for merge sort) to do the
6755 * work.
6756 */
6757static struct strlist *
6758expsort(struct strlist *str)
6759{
6760 int len;
6761 struct strlist *sp;
6762
6763 len = 0;
6764 for (sp = str; sp; sp = sp->next)
6765 len++;
6766 return msort(str, len);
6767}
6768
6769static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006770expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006771{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006772 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006773 '*', '?', '[', 0
6774 };
6775 /* TODO - EXP_REDIR */
6776
6777 while (str) {
6778 struct strlist **savelastp;
6779 struct strlist *sp;
6780 char *p;
6781
6782 if (fflag)
6783 goto nometa;
6784 if (!strpbrk(str->text, metachars))
6785 goto nometa;
6786 savelastp = exparg.lastp;
6787
6788 INT_OFF;
6789 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6790 {
6791 int i = strlen(str->text);
6792 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6793 }
6794
6795 expmeta(expdir, p);
6796 free(expdir);
6797 if (p != str->text)
6798 free(p);
6799 INT_ON;
6800 if (exparg.lastp == savelastp) {
6801 /*
6802 * no matches
6803 */
6804 nometa:
6805 *exparg.lastp = str;
6806 rmescapes(str->text);
6807 exparg.lastp = &str->next;
6808 } else {
6809 *exparg.lastp = NULL;
6810 *savelastp = sp = expsort(*savelastp);
6811 while (sp->next != NULL)
6812 sp = sp->next;
6813 exparg.lastp = &sp->next;
6814 }
6815 str = str->next;
6816 }
6817}
6818
6819/*
6820 * Perform variable substitution and command substitution on an argument,
6821 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6822 * perform splitting and file name expansion. When arglist is NULL, perform
6823 * here document expansion.
6824 */
6825static void
6826expandarg(union node *arg, struct arglist *arglist, int flag)
6827{
6828 struct strlist *sp;
6829 char *p;
6830
6831 argbackq = arg->narg.backquote;
6832 STARTSTACKSTR(expdest);
6833 ifsfirst.next = NULL;
6834 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006835 argstr(arg->narg.text, flag,
6836 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006837 p = _STPUTC('\0', expdest);
6838 expdest = p - 1;
6839 if (arglist == NULL) {
6840 return; /* here document expanded */
6841 }
6842 p = grabstackstr(p);
6843 exparg.lastp = &exparg.list;
6844 /*
6845 * TODO - EXP_REDIR
6846 */
6847 if (flag & EXP_FULL) {
6848 ifsbreakup(p, &exparg);
6849 *exparg.lastp = NULL;
6850 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006851 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006852 } else {
6853 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6854 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006855 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006856 sp->text = p;
6857 *exparg.lastp = sp;
6858 exparg.lastp = &sp->next;
6859 }
6860 if (ifsfirst.next)
6861 ifsfree();
6862 *exparg.lastp = NULL;
6863 if (exparg.list) {
6864 *arglist->lastp = exparg.list;
6865 arglist->lastp = exparg.lastp;
6866 }
6867}
6868
6869/*
6870 * Expand shell variables and backquotes inside a here document.
6871 */
6872static void
6873expandhere(union node *arg, int fd)
6874{
6875 herefd = fd;
6876 expandarg(arg, (struct arglist *)NULL, 0);
6877 full_write(fd, stackblock(), expdest - (char *)stackblock());
6878}
6879
6880/*
6881 * Returns true if the pattern matches the string.
6882 */
6883static int
6884patmatch(char *pattern, const char *string)
6885{
6886 return pmatch(preglob(pattern, 0, 0), string);
6887}
6888
6889/*
6890 * See if a pattern matches in a case statement.
6891 */
6892static int
6893casematch(union node *pattern, char *val)
6894{
6895 struct stackmark smark;
6896 int result;
6897
6898 setstackmark(&smark);
6899 argbackq = pattern->narg.backquote;
6900 STARTSTACKSTR(expdest);
6901 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006902 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6903 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006904 STACKSTRNUL(expdest);
6905 result = patmatch(stackblock(), val);
6906 popstackmark(&smark);
6907 return result;
6908}
6909
6910
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006911/* ============ find_command */
6912
6913struct builtincmd {
6914 const char *name;
6915 int (*builtin)(int, char **);
6916 /* unsigned flags; */
6917};
6918#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006919/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006920 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006921#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006922#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006923
6924struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006925 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006926 union param {
6927 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006928 /* index >= 0 for commands without path (slashes) */
6929 /* (TODO: what exactly does the value mean? PATH position?) */
6930 /* index == -1 for commands with slashes */
6931 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006932 const struct builtincmd *cmd;
6933 struct funcnode *func;
6934 } u;
6935};
6936/* values of cmdtype */
6937#define CMDUNKNOWN -1 /* no entry in table for command */
6938#define CMDNORMAL 0 /* command is an executable program */
6939#define CMDFUNCTION 1 /* command is a shell function */
6940#define CMDBUILTIN 2 /* command is a shell builtin */
6941
6942/* action to find_command() */
6943#define DO_ERR 0x01 /* prints errors */
6944#define DO_ABS 0x02 /* checks absolute paths */
6945#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6946#define DO_ALTPATH 0x08 /* using alternate path */
6947#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6948
6949static void find_command(char *, struct cmdentry *, int, const char *);
6950
6951
6952/* ============ Hashing commands */
6953
6954/*
6955 * When commands are first encountered, they are entered in a hash table.
6956 * This ensures that a full path search will not have to be done for them
6957 * on each invocation.
6958 *
6959 * We should investigate converting to a linear search, even though that
6960 * would make the command name "hash" a misnomer.
6961 */
6962
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006963struct tblentry {
6964 struct tblentry *next; /* next entry in hash chain */
6965 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006966 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006967 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006968 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006969};
6970
Denis Vlasenko01631112007-12-16 17:20:38 +00006971static struct tblentry **cmdtable;
6972#define INIT_G_cmdtable() do { \
6973 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6974} while (0)
6975
6976static int builtinloc = -1; /* index in path of %builtin, or -1 */
6977
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006978
6979static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006980tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006981{
6982 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006983
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006984#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006985 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00006986 if (APPLET_IS_NOEXEC(applet_no)) {
6987 while (*envp)
6988 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006989 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00006990 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006991 /* re-exec ourselves with the new arguments */
6992 execve(bb_busybox_exec_path, argv, envp);
6993 /* If they called chroot or otherwise made the binary no longer
6994 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006995 }
6996#endif
6997
6998 repeat:
6999#ifdef SYSV
7000 do {
7001 execve(cmd, argv, envp);
7002 } while (errno == EINTR);
7003#else
7004 execve(cmd, argv, envp);
7005#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007006 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007007 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007008 return;
7009 }
7010 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007011 char **ap;
7012 char **new;
7013
7014 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007015 continue;
7016 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007017 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007018 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007019 ap += 2;
7020 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007021 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007022 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007023 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007024 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007025 goto repeat;
7026 }
7027}
7028
7029/*
7030 * Exec a program. Never returns. If you change this routine, you may
7031 * have to change the find_command routine as well.
7032 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007033static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007034static void
7035shellexec(char **argv, const char *path, int idx)
7036{
7037 char *cmdname;
7038 int e;
7039 char **envp;
7040 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007041#if ENABLE_FEATURE_SH_STANDALONE
7042 int applet_no = -1;
7043#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007044
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007045 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007046 envp = listvars(VEXPORT, VUNSET, 0);
7047 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007048#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007049 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050#endif
7051 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007052 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007053 e = errno;
7054 } else {
7055 e = ENOENT;
7056 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7057 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007058 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007059 if (errno != ENOENT && errno != ENOTDIR)
7060 e = errno;
7061 }
7062 stunalloc(cmdname);
7063 }
7064 }
7065
7066 /* Map to POSIX errors */
7067 switch (e) {
7068 case EACCES:
7069 exerrno = 126;
7070 break;
7071 case ENOENT:
7072 exerrno = 127;
7073 break;
7074 default:
7075 exerrno = 2;
7076 break;
7077 }
7078 exitstatus = exerrno;
7079 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007080 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007081 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7082 /* NOTREACHED */
7083}
7084
7085static void
7086printentry(struct tblentry *cmdp)
7087{
7088 int idx;
7089 const char *path;
7090 char *name;
7091
7092 idx = cmdp->param.index;
7093 path = pathval();
7094 do {
7095 name = padvance(&path, cmdp->cmdname);
7096 stunalloc(name);
7097 } while (--idx >= 0);
7098 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7099}
7100
7101/*
7102 * Clear out command entries. The argument specifies the first entry in
7103 * PATH which has changed.
7104 */
7105static void
7106clearcmdentry(int firstchange)
7107{
7108 struct tblentry **tblp;
7109 struct tblentry **pp;
7110 struct tblentry *cmdp;
7111
7112 INT_OFF;
7113 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7114 pp = tblp;
7115 while ((cmdp = *pp) != NULL) {
7116 if ((cmdp->cmdtype == CMDNORMAL &&
7117 cmdp->param.index >= firstchange)
7118 || (cmdp->cmdtype == CMDBUILTIN &&
7119 builtinloc >= firstchange)
7120 ) {
7121 *pp = cmdp->next;
7122 free(cmdp);
7123 } else {
7124 pp = &cmdp->next;
7125 }
7126 }
7127 }
7128 INT_ON;
7129}
7130
7131/*
7132 * Locate a command in the command hash table. If "add" is nonzero,
7133 * add the command to the table if it is not already present. The
7134 * variable "lastcmdentry" is set to point to the address of the link
7135 * pointing to the entry, so that delete_cmd_entry can delete the
7136 * entry.
7137 *
7138 * Interrupts must be off if called with add != 0.
7139 */
7140static struct tblentry **lastcmdentry;
7141
7142static struct tblentry *
7143cmdlookup(const char *name, int add)
7144{
7145 unsigned int hashval;
7146 const char *p;
7147 struct tblentry *cmdp;
7148 struct tblentry **pp;
7149
7150 p = name;
7151 hashval = (unsigned char)*p << 4;
7152 while (*p)
7153 hashval += (unsigned char)*p++;
7154 hashval &= 0x7FFF;
7155 pp = &cmdtable[hashval % CMDTABLESIZE];
7156 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7157 if (strcmp(cmdp->cmdname, name) == 0)
7158 break;
7159 pp = &cmdp->next;
7160 }
7161 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007162 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7163 + strlen(name)
7164 /* + 1 - already done because
7165 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007166 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007167 cmdp->cmdtype = CMDUNKNOWN;
7168 strcpy(cmdp->cmdname, name);
7169 }
7170 lastcmdentry = pp;
7171 return cmdp;
7172}
7173
7174/*
7175 * Delete the command entry returned on the last lookup.
7176 */
7177static void
7178delete_cmd_entry(void)
7179{
7180 struct tblentry *cmdp;
7181
7182 INT_OFF;
7183 cmdp = *lastcmdentry;
7184 *lastcmdentry = cmdp->next;
7185 if (cmdp->cmdtype == CMDFUNCTION)
7186 freefunc(cmdp->param.func);
7187 free(cmdp);
7188 INT_ON;
7189}
7190
7191/*
7192 * Add a new command entry, replacing any existing command entry for
7193 * the same name - except special builtins.
7194 */
7195static void
7196addcmdentry(char *name, struct cmdentry *entry)
7197{
7198 struct tblentry *cmdp;
7199
7200 cmdp = cmdlookup(name, 1);
7201 if (cmdp->cmdtype == CMDFUNCTION) {
7202 freefunc(cmdp->param.func);
7203 }
7204 cmdp->cmdtype = entry->cmdtype;
7205 cmdp->param = entry->u;
7206 cmdp->rehash = 0;
7207}
7208
7209static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007210hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007211{
7212 struct tblentry **pp;
7213 struct tblentry *cmdp;
7214 int c;
7215 struct cmdentry entry;
7216 char *name;
7217
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007218 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007219 clearcmdentry(0);
7220 return 0;
7221 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007222
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007223 if (*argptr == NULL) {
7224 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7225 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7226 if (cmdp->cmdtype == CMDNORMAL)
7227 printentry(cmdp);
7228 }
7229 }
7230 return 0;
7231 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007232
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007233 c = 0;
7234 while ((name = *argptr) != NULL) {
7235 cmdp = cmdlookup(name, 0);
7236 if (cmdp != NULL
7237 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007238 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7239 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007240 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007241 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007242 find_command(name, &entry, DO_ERR, pathval());
7243 if (entry.cmdtype == CMDUNKNOWN)
7244 c = 1;
7245 argptr++;
7246 }
7247 return c;
7248}
7249
7250/*
7251 * Called when a cd is done. Marks all commands so the next time they
7252 * are executed they will be rehashed.
7253 */
7254static void
7255hashcd(void)
7256{
7257 struct tblentry **pp;
7258 struct tblentry *cmdp;
7259
7260 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7261 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007262 if (cmdp->cmdtype == CMDNORMAL
7263 || (cmdp->cmdtype == CMDBUILTIN
7264 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7265 && builtinloc > 0)
7266 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007267 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007268 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007269 }
7270 }
7271}
7272
7273/*
7274 * Fix command hash table when PATH changed.
7275 * Called before PATH is changed. The argument is the new value of PATH;
7276 * pathval() still returns the old value at this point.
7277 * Called with interrupts off.
7278 */
7279static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007280changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007281{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007282 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007283 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007284 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007285 int idx_bltin;
7286
7287 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007288 firstchange = 9999; /* assume no change */
7289 idx = 0;
7290 idx_bltin = -1;
7291 for (;;) {
7292 if (*old != *new) {
7293 firstchange = idx;
7294 if ((*old == '\0' && *new == ':')
7295 || (*old == ':' && *new == '\0'))
7296 firstchange++;
7297 old = new; /* ignore subsequent differences */
7298 }
7299 if (*new == '\0')
7300 break;
7301 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7302 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007303 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305 new++, old++;
7306 }
7307 if (builtinloc < 0 && idx_bltin >= 0)
7308 builtinloc = idx_bltin; /* zap builtins */
7309 if (builtinloc >= 0 && idx_bltin < 0)
7310 firstchange = 0;
7311 clearcmdentry(firstchange);
7312 builtinloc = idx_bltin;
7313}
7314
7315#define TEOF 0
7316#define TNL 1
7317#define TREDIR 2
7318#define TWORD 3
7319#define TSEMI 4
7320#define TBACKGND 5
7321#define TAND 6
7322#define TOR 7
7323#define TPIPE 8
7324#define TLP 9
7325#define TRP 10
7326#define TENDCASE 11
7327#define TENDBQUOTE 12
7328#define TNOT 13
7329#define TCASE 14
7330#define TDO 15
7331#define TDONE 16
7332#define TELIF 17
7333#define TELSE 18
7334#define TESAC 19
7335#define TFI 20
7336#define TFOR 21
7337#define TIF 22
7338#define TIN 23
7339#define TTHEN 24
7340#define TUNTIL 25
7341#define TWHILE 26
7342#define TBEGIN 27
7343#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007344typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007345
7346/* first char is indicating which tokens mark the end of a list */
7347static const char *const tokname_array[] = {
7348 "\1end of file",
7349 "\0newline",
7350 "\0redirection",
7351 "\0word",
7352 "\0;",
7353 "\0&",
7354 "\0&&",
7355 "\0||",
7356 "\0|",
7357 "\0(",
7358 "\1)",
7359 "\1;;",
7360 "\1`",
7361#define KWDOFFSET 13
7362 /* the following are keywords */
7363 "\0!",
7364 "\0case",
7365 "\1do",
7366 "\1done",
7367 "\1elif",
7368 "\1else",
7369 "\1esac",
7370 "\1fi",
7371 "\0for",
7372 "\0if",
7373 "\0in",
7374 "\1then",
7375 "\0until",
7376 "\0while",
7377 "\0{",
7378 "\1}",
7379};
7380
7381static const char *
7382tokname(int tok)
7383{
7384 static char buf[16];
7385
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007386//try this:
7387//if (tok < TSEMI) return tokname_array[tok] + 1;
7388//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7389//return buf;
7390
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391 if (tok >= TSEMI)
7392 buf[0] = '"';
7393 sprintf(buf + (tok >= TSEMI), "%s%c",
7394 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7395 return buf;
7396}
7397
7398/* Wrapper around strcmp for qsort/bsearch/... */
7399static int
7400pstrcmp(const void *a, const void *b)
7401{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007402 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007403}
7404
7405static const char *const *
7406findkwd(const char *s)
7407{
7408 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007409 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7410 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411}
7412
7413/*
7414 * Locate and print what a word is...
7415 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007416static int
7417describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418{
7419 struct cmdentry entry;
7420 struct tblentry *cmdp;
7421#if ENABLE_ASH_ALIAS
7422 const struct alias *ap;
7423#endif
7424 const char *path = pathval();
7425
7426 if (describe_command_verbose) {
7427 out1str(command);
7428 }
7429
7430 /* First look at the keywords */
7431 if (findkwd(command)) {
7432 out1str(describe_command_verbose ? " is a shell keyword" : command);
7433 goto out;
7434 }
7435
7436#if ENABLE_ASH_ALIAS
7437 /* Then look at the aliases */
7438 ap = lookupalias(command, 0);
7439 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007440 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441 out1str("alias ");
7442 printalias(ap);
7443 return 0;
7444 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007445 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007446 goto out;
7447 }
7448#endif
7449 /* Then check if it is a tracked alias */
7450 cmdp = cmdlookup(command, 0);
7451 if (cmdp != NULL) {
7452 entry.cmdtype = cmdp->cmdtype;
7453 entry.u = cmdp->param;
7454 } else {
7455 /* Finally use brute force */
7456 find_command(command, &entry, DO_ABS, path);
7457 }
7458
7459 switch (entry.cmdtype) {
7460 case CMDNORMAL: {
7461 int j = entry.u.index;
7462 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007463 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464 p = command;
7465 } else {
7466 do {
7467 p = padvance(&path, command);
7468 stunalloc(p);
7469 } while (--j >= 0);
7470 }
7471 if (describe_command_verbose) {
7472 out1fmt(" is%s %s",
7473 (cmdp ? " a tracked alias for" : nullstr), p
7474 );
7475 } else {
7476 out1str(p);
7477 }
7478 break;
7479 }
7480
7481 case CMDFUNCTION:
7482 if (describe_command_verbose) {
7483 out1str(" is a shell function");
7484 } else {
7485 out1str(command);
7486 }
7487 break;
7488
7489 case CMDBUILTIN:
7490 if (describe_command_verbose) {
7491 out1fmt(" is a %sshell builtin",
7492 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7493 "special " : nullstr
7494 );
7495 } else {
7496 out1str(command);
7497 }
7498 break;
7499
7500 default:
7501 if (describe_command_verbose) {
7502 out1str(": not found\n");
7503 }
7504 return 127;
7505 }
7506 out:
7507 outstr("\n", stdout);
7508 return 0;
7509}
7510
7511static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007512typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007513{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007514 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007515 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007516 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007517
Denis Vlasenko46846e22007-05-20 13:08:31 +00007518 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007519 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007520 i++;
7521 verbose = 0;
7522 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007523 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007524 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007525 }
7526 return err;
7527}
7528
7529#if ENABLE_ASH_CMDCMD
7530static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007531commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007532{
7533 int c;
7534 enum {
7535 VERIFY_BRIEF = 1,
7536 VERIFY_VERBOSE = 2,
7537 } verify = 0;
7538
7539 while ((c = nextopt("pvV")) != '\0')
7540 if (c == 'V')
7541 verify |= VERIFY_VERBOSE;
7542 else if (c == 'v')
7543 verify |= VERIFY_BRIEF;
7544#if DEBUG
7545 else if (c != 'p')
7546 abort();
7547#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007548 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7549 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007550 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007551 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007552
7553 return 0;
7554}
7555#endif
7556
7557
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007558/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007559
Denis Vlasenko340299a2008-11-21 10:36:36 +00007560static int funcblocksize; /* size of structures in function */
7561static int funcstringsize; /* size of strings in node */
7562static void *funcblock; /* block to allocate function from */
7563static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007564
Eric Andersencb57d552001-06-28 07:25:16 +00007565/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007566#define EV_EXIT 01 /* exit after evaluating tree */
7567#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007568#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007569
Denis Vlasenko340299a2008-11-21 10:36:36 +00007570static const short nodesize[N_NUMBER] = {
7571 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7572 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7573 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7574 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7575 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7576 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7577 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7578 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7579 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7580 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7581 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7582 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7583 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7584 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7585 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7586 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7587 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007588#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007589 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007590#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007591 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7592 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7593 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7594 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7595 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7596 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7597 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7598 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7599 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007600};
7601
7602static void calcsize(union node *n);
7603
7604static void
7605sizenodelist(struct nodelist *lp)
7606{
7607 while (lp) {
7608 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7609 calcsize(lp->n);
7610 lp = lp->next;
7611 }
7612}
7613
7614static void
7615calcsize(union node *n)
7616{
7617 if (n == NULL)
7618 return;
7619 funcblocksize += nodesize[n->type];
7620 switch (n->type) {
7621 case NCMD:
7622 calcsize(n->ncmd.redirect);
7623 calcsize(n->ncmd.args);
7624 calcsize(n->ncmd.assign);
7625 break;
7626 case NPIPE:
7627 sizenodelist(n->npipe.cmdlist);
7628 break;
7629 case NREDIR:
7630 case NBACKGND:
7631 case NSUBSHELL:
7632 calcsize(n->nredir.redirect);
7633 calcsize(n->nredir.n);
7634 break;
7635 case NAND:
7636 case NOR:
7637 case NSEMI:
7638 case NWHILE:
7639 case NUNTIL:
7640 calcsize(n->nbinary.ch2);
7641 calcsize(n->nbinary.ch1);
7642 break;
7643 case NIF:
7644 calcsize(n->nif.elsepart);
7645 calcsize(n->nif.ifpart);
7646 calcsize(n->nif.test);
7647 break;
7648 case NFOR:
7649 funcstringsize += strlen(n->nfor.var) + 1;
7650 calcsize(n->nfor.body);
7651 calcsize(n->nfor.args);
7652 break;
7653 case NCASE:
7654 calcsize(n->ncase.cases);
7655 calcsize(n->ncase.expr);
7656 break;
7657 case NCLIST:
7658 calcsize(n->nclist.body);
7659 calcsize(n->nclist.pattern);
7660 calcsize(n->nclist.next);
7661 break;
7662 case NDEFUN:
7663 case NARG:
7664 sizenodelist(n->narg.backquote);
7665 funcstringsize += strlen(n->narg.text) + 1;
7666 calcsize(n->narg.next);
7667 break;
7668 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007669#if ENABLE_ASH_BASH_COMPAT
7670 case NTO2:
7671#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007672 case NCLOBBER:
7673 case NFROM:
7674 case NFROMTO:
7675 case NAPPEND:
7676 calcsize(n->nfile.fname);
7677 calcsize(n->nfile.next);
7678 break;
7679 case NTOFD:
7680 case NFROMFD:
7681 calcsize(n->ndup.vname);
7682 calcsize(n->ndup.next);
7683 break;
7684 case NHERE:
7685 case NXHERE:
7686 calcsize(n->nhere.doc);
7687 calcsize(n->nhere.next);
7688 break;
7689 case NNOT:
7690 calcsize(n->nnot.com);
7691 break;
7692 };
7693}
7694
7695static char *
7696nodeckstrdup(char *s)
7697{
7698 char *rtn = funcstring;
7699
7700 strcpy(funcstring, s);
7701 funcstring += strlen(s) + 1;
7702 return rtn;
7703}
7704
7705static union node *copynode(union node *);
7706
7707static struct nodelist *
7708copynodelist(struct nodelist *lp)
7709{
7710 struct nodelist *start;
7711 struct nodelist **lpp;
7712
7713 lpp = &start;
7714 while (lp) {
7715 *lpp = funcblock;
7716 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7717 (*lpp)->n = copynode(lp->n);
7718 lp = lp->next;
7719 lpp = &(*lpp)->next;
7720 }
7721 *lpp = NULL;
7722 return start;
7723}
7724
7725static union node *
7726copynode(union node *n)
7727{
7728 union node *new;
7729
7730 if (n == NULL)
7731 return NULL;
7732 new = funcblock;
7733 funcblock = (char *) funcblock + nodesize[n->type];
7734
7735 switch (n->type) {
7736 case NCMD:
7737 new->ncmd.redirect = copynode(n->ncmd.redirect);
7738 new->ncmd.args = copynode(n->ncmd.args);
7739 new->ncmd.assign = copynode(n->ncmd.assign);
7740 break;
7741 case NPIPE:
7742 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007743 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007744 break;
7745 case NREDIR:
7746 case NBACKGND:
7747 case NSUBSHELL:
7748 new->nredir.redirect = copynode(n->nredir.redirect);
7749 new->nredir.n = copynode(n->nredir.n);
7750 break;
7751 case NAND:
7752 case NOR:
7753 case NSEMI:
7754 case NWHILE:
7755 case NUNTIL:
7756 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7757 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7758 break;
7759 case NIF:
7760 new->nif.elsepart = copynode(n->nif.elsepart);
7761 new->nif.ifpart = copynode(n->nif.ifpart);
7762 new->nif.test = copynode(n->nif.test);
7763 break;
7764 case NFOR:
7765 new->nfor.var = nodeckstrdup(n->nfor.var);
7766 new->nfor.body = copynode(n->nfor.body);
7767 new->nfor.args = copynode(n->nfor.args);
7768 break;
7769 case NCASE:
7770 new->ncase.cases = copynode(n->ncase.cases);
7771 new->ncase.expr = copynode(n->ncase.expr);
7772 break;
7773 case NCLIST:
7774 new->nclist.body = copynode(n->nclist.body);
7775 new->nclist.pattern = copynode(n->nclist.pattern);
7776 new->nclist.next = copynode(n->nclist.next);
7777 break;
7778 case NDEFUN:
7779 case NARG:
7780 new->narg.backquote = copynodelist(n->narg.backquote);
7781 new->narg.text = nodeckstrdup(n->narg.text);
7782 new->narg.next = copynode(n->narg.next);
7783 break;
7784 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007785#if ENABLE_ASH_BASH_COMPAT
7786 case NTO2:
7787#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007788 case NCLOBBER:
7789 case NFROM:
7790 case NFROMTO:
7791 case NAPPEND:
7792 new->nfile.fname = copynode(n->nfile.fname);
7793 new->nfile.fd = n->nfile.fd;
7794 new->nfile.next = copynode(n->nfile.next);
7795 break;
7796 case NTOFD:
7797 case NFROMFD:
7798 new->ndup.vname = copynode(n->ndup.vname);
7799 new->ndup.dupfd = n->ndup.dupfd;
7800 new->ndup.fd = n->ndup.fd;
7801 new->ndup.next = copynode(n->ndup.next);
7802 break;
7803 case NHERE:
7804 case NXHERE:
7805 new->nhere.doc = copynode(n->nhere.doc);
7806 new->nhere.fd = n->nhere.fd;
7807 new->nhere.next = copynode(n->nhere.next);
7808 break;
7809 case NNOT:
7810 new->nnot.com = copynode(n->nnot.com);
7811 break;
7812 };
7813 new->type = n->type;
7814 return new;
7815}
7816
7817/*
7818 * Make a copy of a parse tree.
7819 */
7820static struct funcnode *
7821copyfunc(union node *n)
7822{
7823 struct funcnode *f;
7824 size_t blocksize;
7825
7826 funcblocksize = offsetof(struct funcnode, n);
7827 funcstringsize = 0;
7828 calcsize(n);
7829 blocksize = funcblocksize;
7830 f = ckmalloc(blocksize + funcstringsize);
7831 funcblock = (char *) f + offsetof(struct funcnode, n);
7832 funcstring = (char *) f + blocksize;
7833 copynode(n);
7834 f->count = 0;
7835 return f;
7836}
7837
7838/*
7839 * Define a shell function.
7840 */
7841static void
7842defun(char *name, union node *func)
7843{
7844 struct cmdentry entry;
7845
7846 INT_OFF;
7847 entry.cmdtype = CMDFUNCTION;
7848 entry.u.func = copyfunc(func);
7849 addcmdentry(name, &entry);
7850 INT_ON;
7851}
7852
7853static int evalskip; /* set if we are skipping commands */
7854/* reasons for skipping commands (see comment on breakcmd routine) */
7855#define SKIPBREAK (1 << 0)
7856#define SKIPCONT (1 << 1)
7857#define SKIPFUNC (1 << 2)
7858#define SKIPFILE (1 << 3)
7859#define SKIPEVAL (1 << 4)
7860static int skipcount; /* number of levels to skip */
7861static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007862static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007863
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007864/* forward decl way out to parsing code - dotrap needs it */
7865static int evalstring(char *s, int mask);
7866
7867/*
7868 * Called to execute a trap. Perhaps we should avoid entering new trap
7869 * handlers while we are executing a trap handler.
7870 */
7871static int
7872dotrap(void)
7873{
7874 char *p;
7875 char *q;
7876 int i;
7877 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007878 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007879
7880 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007881 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007882 xbarrier();
7883
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007884 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007885 if (!*q)
7886 continue;
7887 *q = '\0';
7888
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007889 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007890 if (!p)
7891 continue;
7892 skip = evalstring(p, SKIPEVAL);
7893 exitstatus = savestatus;
7894 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007895 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007896 }
7897
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007898 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007899}
7900
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007901/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007902static void evalloop(union node *, int);
7903static void evalfor(union node *, int);
7904static void evalcase(union node *, int);
7905static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007906static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007907static void evalpipe(union node *, int);
7908static void evalcommand(union node *, int);
7909static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007910static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007911
Eric Andersen62483552001-07-10 06:09:16 +00007912/*
Eric Andersenc470f442003-07-28 09:56:35 +00007913 * Evaluate a parse tree. The value is left in the global variable
7914 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007915 */
Eric Andersenc470f442003-07-28 09:56:35 +00007916static void
7917evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007918{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007919
7920 struct jmploc *volatile savehandler = exception_handler;
7921 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007922 int checkexit = 0;
7923 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007924 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007925
Eric Andersenc470f442003-07-28 09:56:35 +00007926 if (n == NULL) {
7927 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007928 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007929 }
Eric Andersenc470f442003-07-28 09:56:35 +00007930 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007931 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007932
7933 exception_handler = &jmploc;
7934 {
7935 int err = setjmp(jmploc.loc);
7936 if (err) {
7937 /* if it was a signal, check for trap handlers */
7938 if (exception == EXSIG)
7939 goto out;
7940 /* continue on the way out */
7941 exception_handler = savehandler;
7942 longjmp(exception_handler->loc, err);
7943 }
7944 }
7945
Eric Andersenc470f442003-07-28 09:56:35 +00007946 switch (n->type) {
7947 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007948#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007949 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007950 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007951 break;
7952#endif
7953 case NNOT:
7954 evaltree(n->nnot.com, EV_TESTED);
7955 status = !exitstatus;
7956 goto setstatus;
7957 case NREDIR:
7958 expredir(n->nredir.redirect);
7959 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7960 if (!status) {
7961 evaltree(n->nredir.n, flags & EV_TESTED);
7962 status = exitstatus;
7963 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007964 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007965 goto setstatus;
7966 case NCMD:
7967 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007968 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007969 if (eflag && !(flags & EV_TESTED))
7970 checkexit = ~0;
7971 goto calleval;
7972 case NFOR:
7973 evalfn = evalfor;
7974 goto calleval;
7975 case NWHILE:
7976 case NUNTIL:
7977 evalfn = evalloop;
7978 goto calleval;
7979 case NSUBSHELL:
7980 case NBACKGND:
7981 evalfn = evalsubshell;
7982 goto calleval;
7983 case NPIPE:
7984 evalfn = evalpipe;
7985 goto checkexit;
7986 case NCASE:
7987 evalfn = evalcase;
7988 goto calleval;
7989 case NAND:
7990 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007991 case NSEMI: {
7992
Eric Andersenc470f442003-07-28 09:56:35 +00007993#if NAND + 1 != NOR
7994#error NAND + 1 != NOR
7995#endif
7996#if NOR + 1 != NSEMI
7997#error NOR + 1 != NSEMI
7998#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00007999 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008000 evaltree(
8001 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008002 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008003 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008004 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008005 break;
8006 if (!evalskip) {
8007 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008008 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008009 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008010 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008011 evalfn(n, flags);
8012 break;
8013 }
8014 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008015 }
Eric Andersenc470f442003-07-28 09:56:35 +00008016 case NIF:
8017 evaltree(n->nif.test, EV_TESTED);
8018 if (evalskip)
8019 break;
8020 if (exitstatus == 0) {
8021 n = n->nif.ifpart;
8022 goto evaln;
8023 } else if (n->nif.elsepart) {
8024 n = n->nif.elsepart;
8025 goto evaln;
8026 }
8027 goto success;
8028 case NDEFUN:
8029 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008030 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008031 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008032 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008033 exitstatus = status;
8034 break;
8035 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008036
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008037 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008038 exception_handler = savehandler;
8039 out1:
8040 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008041 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008042 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008043 goto exexit;
8044
8045 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008046 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008047 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008048 }
Eric Andersen62483552001-07-10 06:09:16 +00008049}
8050
Eric Andersenc470f442003-07-28 09:56:35 +00008051#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8052static
8053#endif
8054void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8055
Eric Andersenc470f442003-07-28 09:56:35 +00008056static void
8057evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008058{
8059 int status;
8060
8061 loopnest++;
8062 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008063 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008064 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008065 int i;
8066
Eric Andersencb57d552001-06-28 07:25:16 +00008067 evaltree(n->nbinary.ch1, EV_TESTED);
8068 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008069 skipping:
8070 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008071 evalskip = 0;
8072 continue;
8073 }
8074 if (evalskip == SKIPBREAK && --skipcount <= 0)
8075 evalskip = 0;
8076 break;
8077 }
Eric Andersenc470f442003-07-28 09:56:35 +00008078 i = exitstatus;
8079 if (n->type != NWHILE)
8080 i = !i;
8081 if (i != 0)
8082 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008083 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008084 status = exitstatus;
8085 if (evalskip)
8086 goto skipping;
8087 }
8088 loopnest--;
8089 exitstatus = status;
8090}
8091
Eric Andersenc470f442003-07-28 09:56:35 +00008092static void
8093evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008094{
8095 struct arglist arglist;
8096 union node *argp;
8097 struct strlist *sp;
8098 struct stackmark smark;
8099
8100 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008101 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008102 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008103 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008104 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008105 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008106 if (evalskip)
8107 goto out;
8108 }
8109 *arglist.lastp = NULL;
8110
8111 exitstatus = 0;
8112 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008113 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008114 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008115 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008116 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008117 if (evalskip) {
8118 if (evalskip == SKIPCONT && --skipcount <= 0) {
8119 evalskip = 0;
8120 continue;
8121 }
8122 if (evalskip == SKIPBREAK && --skipcount <= 0)
8123 evalskip = 0;
8124 break;
8125 }
8126 }
8127 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008128 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008129 popstackmark(&smark);
8130}
8131
Eric Andersenc470f442003-07-28 09:56:35 +00008132static void
8133evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008134{
8135 union node *cp;
8136 union node *patp;
8137 struct arglist arglist;
8138 struct stackmark smark;
8139
8140 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008141 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008142 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008143 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008144 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008145 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8146 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008147 if (casematch(patp, arglist.list->text)) {
8148 if (evalskip == 0) {
8149 evaltree(cp->nclist.body, flags);
8150 }
8151 goto out;
8152 }
8153 }
8154 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008155 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008156 popstackmark(&smark);
8157}
8158
Eric Andersenc470f442003-07-28 09:56:35 +00008159/*
8160 * Kick off a subshell to evaluate a tree.
8161 */
Eric Andersenc470f442003-07-28 09:56:35 +00008162static void
8163evalsubshell(union node *n, int flags)
8164{
8165 struct job *jp;
8166 int backgnd = (n->type == NBACKGND);
8167 int status;
8168
8169 expredir(n->nredir.redirect);
8170 if (!backgnd && flags & EV_EXIT && !trap[0])
8171 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008172 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008173 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008174 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008175 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008176 flags |= EV_EXIT;
8177 if (backgnd)
8178 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008179 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008180 redirect(n->nredir.redirect, 0);
8181 evaltreenr(n->nredir.n, flags);
8182 /* never returns */
8183 }
8184 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008185 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008186 status = waitforjob(jp);
8187 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008188 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008189}
8190
Eric Andersenc470f442003-07-28 09:56:35 +00008191/*
8192 * Compute the names of the files in a redirection list.
8193 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008194static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008195static void
8196expredir(union node *n)
8197{
8198 union node *redir;
8199
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008200 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008201 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008202
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008203 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008204 fn.lastp = &fn.list;
8205 switch (redir->type) {
8206 case NFROMTO:
8207 case NFROM:
8208 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008209#if ENABLE_ASH_BASH_COMPAT
8210 case NTO2:
8211#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008212 case NCLOBBER:
8213 case NAPPEND:
8214 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008215#if ENABLE_ASH_BASH_COMPAT
8216 store_expfname:
8217#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008218 redir->nfile.expfname = fn.list->text;
8219 break;
8220 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008221 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008222 if (redir->ndup.vname) {
8223 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008224 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008225 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008226#if ENABLE_ASH_BASH_COMPAT
8227//FIXME: we used expandarg with different args!
8228 if (!isdigit_str9(fn.list->text)) {
8229 /* >&file, not >&fd */
8230 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8231 ash_msg_and_raise_error("redir error");
8232 redir->type = NTO2;
8233 goto store_expfname;
8234 }
8235#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008236 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008237 }
8238 break;
8239 }
8240 }
8241}
8242
Eric Andersencb57d552001-06-28 07:25:16 +00008243/*
Eric Andersencb57d552001-06-28 07:25:16 +00008244 * Evaluate a pipeline. All the processes in the pipeline are children
8245 * of the process creating the pipeline. (This differs from some versions
8246 * of the shell, which make the last process in a pipeline the parent
8247 * of all the rest.)
8248 */
Eric Andersenc470f442003-07-28 09:56:35 +00008249static void
8250evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008251{
8252 struct job *jp;
8253 struct nodelist *lp;
8254 int pipelen;
8255 int prevfd;
8256 int pip[2];
8257
Eric Andersenc470f442003-07-28 09:56:35 +00008258 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008259 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008260 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008261 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008262 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008263 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008264 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008265 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008266 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008267 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008268 pip[1] = -1;
8269 if (lp->next) {
8270 if (pipe(pip) < 0) {
8271 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008272 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008273 }
8274 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008275 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008276 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008277 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008278 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008279 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008280 if (prevfd > 0) {
8281 dup2(prevfd, 0);
8282 close(prevfd);
8283 }
8284 if (pip[1] > 1) {
8285 dup2(pip[1], 1);
8286 close(pip[1]);
8287 }
Eric Andersenc470f442003-07-28 09:56:35 +00008288 evaltreenr(lp->n, flags);
8289 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008290 }
8291 if (prevfd >= 0)
8292 close(prevfd);
8293 prevfd = pip[0];
8294 close(pip[1]);
8295 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008296 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008297 exitstatus = waitforjob(jp);
8298 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008299 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008300 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008301}
8302
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008303/*
8304 * Controls whether the shell is interactive or not.
8305 */
8306static void
8307setinteractive(int on)
8308{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008309 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008310
8311 if (++on == is_interactive)
8312 return;
8313 is_interactive = on;
8314 setsignal(SIGINT);
8315 setsignal(SIGQUIT);
8316 setsignal(SIGTERM);
8317#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8318 if (is_interactive > 1) {
8319 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008320 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008321
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008322 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008323 out1fmt(
8324 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008325 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008326 "Enter 'help' for a list of built-in commands."
8327 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008328 bb_banner);
8329 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008330 }
8331 }
8332#endif
8333}
8334
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008335static void
8336optschanged(void)
8337{
8338#if DEBUG
8339 opentrace();
8340#endif
8341 setinteractive(iflag);
8342 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008343#if ENABLE_FEATURE_EDITING_VI
8344 if (viflag)
8345 line_input_state->flags |= VI_MODE;
8346 else
8347 line_input_state->flags &= ~VI_MODE;
8348#else
8349 viflag = 0; /* forcibly keep the option off */
8350#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008351}
8352
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008353static struct localvar *localvars;
8354
8355/*
8356 * Called after a function returns.
8357 * Interrupts must be off.
8358 */
8359static void
8360poplocalvars(void)
8361{
8362 struct localvar *lvp;
8363 struct var *vp;
8364
8365 while ((lvp = localvars) != NULL) {
8366 localvars = lvp->next;
8367 vp = lvp->vp;
8368 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8369 if (vp == NULL) { /* $- saved */
8370 memcpy(optlist, lvp->text, sizeof(optlist));
8371 free((char*)lvp->text);
8372 optschanged();
8373 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8374 unsetvar(vp->text);
8375 } else {
8376 if (vp->func)
8377 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8378 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8379 free((char*)vp->text);
8380 vp->flags = lvp->flags;
8381 vp->text = lvp->text;
8382 }
8383 free(lvp);
8384 }
8385}
8386
8387static int
8388evalfun(struct funcnode *func, int argc, char **argv, int flags)
8389{
8390 volatile struct shparam saveparam;
8391 struct localvar *volatile savelocalvars;
8392 struct jmploc *volatile savehandler;
8393 struct jmploc jmploc;
8394 int e;
8395
8396 saveparam = shellparam;
8397 savelocalvars = localvars;
8398 e = setjmp(jmploc.loc);
8399 if (e) {
8400 goto funcdone;
8401 }
8402 INT_OFF;
8403 savehandler = exception_handler;
8404 exception_handler = &jmploc;
8405 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008406 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008407 func->count++;
8408 funcnest++;
8409 INT_ON;
8410 shellparam.nparam = argc - 1;
8411 shellparam.p = argv + 1;
8412#if ENABLE_ASH_GETOPTS
8413 shellparam.optind = 1;
8414 shellparam.optoff = -1;
8415#endif
8416 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008417 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008418 INT_OFF;
8419 funcnest--;
8420 freefunc(func);
8421 poplocalvars();
8422 localvars = savelocalvars;
8423 freeparam(&shellparam);
8424 shellparam = saveparam;
8425 exception_handler = savehandler;
8426 INT_ON;
8427 evalskip &= ~SKIPFUNC;
8428 return e;
8429}
8430
Denis Vlasenko131ae172007-02-18 13:00:19 +00008431#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008432static char **
8433parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008434{
8435 char *cp, c;
8436
8437 for (;;) {
8438 cp = *++argv;
8439 if (!cp)
8440 return 0;
8441 if (*cp++ != '-')
8442 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008443 c = *cp++;
8444 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008445 break;
8446 if (c == '-' && !*cp) {
8447 argv++;
8448 break;
8449 }
8450 do {
8451 switch (c) {
8452 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008453 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008454 break;
8455 default:
8456 /* run 'typecmd' for other options */
8457 return 0;
8458 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008459 c = *cp++;
8460 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008461 }
8462 return argv;
8463}
8464#endif
8465
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008466/*
8467 * Make a variable a local variable. When a variable is made local, it's
8468 * value and flags are saved in a localvar structure. The saved values
8469 * will be restored when the shell function returns. We handle the name
8470 * "-" as a special case.
8471 */
8472static void
8473mklocal(char *name)
8474{
8475 struct localvar *lvp;
8476 struct var **vpp;
8477 struct var *vp;
8478
8479 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008480 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008481 if (LONE_DASH(name)) {
8482 char *p;
8483 p = ckmalloc(sizeof(optlist));
8484 lvp->text = memcpy(p, optlist, sizeof(optlist));
8485 vp = NULL;
8486 } else {
8487 char *eq;
8488
8489 vpp = hashvar(name);
8490 vp = *findvar(vpp, name);
8491 eq = strchr(name, '=');
8492 if (vp == NULL) {
8493 if (eq)
8494 setvareq(name, VSTRFIXED);
8495 else
8496 setvar(name, NULL, VSTRFIXED);
8497 vp = *vpp; /* the new variable */
8498 lvp->flags = VUNSET;
8499 } else {
8500 lvp->text = vp->text;
8501 lvp->flags = vp->flags;
8502 vp->flags |= VSTRFIXED|VTEXTFIXED;
8503 if (eq)
8504 setvareq(name, 0);
8505 }
8506 }
8507 lvp->vp = vp;
8508 lvp->next = localvars;
8509 localvars = lvp;
8510 INT_ON;
8511}
8512
8513/*
8514 * The "local" command.
8515 */
8516static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008517localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008518{
8519 char *name;
8520
8521 argv = argptr;
8522 while ((name = *argv++) != NULL) {
8523 mklocal(name);
8524 }
8525 return 0;
8526}
8527
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008528static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008529falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008530{
8531 return 1;
8532}
8533
8534static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008535truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008536{
8537 return 0;
8538}
8539
8540static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008541execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008542{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008543 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008544 iflag = 0; /* exit on error */
8545 mflag = 0;
8546 optschanged();
8547 shellexec(argv + 1, pathval(), 0);
8548 }
8549 return 0;
8550}
8551
8552/*
8553 * The return command.
8554 */
8555static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008556returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008557{
8558 /*
8559 * If called outside a function, do what ksh does;
8560 * skip the rest of the file.
8561 */
8562 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8563 return argv[1] ? number(argv[1]) : exitstatus;
8564}
8565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008566/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008567static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008568static int dotcmd(int, char **);
8569static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008570static int exitcmd(int, char **);
8571static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008572#if ENABLE_ASH_GETOPTS
8573static int getoptscmd(int, char **);
8574#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008575#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008576static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008577#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008578#if ENABLE_ASH_MATH_SUPPORT
8579static int letcmd(int, char **);
8580#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008581static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008582static int setcmd(int, char **);
8583static int shiftcmd(int, char **);
8584static int timescmd(int, char **);
8585static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008586static int umaskcmd(int, char **);
8587static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008588static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008590#define BUILTIN_NOSPEC "0"
8591#define BUILTIN_SPECIAL "1"
8592#define BUILTIN_REGULAR "2"
8593#define BUILTIN_SPEC_REG "3"
8594#define BUILTIN_ASSIGN "4"
8595#define BUILTIN_SPEC_ASSG "5"
8596#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008597#define BUILTIN_SPEC_REG_ASSG "7"
8598
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008599/* We do not handle [[ expr ]] bashism bash-compatibly,
8600 * we make it a synonym of [ expr ].
8601 * Basically, word splitting and pathname expansion should NOT be performed
8602 * Examples:
8603 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8604 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8605 * Additional operators:
8606 * || and && should work as -o and -a
8607 * =~ regexp match
8608 * Apart from the above, [[ expr ]] should work as [ expr ]
8609 */
8610
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008611#define echocmd echo_main
8612#define printfcmd printf_main
8613#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008614
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008615/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008616static const struct builtincmd builtintab[] = {
8617 { BUILTIN_SPEC_REG ".", dotcmd },
8618 { BUILTIN_SPEC_REG ":", truecmd },
8619#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008620 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008621#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008622 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008623#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008624#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008625#if ENABLE_ASH_ALIAS
8626 { BUILTIN_REG_ASSG "alias", aliascmd },
8627#endif
8628#if JOBS
8629 { BUILTIN_REGULAR "bg", fg_bgcmd },
8630#endif
8631 { BUILTIN_SPEC_REG "break", breakcmd },
8632 { BUILTIN_REGULAR "cd", cdcmd },
8633 { BUILTIN_NOSPEC "chdir", cdcmd },
8634#if ENABLE_ASH_CMDCMD
8635 { BUILTIN_REGULAR "command", commandcmd },
8636#endif
8637 { BUILTIN_SPEC_REG "continue", breakcmd },
8638#if ENABLE_ASH_BUILTIN_ECHO
8639 { BUILTIN_REGULAR "echo", echocmd },
8640#endif
8641 { BUILTIN_SPEC_REG "eval", evalcmd },
8642 { BUILTIN_SPEC_REG "exec", execcmd },
8643 { BUILTIN_SPEC_REG "exit", exitcmd },
8644 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8645 { BUILTIN_REGULAR "false", falsecmd },
8646#if JOBS
8647 { BUILTIN_REGULAR "fg", fg_bgcmd },
8648#endif
8649#if ENABLE_ASH_GETOPTS
8650 { BUILTIN_REGULAR "getopts", getoptscmd },
8651#endif
8652 { BUILTIN_NOSPEC "hash", hashcmd },
8653#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8654 { BUILTIN_NOSPEC "help", helpcmd },
8655#endif
8656#if JOBS
8657 { BUILTIN_REGULAR "jobs", jobscmd },
8658 { BUILTIN_REGULAR "kill", killcmd },
8659#endif
8660#if ENABLE_ASH_MATH_SUPPORT
8661 { BUILTIN_NOSPEC "let", letcmd },
8662#endif
8663 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008664#if ENABLE_ASH_BUILTIN_PRINTF
8665 { BUILTIN_REGULAR "printf", printfcmd },
8666#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008667 { BUILTIN_NOSPEC "pwd", pwdcmd },
8668 { BUILTIN_REGULAR "read", readcmd },
8669 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8670 { BUILTIN_SPEC_REG "return", returncmd },
8671 { BUILTIN_SPEC_REG "set", setcmd },
8672 { BUILTIN_SPEC_REG "shift", shiftcmd },
8673 { BUILTIN_SPEC_REG "source", dotcmd },
8674#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008675 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008676#endif
8677 { BUILTIN_SPEC_REG "times", timescmd },
8678 { BUILTIN_SPEC_REG "trap", trapcmd },
8679 { BUILTIN_REGULAR "true", truecmd },
8680 { BUILTIN_NOSPEC "type", typecmd },
8681 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8682 { BUILTIN_REGULAR "umask", umaskcmd },
8683#if ENABLE_ASH_ALIAS
8684 { BUILTIN_REGULAR "unalias", unaliascmd },
8685#endif
8686 { BUILTIN_SPEC_REG "unset", unsetcmd },
8687 { BUILTIN_REGULAR "wait", waitcmd },
8688};
8689
Denis Vlasenko80591b02008-03-25 07:49:43 +00008690/* Should match the above table! */
8691#define COMMANDCMD (builtintab + \
8692 2 + \
8693 1 * ENABLE_ASH_BUILTIN_TEST + \
8694 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8695 1 * ENABLE_ASH_ALIAS + \
8696 1 * ENABLE_ASH_JOB_CONTROL + \
8697 3)
8698#define EXECCMD (builtintab + \
8699 2 + \
8700 1 * ENABLE_ASH_BUILTIN_TEST + \
8701 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8702 1 * ENABLE_ASH_ALIAS + \
8703 1 * ENABLE_ASH_JOB_CONTROL + \
8704 3 + \
8705 1 * ENABLE_ASH_CMDCMD + \
8706 1 + \
8707 ENABLE_ASH_BUILTIN_ECHO + \
8708 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008709
8710/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008711 * Search the table of builtin commands.
8712 */
8713static struct builtincmd *
8714find_builtin(const char *name)
8715{
8716 struct builtincmd *bp;
8717
8718 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008719 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008720 pstrcmp
8721 );
8722 return bp;
8723}
8724
8725/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008726 * Execute a simple command.
8727 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008728static int
8729isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008730{
8731 const char *q = endofname(p);
8732 if (p == q)
8733 return 0;
8734 return *q == '=';
8735}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008736static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008737bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008738{
8739 /* Preserve exitstatus of a previous possible redirection
8740 * as POSIX mandates */
8741 return back_exitstatus;
8742}
Eric Andersenc470f442003-07-28 09:56:35 +00008743static void
8744evalcommand(union node *cmd, int flags)
8745{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008746 static const struct builtincmd null_bltin = {
8747 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008748 };
Eric Andersenc470f442003-07-28 09:56:35 +00008749 struct stackmark smark;
8750 union node *argp;
8751 struct arglist arglist;
8752 struct arglist varlist;
8753 char **argv;
8754 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008755 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008756 struct cmdentry cmdentry;
8757 struct job *jp;
8758 char *lastarg;
8759 const char *path;
8760 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008761 int status;
8762 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008763 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008764 smallint cmd_is_exec;
8765 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008766
8767 /* First expand the arguments. */
8768 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8769 setstackmark(&smark);
8770 back_exitstatus = 0;
8771
8772 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008773 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008774 varlist.lastp = &varlist.list;
8775 *varlist.lastp = NULL;
8776 arglist.lastp = &arglist.list;
8777 *arglist.lastp = NULL;
8778
8779 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008780 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008781 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8782 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8783 }
8784
Eric Andersenc470f442003-07-28 09:56:35 +00008785 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8786 struct strlist **spp;
8787
8788 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008789 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008790 expandarg(argp, &arglist, EXP_VARTILDE);
8791 else
8792 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8793
Eric Andersenc470f442003-07-28 09:56:35 +00008794 for (sp = *spp; sp; sp = sp->next)
8795 argc++;
8796 }
8797
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008798 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008799 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008800 TRACE(("evalcommand arg: %s\n", sp->text));
8801 *nargv++ = sp->text;
8802 }
8803 *nargv = NULL;
8804
8805 lastarg = NULL;
8806 if (iflag && funcnest == 0 && argc > 0)
8807 lastarg = nargv[-1];
8808
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008809 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008810 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008811 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008812
8813 path = vpath.text;
8814 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8815 struct strlist **spp;
8816 char *p;
8817
8818 spp = varlist.lastp;
8819 expandarg(argp, &varlist, EXP_VARTILDE);
8820
8821 /*
8822 * Modify the command lookup path, if a PATH= assignment
8823 * is present
8824 */
8825 p = (*spp)->text;
8826 if (varequal(p, path))
8827 path = p;
8828 }
8829
8830 /* Print the command if xflag is set. */
8831 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008832 int n;
8833 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008834
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008835 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008836 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008837
8838 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008839 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008840 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008841 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008842 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008843 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008844 p--;
8845 }
8846 }
8847 sp = arglist.list;
8848 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008849 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008850 }
8851
8852 cmd_is_exec = 0;
8853 spclbltin = -1;
8854
8855 /* Now locate the command. */
8856 if (argc) {
8857 const char *oldpath;
8858 int cmd_flag = DO_ERR;
8859
8860 path += 5;
8861 oldpath = path;
8862 for (;;) {
8863 find_command(argv[0], &cmdentry, cmd_flag, path);
8864 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008865 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008866 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008867 goto bail;
8868 }
8869
8870 /* implement bltin and command here */
8871 if (cmdentry.cmdtype != CMDBUILTIN)
8872 break;
8873 if (spclbltin < 0)
8874 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8875 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008876 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008877#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008878 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008879 path = oldpath;
8880 nargv = parse_command_args(argv, &path);
8881 if (!nargv)
8882 break;
8883 argc -= nargv - argv;
8884 argv = nargv;
8885 cmd_flag |= DO_NOFUNC;
8886 } else
8887#endif
8888 break;
8889 }
8890 }
8891
8892 if (status) {
8893 /* We have a redirection error. */
8894 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008895 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008896 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008897 exitstatus = status;
8898 goto out;
8899 }
8900
8901 /* Execute the command. */
8902 switch (cmdentry.cmdtype) {
8903 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008904
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008905#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008906/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8907 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008908 {
8909 /* find_command() encodes applet_no as (-2 - applet_no) */
8910 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008911 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008912 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008913 /* run <applet>_main() */
8914 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008915 break;
8916 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008917 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008918#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008919 /* Fork off a child process if necessary. */
8920 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008921 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008922 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008923 if (forkshell(jp, cmd, FORK_FG) != 0) {
8924 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008925 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008926 break;
8927 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008928 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008929 }
8930 listsetvar(varlist.list, VEXPORT|VSTACK);
8931 shellexec(argv, path, cmdentry.u.index);
8932 /* NOTREACHED */
8933
8934 case CMDBUILTIN:
8935 cmdenviron = varlist.list;
8936 if (cmdenviron) {
8937 struct strlist *list = cmdenviron;
8938 int i = VNOSET;
8939 if (spclbltin > 0 || argc == 0) {
8940 i = 0;
8941 if (cmd_is_exec && argc > 1)
8942 i = VEXPORT;
8943 }
8944 listsetvar(list, i);
8945 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008946 /* Tight loop with builtins only:
8947 * "while kill -0 $child; do true; done"
8948 * will never exit even if $child died, unless we do this
8949 * to reap the zombie and make kill detect that it's gone: */
8950 dowait(DOWAIT_NONBLOCK, NULL);
8951
Eric Andersenc470f442003-07-28 09:56:35 +00008952 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8953 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008954 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008955 if (i == EXEXIT)
8956 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008957 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008958 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008959 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008960 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008961 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008962 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008963 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008964 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008965 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008966 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008967 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008968 }
8969 break;
8970
8971 case CMDFUNCTION:
8972 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008973 /* See above for the rationale */
8974 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00008975 if (evalfun(cmdentry.u.func, argc, argv, flags))
8976 goto raise;
8977 break;
8978 }
8979
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008980 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008981 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008982 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008983 /* dsl: I think this is intended to be used to support
8984 * '_' in 'vi' command mode during line editing...
8985 * However I implemented that within libedit itself.
8986 */
8987 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008988 }
Eric Andersenc470f442003-07-28 09:56:35 +00008989 popstackmark(&smark);
8990}
8991
8992static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008993evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8994{
Eric Andersenc470f442003-07-28 09:56:35 +00008995 char *volatile savecmdname;
8996 struct jmploc *volatile savehandler;
8997 struct jmploc jmploc;
8998 int i;
8999
9000 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009001 i = setjmp(jmploc.loc);
9002 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009003 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009004 savehandler = exception_handler;
9005 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009006 commandname = argv[0];
9007 argptr = argv + 1;
9008 optptr = NULL; /* initialize nextopt */
9009 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009010 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009011 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009012 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009013 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009014 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009015// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009016 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009017
9018 return i;
9019}
9020
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009021static int
9022goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009023{
9024 return !*endofname(p);
9025}
9026
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009027
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009028/*
9029 * Search for a command. This is called before we fork so that the
9030 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009031 * the child. The check for "goodname" is an overly conservative
9032 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009033 */
Eric Andersenc470f442003-07-28 09:56:35 +00009034static void
9035prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009036{
9037 struct cmdentry entry;
9038
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009039 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9040 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009041}
9042
Eric Andersencb57d552001-06-28 07:25:16 +00009043
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009044/* ============ Builtin commands
9045 *
9046 * Builtin commands whose functions are closely tied to evaluation
9047 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009048 */
9049
9050/*
Eric Andersencb57d552001-06-28 07:25:16 +00009051 * Handle break and continue commands. Break, continue, and return are
9052 * all handled by setting the evalskip flag. The evaluation routines
9053 * above all check this flag, and if it is set they start skipping
9054 * commands rather than executing them. The variable skipcount is
9055 * the number of loops to break/continue, or the number of function
9056 * levels to return. (The latter is always 1.) It should probably
9057 * be an error to break out of more loops than exist, but it isn't
9058 * in the standard shell so we don't make it one here.
9059 */
Eric Andersenc470f442003-07-28 09:56:35 +00009060static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009061breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009062{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009063 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009064
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009065 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009066 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009067 if (n > loopnest)
9068 n = loopnest;
9069 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009070 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009071 skipcount = n;
9072 }
9073 return 0;
9074}
9075
Eric Andersenc470f442003-07-28 09:56:35 +00009076
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009077/* ============ input.c
9078 *
Eric Andersen90898442003-08-06 11:20:52 +00009079 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009080 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009081
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009082enum {
9083 INPUT_PUSH_FILE = 1,
9084 INPUT_NOFILE_OK = 2,
9085};
Eric Andersencb57d552001-06-28 07:25:16 +00009086
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009087static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009088/* values of checkkwd variable */
9089#define CHKALIAS 0x1
9090#define CHKKWD 0x2
9091#define CHKNL 0x4
9092
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009093/*
9094 * Push a string back onto the input at this current parsefile level.
9095 * We handle aliases this way.
9096 */
9097#if !ENABLE_ASH_ALIAS
9098#define pushstring(s, ap) pushstring(s)
9099#endif
9100static void
9101pushstring(char *s, struct alias *ap)
9102{
9103 struct strpush *sp;
9104 int len;
9105
9106 len = strlen(s);
9107 INT_OFF;
9108 if (g_parsefile->strpush) {
9109 sp = ckzalloc(sizeof(*sp));
9110 sp->prev = g_parsefile->strpush;
9111 } else {
9112 sp = &(g_parsefile->basestrpush);
9113 }
9114 g_parsefile->strpush = sp;
9115 sp->prev_string = g_parsefile->next_to_pgetc;
9116 sp->prev_left_in_line = g_parsefile->left_in_line;
9117#if ENABLE_ASH_ALIAS
9118 sp->ap = ap;
9119 if (ap) {
9120 ap->flag |= ALIASINUSE;
9121 sp->string = s;
9122 }
9123#endif
9124 g_parsefile->next_to_pgetc = s;
9125 g_parsefile->left_in_line = len;
9126 INT_ON;
9127}
9128
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009129static void
9130popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009131{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009132 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009133
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009134 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009135#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009136 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009137 if (g_parsefile->next_to_pgetc[-1] == ' '
9138 || g_parsefile->next_to_pgetc[-1] == '\t'
9139 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009140 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009141 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009142 if (sp->string != sp->ap->val) {
9143 free(sp->string);
9144 }
9145 sp->ap->flag &= ~ALIASINUSE;
9146 if (sp->ap->flag & ALIASDEAD) {
9147 unalias(sp->ap->name);
9148 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009149 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009150#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009151 g_parsefile->next_to_pgetc = sp->prev_string;
9152 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009153 g_parsefile->strpush = sp->prev;
9154 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009155 free(sp);
9156 INT_ON;
9157}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009158
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009159//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9160//it peeks whether it is &>, and then pushes back both chars.
9161//This function needs to save last *next_to_pgetc to buf[0]
9162//to make two pungetc() reliable. Currently,
9163// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009164static int
9165preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009166{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009167 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009168 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009169
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009170 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009171#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009172 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009173 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009174 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009175 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009176#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009177 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009178#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009179 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9180 if (nr == 0) {
9181 /* Ctrl+C pressed */
9182 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009183 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009184 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009185 raise(SIGINT);
9186 return 1;
9187 }
Eric Andersenc470f442003-07-28 09:56:35 +00009188 goto retry;
9189 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009190 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009191 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009192 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009193 }
Eric Andersencb57d552001-06-28 07:25:16 +00009194 }
9195#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009196 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009197#endif
9198
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009199#if 0
9200/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009201 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009202 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009203 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009204 if (flags >= 0 && (flags & O_NONBLOCK)) {
9205 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009206 if (fcntl(0, F_SETFL, flags) >= 0) {
9207 out2str("sh: turning off NDELAY mode\n");
9208 goto retry;
9209 }
9210 }
9211 }
9212 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009213#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009214 return nr;
9215}
9216
9217/*
9218 * Refill the input buffer and return the next input character:
9219 *
9220 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009221 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9222 * or we are reading from a string so we can't refill the buffer,
9223 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009224 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9225 * 4) Process input up to the next newline, deleting nul characters.
9226 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009227//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9228#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009229static int
Eric Andersenc470f442003-07-28 09:56:35 +00009230preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009231{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009232 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009233 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009234
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009235 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009236#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009237 if (g_parsefile->left_in_line == -1
9238 && g_parsefile->strpush->ap
9239 && g_parsefile->next_to_pgetc[-1] != ' '
9240 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009241 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009242 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009243 return PEOA;
9244 }
Eric Andersen2870d962001-07-02 17:27:21 +00009245#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009246 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009247 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009248 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9249 g_parsefile->left_in_line,
9250 g_parsefile->next_to_pgetc,
9251 g_parsefile->next_to_pgetc);
9252 if (--g_parsefile->left_in_line >= 0)
9253 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009254 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009255 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009256 * "pgetc" needs refilling.
9257 */
9258
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009259 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009260 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009261 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009262 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009263 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009264 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009265 /* even in failure keep left_in_line and next_to_pgetc
9266 * in lock step, for correct multi-layer pungetc.
9267 * left_in_line was decremented before preadbuffer(),
9268 * must inc next_to_pgetc: */
9269 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009270 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009271 }
Eric Andersencb57d552001-06-28 07:25:16 +00009272
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009273 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009274 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009275 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009276 again:
9277 more = preadfd();
9278 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009279 /* don't try reading again */
9280 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009281 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009282 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009283 return PEOF;
9284 }
9285 }
9286
Denis Vlasenko727752d2008-11-28 03:41:47 +00009287 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009288 * Set g_parsefile->left_in_line
9289 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009290 * NUL chars are deleted.
9291 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009292 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009293 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009294 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009295
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009296 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009297
Denis Vlasenko727752d2008-11-28 03:41:47 +00009298 c = *q;
9299 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009300 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009301 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009302 q++;
9303 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009304 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009305 break;
9306 }
Eric Andersencb57d552001-06-28 07:25:16 +00009307 }
9308
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009309 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009310 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9311 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009312 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009313 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009314 }
9315 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009316 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009317
Eric Andersencb57d552001-06-28 07:25:16 +00009318 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009319 char save = *q;
9320 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009321 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009322 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009323 }
9324
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009325 pgetc_debug("preadbuffer at %d:%p'%s'",
9326 g_parsefile->left_in_line,
9327 g_parsefile->next_to_pgetc,
9328 g_parsefile->next_to_pgetc);
9329 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009330}
9331
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009332#define pgetc_as_macro() \
9333 (--g_parsefile->left_in_line >= 0 \
9334 ? (unsigned char)(*g_parsefile->next_to_pgetc++) \
9335 : preadbuffer() \
9336 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009337
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009338static int
9339pgetc(void)
9340{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009341 pgetc_debug("pgetc_fast at %d:%p'%s'",
9342 g_parsefile->left_in_line,
9343 g_parsefile->next_to_pgetc,
9344 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009345 return pgetc_as_macro();
9346}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009347
9348#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009349#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009350#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009351#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009352#endif
9353
9354/*
9355 * Same as pgetc(), but ignores PEOA.
9356 */
9357#if ENABLE_ASH_ALIAS
9358static int
9359pgetc2(void)
9360{
9361 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009362 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009363 pgetc_debug("pgetc_fast at %d:%p'%s'",
9364 g_parsefile->left_in_line,
9365 g_parsefile->next_to_pgetc,
9366 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009367 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009368 } while (c == PEOA);
9369 return c;
9370}
9371#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009372#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009373#endif
9374
9375/*
9376 * Read a line from the script.
9377 */
9378static char *
9379pfgets(char *line, int len)
9380{
9381 char *p = line;
9382 int nleft = len;
9383 int c;
9384
9385 while (--nleft > 0) {
9386 c = pgetc2();
9387 if (c == PEOF) {
9388 if (p == line)
9389 return NULL;
9390 break;
9391 }
9392 *p++ = c;
9393 if (c == '\n')
9394 break;
9395 }
9396 *p = '\0';
9397 return line;
9398}
9399
Eric Andersenc470f442003-07-28 09:56:35 +00009400/*
9401 * Undo the last call to pgetc. Only one character may be pushed back.
9402 * PEOF may be pushed back.
9403 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009404static void
Eric Andersenc470f442003-07-28 09:56:35 +00009405pungetc(void)
9406{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009407 g_parsefile->left_in_line++;
9408 g_parsefile->next_to_pgetc--;
9409 pgetc_debug("pushed back to %d:%p'%s'",
9410 g_parsefile->left_in_line,
9411 g_parsefile->next_to_pgetc,
9412 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009413}
9414
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009415/*
9416 * To handle the "." command, a stack of input files is used. Pushfile
9417 * adds a new entry to the stack and popfile restores the previous level.
9418 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009419static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009420pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009421{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009422 struct parsefile *pf;
9423
Denis Vlasenko597906c2008-02-20 16:38:54 +00009424 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009425 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009426 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009427 /*pf->strpush = NULL; - ckzalloc did it */
9428 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009429 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009430}
9431
9432static void
9433popfile(void)
9434{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009435 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009436
Denis Vlasenkob012b102007-02-19 22:43:01 +00009437 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009438 if (pf->fd >= 0)
9439 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009440 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009441 while (pf->strpush)
9442 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009443 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009444 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009445 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009446}
9447
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009448/*
9449 * Return to top level.
9450 */
9451static void
9452popallfiles(void)
9453{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009454 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009455 popfile();
9456}
9457
9458/*
9459 * Close the file(s) that the shell is reading commands from. Called
9460 * after a fork is done.
9461 */
9462static void
9463closescript(void)
9464{
9465 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009466 if (g_parsefile->fd > 0) {
9467 close(g_parsefile->fd);
9468 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009469 }
9470}
9471
9472/*
9473 * Like setinputfile, but takes an open file descriptor. Call this with
9474 * interrupts off.
9475 */
9476static void
9477setinputfd(int fd, int push)
9478{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009479 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009480 if (push) {
9481 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009482 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009483 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009484 g_parsefile->fd = fd;
9485 if (g_parsefile->buf == NULL)
9486 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009487 g_parsefile->left_in_buffer = 0;
9488 g_parsefile->left_in_line = 0;
9489 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009490}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009491
Eric Andersenc470f442003-07-28 09:56:35 +00009492/*
9493 * Set the input to take input from a file. If push is set, push the
9494 * old input onto the stack first.
9495 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009496static int
9497setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009498{
9499 int fd;
9500 int fd2;
9501
Denis Vlasenkob012b102007-02-19 22:43:01 +00009502 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009503 fd = open(fname, O_RDONLY);
9504 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009505 if (flags & INPUT_NOFILE_OK)
9506 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009507 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009508 }
Eric Andersenc470f442003-07-28 09:56:35 +00009509 if (fd < 10) {
9510 fd2 = copyfd(fd, 10);
9511 close(fd);
9512 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009513 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009514 fd = fd2;
9515 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009516 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009517 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009518 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009519 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009520}
9521
Eric Andersencb57d552001-06-28 07:25:16 +00009522/*
9523 * Like setinputfile, but takes input from a string.
9524 */
Eric Andersenc470f442003-07-28 09:56:35 +00009525static void
9526setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009527{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009528 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009529 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009530 g_parsefile->next_to_pgetc = string;
9531 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009532 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009533 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009534 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009535}
9536
9537
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009538/* ============ mail.c
9539 *
9540 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009541 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009542
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009543#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009544
Eric Andersencb57d552001-06-28 07:25:16 +00009545#define MAXMBOXES 10
9546
Eric Andersenc470f442003-07-28 09:56:35 +00009547/* times of mailboxes */
9548static time_t mailtime[MAXMBOXES];
9549/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009550static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009551
Eric Andersencb57d552001-06-28 07:25:16 +00009552/*
Eric Andersenc470f442003-07-28 09:56:35 +00009553 * Print appropriate message(s) if mail has arrived.
9554 * If mail_var_path_changed is set,
9555 * then the value of MAIL has mail_var_path_changed,
9556 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009557 */
Eric Andersenc470f442003-07-28 09:56:35 +00009558static void
9559chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009560{
Eric Andersencb57d552001-06-28 07:25:16 +00009561 const char *mpath;
9562 char *p;
9563 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009564 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009565 struct stackmark smark;
9566 struct stat statb;
9567
Eric Andersencb57d552001-06-28 07:25:16 +00009568 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009569 mpath = mpathset() ? mpathval() : mailval();
9570 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009571 p = padvance(&mpath, nullstr);
9572 if (p == NULL)
9573 break;
9574 if (*p == '\0')
9575 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009576 for (q = p; *q; q++)
9577 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009578#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009579 if (q[-1] != '/')
9580 abort();
9581#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009582 q[-1] = '\0'; /* delete trailing '/' */
9583 if (stat(p, &statb) < 0) {
9584 *mtp = 0;
9585 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009586 }
Eric Andersenc470f442003-07-28 09:56:35 +00009587 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9588 fprintf(
9589 stderr, snlfmt,
9590 pathopt ? pathopt : "you have mail"
9591 );
9592 }
9593 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009594 }
Eric Andersenc470f442003-07-28 09:56:35 +00009595 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009596 popstackmark(&smark);
9597}
Eric Andersencb57d552001-06-28 07:25:16 +00009598
Eric Andersenc470f442003-07-28 09:56:35 +00009599static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009600changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009601{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009602 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009603}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009604
Denis Vlasenko131ae172007-02-18 13:00:19 +00009605#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009606
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009607
9608/* ============ ??? */
9609
Eric Andersencb57d552001-06-28 07:25:16 +00009610/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009611 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009612 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009613static void
9614setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009615{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009616 char **newparam;
9617 char **ap;
9618 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009619
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009620 for (nparam = 0; argv[nparam]; nparam++)
9621 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009622 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9623 while (*argv) {
9624 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009625 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009626 *ap = NULL;
9627 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009628 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009629 shellparam.nparam = nparam;
9630 shellparam.p = newparam;
9631#if ENABLE_ASH_GETOPTS
9632 shellparam.optind = 1;
9633 shellparam.optoff = -1;
9634#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009635}
9636
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009637/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009638 * Process shell options. The global variable argptr contains a pointer
9639 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009640 *
9641 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9642 * For a non-interactive shell, an error condition encountered
9643 * by a special built-in ... shall cause the shell to write a diagnostic message
9644 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009645 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009646 * ...
9647 * Utility syntax error (option or operand error) Shall exit
9648 * ...
9649 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9650 * we see that bash does not do that (set "finishes" with error code 1 instead,
9651 * and shell continues), and people rely on this behavior!
9652 * Testcase:
9653 * set -o barfoo 2>/dev/null
9654 * echo $?
9655 *
9656 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009657 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009658static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009659plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009660{
9661 int i;
9662
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009663 if (name) {
9664 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009665 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009666 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009667 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009668 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009669 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009670 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009671 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009672 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009673 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009674 if (val) {
9675 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9676 } else {
9677 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9678 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009679 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009680 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009681}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009682static void
9683setoption(int flag, int val)
9684{
9685 int i;
9686
9687 for (i = 0; i < NOPTS; i++) {
9688 if (optletters(i) == flag) {
9689 optlist[i] = val;
9690 return;
9691 }
9692 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009693 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009694 /* NOTREACHED */
9695}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009696static int
Eric Andersenc470f442003-07-28 09:56:35 +00009697options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009698{
9699 char *p;
9700 int val;
9701 int c;
9702
9703 if (cmdline)
9704 minusc = NULL;
9705 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009706 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009707 if (c != '-' && c != '+')
9708 break;
9709 argptr++;
9710 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009711 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009712 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009713 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009714 if (!cmdline) {
9715 /* "-" means turn off -x and -v */
9716 if (p[0] == '\0')
9717 xflag = vflag = 0;
9718 /* "--" means reset params */
9719 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009720 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009721 }
Eric Andersenc470f442003-07-28 09:56:35 +00009722 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
Eric Andersencb57d552001-06-28 07:25:16 +00009724 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009725 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009726 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009727 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009728 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009729 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009730 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009731 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009732 /* it already printed err message */
9733 return 1; /* error */
9734 }
Eric Andersencb57d552001-06-28 07:25:16 +00009735 if (*argptr)
9736 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009737 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9738 isloginsh = 1;
9739 /* bash does not accept +-login, we also won't */
9740 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009741 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009742 isloginsh = 1;
9743 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009744 } else {
9745 setoption(c, val);
9746 }
9747 }
9748 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009749 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009750}
9751
Eric Andersencb57d552001-06-28 07:25:16 +00009752/*
Eric Andersencb57d552001-06-28 07:25:16 +00009753 * The shift builtin command.
9754 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009755static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009756shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009757{
9758 int n;
9759 char **ap1, **ap2;
9760
9761 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009762 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009763 n = number(argv[1]);
9764 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009765 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009766 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009767 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009768 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009769 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009770 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009771 }
9772 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009773 while ((*ap2++ = *ap1++) != NULL)
9774 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009775#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009776 shellparam.optind = 1;
9777 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009778#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009779 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009780 return 0;
9781}
9782
Eric Andersencb57d552001-06-28 07:25:16 +00009783/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009784 * POSIX requires that 'set' (but not export or readonly) output the
9785 * variables in lexicographic order - by the locale's collating order (sigh).
9786 * Maybe we could keep them in an ordered balanced binary tree
9787 * instead of hashed lists.
9788 * For now just roll 'em through qsort for printing...
9789 */
9790static int
9791showvars(const char *sep_prefix, int on, int off)
9792{
9793 const char *sep;
9794 char **ep, **epend;
9795
9796 ep = listvars(on, off, &epend);
9797 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9798
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009799 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009800
9801 for (; ep < epend; ep++) {
9802 const char *p;
9803 const char *q;
9804
9805 p = strchrnul(*ep, '=');
9806 q = nullstr;
9807 if (*p)
9808 q = single_quote(++p);
9809 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9810 }
9811 return 0;
9812}
9813
9814/*
Eric Andersencb57d552001-06-28 07:25:16 +00009815 * The set command builtin.
9816 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009817static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009818setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009819{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009820 int retval;
9821
Denis Vlasenko68404f12008-03-17 09:00:54 +00009822 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009823 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009824 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009825 retval = 1;
9826 if (!options(0)) { /* if no parse error... */
9827 retval = 0;
9828 optschanged();
9829 if (*argptr != NULL) {
9830 setparam(argptr);
9831 }
Eric Andersencb57d552001-06-28 07:25:16 +00009832 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009833 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009834 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009835}
9836
Denis Vlasenko131ae172007-02-18 13:00:19 +00009837#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009838static void
9839change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009840{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009841 /* Galois LFSR parameter */
9842 /* Taps at 32 31 29 1: */
9843 enum { MASK = 0x8000000b };
9844 /* Another example - taps at 32 31 30 10: */
9845 /* MASK = 0x00400007 */
9846
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009847 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009848 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009849 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009850
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009851 /* LCG has period of 2^32 and alternating lowest bit */
9852 random_LCG = 1664525 * random_LCG + 1013904223;
9853 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9854 t = (random_galois_LFSR << 1);
9855 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9856 t ^= MASK;
9857 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009858 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009859 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009860 * for $RANDOM range. Combining with subtraction is
9861 * just for fun. + and ^ would work equally well. */
9862 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009863 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009864 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009865 vrandom.flags &= ~VNOFUNC;
9866 } else {
9867 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009868 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009869 }
Eric Andersenef02f822004-03-11 13:34:24 +00009870}
Eric Andersen16767e22004-03-16 05:14:10 +00009871#endif
9872
Denis Vlasenko131ae172007-02-18 13:00:19 +00009873#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009874static int
Eric Andersenc470f442003-07-28 09:56:35 +00009875getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009876{
9877 char *p, *q;
9878 char c = '?';
9879 int done = 0;
9880 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009881 char s[12];
9882 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009883
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009884 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009885 return 1;
9886 optnext = optfirst + *param_optind - 1;
9887
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009888 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009889 p = NULL;
9890 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009891 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009892 if (p == NULL || *p == '\0') {
9893 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009894 p = *optnext;
9895 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009896 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009897 p = NULL;
9898 done = 1;
9899 goto out;
9900 }
9901 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009902 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009903 goto atend;
9904 }
9905
9906 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009907 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009908 if (*q == '\0') {
9909 if (optstr[0] == ':') {
9910 s[0] = c;
9911 s[1] = '\0';
9912 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009913 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009914 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009915 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009916 }
9917 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009918 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009919 }
9920 if (*++q == ':')
9921 q++;
9922 }
9923
9924 if (*++q == ':') {
9925 if (*p == '\0' && (p = *optnext) == NULL) {
9926 if (optstr[0] == ':') {
9927 s[0] = c;
9928 s[1] = '\0';
9929 err |= setvarsafe("OPTARG", s, 0);
9930 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009931 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009932 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009933 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009934 c = '?';
9935 }
Eric Andersenc470f442003-07-28 09:56:35 +00009936 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009937 }
9938
9939 if (p == *optnext)
9940 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009941 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009942 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009943 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009944 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009945 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009946 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009947 *param_optind = optnext - optfirst + 1;
9948 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009949 err |= setvarsafe("OPTIND", s, VNOFUNC);
9950 s[0] = c;
9951 s[1] = '\0';
9952 err |= setvarsafe(optvar, s, 0);
9953 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009954 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009955 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009956 flush_stdout_stderr();
9957 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009958 }
9959 return done;
9960}
Eric Andersenc470f442003-07-28 09:56:35 +00009961
9962/*
9963 * The getopts builtin. Shellparam.optnext points to the next argument
9964 * to be processed. Shellparam.optptr points to the next character to
9965 * be processed in the current argument. If shellparam.optnext is NULL,
9966 * then it's the first time getopts has been called.
9967 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009968static int
Eric Andersenc470f442003-07-28 09:56:35 +00009969getoptscmd(int argc, char **argv)
9970{
9971 char **optbase;
9972
9973 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009974 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009975 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009976 optbase = shellparam.p;
9977 if (shellparam.optind > shellparam.nparam + 1) {
9978 shellparam.optind = 1;
9979 shellparam.optoff = -1;
9980 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009981 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009982 optbase = &argv[3];
9983 if (shellparam.optind > argc - 2) {
9984 shellparam.optind = 1;
9985 shellparam.optoff = -1;
9986 }
9987 }
9988
9989 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009990 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009991}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009992#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009993
Eric Andersencb57d552001-06-28 07:25:16 +00009994
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009995/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009996
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009997struct heredoc {
9998 struct heredoc *next; /* next here document in list */
9999 union node *here; /* redirection node */
10000 char *eofmark; /* string indicating end of input */
10001 smallint striptabs; /* if set, strip leading tabs */
10002};
10003
10004static smallint tokpushback; /* last token pushed back */
10005static smallint parsebackquote; /* nonzero if we are inside backquotes */
10006static smallint quoteflag; /* set if (part of) last token was quoted */
10007static token_id_t lasttoken; /* last token read (integer id Txxx) */
10008static struct heredoc *heredoclist; /* list of here documents to read */
10009static char *wordtext; /* text of last word returned by readtoken */
10010static struct nodelist *backquotelist;
10011static union node *redirnode;
10012static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010013/*
10014 * NEOF is returned by parsecmd when it encounters an end of file. It
10015 * must be distinct from NULL, so we use the address of a variable that
10016 * happens to be handy.
10017 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010018#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010019
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010020static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010021static void
10022raise_error_syntax(const char *msg)
10023{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010024 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010025 /* NOTREACHED */
10026}
10027
10028/*
10029 * Called when an unexpected token is read during the parse. The argument
10030 * is the token that is expected, or -1 if more than one type of token can
10031 * occur at this point.
10032 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010033static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010034static void
10035raise_error_unexpected_syntax(int token)
10036{
10037 char msg[64];
10038 int l;
10039
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010040 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010041 if (token >= 0)
10042 sprintf(msg + l, " (expecting %s)", tokname(token));
10043 raise_error_syntax(msg);
10044 /* NOTREACHED */
10045}
Eric Andersencb57d552001-06-28 07:25:16 +000010046
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010047#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010048
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010049/* parsing is heavily cross-recursive, need these forward decls */
10050static union node *andor(void);
10051static union node *pipeline(void);
10052static union node *parse_command(void);
10053static void parseheredoc(void);
10054static char peektoken(void);
10055static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010056
Eric Andersenc470f442003-07-28 09:56:35 +000010057static union node *
10058list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010059{
10060 union node *n1, *n2, *n3;
10061 int tok;
10062
Eric Andersenc470f442003-07-28 09:56:35 +000010063 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10064 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010065 return NULL;
10066 n1 = NULL;
10067 for (;;) {
10068 n2 = andor();
10069 tok = readtoken();
10070 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010071 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010072 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010073 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010074 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010075 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010076 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010077 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010078 n2 = n3;
10079 }
10080 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010081 }
10082 }
10083 if (n1 == NULL) {
10084 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010085 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010086 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010087 n3->type = NSEMI;
10088 n3->nbinary.ch1 = n1;
10089 n3->nbinary.ch2 = n2;
10090 n1 = n3;
10091 }
10092 switch (tok) {
10093 case TBACKGND:
10094 case TSEMI:
10095 tok = readtoken();
10096 /* fall through */
10097 case TNL:
10098 if (tok == TNL) {
10099 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010100 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010101 return n1;
10102 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010103 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010104 }
Eric Andersenc470f442003-07-28 09:56:35 +000010105 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010106 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010107 return n1;
10108 break;
10109 case TEOF:
10110 if (heredoclist)
10111 parseheredoc();
10112 else
Eric Andersenc470f442003-07-28 09:56:35 +000010113 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010114 return n1;
10115 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010116 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010117 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010118 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010119 return n1;
10120 }
10121 }
10122}
10123
Eric Andersenc470f442003-07-28 09:56:35 +000010124static union node *
10125andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010126{
Eric Andersencb57d552001-06-28 07:25:16 +000010127 union node *n1, *n2, *n3;
10128 int t;
10129
Eric Andersencb57d552001-06-28 07:25:16 +000010130 n1 = pipeline();
10131 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010132 t = readtoken();
10133 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010134 t = NAND;
10135 } else if (t == TOR) {
10136 t = NOR;
10137 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010138 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010139 return n1;
10140 }
Eric Andersenc470f442003-07-28 09:56:35 +000010141 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010142 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010143 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010144 n3->type = t;
10145 n3->nbinary.ch1 = n1;
10146 n3->nbinary.ch2 = n2;
10147 n1 = n3;
10148 }
10149}
10150
Eric Andersenc470f442003-07-28 09:56:35 +000010151static union node *
10152pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010153{
Eric Andersencb57d552001-06-28 07:25:16 +000010154 union node *n1, *n2, *pipenode;
10155 struct nodelist *lp, *prev;
10156 int negate;
10157
10158 negate = 0;
10159 TRACE(("pipeline: entered\n"));
10160 if (readtoken() == TNOT) {
10161 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010162 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010163 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010164 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010165 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010166 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010167 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010168 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010169 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010170 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010171 pipenode->npipe.cmdlist = lp;
10172 lp->n = n1;
10173 do {
10174 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010175 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010176 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010177 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010178 prev->next = lp;
10179 } while (readtoken() == TPIPE);
10180 lp->next = NULL;
10181 n1 = pipenode;
10182 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010183 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010184 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010185 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010186 n2->type = NNOT;
10187 n2->nnot.com = n1;
10188 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010189 }
10190 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010191}
10192
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010193static union node *
10194makename(void)
10195{
10196 union node *n;
10197
Denis Vlasenko597906c2008-02-20 16:38:54 +000010198 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010199 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010200 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010201 n->narg.text = wordtext;
10202 n->narg.backquote = backquotelist;
10203 return n;
10204}
10205
10206static void
10207fixredir(union node *n, const char *text, int err)
10208{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010209 int fd;
10210
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010211 TRACE(("Fix redir %s %d\n", text, err));
10212 if (!err)
10213 n->ndup.vname = NULL;
10214
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010215 fd = bb_strtou(text, NULL, 10);
10216 if (!errno && fd >= 0)
10217 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010218 else if (LONE_DASH(text))
10219 n->ndup.dupfd = -1;
10220 else {
10221 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010222 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010223 n->ndup.vname = makename();
10224 }
10225}
10226
10227/*
10228 * Returns true if the text contains nothing to expand (no dollar signs
10229 * or backquotes).
10230 */
10231static int
10232noexpand(char *text)
10233{
10234 char *p;
10235 char c;
10236
10237 p = text;
10238 while ((c = *p++) != '\0') {
10239 if (c == CTLQUOTEMARK)
10240 continue;
10241 if (c == CTLESC)
10242 p++;
10243 else if (SIT(c, BASESYNTAX) == CCTL)
10244 return 0;
10245 }
10246 return 1;
10247}
10248
10249static void
10250parsefname(void)
10251{
10252 union node *n = redirnode;
10253
10254 if (readtoken() != TWORD)
10255 raise_error_unexpected_syntax(-1);
10256 if (n->type == NHERE) {
10257 struct heredoc *here = heredoc;
10258 struct heredoc *p;
10259 int i;
10260
10261 if (quoteflag == 0)
10262 n->type = NXHERE;
10263 TRACE(("Here document %d\n", n->type));
10264 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010265 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010266 rmescapes(wordtext);
10267 here->eofmark = wordtext;
10268 here->next = NULL;
10269 if (heredoclist == NULL)
10270 heredoclist = here;
10271 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010272 for (p = heredoclist; p->next; p = p->next)
10273 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010274 p->next = here;
10275 }
10276 } else if (n->type == NTOFD || n->type == NFROMFD) {
10277 fixredir(n, wordtext, 0);
10278 } else {
10279 n->nfile.fname = makename();
10280 }
10281}
Eric Andersencb57d552001-06-28 07:25:16 +000010282
Eric Andersenc470f442003-07-28 09:56:35 +000010283static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010284simplecmd(void)
10285{
10286 union node *args, **app;
10287 union node *n = NULL;
10288 union node *vars, **vpp;
10289 union node **rpp, *redir;
10290 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010291#if ENABLE_ASH_BASH_COMPAT
10292 smallint double_brackets_flag = 0;
10293#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010294
10295 args = NULL;
10296 app = &args;
10297 vars = NULL;
10298 vpp = &vars;
10299 redir = NULL;
10300 rpp = &redir;
10301
10302 savecheckkwd = CHKALIAS;
10303 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010304 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010305 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010306 t = readtoken();
10307 switch (t) {
10308#if ENABLE_ASH_BASH_COMPAT
10309 case TAND: /* "&&" */
10310 case TOR: /* "||" */
10311 if (!double_brackets_flag) {
10312 tokpushback = 1;
10313 goto out;
10314 }
10315 wordtext = (char *) (t == TAND ? "-a" : "-o");
10316#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010317 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010318 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010319 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010320 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010321 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010322#if ENABLE_ASH_BASH_COMPAT
10323 if (strcmp("[[", wordtext) == 0)
10324 double_brackets_flag = 1;
10325 else if (strcmp("]]", wordtext) == 0)
10326 double_brackets_flag = 0;
10327#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010328 n->narg.backquote = backquotelist;
10329 if (savecheckkwd && isassignment(wordtext)) {
10330 *vpp = n;
10331 vpp = &n->narg.next;
10332 } else {
10333 *app = n;
10334 app = &n->narg.next;
10335 savecheckkwd = 0;
10336 }
10337 break;
10338 case TREDIR:
10339 *rpp = n = redirnode;
10340 rpp = &n->nfile.next;
10341 parsefname(); /* read name of redirection file */
10342 break;
10343 case TLP:
10344 if (args && app == &args->narg.next
10345 && !vars && !redir
10346 ) {
10347 struct builtincmd *bcmd;
10348 const char *name;
10349
10350 /* We have a function */
10351 if (readtoken() != TRP)
10352 raise_error_unexpected_syntax(TRP);
10353 name = n->narg.text;
10354 if (!goodname(name)
10355 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10356 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010357 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010358 }
10359 n->type = NDEFUN;
10360 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10361 n->narg.next = parse_command();
10362 return n;
10363 }
10364 /* fall through */
10365 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010366 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010367 goto out;
10368 }
10369 }
10370 out:
10371 *app = NULL;
10372 *vpp = NULL;
10373 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010374 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010375 n->type = NCMD;
10376 n->ncmd.args = args;
10377 n->ncmd.assign = vars;
10378 n->ncmd.redirect = redir;
10379 return n;
10380}
10381
10382static union node *
10383parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010384{
Eric Andersencb57d552001-06-28 07:25:16 +000010385 union node *n1, *n2;
10386 union node *ap, **app;
10387 union node *cp, **cpp;
10388 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010389 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010390 int t;
10391
10392 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010393 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010394
Eric Andersencb57d552001-06-28 07:25:16 +000010395 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010396 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010397 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010398 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010399 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010400 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010401 n1->type = NIF;
10402 n1->nif.test = list(0);
10403 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010404 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010405 n1->nif.ifpart = list(0);
10406 n2 = n1;
10407 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010408 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010409 n2 = n2->nif.elsepart;
10410 n2->type = NIF;
10411 n2->nif.test = list(0);
10412 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010413 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010414 n2->nif.ifpart = list(0);
10415 }
10416 if (lasttoken == TELSE)
10417 n2->nif.elsepart = list(0);
10418 else {
10419 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010420 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010421 }
Eric Andersenc470f442003-07-28 09:56:35 +000010422 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010423 break;
10424 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010425 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010426 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010427 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010428 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010429 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010430 got = readtoken();
10431 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010432 TRACE(("expecting DO got %s %s\n", tokname(got),
10433 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010434 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010435 }
10436 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010437 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010438 break;
10439 }
10440 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010441 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010442 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010443 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010444 n1->type = NFOR;
10445 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010446 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 if (readtoken() == TIN) {
10448 app = &ap;
10449 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010450 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010451 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010452 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010453 n2->narg.text = wordtext;
10454 n2->narg.backquote = backquotelist;
10455 *app = n2;
10456 app = &n2->narg.next;
10457 }
10458 *app = NULL;
10459 n1->nfor.args = ap;
10460 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010461 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010462 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010463 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010464 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010465 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010466 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010467 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010468 n1->nfor.args = n2;
10469 /*
10470 * Newline or semicolon here is optional (but note
10471 * that the original Bourne shell only allowed NL).
10472 */
10473 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010474 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010475 }
Eric Andersenc470f442003-07-28 09:56:35 +000010476 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010477 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010478 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010479 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010480 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010481 break;
10482 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010483 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010484 n1->type = NCASE;
10485 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010486 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010487 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010488 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010489 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010490 n2->narg.text = wordtext;
10491 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010492 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010493 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010494 } while (readtoken() == TNL);
10495 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010496 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010497 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010498 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010499 checkkwd = CHKNL | CHKKWD;
10500 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010501 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010502 if (lasttoken == TLP)
10503 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010504 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010505 cp->type = NCLIST;
10506 app = &cp->nclist.pattern;
10507 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010508 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010509 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010510 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010511 ap->narg.text = wordtext;
10512 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010513 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010514 break;
10515 app = &ap->narg.next;
10516 readtoken();
10517 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010518 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010519 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010520 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010521 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010522
Eric Andersenc470f442003-07-28 09:56:35 +000010523 cpp = &cp->nclist.next;
10524
10525 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010526 t = readtoken();
10527 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010528 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010529 raise_error_unexpected_syntax(TENDCASE);
10530 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010531 }
Eric Andersenc470f442003-07-28 09:56:35 +000010532 }
Eric Andersencb57d552001-06-28 07:25:16 +000010533 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010534 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010535 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010536 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010537 n1->type = NSUBSHELL;
10538 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010539 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010540 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 break;
10542 case TBEGIN:
10543 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010544 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010545 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010546 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010547 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010548 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010549 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010550 }
10551
Eric Andersenc470f442003-07-28 09:56:35 +000010552 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010553 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010554
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010555 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010556 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010557 checkkwd = CHKKWD | CHKALIAS;
10558 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010559 while (readtoken() == TREDIR) {
10560 *rpp = n2 = redirnode;
10561 rpp = &n2->nfile.next;
10562 parsefname();
10563 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010564 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010565 *rpp = NULL;
10566 if (redir) {
10567 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010568 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010569 n2->type = NREDIR;
10570 n2->nredir.n = n1;
10571 n1 = n2;
10572 }
10573 n1->nredir.redirect = redir;
10574 }
Eric Andersencb57d552001-06-28 07:25:16 +000010575 return n1;
10576}
10577
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010578#if ENABLE_ASH_BASH_COMPAT
10579static int decode_dollar_squote(void)
10580{
10581 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10582 int c, cnt;
10583 char *p;
10584 char buf[4];
10585
10586 c = pgetc();
10587 p = strchr(C_escapes, c);
10588 if (p) {
10589 buf[0] = c;
10590 p = buf;
10591 cnt = 3;
10592 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10593 do {
10594 c = pgetc();
10595 *++p = c;
10596 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10597 pungetc();
10598 } else if (c == 'x') { /* \xHH */
10599 do {
10600 c = pgetc();
10601 *++p = c;
10602 } while (isxdigit(c) && --cnt);
10603 pungetc();
10604 if (cnt == 3) { /* \x but next char is "bad" */
10605 c = 'x';
10606 goto unrecognized;
10607 }
10608 } else { /* simple seq like \\ or \t */
10609 p++;
10610 }
10611 *p = '\0';
10612 p = buf;
10613 c = bb_process_escape_sequence((void*)&p);
10614 } else { /* unrecognized "\z": print both chars unless ' or " */
10615 if (c != '\'' && c != '"') {
10616 unrecognized:
10617 c |= 0x100; /* "please encode \, then me" */
10618 }
10619 }
10620 return c;
10621}
10622#endif
10623
Eric Andersencb57d552001-06-28 07:25:16 +000010624/*
10625 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10626 * is not NULL, read a here document. In the latter case, eofmark is the
10627 * word which marks the end of the document and striptabs is true if
10628 * leading tabs should be stripped from the document. The argument firstc
10629 * is the first character of the input token or document.
10630 *
10631 * Because C does not have internal subroutines, I have simulated them
10632 * using goto's to implement the subroutine linkage. The following macros
10633 * will run code that appears at the end of readtoken1.
10634 */
Eric Andersen2870d962001-07-02 17:27:21 +000010635#define CHECKEND() {goto checkend; checkend_return:;}
10636#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10637#define PARSESUB() {goto parsesub; parsesub_return:;}
10638#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10639#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10640#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010641static int
Eric Andersenc470f442003-07-28 09:56:35 +000010642readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010643{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010644 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010645 int c = firstc;
10646 char *out;
10647 int len;
10648 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010649 struct nodelist *bqlist;
10650 smallint quotef;
10651 smallint dblquote;
10652 smallint oldstyle;
10653 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010654#if ENABLE_ASH_EXPAND_PRMT
10655 smallint pssyntax; /* we are expanding a prompt string */
10656#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010657 int varnest; /* levels of variables expansion */
10658 int arinest; /* levels of arithmetic expansion */
10659 int parenlevel; /* levels of parens in arithmetic */
10660 int dqvarnest; /* levels of variables expansion within double quotes */
10661
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010662 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10663
Eric Andersencb57d552001-06-28 07:25:16 +000010664#if __GNUC__
10665 /* Avoid longjmp clobbering */
10666 (void) &out;
10667 (void) &quotef;
10668 (void) &dblquote;
10669 (void) &varnest;
10670 (void) &arinest;
10671 (void) &parenlevel;
10672 (void) &dqvarnest;
10673 (void) &oldstyle;
10674 (void) &prevsyntax;
10675 (void) &syntax;
10676#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010677 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010678 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010679 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010680 oldstyle = 0;
10681 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010682#if ENABLE_ASH_EXPAND_PRMT
10683 pssyntax = (syntax == PSSYNTAX);
10684 if (pssyntax)
10685 syntax = DQSYNTAX;
10686#endif
10687 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010688 varnest = 0;
10689 arinest = 0;
10690 parenlevel = 0;
10691 dqvarnest = 0;
10692
10693 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010694 loop:
10695 /* For each line, until end of word */
10696 {
Eric Andersenc470f442003-07-28 09:56:35 +000010697 CHECKEND(); /* set c to PEOF if at end of here document */
10698 for (;;) { /* until end of line or end of word */
10699 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010700 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010701 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010702 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010703 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010704 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010705 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010706 if (doprompt)
10707 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010708 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010709 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010710 case CWORD:
10711 USTPUTC(c, out);
10712 break;
10713 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010714 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010715 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010716#if ENABLE_ASH_BASH_COMPAT
10717 if (c == '\\' && bash_dollar_squote) {
10718 c = decode_dollar_squote();
10719 if (c & 0x100) {
10720 USTPUTC('\\', out);
10721 c = (unsigned char)c;
10722 }
10723 }
10724#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010725 USTPUTC(c, out);
10726 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010727 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010728 c = pgetc2();
10729 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010730 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010731 USTPUTC('\\', out);
10732 pungetc();
10733 } else if (c == '\n') {
10734 if (doprompt)
10735 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010736 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010737#if ENABLE_ASH_EXPAND_PRMT
10738 if (c == '$' && pssyntax) {
10739 USTPUTC(CTLESC, out);
10740 USTPUTC('\\', out);
10741 }
10742#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010743 if (dblquote && c != '\\'
10744 && c != '`' && c != '$'
10745 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010746 ) {
10747 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010748 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010749 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010750 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010751 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010752 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010753 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010754 }
10755 break;
10756 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010757 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010758 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010759 if (eofmark == NULL) {
10760 USTPUTC(CTLQUOTEMARK, out);
10761 }
Eric Andersencb57d552001-06-28 07:25:16 +000010762 break;
10763 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010764 syntax = DQSYNTAX;
10765 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010766 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010767 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010768 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010769 if (eofmark != NULL && arinest == 0
10770 && varnest == 0
10771 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010772 USTPUTC(c, out);
10773 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010774 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010775 syntax = BASESYNTAX;
10776 dblquote = 0;
10777 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010778 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010779 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010780 }
10781 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010782 case CVAR: /* '$' */
10783 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010784 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010785 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010786 if (varnest > 0) {
10787 varnest--;
10788 if (dqvarnest > 0) {
10789 dqvarnest--;
10790 }
10791 USTPUTC(CTLENDVAR, out);
10792 } else {
10793 USTPUTC(c, out);
10794 }
10795 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010796#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010797 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010798 parenlevel++;
10799 USTPUTC(c, out);
10800 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010801 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010802 if (parenlevel > 0) {
10803 USTPUTC(c, out);
10804 --parenlevel;
10805 } else {
10806 if (pgetc() == ')') {
10807 if (--arinest == 0) {
10808 USTPUTC(CTLENDARI, out);
10809 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010810 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010811 } else
10812 USTPUTC(')', out);
10813 } else {
10814 /*
10815 * unbalanced parens
10816 * (don't 2nd guess - no error)
10817 */
10818 pungetc();
10819 USTPUTC(')', out);
10820 }
10821 }
10822 break;
10823#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010824 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010825 PARSEBACKQOLD();
10826 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010827 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010828 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010829 case CIGN:
10830 break;
10831 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010832 if (varnest == 0) {
10833#if ENABLE_ASH_BASH_COMPAT
10834 if (c == '&') {
10835 if (pgetc() == '>')
10836 c = 0x100 + '>'; /* flag &> */
10837 pungetc();
10838 }
10839#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010840 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010841 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010842#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010843 if (c != PEOA)
10844#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010845 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010846
Eric Andersencb57d552001-06-28 07:25:16 +000010847 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010848 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010849 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010850 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010851 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010852#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010853 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010854 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010855#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010856 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010857 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010858 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010859 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010860 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010861 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010862 }
10863 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010864 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010865 out = stackblock();
10866 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010867 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10868 && quotef == 0
10869 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010870 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010871 PARSEREDIR(); /* passed as params: out, c */
10872 lasttoken = TREDIR;
10873 return lasttoken;
10874 }
10875 /* else: non-number X seen, interpret it
10876 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010877 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010878 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010879 }
10880 quoteflag = quotef;
10881 backquotelist = bqlist;
10882 grabstackblock(len);
10883 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010884 lasttoken = TWORD;
10885 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010886/* end of readtoken routine */
10887
Eric Andersencb57d552001-06-28 07:25:16 +000010888/*
10889 * Check to see whether we are at the end of the here document. When this
10890 * is called, c is set to the first character of the next input line. If
10891 * we are at the end of the here document, this routine sets the c to PEOF.
10892 */
Eric Andersenc470f442003-07-28 09:56:35 +000010893checkend: {
10894 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010895#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010896 if (c == PEOA) {
10897 c = pgetc2();
10898 }
10899#endif
10900 if (striptabs) {
10901 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010902 c = pgetc2();
10903 }
Eric Andersenc470f442003-07-28 09:56:35 +000010904 }
10905 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010906 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010907 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010908
Eric Andersenc470f442003-07-28 09:56:35 +000010909 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010910 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10911 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010912 if (*p == '\n' && *q == '\0') {
10913 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010914 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010915 needprompt = doprompt;
10916 } else {
10917 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010918 }
10919 }
10920 }
10921 }
Eric Andersenc470f442003-07-28 09:56:35 +000010922 goto checkend_return;
10923}
Eric Andersencb57d552001-06-28 07:25:16 +000010924
Eric Andersencb57d552001-06-28 07:25:16 +000010925/*
10926 * Parse a redirection operator. The variable "out" points to a string
10927 * specifying the fd to be redirected. The variable "c" contains the
10928 * first character of the redirection operator.
10929 */
Eric Andersenc470f442003-07-28 09:56:35 +000010930parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010931 /* out is already checked to be a valid number or "" */
10932 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010933 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010934
Denis Vlasenko597906c2008-02-20 16:38:54 +000010935 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010936 if (c == '>') {
10937 np->nfile.fd = 1;
10938 c = pgetc();
10939 if (c == '>')
10940 np->type = NAPPEND;
10941 else if (c == '|')
10942 np->type = NCLOBBER;
10943 else if (c == '&')
10944 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010945 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010946 else {
10947 np->type = NTO;
10948 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010949 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010950 }
10951#if ENABLE_ASH_BASH_COMPAT
10952 else if (c == 0x100 + '>') { /* this flags &> redirection */
10953 np->nfile.fd = 1;
10954 pgetc(); /* this is '>', no need to check */
10955 np->type = NTO2;
10956 }
10957#endif
10958 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010959 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010960 c = pgetc();
10961 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010962 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010963 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010964 np = stzalloc(sizeof(struct nhere));
10965 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010966 }
10967 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010968 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010969 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010970 c = pgetc();
10971 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010972 heredoc->striptabs = 1;
10973 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010974 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010975 pungetc();
10976 }
10977 break;
10978
10979 case '&':
10980 np->type = NFROMFD;
10981 break;
10982
10983 case '>':
10984 np->type = NFROMTO;
10985 break;
10986
10987 default:
10988 np->type = NFROM;
10989 pungetc();
10990 break;
10991 }
Eric Andersencb57d552001-06-28 07:25:16 +000010992 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010993 if (fd >= 0)
10994 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010995 redirnode = np;
10996 goto parseredir_return;
10997}
Eric Andersencb57d552001-06-28 07:25:16 +000010998
Eric Andersencb57d552001-06-28 07:25:16 +000010999/*
11000 * Parse a substitution. At this point, we have read the dollar sign
11001 * and nothing else.
11002 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011003
11004/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11005 * (assuming ascii char codes, as the original implementation did) */
11006#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011007 (((unsigned)(c) - 33 < 32) \
11008 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011009parsesub: {
11010 int subtype;
11011 int typeloc;
11012 int flags;
11013 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011014 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011015
Eric Andersenc470f442003-07-28 09:56:35 +000011016 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011017 if (c <= PEOA_OR_PEOF
11018 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011019 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011020#if ENABLE_ASH_BASH_COMPAT
11021 if (c == '\'')
11022 bash_dollar_squote = 1;
11023 else
11024#endif
11025 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011026 pungetc();
11027 } else if (c == '(') { /* $(command) or $((arith)) */
11028 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011029#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011030 PARSEARITH();
11031#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011032 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011033#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011034 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011035 pungetc();
11036 PARSEBACKQNEW();
11037 }
11038 } else {
11039 USTPUTC(CTLVAR, out);
11040 typeloc = out - (char *)stackblock();
11041 USTPUTC(VSNORMAL, out);
11042 subtype = VSNORMAL;
11043 if (c == '{') {
11044 c = pgetc();
11045 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011046 c = pgetc();
11047 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011048 c = '#';
11049 else
11050 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011051 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011052 subtype = 0;
11053 }
11054 if (c > PEOA_OR_PEOF && is_name(c)) {
11055 do {
11056 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011057 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011058 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011059 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011060 do {
11061 STPUTC(c, out);
11062 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011063 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011064 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011065 USTPUTC(c, out);
11066 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011067 } else {
11068 badsub:
11069 raise_error_syntax("bad substitution");
11070 }
Eric Andersencb57d552001-06-28 07:25:16 +000011071
Eric Andersenc470f442003-07-28 09:56:35 +000011072 STPUTC('=', out);
11073 flags = 0;
11074 if (subtype == 0) {
11075 switch (c) {
11076 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011077 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011078#if ENABLE_ASH_BASH_COMPAT
11079 if (c == ':' || c == '$' || isdigit(c)) {
11080 pungetc();
11081 subtype = VSSUBSTR;
11082 break;
11083 }
11084#endif
11085 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011086 /*FALLTHROUGH*/
11087 default:
11088 p = strchr(types, c);
11089 if (p == NULL)
11090 goto badsub;
11091 subtype = p - types + VSNORMAL;
11092 break;
11093 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011094 case '#': {
11095 int cc = c;
11096 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11097 c = pgetc();
11098 if (c == cc)
11099 subtype++;
11100 else
11101 pungetc();
11102 break;
11103 }
11104#if ENABLE_ASH_BASH_COMPAT
11105 case '/':
11106 subtype = VSREPLACE;
11107 c = pgetc();
11108 if (c == '/')
11109 subtype++; /* VSREPLACEALL */
11110 else
11111 pungetc();
11112 break;
11113#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011114 }
Eric Andersenc470f442003-07-28 09:56:35 +000011115 } else {
11116 pungetc();
11117 }
11118 if (dblquote || arinest)
11119 flags |= VSQUOTE;
11120 *((char *)stackblock() + typeloc) = subtype | flags;
11121 if (subtype != VSNORMAL) {
11122 varnest++;
11123 if (dblquote || arinest) {
11124 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011125 }
11126 }
11127 }
Eric Andersenc470f442003-07-28 09:56:35 +000011128 goto parsesub_return;
11129}
Eric Andersencb57d552001-06-28 07:25:16 +000011130
Eric Andersencb57d552001-06-28 07:25:16 +000011131/*
11132 * Called to parse command substitutions. Newstyle is set if the command
11133 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11134 * list of commands (passed by reference), and savelen is the number of
11135 * characters on the top of the stack which must be preserved.
11136 */
Eric Andersenc470f442003-07-28 09:56:35 +000011137parsebackq: {
11138 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011139 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011140 union node *n;
11141 char *volatile str;
11142 struct jmploc jmploc;
11143 struct jmploc *volatile savehandler;
11144 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011145 smallint saveprompt = 0;
11146
Eric Andersencb57d552001-06-28 07:25:16 +000011147#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011148 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011149#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011150 savepbq = parsebackquote;
11151 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011152 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011153 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011154 exception_handler = savehandler;
11155 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011156 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011157 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011158 str = NULL;
11159 savelen = out - (char *)stackblock();
11160 if (savelen > 0) {
11161 str = ckmalloc(savelen);
11162 memcpy(str, stackblock(), savelen);
11163 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011164 savehandler = exception_handler;
11165 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011166 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011167 if (oldstyle) {
11168 /* We must read until the closing backquote, giving special
11169 treatment to some slashes, and then push the string and
11170 reread it as input, interpreting it normally. */
11171 char *pout;
11172 int pc;
11173 size_t psavelen;
11174 char *pstr;
11175
11176
11177 STARTSTACKSTR(pout);
11178 for (;;) {
11179 if (needprompt) {
11180 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011181 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011182 pc = pgetc();
11183 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011184 case '`':
11185 goto done;
11186
11187 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011188 pc = pgetc();
11189 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011190 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011191 if (doprompt)
11192 setprompt(2);
11193 /*
11194 * If eating a newline, avoid putting
11195 * the newline into the new character
11196 * stream (via the STPUTC after the
11197 * switch).
11198 */
11199 continue;
11200 }
11201 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011202 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011203 STPUTC('\\', pout);
11204 if (pc > PEOA_OR_PEOF) {
11205 break;
11206 }
11207 /* fall through */
11208
11209 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011210#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011211 case PEOA:
11212#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011213 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011214 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011215
11216 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011217 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011218 needprompt = doprompt;
11219 break;
11220
11221 default:
11222 break;
11223 }
11224 STPUTC(pc, pout);
11225 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011226 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011227 STPUTC('\0', pout);
11228 psavelen = pout - (char *)stackblock();
11229 if (psavelen > 0) {
11230 pstr = grabstackstr(pout);
11231 setinputstring(pstr);
11232 }
11233 }
11234 nlpp = &bqlist;
11235 while (*nlpp)
11236 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011237 *nlpp = stzalloc(sizeof(**nlpp));
11238 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011239 parsebackquote = oldstyle;
11240
11241 if (oldstyle) {
11242 saveprompt = doprompt;
11243 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011244 }
11245
Eric Andersenc470f442003-07-28 09:56:35 +000011246 n = list(2);
11247
11248 if (oldstyle)
11249 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011250 else if (readtoken() != TRP)
11251 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011252
11253 (*nlpp)->n = n;
11254 if (oldstyle) {
11255 /*
11256 * Start reading from old file again, ignoring any pushed back
11257 * tokens left from the backquote parsing
11258 */
11259 popfile();
11260 tokpushback = 0;
11261 }
11262 while (stackblocksize() <= savelen)
11263 growstackblock();
11264 STARTSTACKSTR(out);
11265 if (str) {
11266 memcpy(out, str, savelen);
11267 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011268 INT_OFF;
11269 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011270 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011271 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011272 }
11273 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011274 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011275 if (arinest || dblquote)
11276 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11277 else
11278 USTPUTC(CTLBACKQ, out);
11279 if (oldstyle)
11280 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011281 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011282}
11283
Denis Vlasenko131ae172007-02-18 13:00:19 +000011284#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011285/*
11286 * Parse an arithmetic expansion (indicate start of one and set state)
11287 */
Eric Andersenc470f442003-07-28 09:56:35 +000011288parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011289 if (++arinest == 1) {
11290 prevsyntax = syntax;
11291 syntax = ARISYNTAX;
11292 USTPUTC(CTLARI, out);
11293 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011294 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011295 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011296 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011297 } else {
11298 /*
11299 * we collapse embedded arithmetic expansion to
11300 * parenthesis, which should be equivalent
11301 */
11302 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011303 }
Eric Andersenc470f442003-07-28 09:56:35 +000011304 goto parsearith_return;
11305}
11306#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011307
Eric Andersenc470f442003-07-28 09:56:35 +000011308} /* end of readtoken */
11309
Eric Andersencb57d552001-06-28 07:25:16 +000011310/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011311 * Read the next input token.
11312 * If the token is a word, we set backquotelist to the list of cmds in
11313 * backquotes. We set quoteflag to true if any part of the word was
11314 * quoted.
11315 * If the token is TREDIR, then we set redirnode to a structure containing
11316 * the redirection.
11317 * In all cases, the variable startlinno is set to the number of the line
11318 * on which the token starts.
11319 *
11320 * [Change comment: here documents and internal procedures]
11321 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11322 * word parsing code into a separate routine. In this case, readtoken
11323 * doesn't need to have any internal procedures, but parseword does.
11324 * We could also make parseoperator in essence the main routine, and
11325 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011326 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011327#define NEW_xxreadtoken
11328#ifdef NEW_xxreadtoken
11329/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011330static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011331 '\n', '(', ')', /* singles */
11332 '&', '|', ';', /* doubles */
11333 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011334};
Eric Andersencb57d552001-06-28 07:25:16 +000011335
Denis Vlasenko834dee72008-10-07 09:18:30 +000011336#define xxreadtoken_singles 3
11337#define xxreadtoken_doubles 3
11338
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011339static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011340 TNL, TLP, TRP, /* only single occurrence allowed */
11341 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11342 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011343 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011344};
11345
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011346static int
11347xxreadtoken(void)
11348{
11349 int c;
11350
11351 if (tokpushback) {
11352 tokpushback = 0;
11353 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011354 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011355 if (needprompt) {
11356 setprompt(2);
11357 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011358 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011359 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011360 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011361 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11362 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011363
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011364 if (c == '#') {
11365 while ((c = pgetc()) != '\n' && c != PEOF)
11366 continue;
11367 pungetc();
11368 } else if (c == '\\') {
11369 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011370 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011371 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011372 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011373 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011374 if (doprompt)
11375 setprompt(2);
11376 } else {
11377 const char *p;
11378
11379 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11380 if (c != PEOF) {
11381 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011382 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011383 needprompt = doprompt;
11384 }
11385
11386 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011387 if (p == NULL)
11388 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011389
Denis Vlasenko834dee72008-10-07 09:18:30 +000011390 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11391 int cc = pgetc();
11392 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011393 p += xxreadtoken_doubles + 1;
11394 } else {
11395 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011396#if ENABLE_ASH_BASH_COMPAT
11397 if (c == '&' && cc == '>') /* &> */
11398 break; /* return readtoken1(...) */
11399#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011400 }
11401 }
11402 }
11403 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11404 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011405 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011406 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011407
11408 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011409}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011410#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011411#define RETURN(token) return lasttoken = token
11412static int
11413xxreadtoken(void)
11414{
11415 int c;
11416
11417 if (tokpushback) {
11418 tokpushback = 0;
11419 return lasttoken;
11420 }
11421 if (needprompt) {
11422 setprompt(2);
11423 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011424 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011425 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011426 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011427 switch (c) {
11428 case ' ': case '\t':
11429#if ENABLE_ASH_ALIAS
11430 case PEOA:
11431#endif
11432 continue;
11433 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011434 while ((c = pgetc()) != '\n' && c != PEOF)
11435 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011436 pungetc();
11437 continue;
11438 case '\\':
11439 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011440 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441 if (doprompt)
11442 setprompt(2);
11443 continue;
11444 }
11445 pungetc();
11446 goto breakloop;
11447 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011448 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011449 needprompt = doprompt;
11450 RETURN(TNL);
11451 case PEOF:
11452 RETURN(TEOF);
11453 case '&':
11454 if (pgetc() == '&')
11455 RETURN(TAND);
11456 pungetc();
11457 RETURN(TBACKGND);
11458 case '|':
11459 if (pgetc() == '|')
11460 RETURN(TOR);
11461 pungetc();
11462 RETURN(TPIPE);
11463 case ';':
11464 if (pgetc() == ';')
11465 RETURN(TENDCASE);
11466 pungetc();
11467 RETURN(TSEMI);
11468 case '(':
11469 RETURN(TLP);
11470 case ')':
11471 RETURN(TRP);
11472 default:
11473 goto breakloop;
11474 }
11475 }
11476 breakloop:
11477 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11478#undef RETURN
11479}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011480#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011481
11482static int
11483readtoken(void)
11484{
11485 int t;
11486#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011487 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011488#endif
11489
11490#if ENABLE_ASH_ALIAS
11491 top:
11492#endif
11493
11494 t = xxreadtoken();
11495
11496 /*
11497 * eat newlines
11498 */
11499 if (checkkwd & CHKNL) {
11500 while (t == TNL) {
11501 parseheredoc();
11502 t = xxreadtoken();
11503 }
11504 }
11505
11506 if (t != TWORD || quoteflag) {
11507 goto out;
11508 }
11509
11510 /*
11511 * check for keywords
11512 */
11513 if (checkkwd & CHKKWD) {
11514 const char *const *pp;
11515
11516 pp = findkwd(wordtext);
11517 if (pp) {
11518 lasttoken = t = pp - tokname_array;
11519 TRACE(("keyword %s recognized\n", tokname(t)));
11520 goto out;
11521 }
11522 }
11523
11524 if (checkkwd & CHKALIAS) {
11525#if ENABLE_ASH_ALIAS
11526 struct alias *ap;
11527 ap = lookupalias(wordtext, 1);
11528 if (ap != NULL) {
11529 if (*ap->val) {
11530 pushstring(ap->val, ap);
11531 }
11532 goto top;
11533 }
11534#endif
11535 }
11536 out:
11537 checkkwd = 0;
11538#if DEBUG
11539 if (!alreadyseen)
11540 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11541 else
11542 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11543#endif
11544 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011545}
11546
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011547static char
11548peektoken(void)
11549{
11550 int t;
11551
11552 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011553 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011554 return tokname_array[t][0];
11555}
Eric Andersencb57d552001-06-28 07:25:16 +000011556
11557/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011558 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11559 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011560 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011561static union node *
11562parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011563{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011564 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011565
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011566 tokpushback = 0;
11567 doprompt = interact;
11568 if (doprompt)
11569 setprompt(doprompt);
11570 needprompt = 0;
11571 t = readtoken();
11572 if (t == TEOF)
11573 return NEOF;
11574 if (t == TNL)
11575 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011576 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011577 return list(1);
11578}
11579
11580/*
11581 * Input any here documents.
11582 */
11583static void
11584parseheredoc(void)
11585{
11586 struct heredoc *here;
11587 union node *n;
11588
11589 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011590 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011591
11592 while (here) {
11593 if (needprompt) {
11594 setprompt(2);
11595 }
11596 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11597 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011598 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011599 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011600 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011601 n->narg.text = wordtext;
11602 n->narg.backquote = backquotelist;
11603 here->here->nhere.doc = n;
11604 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011605 }
Eric Andersencb57d552001-06-28 07:25:16 +000011606}
11607
11608
11609/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011610 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011611 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011612#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011613static const char *
11614expandstr(const char *ps)
11615{
11616 union node n;
11617
11618 /* XXX Fix (char *) cast. */
11619 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011620 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011621 popfile();
11622
11623 n.narg.type = NARG;
11624 n.narg.next = NULL;
11625 n.narg.text = wordtext;
11626 n.narg.backquote = backquotelist;
11627
11628 expandarg(&n, NULL, 0);
11629 return stackblock();
11630}
11631#endif
11632
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011633/*
11634 * Execute a command or commands contained in a string.
11635 */
11636static int
11637evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011638{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011639 union node *n;
11640 struct stackmark smark;
11641 int skip;
11642
11643 setinputstring(s);
11644 setstackmark(&smark);
11645
11646 skip = 0;
11647 while ((n = parsecmd(0)) != NEOF) {
11648 evaltree(n, 0);
11649 popstackmark(&smark);
11650 skip = evalskip;
11651 if (skip)
11652 break;
11653 }
11654 popfile();
11655
11656 skip &= mask;
11657 evalskip = skip;
11658 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011659}
11660
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011661/*
11662 * The eval command.
11663 */
11664static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011665evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011666{
11667 char *p;
11668 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011669
Denis Vlasenko68404f12008-03-17 09:00:54 +000011670 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011671 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011672 argv += 2;
11673 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011674 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011675 for (;;) {
11676 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011677 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011678 if (p == NULL)
11679 break;
11680 STPUTC(' ', concat);
11681 }
11682 STPUTC('\0', concat);
11683 p = grabstackstr(concat);
11684 }
11685 evalstring(p, ~SKIPEVAL);
11686
11687 }
11688 return exitstatus;
11689}
11690
11691/*
11692 * Read and execute commands. "Top" is nonzero for the top level command
11693 * loop; it turns on prompting if the shell is interactive.
11694 */
11695static int
11696cmdloop(int top)
11697{
11698 union node *n;
11699 struct stackmark smark;
11700 int inter;
11701 int numeof = 0;
11702
11703 TRACE(("cmdloop(%d) called\n", top));
11704 for (;;) {
11705 int skip;
11706
11707 setstackmark(&smark);
11708#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011709 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011710 showjobs(stderr, SHOW_CHANGED);
11711#endif
11712 inter = 0;
11713 if (iflag && top) {
11714 inter++;
11715#if ENABLE_ASH_MAIL
11716 chkmail();
11717#endif
11718 }
11719 n = parsecmd(inter);
11720 /* showtree(n); DEBUG */
11721 if (n == NEOF) {
11722 if (!top || numeof >= 50)
11723 break;
11724 if (!stoppedjobs()) {
11725 if (!Iflag)
11726 break;
11727 out2str("\nUse \"exit\" to leave shell.\n");
11728 }
11729 numeof++;
11730 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011731 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11732 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011733 numeof = 0;
11734 evaltree(n, 0);
11735 }
11736 popstackmark(&smark);
11737 skip = evalskip;
11738
11739 if (skip) {
11740 evalskip = 0;
11741 return skip & SKIPEVAL;
11742 }
11743 }
11744 return 0;
11745}
11746
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011747/*
11748 * Take commands from a file. To be compatible we should do a path
11749 * search for the file, which is necessary to find sub-commands.
11750 */
11751static char *
11752find_dot_file(char *name)
11753{
11754 char *fullname;
11755 const char *path = pathval();
11756 struct stat statb;
11757
11758 /* don't try this for absolute or relative paths */
11759 if (strchr(name, '/'))
11760 return name;
11761
11762 while ((fullname = padvance(&path, name)) != NULL) {
11763 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11764 /*
11765 * Don't bother freeing here, since it will
11766 * be freed by the caller.
11767 */
11768 return fullname;
11769 }
11770 stunalloc(fullname);
11771 }
11772
11773 /* not found in the PATH */
11774 ash_msg_and_raise_error("%s: not found", name);
11775 /* NOTREACHED */
11776}
11777
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011778static int
11779dotcmd(int argc, char **argv)
11780{
11781 struct strlist *sp;
11782 volatile struct shparam saveparam;
11783 int status = 0;
11784
11785 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011786 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011787
Denis Vlasenko68404f12008-03-17 09:00:54 +000011788 if (argv[1]) { /* That's what SVR2 does */
11789 char *fullname = find_dot_file(argv[1]);
11790 argv += 2;
11791 argc -= 2;
11792 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011793 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011794 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011795 shellparam.nparam = argc;
11796 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011797 };
11798
11799 setinputfile(fullname, INPUT_PUSH_FILE);
11800 commandname = fullname;
11801 cmdloop(0);
11802 popfile();
11803
Denis Vlasenko68404f12008-03-17 09:00:54 +000011804 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011805 freeparam(&shellparam);
11806 shellparam = saveparam;
11807 };
11808 status = exitstatus;
11809 }
11810 return status;
11811}
11812
11813static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011814exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011815{
11816 if (stoppedjobs())
11817 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011818 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011819 exitstatus = number(argv[1]);
11820 raise_exception(EXEXIT);
11821 /* NOTREACHED */
11822}
11823
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011824/*
11825 * Read a file containing shell functions.
11826 */
11827static void
11828readcmdfile(char *name)
11829{
11830 setinputfile(name, INPUT_PUSH_FILE);
11831 cmdloop(0);
11832 popfile();
11833}
11834
11835
Denis Vlasenkocc571512007-02-23 21:10:35 +000011836/* ============ find_command inplementation */
11837
11838/*
11839 * Resolve a command name. If you change this routine, you may have to
11840 * change the shellexec routine as well.
11841 */
11842static void
11843find_command(char *name, struct cmdentry *entry, int act, const char *path)
11844{
11845 struct tblentry *cmdp;
11846 int idx;
11847 int prev;
11848 char *fullname;
11849 struct stat statb;
11850 int e;
11851 int updatetbl;
11852 struct builtincmd *bcmd;
11853
11854 /* If name contains a slash, don't use PATH or hash table */
11855 if (strchr(name, '/') != NULL) {
11856 entry->u.index = -1;
11857 if (act & DO_ABS) {
11858 while (stat(name, &statb) < 0) {
11859#ifdef SYSV
11860 if (errno == EINTR)
11861 continue;
11862#endif
11863 entry->cmdtype = CMDUNKNOWN;
11864 return;
11865 }
11866 }
11867 entry->cmdtype = CMDNORMAL;
11868 return;
11869 }
11870
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011871/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011872
11873 updatetbl = (path == pathval());
11874 if (!updatetbl) {
11875 act |= DO_ALTPATH;
11876 if (strstr(path, "%builtin") != NULL)
11877 act |= DO_ALTBLTIN;
11878 }
11879
11880 /* If name is in the table, check answer will be ok */
11881 cmdp = cmdlookup(name, 0);
11882 if (cmdp != NULL) {
11883 int bit;
11884
11885 switch (cmdp->cmdtype) {
11886 default:
11887#if DEBUG
11888 abort();
11889#endif
11890 case CMDNORMAL:
11891 bit = DO_ALTPATH;
11892 break;
11893 case CMDFUNCTION:
11894 bit = DO_NOFUNC;
11895 break;
11896 case CMDBUILTIN:
11897 bit = DO_ALTBLTIN;
11898 break;
11899 }
11900 if (act & bit) {
11901 updatetbl = 0;
11902 cmdp = NULL;
11903 } else if (cmdp->rehash == 0)
11904 /* if not invalidated by cd, we're done */
11905 goto success;
11906 }
11907
11908 /* If %builtin not in path, check for builtin next */
11909 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011910 if (bcmd) {
11911 if (IS_BUILTIN_REGULAR(bcmd))
11912 goto builtin_success;
11913 if (act & DO_ALTPATH) {
11914 if (!(act & DO_ALTBLTIN))
11915 goto builtin_success;
11916 } else if (builtinloc <= 0) {
11917 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011918 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011919 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011920
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011921#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011922 {
11923 int applet_no = find_applet_by_name(name);
11924 if (applet_no >= 0) {
11925 entry->cmdtype = CMDNORMAL;
11926 entry->u.index = -2 - applet_no;
11927 return;
11928 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011929 }
11930#endif
11931
Denis Vlasenkocc571512007-02-23 21:10:35 +000011932 /* We have to search path. */
11933 prev = -1; /* where to start */
11934 if (cmdp && cmdp->rehash) { /* doing a rehash */
11935 if (cmdp->cmdtype == CMDBUILTIN)
11936 prev = builtinloc;
11937 else
11938 prev = cmdp->param.index;
11939 }
11940
11941 e = ENOENT;
11942 idx = -1;
11943 loop:
11944 while ((fullname = padvance(&path, name)) != NULL) {
11945 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011946 /* NB: code below will still use fullname
11947 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011948 idx++;
11949 if (pathopt) {
11950 if (prefix(pathopt, "builtin")) {
11951 if (bcmd)
11952 goto builtin_success;
11953 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011954 }
11955 if ((act & DO_NOFUNC)
11956 || !prefix(pathopt, "func")
11957 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011958 continue;
11959 }
11960 }
11961 /* if rehash, don't redo absolute path names */
11962 if (fullname[0] == '/' && idx <= prev) {
11963 if (idx < prev)
11964 continue;
11965 TRACE(("searchexec \"%s\": no change\n", name));
11966 goto success;
11967 }
11968 while (stat(fullname, &statb) < 0) {
11969#ifdef SYSV
11970 if (errno == EINTR)
11971 continue;
11972#endif
11973 if (errno != ENOENT && errno != ENOTDIR)
11974 e = errno;
11975 goto loop;
11976 }
11977 e = EACCES; /* if we fail, this will be the error */
11978 if (!S_ISREG(statb.st_mode))
11979 continue;
11980 if (pathopt) { /* this is a %func directory */
11981 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011982 /* NB: stalloc will return space pointed by fullname
11983 * (because we don't have any intervening allocations
11984 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011985 readcmdfile(fullname);
11986 cmdp = cmdlookup(name, 0);
11987 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11988 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11989 stunalloc(fullname);
11990 goto success;
11991 }
11992 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11993 if (!updatetbl) {
11994 entry->cmdtype = CMDNORMAL;
11995 entry->u.index = idx;
11996 return;
11997 }
11998 INT_OFF;
11999 cmdp = cmdlookup(name, 1);
12000 cmdp->cmdtype = CMDNORMAL;
12001 cmdp->param.index = idx;
12002 INT_ON;
12003 goto success;
12004 }
12005
12006 /* We failed. If there was an entry for this command, delete it */
12007 if (cmdp && updatetbl)
12008 delete_cmd_entry();
12009 if (act & DO_ERR)
12010 ash_msg("%s: %s", name, errmsg(e, "not found"));
12011 entry->cmdtype = CMDUNKNOWN;
12012 return;
12013
12014 builtin_success:
12015 if (!updatetbl) {
12016 entry->cmdtype = CMDBUILTIN;
12017 entry->u.cmd = bcmd;
12018 return;
12019 }
12020 INT_OFF;
12021 cmdp = cmdlookup(name, 1);
12022 cmdp->cmdtype = CMDBUILTIN;
12023 cmdp->param.cmd = bcmd;
12024 INT_ON;
12025 success:
12026 cmdp->rehash = 0;
12027 entry->cmdtype = cmdp->cmdtype;
12028 entry->u = cmdp->param;
12029}
12030
12031
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012032/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012033
Eric Andersencb57d552001-06-28 07:25:16 +000012034/*
Eric Andersencb57d552001-06-28 07:25:16 +000012035 * The trap builtin.
12036 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012037static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012038trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012039{
12040 char *action;
12041 char **ap;
12042 int signo;
12043
Eric Andersenc470f442003-07-28 09:56:35 +000012044 nextopt(nullstr);
12045 ap = argptr;
12046 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012047 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012048 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012049 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012050 single_quote(trap[signo]),
12051 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012052 }
12053 }
12054 return 0;
12055 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012056 action = NULL;
12057 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012058 action = *ap++;
12059 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012060 signo = get_signum(*ap);
12061 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012062 ash_msg_and_raise_error("%s: bad trap", *ap);
12063 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012064 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012065 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012066 action = NULL;
12067 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012068 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012069 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012070 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012071 trap[signo] = action;
12072 if (signo != 0)
12073 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012074 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012075 ap++;
12076 }
12077 return 0;
12078}
12079
Eric Andersenc470f442003-07-28 09:56:35 +000012080
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012081/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012082
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012083#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012084/*
12085 * Lists available builtins
12086 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012087static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012088helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012089{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012090 unsigned col;
12091 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012092
12093 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012094 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012095 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012096 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012097 if (col > 60) {
12098 out1fmt("\n");
12099 col = 0;
12100 }
12101 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012102#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012103 {
12104 const char *a = applet_names;
12105 while (*a) {
12106 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12107 if (col > 60) {
12108 out1fmt("\n");
12109 col = 0;
12110 }
12111 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012112 }
12113 }
12114#endif
12115 out1fmt("\n\n");
12116 return EXIT_SUCCESS;
12117}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012118#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012119
Eric Andersencb57d552001-06-28 07:25:16 +000012120/*
Eric Andersencb57d552001-06-28 07:25:16 +000012121 * The export and readonly commands.
12122 */
Eric Andersenc470f442003-07-28 09:56:35 +000012123static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012124exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012125{
12126 struct var *vp;
12127 char *name;
12128 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012129 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012130 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012131
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012132 if (nextopt("p") != 'p') {
12133 aptr = argptr;
12134 name = *aptr;
12135 if (name) {
12136 do {
12137 p = strchr(name, '=');
12138 if (p != NULL) {
12139 p++;
12140 } else {
12141 vp = *findvar(hashvar(name), name);
12142 if (vp) {
12143 vp->flags |= flag;
12144 continue;
12145 }
Eric Andersencb57d552001-06-28 07:25:16 +000012146 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012147 setvar(name, p, flag);
12148 } while ((name = *++aptr) != NULL);
12149 return 0;
12150 }
Eric Andersencb57d552001-06-28 07:25:16 +000012151 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012152 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012153 return 0;
12154}
12155
Eric Andersencb57d552001-06-28 07:25:16 +000012156/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012157 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012158 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012159static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012160unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012161{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012162 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012163
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012164 cmdp = cmdlookup(name, 0);
12165 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12166 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012167}
12168
Eric Andersencb57d552001-06-28 07:25:16 +000012169/*
Eric Andersencb57d552001-06-28 07:25:16 +000012170 * The unset builtin command. We unset the function before we unset the
12171 * variable to allow a function to be unset when there is a readonly variable
12172 * with the same name.
12173 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012174static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012175unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012176{
12177 char **ap;
12178 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012179 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012180 int ret = 0;
12181
12182 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012183 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012184 }
Eric Andersencb57d552001-06-28 07:25:16 +000012185
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012186 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012187 if (flag != 'f') {
12188 i = unsetvar(*ap);
12189 ret |= i;
12190 if (!(i & 2))
12191 continue;
12192 }
12193 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012194 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012195 }
Eric Andersenc470f442003-07-28 09:56:35 +000012196 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012197}
12198
12199
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012200/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012201
Eric Andersenc470f442003-07-28 09:56:35 +000012202#include <sys/times.h>
12203
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012204static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012205 ' ', offsetof(struct tms, tms_utime),
12206 '\n', offsetof(struct tms, tms_stime),
12207 ' ', offsetof(struct tms, tms_cutime),
12208 '\n', offsetof(struct tms, tms_cstime),
12209 0
12210};
Eric Andersencb57d552001-06-28 07:25:16 +000012211
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012212static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012213timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012214{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012215 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012216 const unsigned char *p;
12217 struct tms buf;
12218
12219 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012220 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012221
12222 p = timescmd_str;
12223 do {
12224 t = *(clock_t *)(((char *) &buf) + p[1]);
12225 s = t / clk_tck;
12226 out1fmt("%ldm%ld.%.3lds%c",
12227 s/60, s%60,
12228 ((t - s * clk_tck) * 1000) / clk_tck,
12229 p[0]);
12230 } while (*(p += 2));
12231
Eric Andersencb57d552001-06-28 07:25:16 +000012232 return 0;
12233}
12234
Denis Vlasenko131ae172007-02-18 13:00:19 +000012235#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012236static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012237dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012238{
Eric Andersened9ecf72004-06-22 08:29:45 +000012239 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012240 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012241
Denis Vlasenkob012b102007-02-19 22:43:01 +000012242 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012243 result = arith(s, &errcode);
12244 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012245 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012246 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012247 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012248 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012249 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012250 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012251 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012252 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012253 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012254
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012255 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012256}
Eric Andersenc470f442003-07-28 09:56:35 +000012257
Eric Andersenc470f442003-07-28 09:56:35 +000012258/*
Eric Andersen90898442003-08-06 11:20:52 +000012259 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12260 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12261 *
12262 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012263 */
12264static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012265letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012266{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012267 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012268
Denis Vlasenko68404f12008-03-17 09:00:54 +000012269 argv++;
12270 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012271 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012272 do {
12273 i = dash_arith(*argv);
12274 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012275
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012276 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012277}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012278#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012279
Eric Andersenc470f442003-07-28 09:56:35 +000012280
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012281/* ============ miscbltin.c
12282 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012283 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012284 */
12285
12286#undef rflag
12287
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012288#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012289typedef enum __rlimit_resource rlim_t;
12290#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012291
Eric Andersenc470f442003-07-28 09:56:35 +000012292/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012293 * The read builtin. Options:
12294 * -r Do not interpret '\' specially
12295 * -s Turn off echo (tty only)
12296 * -n NCHARS Read NCHARS max
12297 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12298 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12299 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012300 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012301 * TODO: bash also has:
12302 * -a ARRAY Read into array[0],[1],etc
12303 * -d DELIM End on DELIM char, not newline
12304 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012305 */
Eric Andersenc470f442003-07-28 09:56:35 +000012306static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012307readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012308{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012309 static const char *const arg_REPLY[] = { "REPLY", NULL };
12310
Eric Andersenc470f442003-07-28 09:56:35 +000012311 char **ap;
12312 int backslash;
12313 char c;
12314 int rflag;
12315 char *prompt;
12316 const char *ifs;
12317 char *p;
12318 int startword;
12319 int status;
12320 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012321 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012322#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012323 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012324 int silent = 0;
12325 struct termios tty, old_tty;
12326#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012327#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012328 unsigned end_ms = 0;
12329 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012330#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012331
12332 rflag = 0;
12333 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012334 while ((i = nextopt("p:u:r"
12335 USE_ASH_READ_TIMEOUT("t:")
12336 USE_ASH_READ_NCHARS("n:s")
12337 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012338 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012339 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012340 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012341 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012342#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012343 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012344 nchars = bb_strtou(optionarg, NULL, 10);
12345 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012346 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012347 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012348 break;
12349 case 's':
12350 silent = 1;
12351 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012352#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012353#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012354 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012355 timeout = bb_strtou(optionarg, NULL, 10);
12356 if (errno || timeout > UINT_MAX / 2048)
12357 ash_msg_and_raise_error("invalid timeout");
12358 timeout *= 1000;
12359#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012360 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012361 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012362 /* EINVAL means number is ok, but not terminated by NUL */
12363 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012364 char *p2;
12365 if (*++p) {
12366 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012367 ts.tv_usec = bb_strtou(p, &p2, 10);
12368 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012369 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012370 scale = p2 - p;
12371 /* normalize to usec */
12372 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012373 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012374 while (scale++ < 6)
12375 ts.tv_usec *= 10;
12376 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012377 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012378 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012379 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012380 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012381 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012382 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012383#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012384 break;
12385#endif
12386 case 'r':
12387 rflag = 1;
12388 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012389 case 'u':
12390 fd = bb_strtou(optionarg, NULL, 10);
12391 if (fd < 0 || errno)
12392 ash_msg_and_raise_error("invalid file descriptor");
12393 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012394 default:
12395 break;
12396 }
Eric Andersenc470f442003-07-28 09:56:35 +000012397 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012398 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012399 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012400 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012401 ap = argptr;
12402 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012403 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012404 ifs = bltinlookup("IFS");
12405 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012406 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012407#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012408 tcgetattr(fd, &tty);
12409 old_tty = tty;
12410 if (nchars || silent) {
12411 if (nchars) {
12412 tty.c_lflag &= ~ICANON;
12413 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012414 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012415 if (silent) {
12416 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12417 }
12418 /* if tcgetattr failed, tcsetattr will fail too.
12419 * Ignoring, it's harmless. */
12420 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012421 }
12422#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012423
Eric Andersenc470f442003-07-28 09:56:35 +000012424 status = 0;
12425 startword = 1;
12426 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012427#if ENABLE_ASH_READ_TIMEOUT
12428 if (timeout) /* NB: ensuring end_ms is nonzero */
12429 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12430#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012431 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012432 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012433#if ENABLE_ASH_READ_TIMEOUT
12434 if (end_ms) {
12435 struct pollfd pfd[1];
12436 pfd[0].fd = fd;
12437 pfd[0].events = POLLIN;
12438 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12439 if ((int)timeout <= 0 /* already late? */
12440 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12441 ) { /* timed out! */
12442#if ENABLE_ASH_READ_NCHARS
12443 tcsetattr(fd, TCSANOW, &old_tty);
12444#endif
12445 return 1;
12446 }
12447 }
12448#endif
12449 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012450 status = 1;
12451 break;
12452 }
12453 if (c == '\0')
12454 continue;
12455 if (backslash) {
12456 backslash = 0;
12457 if (c != '\n')
12458 goto put;
12459 continue;
12460 }
12461 if (!rflag && c == '\\') {
12462 backslash++;
12463 continue;
12464 }
12465 if (c == '\n')
12466 break;
12467 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12468 continue;
12469 }
12470 startword = 0;
12471 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12472 STACKSTRNUL(p);
12473 setvar(*ap, stackblock(), 0);
12474 ap++;
12475 startword = 1;
12476 STARTSTACKSTR(p);
12477 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012478 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012479 STPUTC(c, p);
12480 }
12481 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012482/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012483#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012484 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012485#else
12486 while (1);
12487#endif
12488
12489#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012490 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012491#endif
12492
Eric Andersenc470f442003-07-28 09:56:35 +000012493 STACKSTRNUL(p);
12494 /* Remove trailing blanks */
12495 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12496 *p = '\0';
12497 setvar(*ap, stackblock(), 0);
12498 while (*++ap != NULL)
12499 setvar(*ap, nullstr, 0);
12500 return status;
12501}
12502
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012503static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012504umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012505{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012506 static const char permuser[3] ALIGN1 = "ugo";
12507 static const char permmode[3] ALIGN1 = "rwx";
12508 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012509 S_IRUSR, S_IWUSR, S_IXUSR,
12510 S_IRGRP, S_IWGRP, S_IXGRP,
12511 S_IROTH, S_IWOTH, S_IXOTH
12512 };
12513
12514 char *ap;
12515 mode_t mask;
12516 int i;
12517 int symbolic_mode = 0;
12518
12519 while (nextopt("S") != '\0') {
12520 symbolic_mode = 1;
12521 }
12522
Denis Vlasenkob012b102007-02-19 22:43:01 +000012523 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012524 mask = umask(0);
12525 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012526 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012527
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012528 ap = *argptr;
12529 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012530 if (symbolic_mode) {
12531 char buf[18];
12532 char *p = buf;
12533
12534 for (i = 0; i < 3; i++) {
12535 int j;
12536
12537 *p++ = permuser[i];
12538 *p++ = '=';
12539 for (j = 0; j < 3; j++) {
12540 if ((mask & permmask[3 * i + j]) == 0) {
12541 *p++ = permmode[j];
12542 }
12543 }
12544 *p++ = ',';
12545 }
12546 *--p = 0;
12547 puts(buf);
12548 } else {
12549 out1fmt("%.4o\n", mask);
12550 }
12551 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012552 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012553 mask = 0;
12554 do {
12555 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012556 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012557 mask = (mask << 3) + (*ap - '0');
12558 } while (*++ap != '\0');
12559 umask(mask);
12560 } else {
12561 mask = ~mask & 0777;
12562 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012563 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012564 }
12565 umask(~mask & 0777);
12566 }
12567 }
12568 return 0;
12569}
12570
12571/*
12572 * ulimit builtin
12573 *
12574 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12575 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12576 * ash by J.T. Conklin.
12577 *
12578 * Public domain.
12579 */
12580
12581struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012582 uint8_t cmd; /* RLIMIT_xxx fit into it */
12583 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012584 char option;
12585};
12586
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012587static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012588#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012589 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012590#endif
12591#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012592 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012593#endif
12594#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012595 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012596#endif
12597#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012598 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012599#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012600#ifdef RLIMIT_CORE
12601 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012602#endif
12603#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012604 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012605#endif
12606#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012607 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012608#endif
12609#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012610 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012611#endif
12612#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012613 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012614#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012615#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012616 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012617#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012618#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012619 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012620#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012621};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012622static const char limits_name[] =
12623#ifdef RLIMIT_CPU
12624 "time(seconds)" "\0"
12625#endif
12626#ifdef RLIMIT_FSIZE
12627 "file(blocks)" "\0"
12628#endif
12629#ifdef RLIMIT_DATA
12630 "data(kb)" "\0"
12631#endif
12632#ifdef RLIMIT_STACK
12633 "stack(kb)" "\0"
12634#endif
12635#ifdef RLIMIT_CORE
12636 "coredump(blocks)" "\0"
12637#endif
12638#ifdef RLIMIT_RSS
12639 "memory(kb)" "\0"
12640#endif
12641#ifdef RLIMIT_MEMLOCK
12642 "locked memory(kb)" "\0"
12643#endif
12644#ifdef RLIMIT_NPROC
12645 "process" "\0"
12646#endif
12647#ifdef RLIMIT_NOFILE
12648 "nofiles" "\0"
12649#endif
12650#ifdef RLIMIT_AS
12651 "vmemory(kb)" "\0"
12652#endif
12653#ifdef RLIMIT_LOCKS
12654 "locks" "\0"
12655#endif
12656;
Eric Andersenc470f442003-07-28 09:56:35 +000012657
Glenn L McGrath76620622004-01-13 10:19:37 +000012658enum limtype { SOFT = 0x1, HARD = 0x2 };
12659
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012660static void
12661printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012662 const struct limits *l)
12663{
12664 rlim_t val;
12665
12666 val = limit->rlim_max;
12667 if (how & SOFT)
12668 val = limit->rlim_cur;
12669
12670 if (val == RLIM_INFINITY)
12671 out1fmt("unlimited\n");
12672 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012673 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012674 out1fmt("%lld\n", (long long) val);
12675 }
12676}
12677
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012678static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012679ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012680{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012681 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012682 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012683 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012684 const struct limits *l;
12685 int set, all = 0;
12686 int optc, what;
12687 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012688
12689 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012690 while ((optc = nextopt("HSa"
12691#ifdef RLIMIT_CPU
12692 "t"
12693#endif
12694#ifdef RLIMIT_FSIZE
12695 "f"
12696#endif
12697#ifdef RLIMIT_DATA
12698 "d"
12699#endif
12700#ifdef RLIMIT_STACK
12701 "s"
12702#endif
12703#ifdef RLIMIT_CORE
12704 "c"
12705#endif
12706#ifdef RLIMIT_RSS
12707 "m"
12708#endif
12709#ifdef RLIMIT_MEMLOCK
12710 "l"
12711#endif
12712#ifdef RLIMIT_NPROC
12713 "p"
12714#endif
12715#ifdef RLIMIT_NOFILE
12716 "n"
12717#endif
12718#ifdef RLIMIT_AS
12719 "v"
12720#endif
12721#ifdef RLIMIT_LOCKS
12722 "w"
12723#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012724 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012725 switch (optc) {
12726 case 'H':
12727 how = HARD;
12728 break;
12729 case 'S':
12730 how = SOFT;
12731 break;
12732 case 'a':
12733 all = 1;
12734 break;
12735 default:
12736 what = optc;
12737 }
12738
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012739 for (l = limits_tbl; l->option != what; l++)
12740 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012741
12742 set = *argptr ? 1 : 0;
12743 if (set) {
12744 char *p = *argptr;
12745
12746 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012747 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012748 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012749 val = RLIM_INFINITY;
12750 else {
12751 val = (rlim_t) 0;
12752
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012753 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012754 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012755 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012756 if (val < (rlim_t) 0)
12757 break;
12758 }
12759 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012760 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012761 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012762 }
12763 }
12764 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012765 const char *lname = limits_name;
12766 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012767 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012768 out1fmt("%-20s ", lname);
12769 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012770 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012771 }
12772 return 0;
12773 }
12774
12775 getrlimit(l->cmd, &limit);
12776 if (set) {
12777 if (how & HARD)
12778 limit.rlim_max = val;
12779 if (how & SOFT)
12780 limit.rlim_cur = val;
12781 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012782 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012783 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012784 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012785 }
12786 return 0;
12787}
12788
Eric Andersen90898442003-08-06 11:20:52 +000012789
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012790/* ============ Math support */
12791
Denis Vlasenko131ae172007-02-18 13:00:19 +000012792#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012793
12794/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12795
12796 Permission is hereby granted, free of charge, to any person obtaining
12797 a copy of this software and associated documentation files (the
12798 "Software"), to deal in the Software without restriction, including
12799 without limitation the rights to use, copy, modify, merge, publish,
12800 distribute, sublicense, and/or sell copies of the Software, and to
12801 permit persons to whom the Software is furnished to do so, subject to
12802 the following conditions:
12803
12804 The above copyright notice and this permission notice shall be
12805 included in all copies or substantial portions of the Software.
12806
12807 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12808 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12809 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12810 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12811 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12812 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12813 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12814*/
12815
12816/* This is my infix parser/evaluator. It is optimized for size, intended
12817 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012818 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012819 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012820 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012821 * be that which POSIX specifies for shells. */
12822
12823/* The code uses a simple two-stack algorithm. See
12824 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012825 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012826 * this is based (this code differs in that it applies operators immediately
12827 * to the stack instead of adding them to a queue to end up with an
12828 * expression). */
12829
12830/* To use the routine, call it with an expression string and error return
12831 * pointer */
12832
12833/*
12834 * Aug 24, 2001 Manuel Novoa III
12835 *
12836 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12837 *
12838 * 1) In arith_apply():
12839 * a) Cached values of *numptr and &(numptr[-1]).
12840 * b) Removed redundant test for zero denominator.
12841 *
12842 * 2) In arith():
12843 * a) Eliminated redundant code for processing operator tokens by moving
12844 * to a table-based implementation. Also folded handling of parens
12845 * into the table.
12846 * b) Combined all 3 loops which called arith_apply to reduce generated
12847 * code size at the cost of speed.
12848 *
12849 * 3) The following expressions were treated as valid by the original code:
12850 * 1() , 0! , 1 ( *3 ) .
12851 * These bugs have been fixed by internally enclosing the expression in
12852 * parens and then checking that all binary ops and right parens are
12853 * preceded by a valid expression (NUM_TOKEN).
12854 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012855 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012856 * ctype's isspace() if it is used by another busybox applet or if additional
12857 * whitespace chars should be considered. Look below the "#include"s for a
12858 * precompiler test.
12859 */
12860
12861/*
12862 * Aug 26, 2001 Manuel Novoa III
12863 *
12864 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12865 *
12866 * Merge in Aaron's comments previously posted to the busybox list,
12867 * modified slightly to take account of my changes to the code.
12868 *
12869 */
12870
12871/*
12872 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12873 *
12874 * - allow access to variable,
12875 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12876 * - realize assign syntax (VAR=expr, +=, *= etc)
12877 * - realize exponentiation (** operator)
12878 * - realize comma separated - expr, expr
12879 * - realise ++expr --expr expr++ expr--
12880 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012881 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012882 * - was restored loses XOR operator
12883 * - remove one goto label, added three ;-)
12884 * - protect $((num num)) as true zero expr (Manuel`s error)
12885 * - always use special isspace(), see comment from bash ;-)
12886 */
12887
Eric Andersen90898442003-08-06 11:20:52 +000012888#define arith_isspace(arithval) \
12889 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12890
Eric Andersen90898442003-08-06 11:20:52 +000012891typedef unsigned char operator;
12892
12893/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012894 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012895 * precedence. The ID portion is so that multiple operators can have the
12896 * same precedence, ensuring that the leftmost one is evaluated first.
12897 * Consider * and /. */
12898
12899#define tok_decl(prec,id) (((id)<<5)|(prec))
12900#define PREC(op) ((op) & 0x1F)
12901
12902#define TOK_LPAREN tok_decl(0,0)
12903
12904#define TOK_COMMA tok_decl(1,0)
12905
12906#define TOK_ASSIGN tok_decl(2,0)
12907#define TOK_AND_ASSIGN tok_decl(2,1)
12908#define TOK_OR_ASSIGN tok_decl(2,2)
12909#define TOK_XOR_ASSIGN tok_decl(2,3)
12910#define TOK_PLUS_ASSIGN tok_decl(2,4)
12911#define TOK_MINUS_ASSIGN tok_decl(2,5)
12912#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12913#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12914
12915#define TOK_MUL_ASSIGN tok_decl(3,0)
12916#define TOK_DIV_ASSIGN tok_decl(3,1)
12917#define TOK_REM_ASSIGN tok_decl(3,2)
12918
12919/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012920#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012921
12922/* conditional is right associativity too */
12923#define TOK_CONDITIONAL tok_decl(4,0)
12924#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12925
12926#define TOK_OR tok_decl(5,0)
12927
12928#define TOK_AND tok_decl(6,0)
12929
12930#define TOK_BOR tok_decl(7,0)
12931
12932#define TOK_BXOR tok_decl(8,0)
12933
12934#define TOK_BAND tok_decl(9,0)
12935
12936#define TOK_EQ tok_decl(10,0)
12937#define TOK_NE tok_decl(10,1)
12938
12939#define TOK_LT tok_decl(11,0)
12940#define TOK_GT tok_decl(11,1)
12941#define TOK_GE tok_decl(11,2)
12942#define TOK_LE tok_decl(11,3)
12943
12944#define TOK_LSHIFT tok_decl(12,0)
12945#define TOK_RSHIFT tok_decl(12,1)
12946
12947#define TOK_ADD tok_decl(13,0)
12948#define TOK_SUB tok_decl(13,1)
12949
12950#define TOK_MUL tok_decl(14,0)
12951#define TOK_DIV tok_decl(14,1)
12952#define TOK_REM tok_decl(14,2)
12953
12954/* exponent is right associativity */
12955#define TOK_EXPONENT tok_decl(15,1)
12956
12957/* For now unary operators. */
12958#define UNARYPREC 16
12959#define TOK_BNOT tok_decl(UNARYPREC,0)
12960#define TOK_NOT tok_decl(UNARYPREC,1)
12961
12962#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12963#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12964
12965#define PREC_PRE (UNARYPREC+2)
12966
12967#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12968#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12969
12970#define PREC_POST (UNARYPREC+3)
12971
12972#define TOK_POST_INC tok_decl(PREC_POST, 0)
12973#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12974
12975#define SPEC_PREC (UNARYPREC+4)
12976
12977#define TOK_NUM tok_decl(SPEC_PREC, 0)
12978#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12979
12980#define NUMPTR (*numstackptr)
12981
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012982static int
12983tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012984{
12985 operator prec = PREC(op);
12986
12987 convert_prec_is_assing(prec);
12988 return (prec == PREC(TOK_ASSIGN) ||
12989 prec == PREC_PRE || prec == PREC_POST);
12990}
12991
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012992static int
12993is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012994{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012995 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12996 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012997}
12998
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012999typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013000 arith_t val;
13001 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013002 char contidional_second_val_initialized;
13003 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013004 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013005} v_n_t;
13006
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013007typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013008 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013009 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013010} chk_var_recursive_looped_t;
13011
13012static chk_var_recursive_looped_t *prev_chk_var_recursive;
13013
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013014static int
13015arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013016{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 if (t->var) {
13018 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013019
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013020 if (p) {
13021 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013022
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013023 /* recursive try as expression */
13024 chk_var_recursive_looped_t *cur;
13025 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013026
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13028 if (strcmp(cur->var, t->var) == 0) {
13029 /* expression recursion loop detected */
13030 return -5;
13031 }
13032 }
13033 /* save current lookuped var name */
13034 cur = prev_chk_var_recursive;
13035 cur_save.var = t->var;
13036 cur_save.next = cur;
13037 prev_chk_var_recursive = &cur_save;
13038
13039 t->val = arith (p, &errcode);
13040 /* restore previous ptr after recursiving */
13041 prev_chk_var_recursive = cur;
13042 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013043 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013044 /* allow undefined var as 0 */
13045 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013046 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013047 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013048}
13049
13050/* "applying" a token means performing it on the top elements on the integer
13051 * stack. For a unary operator it will only change the top element, but a
13052 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013053static int
13054arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013055{
Eric Andersen90898442003-08-06 11:20:52 +000013056 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013057 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013058 int ret_arith_lookup_val;
13059
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013060 /* There is no operator that can work without arguments */
13061 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013062 numptr_m1 = NUMPTR - 1;
13063
13064 /* check operand is var with noninteger value */
13065 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013066 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013067 return ret_arith_lookup_val;
13068
13069 rez = numptr_m1->val;
13070 if (op == TOK_UMINUS)
13071 rez *= -1;
13072 else if (op == TOK_NOT)
13073 rez = !rez;
13074 else if (op == TOK_BNOT)
13075 rez = ~rez;
13076 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13077 rez++;
13078 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13079 rez--;
13080 else if (op != TOK_UPLUS) {
13081 /* Binary operators */
13082
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013083 /* check and binary operators need two arguments */
13084 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013085
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 /* ... and they pop one */
13087 --NUMPTR;
13088 numptr_val = rez;
13089 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013090 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013091 /* protect $((expr1 ? expr2)) without ": expr" */
13092 goto err;
13093 }
13094 rez = numptr_m1->contidional_second_val;
13095 } else if (numptr_m1->contidional_second_val_initialized) {
13096 /* protect $((expr1 : expr2)) without "expr ? " */
13097 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013098 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013099 numptr_m1 = NUMPTR - 1;
13100 if (op != TOK_ASSIGN) {
13101 /* check operand is var with noninteger value for not '=' */
13102 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13103 if (ret_arith_lookup_val)
13104 return ret_arith_lookup_val;
13105 }
13106 if (op == TOK_CONDITIONAL) {
13107 numptr_m1->contidional_second_val = rez;
13108 }
13109 rez = numptr_m1->val;
13110 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013111 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013112 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013113 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013114 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013115 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013116 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013117 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013118 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013119 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013120 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013121 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013122 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013123 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013124 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013125 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013126 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013127 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013128 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013129 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013130 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013131 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013132 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013133 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013134 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013135 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013136 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013137 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013138 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013139 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013140 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013141 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013142 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013143 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013144 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013145 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013146 /* protect $((expr : expr)) without "expr ? " */
13147 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013148 }
13149 numptr_m1->contidional_second_val_initialized = op;
13150 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013151 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013152 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013153 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013154 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013155 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013156 return -3; /* exponent less than 0 */
13157 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013158 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013159
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013160 if (numptr_val)
13161 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013162 c *= rez;
13163 rez = c;
13164 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013165 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013166 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013167 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013168 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013169 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013170 rez %= numptr_val;
13171 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013172 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013173 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013174
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013175 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013176 /* Hmm, 1=2 ? */
13177 goto err;
13178 }
13179 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013180#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013181 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013182#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013183 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013184#endif
Eric Andersen90898442003-08-06 11:20:52 +000013185 setvar(numptr_m1->var, buf, 0);
13186 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013187 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013188 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013189 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013190 rez++;
13191 }
13192 numptr_m1->val = rez;
13193 /* protect geting var value, is number now */
13194 numptr_m1->var = NULL;
13195 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013196 err:
13197 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013198}
13199
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013200/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013201static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013202 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13203 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13204 '<','<', 0, TOK_LSHIFT,
13205 '>','>', 0, TOK_RSHIFT,
13206 '|','|', 0, TOK_OR,
13207 '&','&', 0, TOK_AND,
13208 '!','=', 0, TOK_NE,
13209 '<','=', 0, TOK_LE,
13210 '>','=', 0, TOK_GE,
13211 '=','=', 0, TOK_EQ,
13212 '|','=', 0, TOK_OR_ASSIGN,
13213 '&','=', 0, TOK_AND_ASSIGN,
13214 '*','=', 0, TOK_MUL_ASSIGN,
13215 '/','=', 0, TOK_DIV_ASSIGN,
13216 '%','=', 0, TOK_REM_ASSIGN,
13217 '+','=', 0, TOK_PLUS_ASSIGN,
13218 '-','=', 0, TOK_MINUS_ASSIGN,
13219 '-','-', 0, TOK_POST_DEC,
13220 '^','=', 0, TOK_XOR_ASSIGN,
13221 '+','+', 0, TOK_POST_INC,
13222 '*','*', 0, TOK_EXPONENT,
13223 '!', 0, TOK_NOT,
13224 '<', 0, TOK_LT,
13225 '>', 0, TOK_GT,
13226 '=', 0, TOK_ASSIGN,
13227 '|', 0, TOK_BOR,
13228 '&', 0, TOK_BAND,
13229 '*', 0, TOK_MUL,
13230 '/', 0, TOK_DIV,
13231 '%', 0, TOK_REM,
13232 '+', 0, TOK_ADD,
13233 '-', 0, TOK_SUB,
13234 '^', 0, TOK_BXOR,
13235 /* uniq */
13236 '~', 0, TOK_BNOT,
13237 ',', 0, TOK_COMMA,
13238 '?', 0, TOK_CONDITIONAL,
13239 ':', 0, TOK_CONDITIONAL_SEP,
13240 ')', 0, TOK_RPAREN,
13241 '(', 0, TOK_LPAREN,
13242 0
13243};
13244/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013245#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013246
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013247static arith_t
13248arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013249{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013250 char arithval; /* Current character under analysis */
13251 operator lasttok, op;
13252 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013253 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013254 const char *p = endexpression;
13255 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013256 v_n_t *numstack, *numstackptr;
13257 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013258
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013259 /* Stack of integers */
13260 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13261 * in any given correct or incorrect expression is left as an exercise to
13262 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013263 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013264 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013265 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013266
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013267 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13268 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013269
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013270 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013271 arithval = *expr;
13272 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013273 if (p == endexpression) {
13274 /* Null expression. */
13275 return 0;
13276 }
13277
13278 /* This is only reached after all tokens have been extracted from the
13279 * input stream. If there are still tokens on the operator stack, they
13280 * are to be applied in order. At the end, there should be a final
13281 * result on the integer stack */
13282
13283 if (expr != endexpression + 1) {
13284 /* If we haven't done so already, */
13285 /* append a closing right paren */
13286 expr = endexpression;
13287 /* and let the loop process it. */
13288 continue;
13289 }
13290 /* At this point, we're done with the expression. */
13291 if (numstackptr != numstack+1) {
13292 /* ... but if there isn't, it's bad */
13293 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013294 *perrcode = -1;
13295 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013296 }
13297 if (numstack->var) {
13298 /* expression is $((var)) only, lookup now */
13299 errcode = arith_lookup_val(numstack);
13300 }
13301 ret:
13302 *perrcode = errcode;
13303 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013304 }
13305
Eric Andersen90898442003-08-06 11:20:52 +000013306 /* Continue processing the expression. */
13307 if (arith_isspace(arithval)) {
13308 /* Skip whitespace */
13309 goto prologue;
13310 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013311 p = endofname(expr);
13312 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013313 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013314
13315 numstackptr->var = alloca(var_name_size);
13316 safe_strncpy(numstackptr->var, expr, var_name_size);
13317 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013318 num:
Eric Andersen90898442003-08-06 11:20:52 +000013319 numstackptr->contidional_second_val_initialized = 0;
13320 numstackptr++;
13321 lasttok = TOK_NUM;
13322 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013323 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013324 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013325 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013326#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013327 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013328#else
13329 numstackptr->val = strtol(expr, (char **) &expr, 0);
13330#endif
Eric Andersen90898442003-08-06 11:20:52 +000013331 goto num;
13332 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013333 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013334 const char *o;
13335
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013336 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013337 /* strange operator not found */
13338 goto err;
13339 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013340 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013341 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013342 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013343 /* found */
13344 expr = o - 1;
13345 break;
13346 }
13347 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013348 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013349 p++;
13350 /* skip zero delim */
13351 p++;
13352 }
13353 op = p[1];
13354
13355 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013356 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13357 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013358
13359 /* Plus and minus are binary (not unary) _only_ if the last
13360 * token was as number, or a right paren (which pretends to be
13361 * a number, since it evaluates to one). Think about it.
13362 * It makes sense. */
13363 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013364 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013365 case TOK_ADD:
13366 op = TOK_UPLUS;
13367 break;
13368 case TOK_SUB:
13369 op = TOK_UMINUS;
13370 break;
13371 case TOK_POST_INC:
13372 op = TOK_PRE_INC;
13373 break;
13374 case TOK_POST_DEC:
13375 op = TOK_PRE_DEC;
13376 break;
Eric Andersen90898442003-08-06 11:20:52 +000013377 }
13378 }
13379 /* We don't want a unary operator to cause recursive descent on the
13380 * stack, because there can be many in a row and it could cause an
13381 * operator to be evaluated before its argument is pushed onto the
13382 * integer stack. */
13383 /* But for binary operators, "apply" everything on the operator
13384 * stack until we find an operator with a lesser priority than the
13385 * one we have just extracted. */
13386 /* Left paren is given the lowest priority so it will never be
13387 * "applied" in this way.
13388 * if associativity is right and priority eq, applied also skip
13389 */
13390 prec = PREC(op);
13391 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13392 /* not left paren or unary */
13393 if (lasttok != TOK_NUM) {
13394 /* binary op must be preceded by a num */
13395 goto err;
13396 }
13397 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013398 if (op == TOK_RPAREN) {
13399 /* The algorithm employed here is simple: while we don't
13400 * hit an open paren nor the bottom of the stack, pop
13401 * tokens and apply them */
13402 if (stackptr[-1] == TOK_LPAREN) {
13403 --stackptr;
13404 /* Any operator directly after a */
13405 lasttok = TOK_NUM;
13406 /* close paren should consider itself binary */
13407 goto prologue;
13408 }
13409 } else {
13410 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013411
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013412 convert_prec_is_assing(prec);
13413 convert_prec_is_assing(prev_prec);
13414 if (prev_prec < prec)
13415 break;
13416 /* check right assoc */
13417 if (prev_prec == prec && is_right_associativity(prec))
13418 break;
13419 }
13420 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13421 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013422 }
13423 if (op == TOK_RPAREN) {
13424 goto err;
13425 }
13426 }
13427
13428 /* Push this operator to the stack and remember it. */
13429 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013430 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013431 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013432 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013433}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013434#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013435
13436
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013437/* ============ main() and helpers */
13438
13439/*
13440 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013441 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013442static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013443static void
13444exitshell(void)
13445{
13446 struct jmploc loc;
13447 char *p;
13448 int status;
13449
13450 status = exitstatus;
13451 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13452 if (setjmp(loc.loc)) {
13453 if (exception == EXEXIT)
13454/* dash bug: it just does _exit(exitstatus) here
13455 * but we have to do setjobctl(0) first!
13456 * (bug is still not fixed in dash-0.5.3 - if you run dash
13457 * under Midnight Commander, on exit from dash MC is backgrounded) */
13458 status = exitstatus;
13459 goto out;
13460 }
13461 exception_handler = &loc;
13462 p = trap[0];
13463 if (p) {
13464 trap[0] = NULL;
13465 evalstring(p, 0);
13466 }
13467 flush_stdout_stderr();
13468 out:
13469 setjobctl(0);
13470 _exit(status);
13471 /* NOTREACHED */
13472}
13473
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013474static void
13475init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013476{
13477 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013478 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013479
13480 /* from trap.c: */
13481 signal(SIGCHLD, SIG_DFL);
13482
13483 /* from var.c: */
13484 {
13485 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013486 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013487 const char *p;
13488 struct stat st1, st2;
13489
13490 initvar();
13491 for (envp = environ; envp && *envp; envp++) {
13492 if (strchr(*envp, '=')) {
13493 setvareq(*envp, VEXPORT|VTEXTFIXED);
13494 }
13495 }
13496
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013497 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013498 setvar("PPID", ppid, 0);
13499
13500 p = lookupvar("PWD");
13501 if (p)
13502 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13503 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13504 p = '\0';
13505 setpwd(p, 0);
13506 }
13507}
13508
13509/*
13510 * Process the shell command line arguments.
13511 */
13512static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013513procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013514{
13515 int i;
13516 const char *xminusc;
13517 char **xargv;
13518
13519 xargv = argv;
13520 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013521 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013522 xargv++;
13523 for (i = 0; i < NOPTS; i++)
13524 optlist[i] = 2;
13525 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013526 if (options(1)) {
13527 /* it already printed err message */
13528 raise_exception(EXERROR);
13529 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013530 xargv = argptr;
13531 xminusc = minusc;
13532 if (*xargv == NULL) {
13533 if (xminusc)
13534 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13535 sflag = 1;
13536 }
13537 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13538 iflag = 1;
13539 if (mflag == 2)
13540 mflag = iflag;
13541 for (i = 0; i < NOPTS; i++)
13542 if (optlist[i] == 2)
13543 optlist[i] = 0;
13544#if DEBUG == 2
13545 debug = 1;
13546#endif
13547 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13548 if (xminusc) {
13549 minusc = *xargv++;
13550 if (*xargv)
13551 goto setarg0;
13552 } else if (!sflag) {
13553 setinputfile(*xargv, 0);
13554 setarg0:
13555 arg0 = *xargv++;
13556 commandname = arg0;
13557 }
13558
13559 shellparam.p = xargv;
13560#if ENABLE_ASH_GETOPTS
13561 shellparam.optind = 1;
13562 shellparam.optoff = -1;
13563#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013564 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013565 while (*xargv) {
13566 shellparam.nparam++;
13567 xargv++;
13568 }
13569 optschanged();
13570}
13571
13572/*
13573 * Read /etc/profile or .profile.
13574 */
13575static void
13576read_profile(const char *name)
13577{
13578 int skip;
13579
13580 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13581 return;
13582 skip = cmdloop(0);
13583 popfile();
13584 if (skip)
13585 exitshell();
13586}
13587
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013588/*
13589 * This routine is called when an error or an interrupt occurs in an
13590 * interactive shell and control is returned to the main command loop.
13591 */
13592static void
13593reset(void)
13594{
13595 /* from eval.c: */
13596 evalskip = 0;
13597 loopnest = 0;
13598 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013599 g_parsefile->left_in_buffer = 0;
13600 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013601 popallfiles();
13602 /* from parser.c: */
13603 tokpushback = 0;
13604 checkkwd = 0;
13605 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013606 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013607}
13608
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013609#if PROFILE
13610static short profile_buf[16384];
13611extern int etext();
13612#endif
13613
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013614/*
13615 * Main routine. We initialize things, parse the arguments, execute
13616 * profiles if we're a login shell, and then call cmdloop to execute
13617 * commands. The setjmp call sets up the location to jump to when an
13618 * exception occurs. When an exception occurs the variable "state"
13619 * is used to figure out how far we had gotten.
13620 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013621int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013622int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013623{
13624 char *shinit;
13625 volatile int state;
13626 struct jmploc jmploc;
13627 struct stackmark smark;
13628
Denis Vlasenko01631112007-12-16 17:20:38 +000013629 /* Initialize global data */
13630 INIT_G_misc();
13631 INIT_G_memstack();
13632 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013633#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013634 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013635#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013636 INIT_G_cmdtable();
13637
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013638#if PROFILE
13639 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13640#endif
13641
13642#if ENABLE_FEATURE_EDITING
13643 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13644#endif
13645 state = 0;
13646 if (setjmp(jmploc.loc)) {
13647 int e;
13648 int s;
13649
13650 reset();
13651
13652 e = exception;
13653 if (e == EXERROR)
13654 exitstatus = 2;
13655 s = state;
13656 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13657 exitshell();
13658
13659 if (e == EXINT) {
13660 outcslow('\n', stderr);
13661 }
13662 popstackmark(&smark);
13663 FORCE_INT_ON; /* enable interrupts */
13664 if (s == 1)
13665 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013666 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013667 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013668 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013669 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013670 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013671 }
13672 exception_handler = &jmploc;
13673#if DEBUG
13674 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013675 trace_puts("Shell args: ");
13676 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013677#endif
13678 rootpid = getpid();
13679
13680#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013681 /* Can use monotonic_ns() for better randomness but for now it is
13682 * not used anywhere else in busybox... so avoid bloat */
13683 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013684#endif
13685 init();
13686 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013687 procargs(argv);
13688
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013689#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13690 if (iflag) {
13691 const char *hp = lookupvar("HISTFILE");
13692
13693 if (hp == NULL) {
13694 hp = lookupvar("HOME");
13695 if (hp != NULL) {
13696 char *defhp = concat_path_file(hp, ".ash_history");
13697 setvar("HISTFILE", defhp, 0);
13698 free(defhp);
13699 }
13700 }
13701 }
13702#endif
13703 if (argv[0] && argv[0][0] == '-')
13704 isloginsh = 1;
13705 if (isloginsh) {
13706 state = 1;
13707 read_profile("/etc/profile");
13708 state1:
13709 state = 2;
13710 read_profile(".profile");
13711 }
13712 state2:
13713 state = 3;
13714 if (
13715#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013716 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013717#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013718 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013719 ) {
13720 shinit = lookupvar("ENV");
13721 if (shinit != NULL && *shinit != '\0') {
13722 read_profile(shinit);
13723 }
13724 }
13725 state3:
13726 state = 4;
13727 if (minusc)
13728 evalstring(minusc, 0);
13729
13730 if (sflag || minusc == NULL) {
13731#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013732 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013733 const char *hp = lookupvar("HISTFILE");
13734
13735 if (hp != NULL)
13736 line_input_state->hist_file = hp;
13737 }
13738#endif
13739 state4: /* XXX ??? - why isn't this before the "if" statement */
13740 cmdloop(1);
13741 }
13742#if PROFILE
13743 monitor(0);
13744#endif
13745#ifdef GPROF
13746 {
13747 extern void _mcleanup(void);
13748 _mcleanup();
13749 }
13750#endif
13751 exitshell();
13752 /* NOTREACHED */
13753}
13754
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013755#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013756const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013757int main(int argc, char **argv)
13758{
13759 return ash_main(argc, argv);
13760}
13761#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013762
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013763
Eric Andersendf82f612001-06-28 07:46:40 +000013764/*-
13765 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013766 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013767 *
13768 * This code is derived from software contributed to Berkeley by
13769 * Kenneth Almquist.
13770 *
13771 * Redistribution and use in source and binary forms, with or without
13772 * modification, are permitted provided that the following conditions
13773 * are met:
13774 * 1. Redistributions of source code must retain the above copyright
13775 * notice, this list of conditions and the following disclaimer.
13776 * 2. Redistributions in binary form must reproduce the above copyright
13777 * notice, this list of conditions and the following disclaimer in the
13778 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013779 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013780 * may be used to endorse or promote products derived from this software
13781 * without specific prior written permission.
13782 *
13783 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13784 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13785 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13786 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13787 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13788 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13789 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13790 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13791 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13792 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13793 * SUCH DAMAGE.
13794 */