blob: 295418c04f569b21ef11360948e39c819b552975 [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 Vlasenkoaa744452007-02-23 01:04:22 +00009159static int
9160preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009161{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009162 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009163 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009164
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009165 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009166#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009167 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009168 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009169 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009170 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009171#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009172 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009173#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009174 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9175 if (nr == 0) {
9176 /* Ctrl+C pressed */
9177 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009178 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009179 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009180 raise(SIGINT);
9181 return 1;
9182 }
Eric Andersenc470f442003-07-28 09:56:35 +00009183 goto retry;
9184 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009185 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009186 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009187 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009188 }
Eric Andersencb57d552001-06-28 07:25:16 +00009189 }
9190#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009191 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009192#endif
9193
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009194#if 0
9195/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009196 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009197 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009198 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009199 if (flags >= 0 && (flags & O_NONBLOCK)) {
9200 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009201 if (fcntl(0, F_SETFL, flags) >= 0) {
9202 out2str("sh: turning off NDELAY mode\n");
9203 goto retry;
9204 }
9205 }
9206 }
9207 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009208#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009209 return nr;
9210}
9211
9212/*
9213 * Refill the input buffer and return the next input character:
9214 *
9215 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009216 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9217 * or we are reading from a string so we can't refill the buffer,
9218 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009219 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9220 * 4) Process input up to the next newline, deleting nul characters.
9221 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009222//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9223#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009224static int
Eric Andersenc470f442003-07-28 09:56:35 +00009225preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009226{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009227 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009228 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009229
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009230 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009231#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009232 if (g_parsefile->left_in_line == -1
9233 && g_parsefile->strpush->ap
9234 && g_parsefile->next_to_pgetc[-1] != ' '
9235 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009236 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009237 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009238 return PEOA;
9239 }
Eric Andersen2870d962001-07-02 17:27:21 +00009240#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009241 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009242 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009243 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9244 g_parsefile->left_in_line,
9245 g_parsefile->next_to_pgetc,
9246 g_parsefile->next_to_pgetc);
9247 if (--g_parsefile->left_in_line >= 0)
9248 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009249 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009250 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009251 * "pgetc" needs refilling.
9252 */
9253
9254 /* -90 is -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009255 * pungetc() may increment it a few times.
9256 * Assuming it won't increment it to 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009257 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009258 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009259 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009260 /* even in failure keep left_in_line and next_to_pgetc
9261 * in lock step, for correct multi-layer pungetc.
9262 * left_in_line was decremented before preadbuffer(),
9263 * must inc next_to_pgetc: */
9264 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009265 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009266 }
Eric Andersencb57d552001-06-28 07:25:16 +00009267
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009268 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009269 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009270 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009271 again:
9272 more = preadfd();
9273 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009274 /* don't try reading again */
9275 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009276 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009277 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009278 return PEOF;
9279 }
9280 }
9281
Denis Vlasenko727752d2008-11-28 03:41:47 +00009282 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009283 * Set g_parsefile->left_in_line
9284 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009285 * NUL chars are deleted.
9286 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009287 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009288 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009289 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009290
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009291 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009292
Denis Vlasenko727752d2008-11-28 03:41:47 +00009293 c = *q;
9294 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009295 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009296 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009297 q++;
9298 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009299 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009300 break;
9301 }
Eric Andersencb57d552001-06-28 07:25:16 +00009302 }
9303
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009304 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009305 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9306 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009307 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009308 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009309 }
9310 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009311 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009312
Eric Andersencb57d552001-06-28 07:25:16 +00009313 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009314 char save = *q;
9315 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009316 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009317 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009318 }
9319
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009320 pgetc_debug("preadbuffer at %d:%p'%s'",
9321 g_parsefile->left_in_line,
9322 g_parsefile->next_to_pgetc,
9323 g_parsefile->next_to_pgetc);
9324 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009325}
9326
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009327#define pgetc_as_macro() \
9328 (--g_parsefile->left_in_line >= 0 \
9329 ? (unsigned char)(*g_parsefile->next_to_pgetc++) \
9330 : preadbuffer() \
9331 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009332
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009333static int
9334pgetc(void)
9335{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009336 pgetc_debug("pgetc_fast at %d:%p'%s'",
9337 g_parsefile->left_in_line,
9338 g_parsefile->next_to_pgetc,
9339 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009340 return pgetc_as_macro();
9341}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009342
9343#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009344#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009345#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009346#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009347#endif
9348
9349/*
9350 * Same as pgetc(), but ignores PEOA.
9351 */
9352#if ENABLE_ASH_ALIAS
9353static int
9354pgetc2(void)
9355{
9356 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009357 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009358 pgetc_debug("pgetc_fast at %d:%p'%s'",
9359 g_parsefile->left_in_line,
9360 g_parsefile->next_to_pgetc,
9361 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009362 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009363 } while (c == PEOA);
9364 return c;
9365}
9366#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009367#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009368#endif
9369
9370/*
9371 * Read a line from the script.
9372 */
9373static char *
9374pfgets(char *line, int len)
9375{
9376 char *p = line;
9377 int nleft = len;
9378 int c;
9379
9380 while (--nleft > 0) {
9381 c = pgetc2();
9382 if (c == PEOF) {
9383 if (p == line)
9384 return NULL;
9385 break;
9386 }
9387 *p++ = c;
9388 if (c == '\n')
9389 break;
9390 }
9391 *p = '\0';
9392 return line;
9393}
9394
Eric Andersenc470f442003-07-28 09:56:35 +00009395/*
9396 * Undo the last call to pgetc. Only one character may be pushed back.
9397 * PEOF may be pushed back.
9398 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009399static void
Eric Andersenc470f442003-07-28 09:56:35 +00009400pungetc(void)
9401{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009402 g_parsefile->left_in_line++;
9403 g_parsefile->next_to_pgetc--;
9404 pgetc_debug("pushed back to %d:%p'%s'",
9405 g_parsefile->left_in_line,
9406 g_parsefile->next_to_pgetc,
9407 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009408}
9409
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009410/*
9411 * To handle the "." command, a stack of input files is used. Pushfile
9412 * adds a new entry to the stack and popfile restores the previous level.
9413 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009414static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009415pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009416{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009417 struct parsefile *pf;
9418
Denis Vlasenko597906c2008-02-20 16:38:54 +00009419 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009420 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009421 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009422 /*pf->strpush = NULL; - ckzalloc did it */
9423 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009424 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009425}
9426
9427static void
9428popfile(void)
9429{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009430 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009431
Denis Vlasenkob012b102007-02-19 22:43:01 +00009432 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009433 if (pf->fd >= 0)
9434 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009435 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009436 while (pf->strpush)
9437 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009438 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009439 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009440 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009441}
9442
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009443/*
9444 * Return to top level.
9445 */
9446static void
9447popallfiles(void)
9448{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009449 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009450 popfile();
9451}
9452
9453/*
9454 * Close the file(s) that the shell is reading commands from. Called
9455 * after a fork is done.
9456 */
9457static void
9458closescript(void)
9459{
9460 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009461 if (g_parsefile->fd > 0) {
9462 close(g_parsefile->fd);
9463 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009464 }
9465}
9466
9467/*
9468 * Like setinputfile, but takes an open file descriptor. Call this with
9469 * interrupts off.
9470 */
9471static void
9472setinputfd(int fd, int push)
9473{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009474 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009475 if (push) {
9476 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009477 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009478 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009479 g_parsefile->fd = fd;
9480 if (g_parsefile->buf == NULL)
9481 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009482 g_parsefile->left_in_buffer = 0;
9483 g_parsefile->left_in_line = 0;
9484 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009485}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009486
Eric Andersenc470f442003-07-28 09:56:35 +00009487/*
9488 * Set the input to take input from a file. If push is set, push the
9489 * old input onto the stack first.
9490 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009491static int
9492setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009493{
9494 int fd;
9495 int fd2;
9496
Denis Vlasenkob012b102007-02-19 22:43:01 +00009497 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009498 fd = open(fname, O_RDONLY);
9499 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009500 if (flags & INPUT_NOFILE_OK)
9501 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009502 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009503 }
Eric Andersenc470f442003-07-28 09:56:35 +00009504 if (fd < 10) {
9505 fd2 = copyfd(fd, 10);
9506 close(fd);
9507 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009508 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009509 fd = fd2;
9510 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009511 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009512 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009513 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009514 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009515}
9516
Eric Andersencb57d552001-06-28 07:25:16 +00009517/*
9518 * Like setinputfile, but takes input from a string.
9519 */
Eric Andersenc470f442003-07-28 09:56:35 +00009520static void
9521setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009522{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009523 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009524 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009525 g_parsefile->next_to_pgetc = string;
9526 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009527 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009528 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009529 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009530}
9531
9532
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009533/* ============ mail.c
9534 *
9535 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009536 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009537
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009538#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009539
Eric Andersencb57d552001-06-28 07:25:16 +00009540#define MAXMBOXES 10
9541
Eric Andersenc470f442003-07-28 09:56:35 +00009542/* times of mailboxes */
9543static time_t mailtime[MAXMBOXES];
9544/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009545static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009546
Eric Andersencb57d552001-06-28 07:25:16 +00009547/*
Eric Andersenc470f442003-07-28 09:56:35 +00009548 * Print appropriate message(s) if mail has arrived.
9549 * If mail_var_path_changed is set,
9550 * then the value of MAIL has mail_var_path_changed,
9551 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009552 */
Eric Andersenc470f442003-07-28 09:56:35 +00009553static void
9554chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009555{
Eric Andersencb57d552001-06-28 07:25:16 +00009556 const char *mpath;
9557 char *p;
9558 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009559 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009560 struct stackmark smark;
9561 struct stat statb;
9562
Eric Andersencb57d552001-06-28 07:25:16 +00009563 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009564 mpath = mpathset() ? mpathval() : mailval();
9565 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009566 p = padvance(&mpath, nullstr);
9567 if (p == NULL)
9568 break;
9569 if (*p == '\0')
9570 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009571 for (q = p; *q; q++)
9572 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009573#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009574 if (q[-1] != '/')
9575 abort();
9576#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009577 q[-1] = '\0'; /* delete trailing '/' */
9578 if (stat(p, &statb) < 0) {
9579 *mtp = 0;
9580 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009581 }
Eric Andersenc470f442003-07-28 09:56:35 +00009582 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9583 fprintf(
9584 stderr, snlfmt,
9585 pathopt ? pathopt : "you have mail"
9586 );
9587 }
9588 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009589 }
Eric Andersenc470f442003-07-28 09:56:35 +00009590 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009591 popstackmark(&smark);
9592}
Eric Andersencb57d552001-06-28 07:25:16 +00009593
Eric Andersenc470f442003-07-28 09:56:35 +00009594static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009595changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009596{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009597 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009598}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009599
Denis Vlasenko131ae172007-02-18 13:00:19 +00009600#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009601
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009602
9603/* ============ ??? */
9604
Eric Andersencb57d552001-06-28 07:25:16 +00009605/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009606 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009607 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009608static void
9609setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009610{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009611 char **newparam;
9612 char **ap;
9613 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009614
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009615 for (nparam = 0; argv[nparam]; nparam++)
9616 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009617 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9618 while (*argv) {
9619 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009620 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009621 *ap = NULL;
9622 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009623 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009624 shellparam.nparam = nparam;
9625 shellparam.p = newparam;
9626#if ENABLE_ASH_GETOPTS
9627 shellparam.optind = 1;
9628 shellparam.optoff = -1;
9629#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009630}
9631
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009632/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009633 * Process shell options. The global variable argptr contains a pointer
9634 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009635 *
9636 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9637 * For a non-interactive shell, an error condition encountered
9638 * by a special built-in ... shall cause the shell to write a diagnostic message
9639 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009640 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009641 * ...
9642 * Utility syntax error (option or operand error) Shall exit
9643 * ...
9644 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9645 * we see that bash does not do that (set "finishes" with error code 1 instead,
9646 * and shell continues), and people rely on this behavior!
9647 * Testcase:
9648 * set -o barfoo 2>/dev/null
9649 * echo $?
9650 *
9651 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009652 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009653static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009654plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009655{
9656 int i;
9657
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009658 if (name) {
9659 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009660 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009661 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009662 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009663 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009664 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009665 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009666 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009667 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009668 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009669 if (val) {
9670 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9671 } else {
9672 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9673 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009674 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009675 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009676}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009677static void
9678setoption(int flag, int val)
9679{
9680 int i;
9681
9682 for (i = 0; i < NOPTS; i++) {
9683 if (optletters(i) == flag) {
9684 optlist[i] = val;
9685 return;
9686 }
9687 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009688 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009689 /* NOTREACHED */
9690}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009691static int
Eric Andersenc470f442003-07-28 09:56:35 +00009692options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009693{
9694 char *p;
9695 int val;
9696 int c;
9697
9698 if (cmdline)
9699 minusc = NULL;
9700 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009701 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009702 if (c != '-' && c != '+')
9703 break;
9704 argptr++;
9705 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009706 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009707 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009708 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009709 if (!cmdline) {
9710 /* "-" means turn off -x and -v */
9711 if (p[0] == '\0')
9712 xflag = vflag = 0;
9713 /* "--" means reset params */
9714 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009715 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009716 }
Eric Andersenc470f442003-07-28 09:56:35 +00009717 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009718 }
Eric Andersencb57d552001-06-28 07:25:16 +00009719 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009720 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009721 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009722 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009723 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009724 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009725 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009726 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009727 /* it already printed err message */
9728 return 1; /* error */
9729 }
Eric Andersencb57d552001-06-28 07:25:16 +00009730 if (*argptr)
9731 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009732 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9733 isloginsh = 1;
9734 /* bash does not accept +-login, we also won't */
9735 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009736 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009737 isloginsh = 1;
9738 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009739 } else {
9740 setoption(c, val);
9741 }
9742 }
9743 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009744 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009745}
9746
Eric Andersencb57d552001-06-28 07:25:16 +00009747/*
Eric Andersencb57d552001-06-28 07:25:16 +00009748 * The shift builtin command.
9749 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009750static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009751shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009752{
9753 int n;
9754 char **ap1, **ap2;
9755
9756 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009757 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009758 n = number(argv[1]);
9759 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009760 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009761 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009762 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009763 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009764 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009765 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009766 }
9767 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009768 while ((*ap2++ = *ap1++) != NULL)
9769 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009770#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009771 shellparam.optind = 1;
9772 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009773#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009774 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009775 return 0;
9776}
9777
Eric Andersencb57d552001-06-28 07:25:16 +00009778/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009779 * POSIX requires that 'set' (but not export or readonly) output the
9780 * variables in lexicographic order - by the locale's collating order (sigh).
9781 * Maybe we could keep them in an ordered balanced binary tree
9782 * instead of hashed lists.
9783 * For now just roll 'em through qsort for printing...
9784 */
9785static int
9786showvars(const char *sep_prefix, int on, int off)
9787{
9788 const char *sep;
9789 char **ep, **epend;
9790
9791 ep = listvars(on, off, &epend);
9792 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9793
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009794 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009795
9796 for (; ep < epend; ep++) {
9797 const char *p;
9798 const char *q;
9799
9800 p = strchrnul(*ep, '=');
9801 q = nullstr;
9802 if (*p)
9803 q = single_quote(++p);
9804 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9805 }
9806 return 0;
9807}
9808
9809/*
Eric Andersencb57d552001-06-28 07:25:16 +00009810 * The set command builtin.
9811 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009812static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009813setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009814{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009815 int retval;
9816
Denis Vlasenko68404f12008-03-17 09:00:54 +00009817 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009818 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009819 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009820 retval = 1;
9821 if (!options(0)) { /* if no parse error... */
9822 retval = 0;
9823 optschanged();
9824 if (*argptr != NULL) {
9825 setparam(argptr);
9826 }
Eric Andersencb57d552001-06-28 07:25:16 +00009827 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009828 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009829 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009830}
9831
Denis Vlasenko131ae172007-02-18 13:00:19 +00009832#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009833static void
9834change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009835{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009836 /* Galois LFSR parameter */
9837 /* Taps at 32 31 29 1: */
9838 enum { MASK = 0x8000000b };
9839 /* Another example - taps at 32 31 30 10: */
9840 /* MASK = 0x00400007 */
9841
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009842 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009843 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009844 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009845
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009846 /* LCG has period of 2^32 and alternating lowest bit */
9847 random_LCG = 1664525 * random_LCG + 1013904223;
9848 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9849 t = (random_galois_LFSR << 1);
9850 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9851 t ^= MASK;
9852 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009853 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009854 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009855 * for $RANDOM range. Combining with subtraction is
9856 * just for fun. + and ^ would work equally well. */
9857 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009858 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009859 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009860 vrandom.flags &= ~VNOFUNC;
9861 } else {
9862 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009863 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009864 }
Eric Andersenef02f822004-03-11 13:34:24 +00009865}
Eric Andersen16767e22004-03-16 05:14:10 +00009866#endif
9867
Denis Vlasenko131ae172007-02-18 13:00:19 +00009868#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009869static int
Eric Andersenc470f442003-07-28 09:56:35 +00009870getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009871{
9872 char *p, *q;
9873 char c = '?';
9874 int done = 0;
9875 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009876 char s[12];
9877 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009878
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009879 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009880 return 1;
9881 optnext = optfirst + *param_optind - 1;
9882
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009883 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009884 p = NULL;
9885 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009886 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009887 if (p == NULL || *p == '\0') {
9888 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009889 p = *optnext;
9890 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009891 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009892 p = NULL;
9893 done = 1;
9894 goto out;
9895 }
9896 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009897 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009898 goto atend;
9899 }
9900
9901 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009902 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009903 if (*q == '\0') {
9904 if (optstr[0] == ':') {
9905 s[0] = c;
9906 s[1] = '\0';
9907 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009908 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009909 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009910 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009911 }
9912 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009913 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009914 }
9915 if (*++q == ':')
9916 q++;
9917 }
9918
9919 if (*++q == ':') {
9920 if (*p == '\0' && (p = *optnext) == NULL) {
9921 if (optstr[0] == ':') {
9922 s[0] = c;
9923 s[1] = '\0';
9924 err |= setvarsafe("OPTARG", s, 0);
9925 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009926 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009927 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009928 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009929 c = '?';
9930 }
Eric Andersenc470f442003-07-28 09:56:35 +00009931 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009932 }
9933
9934 if (p == *optnext)
9935 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009936 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009937 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009938 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009939 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009940 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009941 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009942 *param_optind = optnext - optfirst + 1;
9943 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009944 err |= setvarsafe("OPTIND", s, VNOFUNC);
9945 s[0] = c;
9946 s[1] = '\0';
9947 err |= setvarsafe(optvar, s, 0);
9948 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009949 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009950 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009951 flush_stdout_stderr();
9952 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009953 }
9954 return done;
9955}
Eric Andersenc470f442003-07-28 09:56:35 +00009956
9957/*
9958 * The getopts builtin. Shellparam.optnext points to the next argument
9959 * to be processed. Shellparam.optptr points to the next character to
9960 * be processed in the current argument. If shellparam.optnext is NULL,
9961 * then it's the first time getopts has been called.
9962 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009963static int
Eric Andersenc470f442003-07-28 09:56:35 +00009964getoptscmd(int argc, char **argv)
9965{
9966 char **optbase;
9967
9968 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009969 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009970 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009971 optbase = shellparam.p;
9972 if (shellparam.optind > shellparam.nparam + 1) {
9973 shellparam.optind = 1;
9974 shellparam.optoff = -1;
9975 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009976 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009977 optbase = &argv[3];
9978 if (shellparam.optind > argc - 2) {
9979 shellparam.optind = 1;
9980 shellparam.optoff = -1;
9981 }
9982 }
9983
9984 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009985 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009986}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009987#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009988
Eric Andersencb57d552001-06-28 07:25:16 +00009989
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009990/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009991
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009992struct heredoc {
9993 struct heredoc *next; /* next here document in list */
9994 union node *here; /* redirection node */
9995 char *eofmark; /* string indicating end of input */
9996 smallint striptabs; /* if set, strip leading tabs */
9997};
9998
9999static smallint tokpushback; /* last token pushed back */
10000static smallint parsebackquote; /* nonzero if we are inside backquotes */
10001static smallint quoteflag; /* set if (part of) last token was quoted */
10002static token_id_t lasttoken; /* last token read (integer id Txxx) */
10003static struct heredoc *heredoclist; /* list of here documents to read */
10004static char *wordtext; /* text of last word returned by readtoken */
10005static struct nodelist *backquotelist;
10006static union node *redirnode;
10007static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010008/*
10009 * NEOF is returned by parsecmd when it encounters an end of file. It
10010 * must be distinct from NULL, so we use the address of a variable that
10011 * happens to be handy.
10012 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010013#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010014
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010015static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010016static void
10017raise_error_syntax(const char *msg)
10018{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010019 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010020 /* NOTREACHED */
10021}
10022
10023/*
10024 * Called when an unexpected token is read during the parse. The argument
10025 * is the token that is expected, or -1 if more than one type of token can
10026 * occur at this point.
10027 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010028static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010029static void
10030raise_error_unexpected_syntax(int token)
10031{
10032 char msg[64];
10033 int l;
10034
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010035 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010036 if (token >= 0)
10037 sprintf(msg + l, " (expecting %s)", tokname(token));
10038 raise_error_syntax(msg);
10039 /* NOTREACHED */
10040}
Eric Andersencb57d552001-06-28 07:25:16 +000010041
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010042#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010043
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010044/* parsing is heavily cross-recursive, need these forward decls */
10045static union node *andor(void);
10046static union node *pipeline(void);
10047static union node *parse_command(void);
10048static void parseheredoc(void);
10049static char peektoken(void);
10050static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010051
Eric Andersenc470f442003-07-28 09:56:35 +000010052static union node *
10053list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010054{
10055 union node *n1, *n2, *n3;
10056 int tok;
10057
Eric Andersenc470f442003-07-28 09:56:35 +000010058 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10059 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010060 return NULL;
10061 n1 = NULL;
10062 for (;;) {
10063 n2 = andor();
10064 tok = readtoken();
10065 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010066 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010067 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010068 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010069 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010070 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010071 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010072 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010073 n2 = n3;
10074 }
10075 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010076 }
10077 }
10078 if (n1 == NULL) {
10079 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010080 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010081 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010082 n3->type = NSEMI;
10083 n3->nbinary.ch1 = n1;
10084 n3->nbinary.ch2 = n2;
10085 n1 = n3;
10086 }
10087 switch (tok) {
10088 case TBACKGND:
10089 case TSEMI:
10090 tok = readtoken();
10091 /* fall through */
10092 case TNL:
10093 if (tok == TNL) {
10094 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010095 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010096 return n1;
10097 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010098 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010099 }
Eric Andersenc470f442003-07-28 09:56:35 +000010100 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010101 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010102 return n1;
10103 break;
10104 case TEOF:
10105 if (heredoclist)
10106 parseheredoc();
10107 else
Eric Andersenc470f442003-07-28 09:56:35 +000010108 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010109 return n1;
10110 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010111 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010112 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010113 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010114 return n1;
10115 }
10116 }
10117}
10118
Eric Andersenc470f442003-07-28 09:56:35 +000010119static union node *
10120andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010121{
Eric Andersencb57d552001-06-28 07:25:16 +000010122 union node *n1, *n2, *n3;
10123 int t;
10124
Eric Andersencb57d552001-06-28 07:25:16 +000010125 n1 = pipeline();
10126 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010127 t = readtoken();
10128 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010129 t = NAND;
10130 } else if (t == TOR) {
10131 t = NOR;
10132 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010133 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010134 return n1;
10135 }
Eric Andersenc470f442003-07-28 09:56:35 +000010136 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010137 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010138 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010139 n3->type = t;
10140 n3->nbinary.ch1 = n1;
10141 n3->nbinary.ch2 = n2;
10142 n1 = n3;
10143 }
10144}
10145
Eric Andersenc470f442003-07-28 09:56:35 +000010146static union node *
10147pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010148{
Eric Andersencb57d552001-06-28 07:25:16 +000010149 union node *n1, *n2, *pipenode;
10150 struct nodelist *lp, *prev;
10151 int negate;
10152
10153 negate = 0;
10154 TRACE(("pipeline: entered\n"));
10155 if (readtoken() == TNOT) {
10156 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010157 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010158 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010159 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010160 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010161 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010162 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010163 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010164 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010165 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010166 pipenode->npipe.cmdlist = lp;
10167 lp->n = n1;
10168 do {
10169 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010170 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010171 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010172 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010173 prev->next = lp;
10174 } while (readtoken() == TPIPE);
10175 lp->next = NULL;
10176 n1 = pipenode;
10177 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010178 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010179 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010180 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010181 n2->type = NNOT;
10182 n2->nnot.com = n1;
10183 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010184 }
10185 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010186}
10187
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010188static union node *
10189makename(void)
10190{
10191 union node *n;
10192
Denis Vlasenko597906c2008-02-20 16:38:54 +000010193 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010194 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010195 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010196 n->narg.text = wordtext;
10197 n->narg.backquote = backquotelist;
10198 return n;
10199}
10200
10201static void
10202fixredir(union node *n, const char *text, int err)
10203{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010204 int fd;
10205
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010206 TRACE(("Fix redir %s %d\n", text, err));
10207 if (!err)
10208 n->ndup.vname = NULL;
10209
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010210 fd = bb_strtou(text, NULL, 10);
10211 if (!errno && fd >= 0)
10212 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010213 else if (LONE_DASH(text))
10214 n->ndup.dupfd = -1;
10215 else {
10216 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010217 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010218 n->ndup.vname = makename();
10219 }
10220}
10221
10222/*
10223 * Returns true if the text contains nothing to expand (no dollar signs
10224 * or backquotes).
10225 */
10226static int
10227noexpand(char *text)
10228{
10229 char *p;
10230 char c;
10231
10232 p = text;
10233 while ((c = *p++) != '\0') {
10234 if (c == CTLQUOTEMARK)
10235 continue;
10236 if (c == CTLESC)
10237 p++;
10238 else if (SIT(c, BASESYNTAX) == CCTL)
10239 return 0;
10240 }
10241 return 1;
10242}
10243
10244static void
10245parsefname(void)
10246{
10247 union node *n = redirnode;
10248
10249 if (readtoken() != TWORD)
10250 raise_error_unexpected_syntax(-1);
10251 if (n->type == NHERE) {
10252 struct heredoc *here = heredoc;
10253 struct heredoc *p;
10254 int i;
10255
10256 if (quoteflag == 0)
10257 n->type = NXHERE;
10258 TRACE(("Here document %d\n", n->type));
10259 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010260 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010261 rmescapes(wordtext);
10262 here->eofmark = wordtext;
10263 here->next = NULL;
10264 if (heredoclist == NULL)
10265 heredoclist = here;
10266 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010267 for (p = heredoclist; p->next; p = p->next)
10268 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010269 p->next = here;
10270 }
10271 } else if (n->type == NTOFD || n->type == NFROMFD) {
10272 fixredir(n, wordtext, 0);
10273 } else {
10274 n->nfile.fname = makename();
10275 }
10276}
Eric Andersencb57d552001-06-28 07:25:16 +000010277
Eric Andersenc470f442003-07-28 09:56:35 +000010278static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010279simplecmd(void)
10280{
10281 union node *args, **app;
10282 union node *n = NULL;
10283 union node *vars, **vpp;
10284 union node **rpp, *redir;
10285 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010286#if ENABLE_ASH_BASH_COMPAT
10287 smallint double_brackets_flag = 0;
10288#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010289
10290 args = NULL;
10291 app = &args;
10292 vars = NULL;
10293 vpp = &vars;
10294 redir = NULL;
10295 rpp = &redir;
10296
10297 savecheckkwd = CHKALIAS;
10298 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010299 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010300 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010301 t = readtoken();
10302 switch (t) {
10303#if ENABLE_ASH_BASH_COMPAT
10304 case TAND: /* "&&" */
10305 case TOR: /* "||" */
10306 if (!double_brackets_flag) {
10307 tokpushback = 1;
10308 goto out;
10309 }
10310 wordtext = (char *) (t == TAND ? "-a" : "-o");
10311#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010312 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010313 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010314 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010315 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010316 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010317#if ENABLE_ASH_BASH_COMPAT
10318 if (strcmp("[[", wordtext) == 0)
10319 double_brackets_flag = 1;
10320 else if (strcmp("]]", wordtext) == 0)
10321 double_brackets_flag = 0;
10322#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010323 n->narg.backquote = backquotelist;
10324 if (savecheckkwd && isassignment(wordtext)) {
10325 *vpp = n;
10326 vpp = &n->narg.next;
10327 } else {
10328 *app = n;
10329 app = &n->narg.next;
10330 savecheckkwd = 0;
10331 }
10332 break;
10333 case TREDIR:
10334 *rpp = n = redirnode;
10335 rpp = &n->nfile.next;
10336 parsefname(); /* read name of redirection file */
10337 break;
10338 case TLP:
10339 if (args && app == &args->narg.next
10340 && !vars && !redir
10341 ) {
10342 struct builtincmd *bcmd;
10343 const char *name;
10344
10345 /* We have a function */
10346 if (readtoken() != TRP)
10347 raise_error_unexpected_syntax(TRP);
10348 name = n->narg.text;
10349 if (!goodname(name)
10350 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10351 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010352 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353 }
10354 n->type = NDEFUN;
10355 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10356 n->narg.next = parse_command();
10357 return n;
10358 }
10359 /* fall through */
10360 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010361 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010362 goto out;
10363 }
10364 }
10365 out:
10366 *app = NULL;
10367 *vpp = NULL;
10368 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010369 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010370 n->type = NCMD;
10371 n->ncmd.args = args;
10372 n->ncmd.assign = vars;
10373 n->ncmd.redirect = redir;
10374 return n;
10375}
10376
10377static union node *
10378parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010379{
Eric Andersencb57d552001-06-28 07:25:16 +000010380 union node *n1, *n2;
10381 union node *ap, **app;
10382 union node *cp, **cpp;
10383 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010384 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010385 int t;
10386
10387 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010388 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010389
Eric Andersencb57d552001-06-28 07:25:16 +000010390 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010391 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010392 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010393 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010394 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010395 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010396 n1->type = NIF;
10397 n1->nif.test = list(0);
10398 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010399 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010400 n1->nif.ifpart = list(0);
10401 n2 = n1;
10402 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010403 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010404 n2 = n2->nif.elsepart;
10405 n2->type = NIF;
10406 n2->nif.test = list(0);
10407 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010408 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010409 n2->nif.ifpart = list(0);
10410 }
10411 if (lasttoken == TELSE)
10412 n2->nif.elsepart = list(0);
10413 else {
10414 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010415 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010416 }
Eric Andersenc470f442003-07-28 09:56:35 +000010417 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010418 break;
10419 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010420 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010421 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010422 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010423 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010424 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010425 got = readtoken();
10426 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010427 TRACE(("expecting DO got %s %s\n", tokname(got),
10428 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010429 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010430 }
10431 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010432 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010433 break;
10434 }
10435 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010436 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010437 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010438 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010439 n1->type = NFOR;
10440 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010441 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010442 if (readtoken() == TIN) {
10443 app = &ap;
10444 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010445 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010446 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010447 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010448 n2->narg.text = wordtext;
10449 n2->narg.backquote = backquotelist;
10450 *app = n2;
10451 app = &n2->narg.next;
10452 }
10453 *app = NULL;
10454 n1->nfor.args = ap;
10455 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010456 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010457 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010458 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010459 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010460 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010461 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010462 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010463 n1->nfor.args = n2;
10464 /*
10465 * Newline or semicolon here is optional (but note
10466 * that the original Bourne shell only allowed NL).
10467 */
10468 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010469 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010470 }
Eric Andersenc470f442003-07-28 09:56:35 +000010471 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010472 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010474 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010475 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010476 break;
10477 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010478 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010479 n1->type = NCASE;
10480 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010481 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010482 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010483 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010484 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010485 n2->narg.text = wordtext;
10486 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010487 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010488 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010489 } while (readtoken() == TNL);
10490 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010492 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010493 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010494 checkkwd = CHKNL | CHKKWD;
10495 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010496 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010497 if (lasttoken == TLP)
10498 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010499 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010500 cp->type = NCLIST;
10501 app = &cp->nclist.pattern;
10502 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010503 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010504 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010505 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010506 ap->narg.text = wordtext;
10507 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010508 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010509 break;
10510 app = &ap->narg.next;
10511 readtoken();
10512 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010513 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010514 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010515 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010516 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010517
Eric Andersenc470f442003-07-28 09:56:35 +000010518 cpp = &cp->nclist.next;
10519
10520 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010521 t = readtoken();
10522 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010523 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010524 raise_error_unexpected_syntax(TENDCASE);
10525 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010526 }
Eric Andersenc470f442003-07-28 09:56:35 +000010527 }
Eric Andersencb57d552001-06-28 07:25:16 +000010528 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010529 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010530 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010531 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010532 n1->type = NSUBSHELL;
10533 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010534 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010535 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010536 break;
10537 case TBEGIN:
10538 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010539 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010540 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010542 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010543 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010544 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010545 }
10546
Eric Andersenc470f442003-07-28 09:56:35 +000010547 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010548 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010549
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010550 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010551 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010552 checkkwd = CHKKWD | CHKALIAS;
10553 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010554 while (readtoken() == TREDIR) {
10555 *rpp = n2 = redirnode;
10556 rpp = &n2->nfile.next;
10557 parsefname();
10558 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010559 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010560 *rpp = NULL;
10561 if (redir) {
10562 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010563 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010564 n2->type = NREDIR;
10565 n2->nredir.n = n1;
10566 n1 = n2;
10567 }
10568 n1->nredir.redirect = redir;
10569 }
Eric Andersencb57d552001-06-28 07:25:16 +000010570 return n1;
10571}
10572
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010573#if ENABLE_ASH_BASH_COMPAT
10574static int decode_dollar_squote(void)
10575{
10576 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10577 int c, cnt;
10578 char *p;
10579 char buf[4];
10580
10581 c = pgetc();
10582 p = strchr(C_escapes, c);
10583 if (p) {
10584 buf[0] = c;
10585 p = buf;
10586 cnt = 3;
10587 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10588 do {
10589 c = pgetc();
10590 *++p = c;
10591 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10592 pungetc();
10593 } else if (c == 'x') { /* \xHH */
10594 do {
10595 c = pgetc();
10596 *++p = c;
10597 } while (isxdigit(c) && --cnt);
10598 pungetc();
10599 if (cnt == 3) { /* \x but next char is "bad" */
10600 c = 'x';
10601 goto unrecognized;
10602 }
10603 } else { /* simple seq like \\ or \t */
10604 p++;
10605 }
10606 *p = '\0';
10607 p = buf;
10608 c = bb_process_escape_sequence((void*)&p);
10609 } else { /* unrecognized "\z": print both chars unless ' or " */
10610 if (c != '\'' && c != '"') {
10611 unrecognized:
10612 c |= 0x100; /* "please encode \, then me" */
10613 }
10614 }
10615 return c;
10616}
10617#endif
10618
Eric Andersencb57d552001-06-28 07:25:16 +000010619/*
10620 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10621 * is not NULL, read a here document. In the latter case, eofmark is the
10622 * word which marks the end of the document and striptabs is true if
10623 * leading tabs should be stripped from the document. The argument firstc
10624 * is the first character of the input token or document.
10625 *
10626 * Because C does not have internal subroutines, I have simulated them
10627 * using goto's to implement the subroutine linkage. The following macros
10628 * will run code that appears at the end of readtoken1.
10629 */
Eric Andersen2870d962001-07-02 17:27:21 +000010630#define CHECKEND() {goto checkend; checkend_return:;}
10631#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10632#define PARSESUB() {goto parsesub; parsesub_return:;}
10633#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10634#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10635#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010636static int
Eric Andersenc470f442003-07-28 09:56:35 +000010637readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010638{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010639 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010640 int c = firstc;
10641 char *out;
10642 int len;
10643 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010644 struct nodelist *bqlist;
10645 smallint quotef;
10646 smallint dblquote;
10647 smallint oldstyle;
10648 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010649#if ENABLE_ASH_EXPAND_PRMT
10650 smallint pssyntax; /* we are expanding a prompt string */
10651#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010652 int varnest; /* levels of variables expansion */
10653 int arinest; /* levels of arithmetic expansion */
10654 int parenlevel; /* levels of parens in arithmetic */
10655 int dqvarnest; /* levels of variables expansion within double quotes */
10656
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010657 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10658
Eric Andersencb57d552001-06-28 07:25:16 +000010659#if __GNUC__
10660 /* Avoid longjmp clobbering */
10661 (void) &out;
10662 (void) &quotef;
10663 (void) &dblquote;
10664 (void) &varnest;
10665 (void) &arinest;
10666 (void) &parenlevel;
10667 (void) &dqvarnest;
10668 (void) &oldstyle;
10669 (void) &prevsyntax;
10670 (void) &syntax;
10671#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010672 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010673 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010674 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010675 oldstyle = 0;
10676 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010677#if ENABLE_ASH_EXPAND_PRMT
10678 pssyntax = (syntax == PSSYNTAX);
10679 if (pssyntax)
10680 syntax = DQSYNTAX;
10681#endif
10682 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010683 varnest = 0;
10684 arinest = 0;
10685 parenlevel = 0;
10686 dqvarnest = 0;
10687
10688 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010689 loop:
10690 /* For each line, until end of word */
10691 {
Eric Andersenc470f442003-07-28 09:56:35 +000010692 CHECKEND(); /* set c to PEOF if at end of here document */
10693 for (;;) { /* until end of line or end of word */
10694 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010695 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010696 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010697 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010698 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010699 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010700 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010701 if (doprompt)
10702 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010703 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010704 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010705 case CWORD:
10706 USTPUTC(c, out);
10707 break;
10708 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010709 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010710 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010711#if ENABLE_ASH_BASH_COMPAT
10712 if (c == '\\' && bash_dollar_squote) {
10713 c = decode_dollar_squote();
10714 if (c & 0x100) {
10715 USTPUTC('\\', out);
10716 c = (unsigned char)c;
10717 }
10718 }
10719#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010720 USTPUTC(c, out);
10721 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010722 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010723 c = pgetc2();
10724 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010725 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010726 USTPUTC('\\', out);
10727 pungetc();
10728 } else if (c == '\n') {
10729 if (doprompt)
10730 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010731 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010732#if ENABLE_ASH_EXPAND_PRMT
10733 if (c == '$' && pssyntax) {
10734 USTPUTC(CTLESC, out);
10735 USTPUTC('\\', out);
10736 }
10737#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010738 if (dblquote && c != '\\'
10739 && c != '`' && c != '$'
10740 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010741 ) {
10742 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010743 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010744 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010745 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010746 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010747 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010748 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010749 }
10750 break;
10751 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010752 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010753 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010754 if (eofmark == NULL) {
10755 USTPUTC(CTLQUOTEMARK, out);
10756 }
Eric Andersencb57d552001-06-28 07:25:16 +000010757 break;
10758 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010759 syntax = DQSYNTAX;
10760 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010761 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010762 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010763 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010764 if (eofmark != NULL && arinest == 0
10765 && varnest == 0
10766 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010767 USTPUTC(c, out);
10768 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010769 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010770 syntax = BASESYNTAX;
10771 dblquote = 0;
10772 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010773 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010774 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010775 }
10776 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010777 case CVAR: /* '$' */
10778 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010779 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010780 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010781 if (varnest > 0) {
10782 varnest--;
10783 if (dqvarnest > 0) {
10784 dqvarnest--;
10785 }
10786 USTPUTC(CTLENDVAR, out);
10787 } else {
10788 USTPUTC(c, out);
10789 }
10790 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010791#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010792 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010793 parenlevel++;
10794 USTPUTC(c, out);
10795 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010796 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010797 if (parenlevel > 0) {
10798 USTPUTC(c, out);
10799 --parenlevel;
10800 } else {
10801 if (pgetc() == ')') {
10802 if (--arinest == 0) {
10803 USTPUTC(CTLENDARI, out);
10804 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010805 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010806 } else
10807 USTPUTC(')', out);
10808 } else {
10809 /*
10810 * unbalanced parens
10811 * (don't 2nd guess - no error)
10812 */
10813 pungetc();
10814 USTPUTC(')', out);
10815 }
10816 }
10817 break;
10818#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010819 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010820 PARSEBACKQOLD();
10821 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010822 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010823 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010824 case CIGN:
10825 break;
10826 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010827 if (varnest == 0) {
10828#if ENABLE_ASH_BASH_COMPAT
10829 if (c == '&') {
10830 if (pgetc() == '>')
10831 c = 0x100 + '>'; /* flag &> */
10832 pungetc();
10833 }
10834#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010835 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010836 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010837#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010838 if (c != PEOA)
10839#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010840 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010841
Eric Andersencb57d552001-06-28 07:25:16 +000010842 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010843 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010844 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010845 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010846 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010847#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010848 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010849 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010850#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010851 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010852 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010853 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010854 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010855 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010856 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010857 }
10858 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010859 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010860 out = stackblock();
10861 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010862 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10863 && quotef == 0
10864 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010865 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010866 PARSEREDIR(); /* passed as params: out, c */
10867 lasttoken = TREDIR;
10868 return lasttoken;
10869 }
10870 /* else: non-number X seen, interpret it
10871 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010872 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010873 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010874 }
10875 quoteflag = quotef;
10876 backquotelist = bqlist;
10877 grabstackblock(len);
10878 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010879 lasttoken = TWORD;
10880 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010881/* end of readtoken routine */
10882
Eric Andersencb57d552001-06-28 07:25:16 +000010883/*
10884 * Check to see whether we are at the end of the here document. When this
10885 * is called, c is set to the first character of the next input line. If
10886 * we are at the end of the here document, this routine sets the c to PEOF.
10887 */
Eric Andersenc470f442003-07-28 09:56:35 +000010888checkend: {
10889 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010890#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010891 if (c == PEOA) {
10892 c = pgetc2();
10893 }
10894#endif
10895 if (striptabs) {
10896 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010897 c = pgetc2();
10898 }
Eric Andersenc470f442003-07-28 09:56:35 +000010899 }
10900 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010901 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010902 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010903
Eric Andersenc470f442003-07-28 09:56:35 +000010904 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010905 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10906 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010907 if (*p == '\n' && *q == '\0') {
10908 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010909 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010910 needprompt = doprompt;
10911 } else {
10912 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010913 }
10914 }
10915 }
10916 }
Eric Andersenc470f442003-07-28 09:56:35 +000010917 goto checkend_return;
10918}
Eric Andersencb57d552001-06-28 07:25:16 +000010919
Eric Andersencb57d552001-06-28 07:25:16 +000010920/*
10921 * Parse a redirection operator. The variable "out" points to a string
10922 * specifying the fd to be redirected. The variable "c" contains the
10923 * first character of the redirection operator.
10924 */
Eric Andersenc470f442003-07-28 09:56:35 +000010925parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010926 /* out is already checked to be a valid number or "" */
10927 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010928 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010929
Denis Vlasenko597906c2008-02-20 16:38:54 +000010930 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010931 if (c == '>') {
10932 np->nfile.fd = 1;
10933 c = pgetc();
10934 if (c == '>')
10935 np->type = NAPPEND;
10936 else if (c == '|')
10937 np->type = NCLOBBER;
10938 else if (c == '&')
10939 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010940 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010941 else {
10942 np->type = NTO;
10943 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010944 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010945 }
10946#if ENABLE_ASH_BASH_COMPAT
10947 else if (c == 0x100 + '>') { /* this flags &> redirection */
10948 np->nfile.fd = 1;
10949 pgetc(); /* this is '>', no need to check */
10950 np->type = NTO2;
10951 }
10952#endif
10953 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010954 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010955 c = pgetc();
10956 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010957 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010958 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010959 np = stzalloc(sizeof(struct nhere));
10960 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010961 }
10962 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010963 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010964 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010965 c = pgetc();
10966 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010967 heredoc->striptabs = 1;
10968 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010969 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010970 pungetc();
10971 }
10972 break;
10973
10974 case '&':
10975 np->type = NFROMFD;
10976 break;
10977
10978 case '>':
10979 np->type = NFROMTO;
10980 break;
10981
10982 default:
10983 np->type = NFROM;
10984 pungetc();
10985 break;
10986 }
Eric Andersencb57d552001-06-28 07:25:16 +000010987 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010988 if (fd >= 0)
10989 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010990 redirnode = np;
10991 goto parseredir_return;
10992}
Eric Andersencb57d552001-06-28 07:25:16 +000010993
Eric Andersencb57d552001-06-28 07:25:16 +000010994/*
10995 * Parse a substitution. At this point, we have read the dollar sign
10996 * and nothing else.
10997 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010998
10999/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11000 * (assuming ascii char codes, as the original implementation did) */
11001#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011002 (((unsigned)(c) - 33 < 32) \
11003 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011004parsesub: {
11005 int subtype;
11006 int typeloc;
11007 int flags;
11008 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011009 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011010
Eric Andersenc470f442003-07-28 09:56:35 +000011011 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011012 if (c <= PEOA_OR_PEOF
11013 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011014 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011015#if ENABLE_ASH_BASH_COMPAT
11016 if (c == '\'')
11017 bash_dollar_squote = 1;
11018 else
11019#endif
11020 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011021 pungetc();
11022 } else if (c == '(') { /* $(command) or $((arith)) */
11023 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011024#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011025 PARSEARITH();
11026#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011027 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011028#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011029 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011030 pungetc();
11031 PARSEBACKQNEW();
11032 }
11033 } else {
11034 USTPUTC(CTLVAR, out);
11035 typeloc = out - (char *)stackblock();
11036 USTPUTC(VSNORMAL, out);
11037 subtype = VSNORMAL;
11038 if (c == '{') {
11039 c = pgetc();
11040 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011041 c = pgetc();
11042 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011043 c = '#';
11044 else
11045 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011046 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011047 subtype = 0;
11048 }
11049 if (c > PEOA_OR_PEOF && is_name(c)) {
11050 do {
11051 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011052 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011053 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011054 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011055 do {
11056 STPUTC(c, out);
11057 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011058 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011059 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011060 USTPUTC(c, out);
11061 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011062 } else {
11063 badsub:
11064 raise_error_syntax("bad substitution");
11065 }
Eric Andersencb57d552001-06-28 07:25:16 +000011066
Eric Andersenc470f442003-07-28 09:56:35 +000011067 STPUTC('=', out);
11068 flags = 0;
11069 if (subtype == 0) {
11070 switch (c) {
11071 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011072 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011073#if ENABLE_ASH_BASH_COMPAT
11074 if (c == ':' || c == '$' || isdigit(c)) {
11075 pungetc();
11076 subtype = VSSUBSTR;
11077 break;
11078 }
11079#endif
11080 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011081 /*FALLTHROUGH*/
11082 default:
11083 p = strchr(types, c);
11084 if (p == NULL)
11085 goto badsub;
11086 subtype = p - types + VSNORMAL;
11087 break;
11088 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011089 case '#': {
11090 int cc = c;
11091 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11092 c = pgetc();
11093 if (c == cc)
11094 subtype++;
11095 else
11096 pungetc();
11097 break;
11098 }
11099#if ENABLE_ASH_BASH_COMPAT
11100 case '/':
11101 subtype = VSREPLACE;
11102 c = pgetc();
11103 if (c == '/')
11104 subtype++; /* VSREPLACEALL */
11105 else
11106 pungetc();
11107 break;
11108#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011109 }
Eric Andersenc470f442003-07-28 09:56:35 +000011110 } else {
11111 pungetc();
11112 }
11113 if (dblquote || arinest)
11114 flags |= VSQUOTE;
11115 *((char *)stackblock() + typeloc) = subtype | flags;
11116 if (subtype != VSNORMAL) {
11117 varnest++;
11118 if (dblquote || arinest) {
11119 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011120 }
11121 }
11122 }
Eric Andersenc470f442003-07-28 09:56:35 +000011123 goto parsesub_return;
11124}
Eric Andersencb57d552001-06-28 07:25:16 +000011125
Eric Andersencb57d552001-06-28 07:25:16 +000011126/*
11127 * Called to parse command substitutions. Newstyle is set if the command
11128 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11129 * list of commands (passed by reference), and savelen is the number of
11130 * characters on the top of the stack which must be preserved.
11131 */
Eric Andersenc470f442003-07-28 09:56:35 +000011132parsebackq: {
11133 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011134 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011135 union node *n;
11136 char *volatile str;
11137 struct jmploc jmploc;
11138 struct jmploc *volatile savehandler;
11139 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011140 smallint saveprompt = 0;
11141
Eric Andersencb57d552001-06-28 07:25:16 +000011142#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011143 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011144#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011145 savepbq = parsebackquote;
11146 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011147 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011148 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011149 exception_handler = savehandler;
11150 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011151 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011152 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011153 str = NULL;
11154 savelen = out - (char *)stackblock();
11155 if (savelen > 0) {
11156 str = ckmalloc(savelen);
11157 memcpy(str, stackblock(), savelen);
11158 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011159 savehandler = exception_handler;
11160 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011161 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011162 if (oldstyle) {
11163 /* We must read until the closing backquote, giving special
11164 treatment to some slashes, and then push the string and
11165 reread it as input, interpreting it normally. */
11166 char *pout;
11167 int pc;
11168 size_t psavelen;
11169 char *pstr;
11170
11171
11172 STARTSTACKSTR(pout);
11173 for (;;) {
11174 if (needprompt) {
11175 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011176 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011177 pc = pgetc();
11178 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011179 case '`':
11180 goto done;
11181
11182 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011183 pc = pgetc();
11184 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011185 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011186 if (doprompt)
11187 setprompt(2);
11188 /*
11189 * If eating a newline, avoid putting
11190 * the newline into the new character
11191 * stream (via the STPUTC after the
11192 * switch).
11193 */
11194 continue;
11195 }
11196 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011197 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011198 STPUTC('\\', pout);
11199 if (pc > PEOA_OR_PEOF) {
11200 break;
11201 }
11202 /* fall through */
11203
11204 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011205#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011206 case PEOA:
11207#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011208 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011209 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011210
11211 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011212 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011213 needprompt = doprompt;
11214 break;
11215
11216 default:
11217 break;
11218 }
11219 STPUTC(pc, pout);
11220 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011221 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011222 STPUTC('\0', pout);
11223 psavelen = pout - (char *)stackblock();
11224 if (psavelen > 0) {
11225 pstr = grabstackstr(pout);
11226 setinputstring(pstr);
11227 }
11228 }
11229 nlpp = &bqlist;
11230 while (*nlpp)
11231 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011232 *nlpp = stzalloc(sizeof(**nlpp));
11233 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011234 parsebackquote = oldstyle;
11235
11236 if (oldstyle) {
11237 saveprompt = doprompt;
11238 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011239 }
11240
Eric Andersenc470f442003-07-28 09:56:35 +000011241 n = list(2);
11242
11243 if (oldstyle)
11244 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011245 else if (readtoken() != TRP)
11246 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011247
11248 (*nlpp)->n = n;
11249 if (oldstyle) {
11250 /*
11251 * Start reading from old file again, ignoring any pushed back
11252 * tokens left from the backquote parsing
11253 */
11254 popfile();
11255 tokpushback = 0;
11256 }
11257 while (stackblocksize() <= savelen)
11258 growstackblock();
11259 STARTSTACKSTR(out);
11260 if (str) {
11261 memcpy(out, str, savelen);
11262 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011263 INT_OFF;
11264 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011265 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011266 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011267 }
11268 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011269 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011270 if (arinest || dblquote)
11271 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11272 else
11273 USTPUTC(CTLBACKQ, out);
11274 if (oldstyle)
11275 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011276 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011277}
11278
Denis Vlasenko131ae172007-02-18 13:00:19 +000011279#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011280/*
11281 * Parse an arithmetic expansion (indicate start of one and set state)
11282 */
Eric Andersenc470f442003-07-28 09:56:35 +000011283parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011284 if (++arinest == 1) {
11285 prevsyntax = syntax;
11286 syntax = ARISYNTAX;
11287 USTPUTC(CTLARI, out);
11288 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011289 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011290 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011291 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011292 } else {
11293 /*
11294 * we collapse embedded arithmetic expansion to
11295 * parenthesis, which should be equivalent
11296 */
11297 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011298 }
Eric Andersenc470f442003-07-28 09:56:35 +000011299 goto parsearith_return;
11300}
11301#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011302
Eric Andersenc470f442003-07-28 09:56:35 +000011303} /* end of readtoken */
11304
Eric Andersencb57d552001-06-28 07:25:16 +000011305/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011306 * Read the next input token.
11307 * If the token is a word, we set backquotelist to the list of cmds in
11308 * backquotes. We set quoteflag to true if any part of the word was
11309 * quoted.
11310 * If the token is TREDIR, then we set redirnode to a structure containing
11311 * the redirection.
11312 * In all cases, the variable startlinno is set to the number of the line
11313 * on which the token starts.
11314 *
11315 * [Change comment: here documents and internal procedures]
11316 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11317 * word parsing code into a separate routine. In this case, readtoken
11318 * doesn't need to have any internal procedures, but parseword does.
11319 * We could also make parseoperator in essence the main routine, and
11320 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011321 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011322#define NEW_xxreadtoken
11323#ifdef NEW_xxreadtoken
11324/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011325static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011326 '\n', '(', ')', /* singles */
11327 '&', '|', ';', /* doubles */
11328 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011329};
Eric Andersencb57d552001-06-28 07:25:16 +000011330
Denis Vlasenko834dee72008-10-07 09:18:30 +000011331#define xxreadtoken_singles 3
11332#define xxreadtoken_doubles 3
11333
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011334static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011335 TNL, TLP, TRP, /* only single occurrence allowed */
11336 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11337 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011338 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011339};
11340
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011341static int
11342xxreadtoken(void)
11343{
11344 int c;
11345
11346 if (tokpushback) {
11347 tokpushback = 0;
11348 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011350 if (needprompt) {
11351 setprompt(2);
11352 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011353 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011354 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011355 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011356 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11357 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011358
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011359 if (c == '#') {
11360 while ((c = pgetc()) != '\n' && c != PEOF)
11361 continue;
11362 pungetc();
11363 } else if (c == '\\') {
11364 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011365 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011366 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011367 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011368 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011369 if (doprompt)
11370 setprompt(2);
11371 } else {
11372 const char *p;
11373
11374 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11375 if (c != PEOF) {
11376 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011377 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011378 needprompt = doprompt;
11379 }
11380
11381 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011382 if (p == NULL)
11383 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011384
Denis Vlasenko834dee72008-10-07 09:18:30 +000011385 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11386 int cc = pgetc();
11387 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011388 p += xxreadtoken_doubles + 1;
11389 } else {
11390 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011391#if ENABLE_ASH_BASH_COMPAT
11392 if (c == '&' && cc == '>') /* &> */
11393 break; /* return readtoken1(...) */
11394#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011395 }
11396 }
11397 }
11398 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11399 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011400 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011401 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011402
11403 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011404}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011405#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011406#define RETURN(token) return lasttoken = token
11407static int
11408xxreadtoken(void)
11409{
11410 int c;
11411
11412 if (tokpushback) {
11413 tokpushback = 0;
11414 return lasttoken;
11415 }
11416 if (needprompt) {
11417 setprompt(2);
11418 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011419 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011420 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011421 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011422 switch (c) {
11423 case ' ': case '\t':
11424#if ENABLE_ASH_ALIAS
11425 case PEOA:
11426#endif
11427 continue;
11428 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011429 while ((c = pgetc()) != '\n' && c != PEOF)
11430 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011431 pungetc();
11432 continue;
11433 case '\\':
11434 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011435 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011436 if (doprompt)
11437 setprompt(2);
11438 continue;
11439 }
11440 pungetc();
11441 goto breakloop;
11442 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011443 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011444 needprompt = doprompt;
11445 RETURN(TNL);
11446 case PEOF:
11447 RETURN(TEOF);
11448 case '&':
11449 if (pgetc() == '&')
11450 RETURN(TAND);
11451 pungetc();
11452 RETURN(TBACKGND);
11453 case '|':
11454 if (pgetc() == '|')
11455 RETURN(TOR);
11456 pungetc();
11457 RETURN(TPIPE);
11458 case ';':
11459 if (pgetc() == ';')
11460 RETURN(TENDCASE);
11461 pungetc();
11462 RETURN(TSEMI);
11463 case '(':
11464 RETURN(TLP);
11465 case ')':
11466 RETURN(TRP);
11467 default:
11468 goto breakloop;
11469 }
11470 }
11471 breakloop:
11472 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11473#undef RETURN
11474}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011475#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011476
11477static int
11478readtoken(void)
11479{
11480 int t;
11481#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011482 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011483#endif
11484
11485#if ENABLE_ASH_ALIAS
11486 top:
11487#endif
11488
11489 t = xxreadtoken();
11490
11491 /*
11492 * eat newlines
11493 */
11494 if (checkkwd & CHKNL) {
11495 while (t == TNL) {
11496 parseheredoc();
11497 t = xxreadtoken();
11498 }
11499 }
11500
11501 if (t != TWORD || quoteflag) {
11502 goto out;
11503 }
11504
11505 /*
11506 * check for keywords
11507 */
11508 if (checkkwd & CHKKWD) {
11509 const char *const *pp;
11510
11511 pp = findkwd(wordtext);
11512 if (pp) {
11513 lasttoken = t = pp - tokname_array;
11514 TRACE(("keyword %s recognized\n", tokname(t)));
11515 goto out;
11516 }
11517 }
11518
11519 if (checkkwd & CHKALIAS) {
11520#if ENABLE_ASH_ALIAS
11521 struct alias *ap;
11522 ap = lookupalias(wordtext, 1);
11523 if (ap != NULL) {
11524 if (*ap->val) {
11525 pushstring(ap->val, ap);
11526 }
11527 goto top;
11528 }
11529#endif
11530 }
11531 out:
11532 checkkwd = 0;
11533#if DEBUG
11534 if (!alreadyseen)
11535 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11536 else
11537 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11538#endif
11539 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011540}
11541
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011542static char
11543peektoken(void)
11544{
11545 int t;
11546
11547 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011548 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011549 return tokname_array[t][0];
11550}
Eric Andersencb57d552001-06-28 07:25:16 +000011551
11552/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011553 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11554 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011555 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011556static union node *
11557parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011558{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011559 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011560
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011561 tokpushback = 0;
11562 doprompt = interact;
11563 if (doprompt)
11564 setprompt(doprompt);
11565 needprompt = 0;
11566 t = readtoken();
11567 if (t == TEOF)
11568 return NEOF;
11569 if (t == TNL)
11570 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011571 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011572 return list(1);
11573}
11574
11575/*
11576 * Input any here documents.
11577 */
11578static void
11579parseheredoc(void)
11580{
11581 struct heredoc *here;
11582 union node *n;
11583
11584 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011585 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011586
11587 while (here) {
11588 if (needprompt) {
11589 setprompt(2);
11590 }
11591 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11592 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011593 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011594 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011595 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011596 n->narg.text = wordtext;
11597 n->narg.backquote = backquotelist;
11598 here->here->nhere.doc = n;
11599 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011600 }
Eric Andersencb57d552001-06-28 07:25:16 +000011601}
11602
11603
11604/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011605 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011606 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011607#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011608static const char *
11609expandstr(const char *ps)
11610{
11611 union node n;
11612
11613 /* XXX Fix (char *) cast. */
11614 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011615 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011616 popfile();
11617
11618 n.narg.type = NARG;
11619 n.narg.next = NULL;
11620 n.narg.text = wordtext;
11621 n.narg.backquote = backquotelist;
11622
11623 expandarg(&n, NULL, 0);
11624 return stackblock();
11625}
11626#endif
11627
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011628/*
11629 * Execute a command or commands contained in a string.
11630 */
11631static int
11632evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011633{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011634 union node *n;
11635 struct stackmark smark;
11636 int skip;
11637
11638 setinputstring(s);
11639 setstackmark(&smark);
11640
11641 skip = 0;
11642 while ((n = parsecmd(0)) != NEOF) {
11643 evaltree(n, 0);
11644 popstackmark(&smark);
11645 skip = evalskip;
11646 if (skip)
11647 break;
11648 }
11649 popfile();
11650
11651 skip &= mask;
11652 evalskip = skip;
11653 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011654}
11655
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011656/*
11657 * The eval command.
11658 */
11659static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011660evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011661{
11662 char *p;
11663 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011664
Denis Vlasenko68404f12008-03-17 09:00:54 +000011665 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011666 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011667 argv += 2;
11668 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011669 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011670 for (;;) {
11671 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011672 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011673 if (p == NULL)
11674 break;
11675 STPUTC(' ', concat);
11676 }
11677 STPUTC('\0', concat);
11678 p = grabstackstr(concat);
11679 }
11680 evalstring(p, ~SKIPEVAL);
11681
11682 }
11683 return exitstatus;
11684}
11685
11686/*
11687 * Read and execute commands. "Top" is nonzero for the top level command
11688 * loop; it turns on prompting if the shell is interactive.
11689 */
11690static int
11691cmdloop(int top)
11692{
11693 union node *n;
11694 struct stackmark smark;
11695 int inter;
11696 int numeof = 0;
11697
11698 TRACE(("cmdloop(%d) called\n", top));
11699 for (;;) {
11700 int skip;
11701
11702 setstackmark(&smark);
11703#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011704 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011705 showjobs(stderr, SHOW_CHANGED);
11706#endif
11707 inter = 0;
11708 if (iflag && top) {
11709 inter++;
11710#if ENABLE_ASH_MAIL
11711 chkmail();
11712#endif
11713 }
11714 n = parsecmd(inter);
11715 /* showtree(n); DEBUG */
11716 if (n == NEOF) {
11717 if (!top || numeof >= 50)
11718 break;
11719 if (!stoppedjobs()) {
11720 if (!Iflag)
11721 break;
11722 out2str("\nUse \"exit\" to leave shell.\n");
11723 }
11724 numeof++;
11725 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011726 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11727 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011728 numeof = 0;
11729 evaltree(n, 0);
11730 }
11731 popstackmark(&smark);
11732 skip = evalskip;
11733
11734 if (skip) {
11735 evalskip = 0;
11736 return skip & SKIPEVAL;
11737 }
11738 }
11739 return 0;
11740}
11741
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011742/*
11743 * Take commands from a file. To be compatible we should do a path
11744 * search for the file, which is necessary to find sub-commands.
11745 */
11746static char *
11747find_dot_file(char *name)
11748{
11749 char *fullname;
11750 const char *path = pathval();
11751 struct stat statb;
11752
11753 /* don't try this for absolute or relative paths */
11754 if (strchr(name, '/'))
11755 return name;
11756
11757 while ((fullname = padvance(&path, name)) != NULL) {
11758 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11759 /*
11760 * Don't bother freeing here, since it will
11761 * be freed by the caller.
11762 */
11763 return fullname;
11764 }
11765 stunalloc(fullname);
11766 }
11767
11768 /* not found in the PATH */
11769 ash_msg_and_raise_error("%s: not found", name);
11770 /* NOTREACHED */
11771}
11772
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011773static int
11774dotcmd(int argc, char **argv)
11775{
11776 struct strlist *sp;
11777 volatile struct shparam saveparam;
11778 int status = 0;
11779
11780 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011781 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011782
Denis Vlasenko68404f12008-03-17 09:00:54 +000011783 if (argv[1]) { /* That's what SVR2 does */
11784 char *fullname = find_dot_file(argv[1]);
11785 argv += 2;
11786 argc -= 2;
11787 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011788 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011789 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011790 shellparam.nparam = argc;
11791 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011792 };
11793
11794 setinputfile(fullname, INPUT_PUSH_FILE);
11795 commandname = fullname;
11796 cmdloop(0);
11797 popfile();
11798
Denis Vlasenko68404f12008-03-17 09:00:54 +000011799 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011800 freeparam(&shellparam);
11801 shellparam = saveparam;
11802 };
11803 status = exitstatus;
11804 }
11805 return status;
11806}
11807
11808static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011809exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011810{
11811 if (stoppedjobs())
11812 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011813 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011814 exitstatus = number(argv[1]);
11815 raise_exception(EXEXIT);
11816 /* NOTREACHED */
11817}
11818
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011819/*
11820 * Read a file containing shell functions.
11821 */
11822static void
11823readcmdfile(char *name)
11824{
11825 setinputfile(name, INPUT_PUSH_FILE);
11826 cmdloop(0);
11827 popfile();
11828}
11829
11830
Denis Vlasenkocc571512007-02-23 21:10:35 +000011831/* ============ find_command inplementation */
11832
11833/*
11834 * Resolve a command name. If you change this routine, you may have to
11835 * change the shellexec routine as well.
11836 */
11837static void
11838find_command(char *name, struct cmdentry *entry, int act, const char *path)
11839{
11840 struct tblentry *cmdp;
11841 int idx;
11842 int prev;
11843 char *fullname;
11844 struct stat statb;
11845 int e;
11846 int updatetbl;
11847 struct builtincmd *bcmd;
11848
11849 /* If name contains a slash, don't use PATH or hash table */
11850 if (strchr(name, '/') != NULL) {
11851 entry->u.index = -1;
11852 if (act & DO_ABS) {
11853 while (stat(name, &statb) < 0) {
11854#ifdef SYSV
11855 if (errno == EINTR)
11856 continue;
11857#endif
11858 entry->cmdtype = CMDUNKNOWN;
11859 return;
11860 }
11861 }
11862 entry->cmdtype = CMDNORMAL;
11863 return;
11864 }
11865
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011866/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011867
11868 updatetbl = (path == pathval());
11869 if (!updatetbl) {
11870 act |= DO_ALTPATH;
11871 if (strstr(path, "%builtin") != NULL)
11872 act |= DO_ALTBLTIN;
11873 }
11874
11875 /* If name is in the table, check answer will be ok */
11876 cmdp = cmdlookup(name, 0);
11877 if (cmdp != NULL) {
11878 int bit;
11879
11880 switch (cmdp->cmdtype) {
11881 default:
11882#if DEBUG
11883 abort();
11884#endif
11885 case CMDNORMAL:
11886 bit = DO_ALTPATH;
11887 break;
11888 case CMDFUNCTION:
11889 bit = DO_NOFUNC;
11890 break;
11891 case CMDBUILTIN:
11892 bit = DO_ALTBLTIN;
11893 break;
11894 }
11895 if (act & bit) {
11896 updatetbl = 0;
11897 cmdp = NULL;
11898 } else if (cmdp->rehash == 0)
11899 /* if not invalidated by cd, we're done */
11900 goto success;
11901 }
11902
11903 /* If %builtin not in path, check for builtin next */
11904 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011905 if (bcmd) {
11906 if (IS_BUILTIN_REGULAR(bcmd))
11907 goto builtin_success;
11908 if (act & DO_ALTPATH) {
11909 if (!(act & DO_ALTBLTIN))
11910 goto builtin_success;
11911 } else if (builtinloc <= 0) {
11912 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011913 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011914 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011915
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011916#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011917 {
11918 int applet_no = find_applet_by_name(name);
11919 if (applet_no >= 0) {
11920 entry->cmdtype = CMDNORMAL;
11921 entry->u.index = -2 - applet_no;
11922 return;
11923 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011924 }
11925#endif
11926
Denis Vlasenkocc571512007-02-23 21:10:35 +000011927 /* We have to search path. */
11928 prev = -1; /* where to start */
11929 if (cmdp && cmdp->rehash) { /* doing a rehash */
11930 if (cmdp->cmdtype == CMDBUILTIN)
11931 prev = builtinloc;
11932 else
11933 prev = cmdp->param.index;
11934 }
11935
11936 e = ENOENT;
11937 idx = -1;
11938 loop:
11939 while ((fullname = padvance(&path, name)) != NULL) {
11940 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011941 /* NB: code below will still use fullname
11942 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011943 idx++;
11944 if (pathopt) {
11945 if (prefix(pathopt, "builtin")) {
11946 if (bcmd)
11947 goto builtin_success;
11948 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011949 }
11950 if ((act & DO_NOFUNC)
11951 || !prefix(pathopt, "func")
11952 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011953 continue;
11954 }
11955 }
11956 /* if rehash, don't redo absolute path names */
11957 if (fullname[0] == '/' && idx <= prev) {
11958 if (idx < prev)
11959 continue;
11960 TRACE(("searchexec \"%s\": no change\n", name));
11961 goto success;
11962 }
11963 while (stat(fullname, &statb) < 0) {
11964#ifdef SYSV
11965 if (errno == EINTR)
11966 continue;
11967#endif
11968 if (errno != ENOENT && errno != ENOTDIR)
11969 e = errno;
11970 goto loop;
11971 }
11972 e = EACCES; /* if we fail, this will be the error */
11973 if (!S_ISREG(statb.st_mode))
11974 continue;
11975 if (pathopt) { /* this is a %func directory */
11976 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011977 /* NB: stalloc will return space pointed by fullname
11978 * (because we don't have any intervening allocations
11979 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011980 readcmdfile(fullname);
11981 cmdp = cmdlookup(name, 0);
11982 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11983 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11984 stunalloc(fullname);
11985 goto success;
11986 }
11987 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11988 if (!updatetbl) {
11989 entry->cmdtype = CMDNORMAL;
11990 entry->u.index = idx;
11991 return;
11992 }
11993 INT_OFF;
11994 cmdp = cmdlookup(name, 1);
11995 cmdp->cmdtype = CMDNORMAL;
11996 cmdp->param.index = idx;
11997 INT_ON;
11998 goto success;
11999 }
12000
12001 /* We failed. If there was an entry for this command, delete it */
12002 if (cmdp && updatetbl)
12003 delete_cmd_entry();
12004 if (act & DO_ERR)
12005 ash_msg("%s: %s", name, errmsg(e, "not found"));
12006 entry->cmdtype = CMDUNKNOWN;
12007 return;
12008
12009 builtin_success:
12010 if (!updatetbl) {
12011 entry->cmdtype = CMDBUILTIN;
12012 entry->u.cmd = bcmd;
12013 return;
12014 }
12015 INT_OFF;
12016 cmdp = cmdlookup(name, 1);
12017 cmdp->cmdtype = CMDBUILTIN;
12018 cmdp->param.cmd = bcmd;
12019 INT_ON;
12020 success:
12021 cmdp->rehash = 0;
12022 entry->cmdtype = cmdp->cmdtype;
12023 entry->u = cmdp->param;
12024}
12025
12026
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012027/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012028
Eric Andersencb57d552001-06-28 07:25:16 +000012029/*
Eric Andersencb57d552001-06-28 07:25:16 +000012030 * The trap builtin.
12031 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012032static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012033trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012034{
12035 char *action;
12036 char **ap;
12037 int signo;
12038
Eric Andersenc470f442003-07-28 09:56:35 +000012039 nextopt(nullstr);
12040 ap = argptr;
12041 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012042 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012043 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012044 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012045 single_quote(trap[signo]),
12046 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012047 }
12048 }
12049 return 0;
12050 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012051 action = NULL;
12052 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012053 action = *ap++;
12054 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012055 signo = get_signum(*ap);
12056 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012057 ash_msg_and_raise_error("%s: bad trap", *ap);
12058 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012059 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012060 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012061 action = NULL;
12062 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012063 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012064 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012065 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012066 trap[signo] = action;
12067 if (signo != 0)
12068 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012069 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012070 ap++;
12071 }
12072 return 0;
12073}
12074
Eric Andersenc470f442003-07-28 09:56:35 +000012075
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012076/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012077
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012078#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012079/*
12080 * Lists available builtins
12081 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012082static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012083helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012084{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012085 unsigned col;
12086 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012087
12088 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012089 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012090 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012091 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012092 if (col > 60) {
12093 out1fmt("\n");
12094 col = 0;
12095 }
12096 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012097#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012098 {
12099 const char *a = applet_names;
12100 while (*a) {
12101 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12102 if (col > 60) {
12103 out1fmt("\n");
12104 col = 0;
12105 }
12106 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012107 }
12108 }
12109#endif
12110 out1fmt("\n\n");
12111 return EXIT_SUCCESS;
12112}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012113#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012114
Eric Andersencb57d552001-06-28 07:25:16 +000012115/*
Eric Andersencb57d552001-06-28 07:25:16 +000012116 * The export and readonly commands.
12117 */
Eric Andersenc470f442003-07-28 09:56:35 +000012118static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012119exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012120{
12121 struct var *vp;
12122 char *name;
12123 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012124 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012125 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012126
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012127 if (nextopt("p") != 'p') {
12128 aptr = argptr;
12129 name = *aptr;
12130 if (name) {
12131 do {
12132 p = strchr(name, '=');
12133 if (p != NULL) {
12134 p++;
12135 } else {
12136 vp = *findvar(hashvar(name), name);
12137 if (vp) {
12138 vp->flags |= flag;
12139 continue;
12140 }
Eric Andersencb57d552001-06-28 07:25:16 +000012141 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012142 setvar(name, p, flag);
12143 } while ((name = *++aptr) != NULL);
12144 return 0;
12145 }
Eric Andersencb57d552001-06-28 07:25:16 +000012146 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012147 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012148 return 0;
12149}
12150
Eric Andersencb57d552001-06-28 07:25:16 +000012151/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012152 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012153 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012154static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012155unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012156{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012157 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012158
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012159 cmdp = cmdlookup(name, 0);
12160 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12161 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012162}
12163
Eric Andersencb57d552001-06-28 07:25:16 +000012164/*
Eric Andersencb57d552001-06-28 07:25:16 +000012165 * The unset builtin command. We unset the function before we unset the
12166 * variable to allow a function to be unset when there is a readonly variable
12167 * with the same name.
12168 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012169static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012170unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012171{
12172 char **ap;
12173 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012174 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012175 int ret = 0;
12176
12177 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012178 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012179 }
Eric Andersencb57d552001-06-28 07:25:16 +000012180
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012181 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012182 if (flag != 'f') {
12183 i = unsetvar(*ap);
12184 ret |= i;
12185 if (!(i & 2))
12186 continue;
12187 }
12188 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012189 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012190 }
Eric Andersenc470f442003-07-28 09:56:35 +000012191 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012192}
12193
12194
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012195/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012196
Eric Andersenc470f442003-07-28 09:56:35 +000012197#include <sys/times.h>
12198
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012199static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012200 ' ', offsetof(struct tms, tms_utime),
12201 '\n', offsetof(struct tms, tms_stime),
12202 ' ', offsetof(struct tms, tms_cutime),
12203 '\n', offsetof(struct tms, tms_cstime),
12204 0
12205};
Eric Andersencb57d552001-06-28 07:25:16 +000012206
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012207static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012208timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012209{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012210 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012211 const unsigned char *p;
12212 struct tms buf;
12213
12214 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012215 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012216
12217 p = timescmd_str;
12218 do {
12219 t = *(clock_t *)(((char *) &buf) + p[1]);
12220 s = t / clk_tck;
12221 out1fmt("%ldm%ld.%.3lds%c",
12222 s/60, s%60,
12223 ((t - s * clk_tck) * 1000) / clk_tck,
12224 p[0]);
12225 } while (*(p += 2));
12226
Eric Andersencb57d552001-06-28 07:25:16 +000012227 return 0;
12228}
12229
Denis Vlasenko131ae172007-02-18 13:00:19 +000012230#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012231static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012232dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012233{
Eric Andersened9ecf72004-06-22 08:29:45 +000012234 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012235 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012236
Denis Vlasenkob012b102007-02-19 22:43:01 +000012237 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012238 result = arith(s, &errcode);
12239 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012240 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012241 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012242 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012243 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012244 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012245 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012246 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012247 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012248 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012249
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012250 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012251}
Eric Andersenc470f442003-07-28 09:56:35 +000012252
Eric Andersenc470f442003-07-28 09:56:35 +000012253/*
Eric Andersen90898442003-08-06 11:20:52 +000012254 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12255 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12256 *
12257 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012258 */
12259static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012260letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012261{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012262 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012263
Denis Vlasenko68404f12008-03-17 09:00:54 +000012264 argv++;
12265 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012266 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012267 do {
12268 i = dash_arith(*argv);
12269 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012270
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012271 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012272}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012273#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012274
Eric Andersenc470f442003-07-28 09:56:35 +000012275
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012276/* ============ miscbltin.c
12277 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012278 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012279 */
12280
12281#undef rflag
12282
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012283#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012284typedef enum __rlimit_resource rlim_t;
12285#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012286
Eric Andersenc470f442003-07-28 09:56:35 +000012287/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012288 * The read builtin. Options:
12289 * -r Do not interpret '\' specially
12290 * -s Turn off echo (tty only)
12291 * -n NCHARS Read NCHARS max
12292 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12293 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12294 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012295 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012296 * TODO: bash also has:
12297 * -a ARRAY Read into array[0],[1],etc
12298 * -d DELIM End on DELIM char, not newline
12299 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012300 */
Eric Andersenc470f442003-07-28 09:56:35 +000012301static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012302readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012303{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012304 static const char *const arg_REPLY[] = { "REPLY", NULL };
12305
Eric Andersenc470f442003-07-28 09:56:35 +000012306 char **ap;
12307 int backslash;
12308 char c;
12309 int rflag;
12310 char *prompt;
12311 const char *ifs;
12312 char *p;
12313 int startword;
12314 int status;
12315 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012316 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012317#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012318 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012319 int silent = 0;
12320 struct termios tty, old_tty;
12321#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012322#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012323 unsigned end_ms = 0;
12324 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012325#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012326
12327 rflag = 0;
12328 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012329 while ((i = nextopt("p:u:r"
12330 USE_ASH_READ_TIMEOUT("t:")
12331 USE_ASH_READ_NCHARS("n:s")
12332 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012333 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012334 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012335 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012336 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012337#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012338 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012339 nchars = bb_strtou(optionarg, NULL, 10);
12340 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012341 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012342 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012343 break;
12344 case 's':
12345 silent = 1;
12346 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012347#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012348#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012349 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012350 timeout = bb_strtou(optionarg, NULL, 10);
12351 if (errno || timeout > UINT_MAX / 2048)
12352 ash_msg_and_raise_error("invalid timeout");
12353 timeout *= 1000;
12354#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012355 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012356 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012357 /* EINVAL means number is ok, but not terminated by NUL */
12358 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012359 char *p2;
12360 if (*++p) {
12361 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012362 ts.tv_usec = bb_strtou(p, &p2, 10);
12363 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012364 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012365 scale = p2 - p;
12366 /* normalize to usec */
12367 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012368 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012369 while (scale++ < 6)
12370 ts.tv_usec *= 10;
12371 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012372 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012373 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012374 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012375 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012376 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012377 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012378#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012379 break;
12380#endif
12381 case 'r':
12382 rflag = 1;
12383 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012384 case 'u':
12385 fd = bb_strtou(optionarg, NULL, 10);
12386 if (fd < 0 || errno)
12387 ash_msg_and_raise_error("invalid file descriptor");
12388 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012389 default:
12390 break;
12391 }
Eric Andersenc470f442003-07-28 09:56:35 +000012392 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012393 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012394 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012395 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012396 ap = argptr;
12397 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012398 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012399 ifs = bltinlookup("IFS");
12400 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012401 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012402#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012403 tcgetattr(fd, &tty);
12404 old_tty = tty;
12405 if (nchars || silent) {
12406 if (nchars) {
12407 tty.c_lflag &= ~ICANON;
12408 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012409 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012410 if (silent) {
12411 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12412 }
12413 /* if tcgetattr failed, tcsetattr will fail too.
12414 * Ignoring, it's harmless. */
12415 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012416 }
12417#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012418
Eric Andersenc470f442003-07-28 09:56:35 +000012419 status = 0;
12420 startword = 1;
12421 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012422#if ENABLE_ASH_READ_TIMEOUT
12423 if (timeout) /* NB: ensuring end_ms is nonzero */
12424 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12425#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012426 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012427 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012428#if ENABLE_ASH_READ_TIMEOUT
12429 if (end_ms) {
12430 struct pollfd pfd[1];
12431 pfd[0].fd = fd;
12432 pfd[0].events = POLLIN;
12433 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12434 if ((int)timeout <= 0 /* already late? */
12435 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12436 ) { /* timed out! */
12437#if ENABLE_ASH_READ_NCHARS
12438 tcsetattr(fd, TCSANOW, &old_tty);
12439#endif
12440 return 1;
12441 }
12442 }
12443#endif
12444 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012445 status = 1;
12446 break;
12447 }
12448 if (c == '\0')
12449 continue;
12450 if (backslash) {
12451 backslash = 0;
12452 if (c != '\n')
12453 goto put;
12454 continue;
12455 }
12456 if (!rflag && c == '\\') {
12457 backslash++;
12458 continue;
12459 }
12460 if (c == '\n')
12461 break;
12462 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12463 continue;
12464 }
12465 startword = 0;
12466 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12467 STACKSTRNUL(p);
12468 setvar(*ap, stackblock(), 0);
12469 ap++;
12470 startword = 1;
12471 STARTSTACKSTR(p);
12472 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012473 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012474 STPUTC(c, p);
12475 }
12476 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012477/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012478#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012479 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012480#else
12481 while (1);
12482#endif
12483
12484#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012485 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012486#endif
12487
Eric Andersenc470f442003-07-28 09:56:35 +000012488 STACKSTRNUL(p);
12489 /* Remove trailing blanks */
12490 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12491 *p = '\0';
12492 setvar(*ap, stackblock(), 0);
12493 while (*++ap != NULL)
12494 setvar(*ap, nullstr, 0);
12495 return status;
12496}
12497
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012498static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012499umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012500{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012501 static const char permuser[3] ALIGN1 = "ugo";
12502 static const char permmode[3] ALIGN1 = "rwx";
12503 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012504 S_IRUSR, S_IWUSR, S_IXUSR,
12505 S_IRGRP, S_IWGRP, S_IXGRP,
12506 S_IROTH, S_IWOTH, S_IXOTH
12507 };
12508
12509 char *ap;
12510 mode_t mask;
12511 int i;
12512 int symbolic_mode = 0;
12513
12514 while (nextopt("S") != '\0') {
12515 symbolic_mode = 1;
12516 }
12517
Denis Vlasenkob012b102007-02-19 22:43:01 +000012518 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012519 mask = umask(0);
12520 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012521 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012522
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012523 ap = *argptr;
12524 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012525 if (symbolic_mode) {
12526 char buf[18];
12527 char *p = buf;
12528
12529 for (i = 0; i < 3; i++) {
12530 int j;
12531
12532 *p++ = permuser[i];
12533 *p++ = '=';
12534 for (j = 0; j < 3; j++) {
12535 if ((mask & permmask[3 * i + j]) == 0) {
12536 *p++ = permmode[j];
12537 }
12538 }
12539 *p++ = ',';
12540 }
12541 *--p = 0;
12542 puts(buf);
12543 } else {
12544 out1fmt("%.4o\n", mask);
12545 }
12546 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012547 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012548 mask = 0;
12549 do {
12550 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012551 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012552 mask = (mask << 3) + (*ap - '0');
12553 } while (*++ap != '\0');
12554 umask(mask);
12555 } else {
12556 mask = ~mask & 0777;
12557 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012558 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012559 }
12560 umask(~mask & 0777);
12561 }
12562 }
12563 return 0;
12564}
12565
12566/*
12567 * ulimit builtin
12568 *
12569 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12570 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12571 * ash by J.T. Conklin.
12572 *
12573 * Public domain.
12574 */
12575
12576struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012577 uint8_t cmd; /* RLIMIT_xxx fit into it */
12578 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012579 char option;
12580};
12581
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012582static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012583#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012584 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012585#endif
12586#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012587 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012588#endif
12589#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012590 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012591#endif
12592#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012593 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012594#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012595#ifdef RLIMIT_CORE
12596 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012597#endif
12598#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012599 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012600#endif
12601#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012602 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012603#endif
12604#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012605 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012606#endif
12607#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012608 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012609#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012610#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012611 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012612#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012613#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012614 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012615#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012616};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012617static const char limits_name[] =
12618#ifdef RLIMIT_CPU
12619 "time(seconds)" "\0"
12620#endif
12621#ifdef RLIMIT_FSIZE
12622 "file(blocks)" "\0"
12623#endif
12624#ifdef RLIMIT_DATA
12625 "data(kb)" "\0"
12626#endif
12627#ifdef RLIMIT_STACK
12628 "stack(kb)" "\0"
12629#endif
12630#ifdef RLIMIT_CORE
12631 "coredump(blocks)" "\0"
12632#endif
12633#ifdef RLIMIT_RSS
12634 "memory(kb)" "\0"
12635#endif
12636#ifdef RLIMIT_MEMLOCK
12637 "locked memory(kb)" "\0"
12638#endif
12639#ifdef RLIMIT_NPROC
12640 "process" "\0"
12641#endif
12642#ifdef RLIMIT_NOFILE
12643 "nofiles" "\0"
12644#endif
12645#ifdef RLIMIT_AS
12646 "vmemory(kb)" "\0"
12647#endif
12648#ifdef RLIMIT_LOCKS
12649 "locks" "\0"
12650#endif
12651;
Eric Andersenc470f442003-07-28 09:56:35 +000012652
Glenn L McGrath76620622004-01-13 10:19:37 +000012653enum limtype { SOFT = 0x1, HARD = 0x2 };
12654
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012655static void
12656printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012657 const struct limits *l)
12658{
12659 rlim_t val;
12660
12661 val = limit->rlim_max;
12662 if (how & SOFT)
12663 val = limit->rlim_cur;
12664
12665 if (val == RLIM_INFINITY)
12666 out1fmt("unlimited\n");
12667 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012668 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012669 out1fmt("%lld\n", (long long) val);
12670 }
12671}
12672
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012673static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012674ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012675{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012676 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012677 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012678 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012679 const struct limits *l;
12680 int set, all = 0;
12681 int optc, what;
12682 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012683
12684 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012685 while ((optc = nextopt("HSa"
12686#ifdef RLIMIT_CPU
12687 "t"
12688#endif
12689#ifdef RLIMIT_FSIZE
12690 "f"
12691#endif
12692#ifdef RLIMIT_DATA
12693 "d"
12694#endif
12695#ifdef RLIMIT_STACK
12696 "s"
12697#endif
12698#ifdef RLIMIT_CORE
12699 "c"
12700#endif
12701#ifdef RLIMIT_RSS
12702 "m"
12703#endif
12704#ifdef RLIMIT_MEMLOCK
12705 "l"
12706#endif
12707#ifdef RLIMIT_NPROC
12708 "p"
12709#endif
12710#ifdef RLIMIT_NOFILE
12711 "n"
12712#endif
12713#ifdef RLIMIT_AS
12714 "v"
12715#endif
12716#ifdef RLIMIT_LOCKS
12717 "w"
12718#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012719 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012720 switch (optc) {
12721 case 'H':
12722 how = HARD;
12723 break;
12724 case 'S':
12725 how = SOFT;
12726 break;
12727 case 'a':
12728 all = 1;
12729 break;
12730 default:
12731 what = optc;
12732 }
12733
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012734 for (l = limits_tbl; l->option != what; l++)
12735 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012736
12737 set = *argptr ? 1 : 0;
12738 if (set) {
12739 char *p = *argptr;
12740
12741 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012742 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012743 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012744 val = RLIM_INFINITY;
12745 else {
12746 val = (rlim_t) 0;
12747
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012748 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012749 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012750 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012751 if (val < (rlim_t) 0)
12752 break;
12753 }
12754 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012755 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012756 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012757 }
12758 }
12759 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012760 const char *lname = limits_name;
12761 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012762 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012763 out1fmt("%-20s ", lname);
12764 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012765 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012766 }
12767 return 0;
12768 }
12769
12770 getrlimit(l->cmd, &limit);
12771 if (set) {
12772 if (how & HARD)
12773 limit.rlim_max = val;
12774 if (how & SOFT)
12775 limit.rlim_cur = val;
12776 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012777 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012778 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012779 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012780 }
12781 return 0;
12782}
12783
Eric Andersen90898442003-08-06 11:20:52 +000012784
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012785/* ============ Math support */
12786
Denis Vlasenko131ae172007-02-18 13:00:19 +000012787#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012788
12789/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12790
12791 Permission is hereby granted, free of charge, to any person obtaining
12792 a copy of this software and associated documentation files (the
12793 "Software"), to deal in the Software without restriction, including
12794 without limitation the rights to use, copy, modify, merge, publish,
12795 distribute, sublicense, and/or sell copies of the Software, and to
12796 permit persons to whom the Software is furnished to do so, subject to
12797 the following conditions:
12798
12799 The above copyright notice and this permission notice shall be
12800 included in all copies or substantial portions of the Software.
12801
12802 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12803 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12804 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12805 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12806 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12807 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12808 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12809*/
12810
12811/* This is my infix parser/evaluator. It is optimized for size, intended
12812 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012813 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012814 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012815 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012816 * be that which POSIX specifies for shells. */
12817
12818/* The code uses a simple two-stack algorithm. See
12819 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012820 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012821 * this is based (this code differs in that it applies operators immediately
12822 * to the stack instead of adding them to a queue to end up with an
12823 * expression). */
12824
12825/* To use the routine, call it with an expression string and error return
12826 * pointer */
12827
12828/*
12829 * Aug 24, 2001 Manuel Novoa III
12830 *
12831 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12832 *
12833 * 1) In arith_apply():
12834 * a) Cached values of *numptr and &(numptr[-1]).
12835 * b) Removed redundant test for zero denominator.
12836 *
12837 * 2) In arith():
12838 * a) Eliminated redundant code for processing operator tokens by moving
12839 * to a table-based implementation. Also folded handling of parens
12840 * into the table.
12841 * b) Combined all 3 loops which called arith_apply to reduce generated
12842 * code size at the cost of speed.
12843 *
12844 * 3) The following expressions were treated as valid by the original code:
12845 * 1() , 0! , 1 ( *3 ) .
12846 * These bugs have been fixed by internally enclosing the expression in
12847 * parens and then checking that all binary ops and right parens are
12848 * preceded by a valid expression (NUM_TOKEN).
12849 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012850 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012851 * ctype's isspace() if it is used by another busybox applet or if additional
12852 * whitespace chars should be considered. Look below the "#include"s for a
12853 * precompiler test.
12854 */
12855
12856/*
12857 * Aug 26, 2001 Manuel Novoa III
12858 *
12859 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12860 *
12861 * Merge in Aaron's comments previously posted to the busybox list,
12862 * modified slightly to take account of my changes to the code.
12863 *
12864 */
12865
12866/*
12867 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12868 *
12869 * - allow access to variable,
12870 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12871 * - realize assign syntax (VAR=expr, +=, *= etc)
12872 * - realize exponentiation (** operator)
12873 * - realize comma separated - expr, expr
12874 * - realise ++expr --expr expr++ expr--
12875 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012876 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012877 * - was restored loses XOR operator
12878 * - remove one goto label, added three ;-)
12879 * - protect $((num num)) as true zero expr (Manuel`s error)
12880 * - always use special isspace(), see comment from bash ;-)
12881 */
12882
Eric Andersen90898442003-08-06 11:20:52 +000012883#define arith_isspace(arithval) \
12884 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12885
Eric Andersen90898442003-08-06 11:20:52 +000012886typedef unsigned char operator;
12887
12888/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012889 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012890 * precedence. The ID portion is so that multiple operators can have the
12891 * same precedence, ensuring that the leftmost one is evaluated first.
12892 * Consider * and /. */
12893
12894#define tok_decl(prec,id) (((id)<<5)|(prec))
12895#define PREC(op) ((op) & 0x1F)
12896
12897#define TOK_LPAREN tok_decl(0,0)
12898
12899#define TOK_COMMA tok_decl(1,0)
12900
12901#define TOK_ASSIGN tok_decl(2,0)
12902#define TOK_AND_ASSIGN tok_decl(2,1)
12903#define TOK_OR_ASSIGN tok_decl(2,2)
12904#define TOK_XOR_ASSIGN tok_decl(2,3)
12905#define TOK_PLUS_ASSIGN tok_decl(2,4)
12906#define TOK_MINUS_ASSIGN tok_decl(2,5)
12907#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12908#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12909
12910#define TOK_MUL_ASSIGN tok_decl(3,0)
12911#define TOK_DIV_ASSIGN tok_decl(3,1)
12912#define TOK_REM_ASSIGN tok_decl(3,2)
12913
12914/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012915#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012916
12917/* conditional is right associativity too */
12918#define TOK_CONDITIONAL tok_decl(4,0)
12919#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12920
12921#define TOK_OR tok_decl(5,0)
12922
12923#define TOK_AND tok_decl(6,0)
12924
12925#define TOK_BOR tok_decl(7,0)
12926
12927#define TOK_BXOR tok_decl(8,0)
12928
12929#define TOK_BAND tok_decl(9,0)
12930
12931#define TOK_EQ tok_decl(10,0)
12932#define TOK_NE tok_decl(10,1)
12933
12934#define TOK_LT tok_decl(11,0)
12935#define TOK_GT tok_decl(11,1)
12936#define TOK_GE tok_decl(11,2)
12937#define TOK_LE tok_decl(11,3)
12938
12939#define TOK_LSHIFT tok_decl(12,0)
12940#define TOK_RSHIFT tok_decl(12,1)
12941
12942#define TOK_ADD tok_decl(13,0)
12943#define TOK_SUB tok_decl(13,1)
12944
12945#define TOK_MUL tok_decl(14,0)
12946#define TOK_DIV tok_decl(14,1)
12947#define TOK_REM tok_decl(14,2)
12948
12949/* exponent is right associativity */
12950#define TOK_EXPONENT tok_decl(15,1)
12951
12952/* For now unary operators. */
12953#define UNARYPREC 16
12954#define TOK_BNOT tok_decl(UNARYPREC,0)
12955#define TOK_NOT tok_decl(UNARYPREC,1)
12956
12957#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12958#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12959
12960#define PREC_PRE (UNARYPREC+2)
12961
12962#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12963#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12964
12965#define PREC_POST (UNARYPREC+3)
12966
12967#define TOK_POST_INC tok_decl(PREC_POST, 0)
12968#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12969
12970#define SPEC_PREC (UNARYPREC+4)
12971
12972#define TOK_NUM tok_decl(SPEC_PREC, 0)
12973#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12974
12975#define NUMPTR (*numstackptr)
12976
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012977static int
12978tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012979{
12980 operator prec = PREC(op);
12981
12982 convert_prec_is_assing(prec);
12983 return (prec == PREC(TOK_ASSIGN) ||
12984 prec == PREC_PRE || prec == PREC_POST);
12985}
12986
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012987static int
12988is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012989{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012990 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12991 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012992}
12993
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012994typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012995 arith_t val;
12996 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012997 char contidional_second_val_initialized;
12998 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012999 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013000} v_n_t;
13001
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013002typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013003 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013004 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013005} chk_var_recursive_looped_t;
13006
13007static chk_var_recursive_looped_t *prev_chk_var_recursive;
13008
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013009static int
13010arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013011{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013012 if (t->var) {
13013 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013014
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013015 if (p) {
13016 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013017
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013018 /* recursive try as expression */
13019 chk_var_recursive_looped_t *cur;
13020 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013021
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013022 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13023 if (strcmp(cur->var, t->var) == 0) {
13024 /* expression recursion loop detected */
13025 return -5;
13026 }
13027 }
13028 /* save current lookuped var name */
13029 cur = prev_chk_var_recursive;
13030 cur_save.var = t->var;
13031 cur_save.next = cur;
13032 prev_chk_var_recursive = &cur_save;
13033
13034 t->val = arith (p, &errcode);
13035 /* restore previous ptr after recursiving */
13036 prev_chk_var_recursive = cur;
13037 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013038 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013039 /* allow undefined var as 0 */
13040 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013041 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013042 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013043}
13044
13045/* "applying" a token means performing it on the top elements on the integer
13046 * stack. For a unary operator it will only change the top element, but a
13047 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013048static int
13049arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013050{
Eric Andersen90898442003-08-06 11:20:52 +000013051 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013052 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013053 int ret_arith_lookup_val;
13054
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013055 /* There is no operator that can work without arguments */
13056 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013057 numptr_m1 = NUMPTR - 1;
13058
13059 /* check operand is var with noninteger value */
13060 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013061 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013062 return ret_arith_lookup_val;
13063
13064 rez = numptr_m1->val;
13065 if (op == TOK_UMINUS)
13066 rez *= -1;
13067 else if (op == TOK_NOT)
13068 rez = !rez;
13069 else if (op == TOK_BNOT)
13070 rez = ~rez;
13071 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13072 rez++;
13073 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13074 rez--;
13075 else if (op != TOK_UPLUS) {
13076 /* Binary operators */
13077
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013078 /* check and binary operators need two arguments */
13079 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013080
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 /* ... and they pop one */
13082 --NUMPTR;
13083 numptr_val = rez;
13084 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013085 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 /* protect $((expr1 ? expr2)) without ": expr" */
13087 goto err;
13088 }
13089 rez = numptr_m1->contidional_second_val;
13090 } else if (numptr_m1->contidional_second_val_initialized) {
13091 /* protect $((expr1 : expr2)) without "expr ? " */
13092 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013093 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 numptr_m1 = NUMPTR - 1;
13095 if (op != TOK_ASSIGN) {
13096 /* check operand is var with noninteger value for not '=' */
13097 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13098 if (ret_arith_lookup_val)
13099 return ret_arith_lookup_val;
13100 }
13101 if (op == TOK_CONDITIONAL) {
13102 numptr_m1->contidional_second_val = rez;
13103 }
13104 rez = numptr_m1->val;
13105 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013106 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013107 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013108 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013109 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013110 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013112 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013113 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013114 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013115 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013116 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013117 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013118 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013119 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013120 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013121 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013122 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013124 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013125 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013126 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013127 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013128 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013129 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013130 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013131 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013132 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013133 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013134 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013135 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013136 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013138 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013139 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013140 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013141 /* protect $((expr : expr)) without "expr ? " */
13142 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013143 }
13144 numptr_m1->contidional_second_val_initialized = op;
13145 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013146 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013147 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013148 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013149 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013150 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013151 return -3; /* exponent less than 0 */
13152 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013153 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013154
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013155 if (numptr_val)
13156 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013157 c *= rez;
13158 rez = c;
13159 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013160 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013161 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013162 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013163 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013164 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013165 rez %= numptr_val;
13166 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013167 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013168 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013169
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013170 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013171 /* Hmm, 1=2 ? */
13172 goto err;
13173 }
13174 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013175#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013176 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013177#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013178 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013179#endif
Eric Andersen90898442003-08-06 11:20:52 +000013180 setvar(numptr_m1->var, buf, 0);
13181 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013182 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013183 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013184 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013185 rez++;
13186 }
13187 numptr_m1->val = rez;
13188 /* protect geting var value, is number now */
13189 numptr_m1->var = NULL;
13190 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013191 err:
13192 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013193}
13194
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013195/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013196static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013197 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13198 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13199 '<','<', 0, TOK_LSHIFT,
13200 '>','>', 0, TOK_RSHIFT,
13201 '|','|', 0, TOK_OR,
13202 '&','&', 0, TOK_AND,
13203 '!','=', 0, TOK_NE,
13204 '<','=', 0, TOK_LE,
13205 '>','=', 0, TOK_GE,
13206 '=','=', 0, TOK_EQ,
13207 '|','=', 0, TOK_OR_ASSIGN,
13208 '&','=', 0, TOK_AND_ASSIGN,
13209 '*','=', 0, TOK_MUL_ASSIGN,
13210 '/','=', 0, TOK_DIV_ASSIGN,
13211 '%','=', 0, TOK_REM_ASSIGN,
13212 '+','=', 0, TOK_PLUS_ASSIGN,
13213 '-','=', 0, TOK_MINUS_ASSIGN,
13214 '-','-', 0, TOK_POST_DEC,
13215 '^','=', 0, TOK_XOR_ASSIGN,
13216 '+','+', 0, TOK_POST_INC,
13217 '*','*', 0, TOK_EXPONENT,
13218 '!', 0, TOK_NOT,
13219 '<', 0, TOK_LT,
13220 '>', 0, TOK_GT,
13221 '=', 0, TOK_ASSIGN,
13222 '|', 0, TOK_BOR,
13223 '&', 0, TOK_BAND,
13224 '*', 0, TOK_MUL,
13225 '/', 0, TOK_DIV,
13226 '%', 0, TOK_REM,
13227 '+', 0, TOK_ADD,
13228 '-', 0, TOK_SUB,
13229 '^', 0, TOK_BXOR,
13230 /* uniq */
13231 '~', 0, TOK_BNOT,
13232 ',', 0, TOK_COMMA,
13233 '?', 0, TOK_CONDITIONAL,
13234 ':', 0, TOK_CONDITIONAL_SEP,
13235 ')', 0, TOK_RPAREN,
13236 '(', 0, TOK_LPAREN,
13237 0
13238};
13239/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013240#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013241
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013242static arith_t
13243arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013244{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013245 char arithval; /* Current character under analysis */
13246 operator lasttok, op;
13247 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013248 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013249 const char *p = endexpression;
13250 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013251 v_n_t *numstack, *numstackptr;
13252 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013253
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013254 /* Stack of integers */
13255 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13256 * in any given correct or incorrect expression is left as an exercise to
13257 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013258 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013259 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013260 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013261
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013262 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13263 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013264
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013265 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013266 arithval = *expr;
13267 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013268 if (p == endexpression) {
13269 /* Null expression. */
13270 return 0;
13271 }
13272
13273 /* This is only reached after all tokens have been extracted from the
13274 * input stream. If there are still tokens on the operator stack, they
13275 * are to be applied in order. At the end, there should be a final
13276 * result on the integer stack */
13277
13278 if (expr != endexpression + 1) {
13279 /* If we haven't done so already, */
13280 /* append a closing right paren */
13281 expr = endexpression;
13282 /* and let the loop process it. */
13283 continue;
13284 }
13285 /* At this point, we're done with the expression. */
13286 if (numstackptr != numstack+1) {
13287 /* ... but if there isn't, it's bad */
13288 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013289 *perrcode = -1;
13290 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013291 }
13292 if (numstack->var) {
13293 /* expression is $((var)) only, lookup now */
13294 errcode = arith_lookup_val(numstack);
13295 }
13296 ret:
13297 *perrcode = errcode;
13298 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013299 }
13300
Eric Andersen90898442003-08-06 11:20:52 +000013301 /* Continue processing the expression. */
13302 if (arith_isspace(arithval)) {
13303 /* Skip whitespace */
13304 goto prologue;
13305 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013306 p = endofname(expr);
13307 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013308 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013309
13310 numstackptr->var = alloca(var_name_size);
13311 safe_strncpy(numstackptr->var, expr, var_name_size);
13312 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013313 num:
Eric Andersen90898442003-08-06 11:20:52 +000013314 numstackptr->contidional_second_val_initialized = 0;
13315 numstackptr++;
13316 lasttok = TOK_NUM;
13317 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013318 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013319 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013320 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013321#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013322 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013323#else
13324 numstackptr->val = strtol(expr, (char **) &expr, 0);
13325#endif
Eric Andersen90898442003-08-06 11:20:52 +000013326 goto num;
13327 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013328 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013329 const char *o;
13330
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013331 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013332 /* strange operator not found */
13333 goto err;
13334 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013335 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013336 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013337 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013338 /* found */
13339 expr = o - 1;
13340 break;
13341 }
13342 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013343 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013344 p++;
13345 /* skip zero delim */
13346 p++;
13347 }
13348 op = p[1];
13349
13350 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013351 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13352 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013353
13354 /* Plus and minus are binary (not unary) _only_ if the last
13355 * token was as number, or a right paren (which pretends to be
13356 * a number, since it evaluates to one). Think about it.
13357 * It makes sense. */
13358 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013359 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013360 case TOK_ADD:
13361 op = TOK_UPLUS;
13362 break;
13363 case TOK_SUB:
13364 op = TOK_UMINUS;
13365 break;
13366 case TOK_POST_INC:
13367 op = TOK_PRE_INC;
13368 break;
13369 case TOK_POST_DEC:
13370 op = TOK_PRE_DEC;
13371 break;
Eric Andersen90898442003-08-06 11:20:52 +000013372 }
13373 }
13374 /* We don't want a unary operator to cause recursive descent on the
13375 * stack, because there can be many in a row and it could cause an
13376 * operator to be evaluated before its argument is pushed onto the
13377 * integer stack. */
13378 /* But for binary operators, "apply" everything on the operator
13379 * stack until we find an operator with a lesser priority than the
13380 * one we have just extracted. */
13381 /* Left paren is given the lowest priority so it will never be
13382 * "applied" in this way.
13383 * if associativity is right and priority eq, applied also skip
13384 */
13385 prec = PREC(op);
13386 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13387 /* not left paren or unary */
13388 if (lasttok != TOK_NUM) {
13389 /* binary op must be preceded by a num */
13390 goto err;
13391 }
13392 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013393 if (op == TOK_RPAREN) {
13394 /* The algorithm employed here is simple: while we don't
13395 * hit an open paren nor the bottom of the stack, pop
13396 * tokens and apply them */
13397 if (stackptr[-1] == TOK_LPAREN) {
13398 --stackptr;
13399 /* Any operator directly after a */
13400 lasttok = TOK_NUM;
13401 /* close paren should consider itself binary */
13402 goto prologue;
13403 }
13404 } else {
13405 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013406
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013407 convert_prec_is_assing(prec);
13408 convert_prec_is_assing(prev_prec);
13409 if (prev_prec < prec)
13410 break;
13411 /* check right assoc */
13412 if (prev_prec == prec && is_right_associativity(prec))
13413 break;
13414 }
13415 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13416 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013417 }
13418 if (op == TOK_RPAREN) {
13419 goto err;
13420 }
13421 }
13422
13423 /* Push this operator to the stack and remember it. */
13424 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013425 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013426 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013427 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013428}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013429#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013430
13431
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013432/* ============ main() and helpers */
13433
13434/*
13435 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013436 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013437static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013438static void
13439exitshell(void)
13440{
13441 struct jmploc loc;
13442 char *p;
13443 int status;
13444
13445 status = exitstatus;
13446 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13447 if (setjmp(loc.loc)) {
13448 if (exception == EXEXIT)
13449/* dash bug: it just does _exit(exitstatus) here
13450 * but we have to do setjobctl(0) first!
13451 * (bug is still not fixed in dash-0.5.3 - if you run dash
13452 * under Midnight Commander, on exit from dash MC is backgrounded) */
13453 status = exitstatus;
13454 goto out;
13455 }
13456 exception_handler = &loc;
13457 p = trap[0];
13458 if (p) {
13459 trap[0] = NULL;
13460 evalstring(p, 0);
13461 }
13462 flush_stdout_stderr();
13463 out:
13464 setjobctl(0);
13465 _exit(status);
13466 /* NOTREACHED */
13467}
13468
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013469static void
13470init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013471{
13472 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013473 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013474
13475 /* from trap.c: */
13476 signal(SIGCHLD, SIG_DFL);
13477
13478 /* from var.c: */
13479 {
13480 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013481 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013482 const char *p;
13483 struct stat st1, st2;
13484
13485 initvar();
13486 for (envp = environ; envp && *envp; envp++) {
13487 if (strchr(*envp, '=')) {
13488 setvareq(*envp, VEXPORT|VTEXTFIXED);
13489 }
13490 }
13491
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013492 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013493 setvar("PPID", ppid, 0);
13494
13495 p = lookupvar("PWD");
13496 if (p)
13497 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13498 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13499 p = '\0';
13500 setpwd(p, 0);
13501 }
13502}
13503
13504/*
13505 * Process the shell command line arguments.
13506 */
13507static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013508procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013509{
13510 int i;
13511 const char *xminusc;
13512 char **xargv;
13513
13514 xargv = argv;
13515 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013516 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013517 xargv++;
13518 for (i = 0; i < NOPTS; i++)
13519 optlist[i] = 2;
13520 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013521 if (options(1)) {
13522 /* it already printed err message */
13523 raise_exception(EXERROR);
13524 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013525 xargv = argptr;
13526 xminusc = minusc;
13527 if (*xargv == NULL) {
13528 if (xminusc)
13529 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13530 sflag = 1;
13531 }
13532 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13533 iflag = 1;
13534 if (mflag == 2)
13535 mflag = iflag;
13536 for (i = 0; i < NOPTS; i++)
13537 if (optlist[i] == 2)
13538 optlist[i] = 0;
13539#if DEBUG == 2
13540 debug = 1;
13541#endif
13542 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13543 if (xminusc) {
13544 minusc = *xargv++;
13545 if (*xargv)
13546 goto setarg0;
13547 } else if (!sflag) {
13548 setinputfile(*xargv, 0);
13549 setarg0:
13550 arg0 = *xargv++;
13551 commandname = arg0;
13552 }
13553
13554 shellparam.p = xargv;
13555#if ENABLE_ASH_GETOPTS
13556 shellparam.optind = 1;
13557 shellparam.optoff = -1;
13558#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013559 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013560 while (*xargv) {
13561 shellparam.nparam++;
13562 xargv++;
13563 }
13564 optschanged();
13565}
13566
13567/*
13568 * Read /etc/profile or .profile.
13569 */
13570static void
13571read_profile(const char *name)
13572{
13573 int skip;
13574
13575 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13576 return;
13577 skip = cmdloop(0);
13578 popfile();
13579 if (skip)
13580 exitshell();
13581}
13582
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013583/*
13584 * This routine is called when an error or an interrupt occurs in an
13585 * interactive shell and control is returned to the main command loop.
13586 */
13587static void
13588reset(void)
13589{
13590 /* from eval.c: */
13591 evalskip = 0;
13592 loopnest = 0;
13593 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013594 g_parsefile->left_in_buffer = 0;
13595 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013596 popallfiles();
13597 /* from parser.c: */
13598 tokpushback = 0;
13599 checkkwd = 0;
13600 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013601 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013602}
13603
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013604#if PROFILE
13605static short profile_buf[16384];
13606extern int etext();
13607#endif
13608
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013609/*
13610 * Main routine. We initialize things, parse the arguments, execute
13611 * profiles if we're a login shell, and then call cmdloop to execute
13612 * commands. The setjmp call sets up the location to jump to when an
13613 * exception occurs. When an exception occurs the variable "state"
13614 * is used to figure out how far we had gotten.
13615 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013616int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013617int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013618{
13619 char *shinit;
13620 volatile int state;
13621 struct jmploc jmploc;
13622 struct stackmark smark;
13623
Denis Vlasenko01631112007-12-16 17:20:38 +000013624 /* Initialize global data */
13625 INIT_G_misc();
13626 INIT_G_memstack();
13627 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013628#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013629 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013630#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013631 INIT_G_cmdtable();
13632
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013633#if PROFILE
13634 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13635#endif
13636
13637#if ENABLE_FEATURE_EDITING
13638 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13639#endif
13640 state = 0;
13641 if (setjmp(jmploc.loc)) {
13642 int e;
13643 int s;
13644
13645 reset();
13646
13647 e = exception;
13648 if (e == EXERROR)
13649 exitstatus = 2;
13650 s = state;
13651 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13652 exitshell();
13653
13654 if (e == EXINT) {
13655 outcslow('\n', stderr);
13656 }
13657 popstackmark(&smark);
13658 FORCE_INT_ON; /* enable interrupts */
13659 if (s == 1)
13660 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013661 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013662 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013663 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013664 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013665 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013666 }
13667 exception_handler = &jmploc;
13668#if DEBUG
13669 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013670 trace_puts("Shell args: ");
13671 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013672#endif
13673 rootpid = getpid();
13674
13675#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013676 /* Can use monotonic_ns() for better randomness but for now it is
13677 * not used anywhere else in busybox... so avoid bloat */
13678 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013679#endif
13680 init();
13681 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013682 procargs(argv);
13683
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013684#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13685 if (iflag) {
13686 const char *hp = lookupvar("HISTFILE");
13687
13688 if (hp == NULL) {
13689 hp = lookupvar("HOME");
13690 if (hp != NULL) {
13691 char *defhp = concat_path_file(hp, ".ash_history");
13692 setvar("HISTFILE", defhp, 0);
13693 free(defhp);
13694 }
13695 }
13696 }
13697#endif
13698 if (argv[0] && argv[0][0] == '-')
13699 isloginsh = 1;
13700 if (isloginsh) {
13701 state = 1;
13702 read_profile("/etc/profile");
13703 state1:
13704 state = 2;
13705 read_profile(".profile");
13706 }
13707 state2:
13708 state = 3;
13709 if (
13710#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013711 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013712#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013713 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013714 ) {
13715 shinit = lookupvar("ENV");
13716 if (shinit != NULL && *shinit != '\0') {
13717 read_profile(shinit);
13718 }
13719 }
13720 state3:
13721 state = 4;
13722 if (minusc)
13723 evalstring(minusc, 0);
13724
13725 if (sflag || minusc == NULL) {
13726#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013727 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013728 const char *hp = lookupvar("HISTFILE");
13729
13730 if (hp != NULL)
13731 line_input_state->hist_file = hp;
13732 }
13733#endif
13734 state4: /* XXX ??? - why isn't this before the "if" statement */
13735 cmdloop(1);
13736 }
13737#if PROFILE
13738 monitor(0);
13739#endif
13740#ifdef GPROF
13741 {
13742 extern void _mcleanup(void);
13743 _mcleanup();
13744 }
13745#endif
13746 exitshell();
13747 /* NOTREACHED */
13748}
13749
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013750#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013751const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013752int main(int argc, char **argv)
13753{
13754 return ash_main(argc, argv);
13755}
13756#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013757
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013758
Eric Andersendf82f612001-06-28 07:46:40 +000013759/*-
13760 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013761 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013762 *
13763 * This code is derived from software contributed to Berkeley by
13764 * Kenneth Almquist.
13765 *
13766 * Redistribution and use in source and binary forms, with or without
13767 * modification, are permitted provided that the following conditions
13768 * are met:
13769 * 1. Redistributions of source code must retain the above copyright
13770 * notice, this list of conditions and the following disclaimer.
13771 * 2. Redistributions in binary form must reproduce the above copyright
13772 * notice, this list of conditions and the following disclaimer in the
13773 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013774 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013775 * may be used to endorse or promote products derived from this software
13776 * without specific prior written permission.
13777 *
13778 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13779 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13780 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13781 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13782 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13783 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13784 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13785 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13786 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13787 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13788 * SUCH DAMAGE.
13789 */