blob: 66bfa67ba305283aa4fcf344497d79f529240e65 [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 */
1036 char *prevstring;
1037 int prevnleft;
1038#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) */
1048 int nleft; /* number of chars left in this line */
1049 int lleft; /* number of chars left in this buffer */
1050 char *nextc; /* next char in buffer */
1051 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
Eric Andersenc470f442003-07-28 09:56:35 +00009082#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009083
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009084enum {
9085 INPUT_PUSH_FILE = 1,
9086 INPUT_NOFILE_OK = 2,
9087};
Eric Andersencb57d552001-06-28 07:25:16 +00009088
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009089static int plinno = 1; /* input line number */
9090/* number of characters left in input buffer */
9091static int parsenleft; /* copy of parsefile->nleft */
9092static int parselleft; /* copy of parsefile->lleft */
9093/* next character in input buffer */
9094static char *parsenextc; /* copy of parsefile->nextc */
9095
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009096static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009097/* values of checkkwd variable */
9098#define CHKALIAS 0x1
9099#define CHKKWD 0x2
9100#define CHKNL 0x4
9101
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009102static void
9103popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009104{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009105 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009106
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009107 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009108#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009109 if (sp->ap) {
9110 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9111 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009112 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009113 if (sp->string != sp->ap->val) {
9114 free(sp->string);
9115 }
9116 sp->ap->flag &= ~ALIASINUSE;
9117 if (sp->ap->flag & ALIASDEAD) {
9118 unalias(sp->ap->name);
9119 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009120 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009121#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009122 parsenextc = sp->prevstring;
9123 parsenleft = sp->prevnleft;
9124/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009125 g_parsefile->strpush = sp->prev;
9126 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009127 free(sp);
9128 INT_ON;
9129}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009130
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009131static int
9132preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009133{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009134 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009135 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009136 parsenextc = buf;
9137
Denis Vlasenko38f63192007-01-22 09:03:07 +00009138#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009139 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009140 if (!iflag || g_parsefile->fd)
9141 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009142 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009143#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009144 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009145#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009146 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9147 if (nr == 0) {
9148 /* Ctrl+C pressed */
9149 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009150 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009151 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009152 raise(SIGINT);
9153 return 1;
9154 }
Eric Andersenc470f442003-07-28 09:56:35 +00009155 goto retry;
9156 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009157 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009158 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009159 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009160 }
Eric Andersencb57d552001-06-28 07:25:16 +00009161 }
9162#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009163 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009164#endif
9165
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009166#if 0
9167/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009168 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009169 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009170 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009171 if (flags >= 0 && (flags & O_NONBLOCK)) {
9172 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009173 if (fcntl(0, F_SETFL, flags) >= 0) {
9174 out2str("sh: turning off NDELAY mode\n");
9175 goto retry;
9176 }
9177 }
9178 }
9179 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009180#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009181 return nr;
9182}
9183
9184/*
9185 * Refill the input buffer and return the next input character:
9186 *
9187 * 1) If a string was pushed back on the input, pop it;
9188 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9189 * from a string so we can't refill the buffer, return EOF.
9190 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9191 * 4) Process input up to the next newline, deleting nul characters.
9192 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009193static int
Eric Andersenc470f442003-07-28 09:56:35 +00009194preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009195{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009196 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 int more;
9198 char savec;
9199
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009200 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009201#if ENABLE_ASH_ALIAS
Denis Vlasenko16898402008-11-25 01:34:52 +00009202 if (parsenleft == -1 && g_parsefile->strpush->ap
9203 && parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
9204 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009205 return PEOA;
9206 }
Eric Andersen2870d962001-07-02 17:27:21 +00009207#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009208 popstring();
9209 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009210 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009211 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009212 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009213 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009215
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009216 more = parselleft;
9217 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009218 again:
9219 more = preadfd();
9220 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009221 parselleft = parsenleft = EOF_NLEFT;
9222 return PEOF;
9223 }
9224 }
9225
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009226 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009227
9228 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009229 for (;;) {
9230 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009231
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009232 more--;
9233 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009234
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009235 if (!c)
9236 memmove(q, q + 1, more);
9237 else {
9238 q++;
9239 if (c == '\n') {
9240 parsenleft = q - parsenextc - 1;
9241 break;
9242 }
Eric Andersencb57d552001-06-28 07:25:16 +00009243 }
9244
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009245 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009246 parsenleft = q - parsenextc - 1;
9247 if (parsenleft < 0)
9248 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009249 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009250 }
9251 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009252 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009253
9254 savec = *q;
9255 *q = '\0';
9256
9257 if (vflag) {
9258 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009259 }
9260
9261 *q = savec;
9262
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009263 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009264}
9265
Denis Vlasenko834dee72008-10-07 09:18:30 +00009266#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009267static int
9268pgetc(void)
9269{
9270 return pgetc_as_macro();
9271}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009272
9273#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009274#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009275#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009276#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009277#endif
9278
9279/*
9280 * Same as pgetc(), but ignores PEOA.
9281 */
9282#if ENABLE_ASH_ALIAS
9283static int
9284pgetc2(void)
9285{
9286 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009287 do {
Denis Vlasenko834dee72008-10-07 09:18:30 +00009288 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009289 } while (c == PEOA);
9290 return c;
9291}
9292#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009293#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009294#endif
9295
9296/*
9297 * Read a line from the script.
9298 */
9299static char *
9300pfgets(char *line, int len)
9301{
9302 char *p = line;
9303 int nleft = len;
9304 int c;
9305
9306 while (--nleft > 0) {
9307 c = pgetc2();
9308 if (c == PEOF) {
9309 if (p == line)
9310 return NULL;
9311 break;
9312 }
9313 *p++ = c;
9314 if (c == '\n')
9315 break;
9316 }
9317 *p = '\0';
9318 return line;
9319}
9320
Eric Andersenc470f442003-07-28 09:56:35 +00009321/*
9322 * Undo the last call to pgetc. Only one character may be pushed back.
9323 * PEOF may be pushed back.
9324 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009325static void
Eric Andersenc470f442003-07-28 09:56:35 +00009326pungetc(void)
9327{
Denis Vlasenko16898402008-11-25 01:34:52 +00009328 /* check is needed for ash -c 'echo 5&' + BASH_COMPAT to work */
9329 if (parsenleft < 0)
9330 return;
Eric Andersenc470f442003-07-28 09:56:35 +00009331 parsenleft++;
9332 parsenextc--;
9333}
Eric Andersencb57d552001-06-28 07:25:16 +00009334
9335/*
9336 * Push a string back onto the input at this current parsefile level.
9337 * We handle aliases this way.
9338 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009339#if !ENABLE_ASH_ALIAS
9340#define pushstring(s, ap) pushstring(s)
9341#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009342static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009343pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009344{
Eric Andersencb57d552001-06-28 07:25:16 +00009345 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009346 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009347
Eric Andersenc470f442003-07-28 09:56:35 +00009348 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009349 INT_OFF;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009350 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009351 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009352 sp->prev = g_parsefile->strpush;
9353 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009354 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009355 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009356 sp->prevstring = parsenextc;
9357 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009358#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009359 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009360 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009361 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009362 sp->string = s;
9363 }
Eric Andersen2870d962001-07-02 17:27:21 +00009364#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009365 parsenextc = s;
9366 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009367 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009368}
9369
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009370/*
9371 * To handle the "." command, a stack of input files is used. Pushfile
9372 * adds a new entry to the stack and popfile restores the previous level.
9373 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009374static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009375pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009376{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009377 struct parsefile *pf;
9378
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009379 g_parsefile->nleft = parsenleft;
9380 g_parsefile->lleft = parselleft;
9381 g_parsefile->nextc = parsenextc;
9382 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009383 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009384 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009385 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009386 /*pf->strpush = NULL; - ckzalloc did it */
9387 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009388 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009389}
9390
9391static void
9392popfile(void)
9393{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009394 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009395
Denis Vlasenkob012b102007-02-19 22:43:01 +00009396 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009397 if (pf->fd >= 0)
9398 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009399 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009400 while (pf->strpush)
9401 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009402 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009403 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009404 parsenleft = g_parsefile->nleft;
9405 parselleft = g_parsefile->lleft;
9406 parsenextc = g_parsefile->nextc;
9407 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009408 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009409}
9410
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009411/*
9412 * Return to top level.
9413 */
9414static void
9415popallfiles(void)
9416{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009417 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009418 popfile();
9419}
9420
9421/*
9422 * Close the file(s) that the shell is reading commands from. Called
9423 * after a fork is done.
9424 */
9425static void
9426closescript(void)
9427{
9428 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009429 if (g_parsefile->fd > 0) {
9430 close(g_parsefile->fd);
9431 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009432 }
9433}
9434
9435/*
9436 * Like setinputfile, but takes an open file descriptor. Call this with
9437 * interrupts off.
9438 */
9439static void
9440setinputfd(int fd, int push)
9441{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009442 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009443 if (push) {
9444 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009445 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009446 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009447 g_parsefile->fd = fd;
9448 if (g_parsefile->buf == NULL)
9449 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009450 parselleft = parsenleft = 0;
9451 plinno = 1;
9452}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009453
Eric Andersenc470f442003-07-28 09:56:35 +00009454/*
9455 * Set the input to take input from a file. If push is set, push the
9456 * old input onto the stack first.
9457 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009458static int
9459setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009460{
9461 int fd;
9462 int fd2;
9463
Denis Vlasenkob012b102007-02-19 22:43:01 +00009464 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009465 fd = open(fname, O_RDONLY);
9466 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009467 if (flags & INPUT_NOFILE_OK)
9468 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009469 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009470 }
Eric Andersenc470f442003-07-28 09:56:35 +00009471 if (fd < 10) {
9472 fd2 = copyfd(fd, 10);
9473 close(fd);
9474 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009475 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009476 fd = fd2;
9477 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009478 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009479 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009480 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009481 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009482}
9483
Eric Andersencb57d552001-06-28 07:25:16 +00009484/*
9485 * Like setinputfile, but takes input from a string.
9486 */
Eric Andersenc470f442003-07-28 09:56:35 +00009487static void
9488setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009489{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009490 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009491 pushfile();
9492 parsenextc = string;
9493 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009494 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009495 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009496 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009497}
9498
9499
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009500/* ============ mail.c
9501 *
9502 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009503 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009504
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009505#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009506
Eric Andersencb57d552001-06-28 07:25:16 +00009507#define MAXMBOXES 10
9508
Eric Andersenc470f442003-07-28 09:56:35 +00009509/* times of mailboxes */
9510static time_t mailtime[MAXMBOXES];
9511/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009512static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009513
Eric Andersencb57d552001-06-28 07:25:16 +00009514/*
Eric Andersenc470f442003-07-28 09:56:35 +00009515 * Print appropriate message(s) if mail has arrived.
9516 * If mail_var_path_changed is set,
9517 * then the value of MAIL has mail_var_path_changed,
9518 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009519 */
Eric Andersenc470f442003-07-28 09:56:35 +00009520static void
9521chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009522{
Eric Andersencb57d552001-06-28 07:25:16 +00009523 const char *mpath;
9524 char *p;
9525 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009526 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009527 struct stackmark smark;
9528 struct stat statb;
9529
Eric Andersencb57d552001-06-28 07:25:16 +00009530 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009531 mpath = mpathset() ? mpathval() : mailval();
9532 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009533 p = padvance(&mpath, nullstr);
9534 if (p == NULL)
9535 break;
9536 if (*p == '\0')
9537 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009538 for (q = p; *q; q++)
9539 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009540#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009541 if (q[-1] != '/')
9542 abort();
9543#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009544 q[-1] = '\0'; /* delete trailing '/' */
9545 if (stat(p, &statb) < 0) {
9546 *mtp = 0;
9547 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009548 }
Eric Andersenc470f442003-07-28 09:56:35 +00009549 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9550 fprintf(
9551 stderr, snlfmt,
9552 pathopt ? pathopt : "you have mail"
9553 );
9554 }
9555 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009556 }
Eric Andersenc470f442003-07-28 09:56:35 +00009557 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009558 popstackmark(&smark);
9559}
Eric Andersencb57d552001-06-28 07:25:16 +00009560
Eric Andersenc470f442003-07-28 09:56:35 +00009561static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009562changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009563{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009564 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009565}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009566
Denis Vlasenko131ae172007-02-18 13:00:19 +00009567#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009568
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009569
9570/* ============ ??? */
9571
Eric Andersencb57d552001-06-28 07:25:16 +00009572/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009573 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009574 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009575static void
9576setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009577{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009578 char **newparam;
9579 char **ap;
9580 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009581
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009582 for (nparam = 0; argv[nparam]; nparam++)
9583 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009584 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9585 while (*argv) {
9586 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009587 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009588 *ap = NULL;
9589 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009590 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009591 shellparam.nparam = nparam;
9592 shellparam.p = newparam;
9593#if ENABLE_ASH_GETOPTS
9594 shellparam.optind = 1;
9595 shellparam.optoff = -1;
9596#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009597}
9598
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009599/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009600 * Process shell options. The global variable argptr contains a pointer
9601 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009602 *
9603 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9604 * For a non-interactive shell, an error condition encountered
9605 * by a special built-in ... shall cause the shell to write a diagnostic message
9606 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009607 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009608 * ...
9609 * Utility syntax error (option or operand error) Shall exit
9610 * ...
9611 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9612 * we see that bash does not do that (set "finishes" with error code 1 instead,
9613 * and shell continues), and people rely on this behavior!
9614 * Testcase:
9615 * set -o barfoo 2>/dev/null
9616 * echo $?
9617 *
9618 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009619 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009620static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009621plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009622{
9623 int i;
9624
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009625 if (name) {
9626 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009627 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009628 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009629 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009630 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009631 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009632 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009633 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009634 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009635 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009636 if (val) {
9637 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9638 } else {
9639 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9640 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009641 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009642 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009643}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009644static void
9645setoption(int flag, int val)
9646{
9647 int i;
9648
9649 for (i = 0; i < NOPTS; i++) {
9650 if (optletters(i) == flag) {
9651 optlist[i] = val;
9652 return;
9653 }
9654 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009655 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009656 /* NOTREACHED */
9657}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009658static int
Eric Andersenc470f442003-07-28 09:56:35 +00009659options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009660{
9661 char *p;
9662 int val;
9663 int c;
9664
9665 if (cmdline)
9666 minusc = NULL;
9667 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009668 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009669 if (c != '-' && c != '+')
9670 break;
9671 argptr++;
9672 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009673 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009674 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009675 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009676 if (!cmdline) {
9677 /* "-" means turn off -x and -v */
9678 if (p[0] == '\0')
9679 xflag = vflag = 0;
9680 /* "--" means reset params */
9681 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009682 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009683 }
Eric Andersenc470f442003-07-28 09:56:35 +00009684 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009685 }
Eric Andersencb57d552001-06-28 07:25:16 +00009686 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009687 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009688 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009689 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009690 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009691 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009692 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009693 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009694 /* it already printed err message */
9695 return 1; /* error */
9696 }
Eric Andersencb57d552001-06-28 07:25:16 +00009697 if (*argptr)
9698 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009699 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9700 isloginsh = 1;
9701 /* bash does not accept +-login, we also won't */
9702 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009703 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009704 isloginsh = 1;
9705 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009706 } else {
9707 setoption(c, val);
9708 }
9709 }
9710 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009711 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009712}
9713
Eric Andersencb57d552001-06-28 07:25:16 +00009714/*
Eric Andersencb57d552001-06-28 07:25:16 +00009715 * The shift builtin command.
9716 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009717static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009718shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009719{
9720 int n;
9721 char **ap1, **ap2;
9722
9723 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009724 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009725 n = number(argv[1]);
9726 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009727 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009728 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009729 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009730 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009731 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009732 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009733 }
9734 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009735 while ((*ap2++ = *ap1++) != NULL)
9736 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009737#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009738 shellparam.optind = 1;
9739 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009740#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009741 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009742 return 0;
9743}
9744
Eric Andersencb57d552001-06-28 07:25:16 +00009745/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009746 * POSIX requires that 'set' (but not export or readonly) output the
9747 * variables in lexicographic order - by the locale's collating order (sigh).
9748 * Maybe we could keep them in an ordered balanced binary tree
9749 * instead of hashed lists.
9750 * For now just roll 'em through qsort for printing...
9751 */
9752static int
9753showvars(const char *sep_prefix, int on, int off)
9754{
9755 const char *sep;
9756 char **ep, **epend;
9757
9758 ep = listvars(on, off, &epend);
9759 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9760
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009761 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009762
9763 for (; ep < epend; ep++) {
9764 const char *p;
9765 const char *q;
9766
9767 p = strchrnul(*ep, '=');
9768 q = nullstr;
9769 if (*p)
9770 q = single_quote(++p);
9771 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9772 }
9773 return 0;
9774}
9775
9776/*
Eric Andersencb57d552001-06-28 07:25:16 +00009777 * The set command builtin.
9778 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009779static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009780setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009781{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009782 int retval;
9783
Denis Vlasenko68404f12008-03-17 09:00:54 +00009784 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009785 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009786 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009787 retval = 1;
9788 if (!options(0)) { /* if no parse error... */
9789 retval = 0;
9790 optschanged();
9791 if (*argptr != NULL) {
9792 setparam(argptr);
9793 }
Eric Andersencb57d552001-06-28 07:25:16 +00009794 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009795 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009796 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009797}
9798
Denis Vlasenko131ae172007-02-18 13:00:19 +00009799#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009800static void
9801change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009802{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009803 /* Galois LFSR parameter */
9804 /* Taps at 32 31 29 1: */
9805 enum { MASK = 0x8000000b };
9806 /* Another example - taps at 32 31 30 10: */
9807 /* MASK = 0x00400007 */
9808
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009809 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009810 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009811 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009812
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009813 /* LCG has period of 2^32 and alternating lowest bit */
9814 random_LCG = 1664525 * random_LCG + 1013904223;
9815 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9816 t = (random_galois_LFSR << 1);
9817 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9818 t ^= MASK;
9819 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009820 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009821 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009822 * for $RANDOM range. Combining with subtraction is
9823 * just for fun. + and ^ would work equally well. */
9824 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009825 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009826 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009827 vrandom.flags &= ~VNOFUNC;
9828 } else {
9829 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009830 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009831 }
Eric Andersenef02f822004-03-11 13:34:24 +00009832}
Eric Andersen16767e22004-03-16 05:14:10 +00009833#endif
9834
Denis Vlasenko131ae172007-02-18 13:00:19 +00009835#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009836static int
Eric Andersenc470f442003-07-28 09:56:35 +00009837getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009838{
9839 char *p, *q;
9840 char c = '?';
9841 int done = 0;
9842 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009843 char s[12];
9844 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009845
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009846 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009847 return 1;
9848 optnext = optfirst + *param_optind - 1;
9849
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009850 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009851 p = NULL;
9852 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009853 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009854 if (p == NULL || *p == '\0') {
9855 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009856 p = *optnext;
9857 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009858 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009859 p = NULL;
9860 done = 1;
9861 goto out;
9862 }
9863 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009864 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009865 goto atend;
9866 }
9867
9868 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009869 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009870 if (*q == '\0') {
9871 if (optstr[0] == ':') {
9872 s[0] = c;
9873 s[1] = '\0';
9874 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009875 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009876 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009877 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009878 }
9879 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009880 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009881 }
9882 if (*++q == ':')
9883 q++;
9884 }
9885
9886 if (*++q == ':') {
9887 if (*p == '\0' && (p = *optnext) == NULL) {
9888 if (optstr[0] == ':') {
9889 s[0] = c;
9890 s[1] = '\0';
9891 err |= setvarsafe("OPTARG", s, 0);
9892 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009893 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009894 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009895 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009896 c = '?';
9897 }
Eric Andersenc470f442003-07-28 09:56:35 +00009898 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009899 }
9900
9901 if (p == *optnext)
9902 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009903 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009904 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009905 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009906 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009907 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009908 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009909 *param_optind = optnext - optfirst + 1;
9910 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009911 err |= setvarsafe("OPTIND", s, VNOFUNC);
9912 s[0] = c;
9913 s[1] = '\0';
9914 err |= setvarsafe(optvar, s, 0);
9915 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009916 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009917 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009918 flush_stdout_stderr();
9919 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009920 }
9921 return done;
9922}
Eric Andersenc470f442003-07-28 09:56:35 +00009923
9924/*
9925 * The getopts builtin. Shellparam.optnext points to the next argument
9926 * to be processed. Shellparam.optptr points to the next character to
9927 * be processed in the current argument. If shellparam.optnext is NULL,
9928 * then it's the first time getopts has been called.
9929 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009930static int
Eric Andersenc470f442003-07-28 09:56:35 +00009931getoptscmd(int argc, char **argv)
9932{
9933 char **optbase;
9934
9935 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009936 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009937 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009938 optbase = shellparam.p;
9939 if (shellparam.optind > shellparam.nparam + 1) {
9940 shellparam.optind = 1;
9941 shellparam.optoff = -1;
9942 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009943 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009944 optbase = &argv[3];
9945 if (shellparam.optind > argc - 2) {
9946 shellparam.optind = 1;
9947 shellparam.optoff = -1;
9948 }
9949 }
9950
9951 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009952 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009953}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009954#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009955
Eric Andersencb57d552001-06-28 07:25:16 +00009956
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009957/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009958
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009959struct heredoc {
9960 struct heredoc *next; /* next here document in list */
9961 union node *here; /* redirection node */
9962 char *eofmark; /* string indicating end of input */
9963 smallint striptabs; /* if set, strip leading tabs */
9964};
9965
9966static smallint tokpushback; /* last token pushed back */
9967static smallint parsebackquote; /* nonzero if we are inside backquotes */
9968static smallint quoteflag; /* set if (part of) last token was quoted */
9969static token_id_t lasttoken; /* last token read (integer id Txxx) */
9970static struct heredoc *heredoclist; /* list of here documents to read */
9971static char *wordtext; /* text of last word returned by readtoken */
9972static struct nodelist *backquotelist;
9973static union node *redirnode;
9974static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009975/*
9976 * NEOF is returned by parsecmd when it encounters an end of file. It
9977 * must be distinct from NULL, so we use the address of a variable that
9978 * happens to be handy.
9979 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009980#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009981
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009982static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009983static void
9984raise_error_syntax(const char *msg)
9985{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009986 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009987 /* NOTREACHED */
9988}
9989
9990/*
9991 * Called when an unexpected token is read during the parse. The argument
9992 * is the token that is expected, or -1 if more than one type of token can
9993 * occur at this point.
9994 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009995static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009996static void
9997raise_error_unexpected_syntax(int token)
9998{
9999 char msg[64];
10000 int l;
10001
10002 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10003 if (token >= 0)
10004 sprintf(msg + l, " (expecting %s)", tokname(token));
10005 raise_error_syntax(msg);
10006 /* NOTREACHED */
10007}
Eric Andersencb57d552001-06-28 07:25:16 +000010008
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010009#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010010
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010011/* parsing is heavily cross-recursive, need these forward decls */
10012static union node *andor(void);
10013static union node *pipeline(void);
10014static union node *parse_command(void);
10015static void parseheredoc(void);
10016static char peektoken(void);
10017static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010018
Eric Andersenc470f442003-07-28 09:56:35 +000010019static union node *
10020list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010021{
10022 union node *n1, *n2, *n3;
10023 int tok;
10024
Eric Andersenc470f442003-07-28 09:56:35 +000010025 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10026 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010027 return NULL;
10028 n1 = NULL;
10029 for (;;) {
10030 n2 = andor();
10031 tok = readtoken();
10032 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010033 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010034 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010035 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010036 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010037 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010038 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010039 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010040 n2 = n3;
10041 }
10042 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010043 }
10044 }
10045 if (n1 == NULL) {
10046 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010047 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010048 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010049 n3->type = NSEMI;
10050 n3->nbinary.ch1 = n1;
10051 n3->nbinary.ch2 = n2;
10052 n1 = n3;
10053 }
10054 switch (tok) {
10055 case TBACKGND:
10056 case TSEMI:
10057 tok = readtoken();
10058 /* fall through */
10059 case TNL:
10060 if (tok == TNL) {
10061 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010062 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010063 return n1;
10064 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010065 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010066 }
Eric Andersenc470f442003-07-28 09:56:35 +000010067 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010068 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010069 return n1;
10070 break;
10071 case TEOF:
10072 if (heredoclist)
10073 parseheredoc();
10074 else
Eric Andersenc470f442003-07-28 09:56:35 +000010075 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010076 return n1;
10077 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010078 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010079 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010080 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010081 return n1;
10082 }
10083 }
10084}
10085
Eric Andersenc470f442003-07-28 09:56:35 +000010086static union node *
10087andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010088{
Eric Andersencb57d552001-06-28 07:25:16 +000010089 union node *n1, *n2, *n3;
10090 int t;
10091
Eric Andersencb57d552001-06-28 07:25:16 +000010092 n1 = pipeline();
10093 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010094 t = readtoken();
10095 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010096 t = NAND;
10097 } else if (t == TOR) {
10098 t = NOR;
10099 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010100 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010101 return n1;
10102 }
Eric Andersenc470f442003-07-28 09:56:35 +000010103 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010104 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010105 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010106 n3->type = t;
10107 n3->nbinary.ch1 = n1;
10108 n3->nbinary.ch2 = n2;
10109 n1 = n3;
10110 }
10111}
10112
Eric Andersenc470f442003-07-28 09:56:35 +000010113static union node *
10114pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010115{
Eric Andersencb57d552001-06-28 07:25:16 +000010116 union node *n1, *n2, *pipenode;
10117 struct nodelist *lp, *prev;
10118 int negate;
10119
10120 negate = 0;
10121 TRACE(("pipeline: entered\n"));
10122 if (readtoken() == TNOT) {
10123 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010124 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010125 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010126 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010127 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010128 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010129 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010130 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010131 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010132 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010133 pipenode->npipe.cmdlist = lp;
10134 lp->n = n1;
10135 do {
10136 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010137 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010138 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010139 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010140 prev->next = lp;
10141 } while (readtoken() == TPIPE);
10142 lp->next = NULL;
10143 n1 = pipenode;
10144 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010145 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010146 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010147 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010148 n2->type = NNOT;
10149 n2->nnot.com = n1;
10150 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010151 }
10152 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010153}
10154
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010155static union node *
10156makename(void)
10157{
10158 union node *n;
10159
Denis Vlasenko597906c2008-02-20 16:38:54 +000010160 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010161 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010162 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010163 n->narg.text = wordtext;
10164 n->narg.backquote = backquotelist;
10165 return n;
10166}
10167
10168static void
10169fixredir(union node *n, const char *text, int err)
10170{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010171 int fd;
10172
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010173 TRACE(("Fix redir %s %d\n", text, err));
10174 if (!err)
10175 n->ndup.vname = NULL;
10176
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010177 fd = bb_strtou(text, NULL, 10);
10178 if (!errno && fd >= 0)
10179 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010180 else if (LONE_DASH(text))
10181 n->ndup.dupfd = -1;
10182 else {
10183 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010184 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010185 n->ndup.vname = makename();
10186 }
10187}
10188
10189/*
10190 * Returns true if the text contains nothing to expand (no dollar signs
10191 * or backquotes).
10192 */
10193static int
10194noexpand(char *text)
10195{
10196 char *p;
10197 char c;
10198
10199 p = text;
10200 while ((c = *p++) != '\0') {
10201 if (c == CTLQUOTEMARK)
10202 continue;
10203 if (c == CTLESC)
10204 p++;
10205 else if (SIT(c, BASESYNTAX) == CCTL)
10206 return 0;
10207 }
10208 return 1;
10209}
10210
10211static void
10212parsefname(void)
10213{
10214 union node *n = redirnode;
10215
10216 if (readtoken() != TWORD)
10217 raise_error_unexpected_syntax(-1);
10218 if (n->type == NHERE) {
10219 struct heredoc *here = heredoc;
10220 struct heredoc *p;
10221 int i;
10222
10223 if (quoteflag == 0)
10224 n->type = NXHERE;
10225 TRACE(("Here document %d\n", n->type));
10226 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010227 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010228 rmescapes(wordtext);
10229 here->eofmark = wordtext;
10230 here->next = NULL;
10231 if (heredoclist == NULL)
10232 heredoclist = here;
10233 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010234 for (p = heredoclist; p->next; p = p->next)
10235 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010236 p->next = here;
10237 }
10238 } else if (n->type == NTOFD || n->type == NFROMFD) {
10239 fixredir(n, wordtext, 0);
10240 } else {
10241 n->nfile.fname = makename();
10242 }
10243}
Eric Andersencb57d552001-06-28 07:25:16 +000010244
Eric Andersenc470f442003-07-28 09:56:35 +000010245static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246simplecmd(void)
10247{
10248 union node *args, **app;
10249 union node *n = NULL;
10250 union node *vars, **vpp;
10251 union node **rpp, *redir;
10252 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010253#if ENABLE_ASH_BASH_COMPAT
10254 smallint double_brackets_flag = 0;
10255#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010256
10257 args = NULL;
10258 app = &args;
10259 vars = NULL;
10260 vpp = &vars;
10261 redir = NULL;
10262 rpp = &redir;
10263
10264 savecheckkwd = CHKALIAS;
10265 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010266 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010267 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010268 t = readtoken();
10269 switch (t) {
10270#if ENABLE_ASH_BASH_COMPAT
10271 case TAND: /* "&&" */
10272 case TOR: /* "||" */
10273 if (!double_brackets_flag) {
10274 tokpushback = 1;
10275 goto out;
10276 }
10277 wordtext = (char *) (t == TAND ? "-a" : "-o");
10278#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010279 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010280 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010281 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010282 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010283 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010284#if ENABLE_ASH_BASH_COMPAT
10285 if (strcmp("[[", wordtext) == 0)
10286 double_brackets_flag = 1;
10287 else if (strcmp("]]", wordtext) == 0)
10288 double_brackets_flag = 0;
10289#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010290 n->narg.backquote = backquotelist;
10291 if (savecheckkwd && isassignment(wordtext)) {
10292 *vpp = n;
10293 vpp = &n->narg.next;
10294 } else {
10295 *app = n;
10296 app = &n->narg.next;
10297 savecheckkwd = 0;
10298 }
10299 break;
10300 case TREDIR:
10301 *rpp = n = redirnode;
10302 rpp = &n->nfile.next;
10303 parsefname(); /* read name of redirection file */
10304 break;
10305 case TLP:
10306 if (args && app == &args->narg.next
10307 && !vars && !redir
10308 ) {
10309 struct builtincmd *bcmd;
10310 const char *name;
10311
10312 /* We have a function */
10313 if (readtoken() != TRP)
10314 raise_error_unexpected_syntax(TRP);
10315 name = n->narg.text;
10316 if (!goodname(name)
10317 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10318 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010319 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010320 }
10321 n->type = NDEFUN;
10322 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10323 n->narg.next = parse_command();
10324 return n;
10325 }
10326 /* fall through */
10327 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010328 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010329 goto out;
10330 }
10331 }
10332 out:
10333 *app = NULL;
10334 *vpp = NULL;
10335 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010336 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010337 n->type = NCMD;
10338 n->ncmd.args = args;
10339 n->ncmd.assign = vars;
10340 n->ncmd.redirect = redir;
10341 return n;
10342}
10343
10344static union node *
10345parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010346{
Eric Andersencb57d552001-06-28 07:25:16 +000010347 union node *n1, *n2;
10348 union node *ap, **app;
10349 union node *cp, **cpp;
10350 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010351 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010352 int t;
10353
10354 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010355 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010356
Eric Andersencb57d552001-06-28 07:25:16 +000010357 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010358 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010359 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010360 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010361 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010362 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010363 n1->type = NIF;
10364 n1->nif.test = list(0);
10365 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010366 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010367 n1->nif.ifpart = list(0);
10368 n2 = n1;
10369 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010370 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010371 n2 = n2->nif.elsepart;
10372 n2->type = NIF;
10373 n2->nif.test = list(0);
10374 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010375 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010376 n2->nif.ifpart = list(0);
10377 }
10378 if (lasttoken == TELSE)
10379 n2->nif.elsepart = list(0);
10380 else {
10381 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010382 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010383 }
Eric Andersenc470f442003-07-28 09:56:35 +000010384 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010385 break;
10386 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010387 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010388 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010389 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010390 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010392 got = readtoken();
10393 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010394 TRACE(("expecting DO got %s %s\n", tokname(got),
10395 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010396 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010397 }
10398 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010399 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010400 break;
10401 }
10402 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010403 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010404 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010405 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010406 n1->type = NFOR;
10407 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010408 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010409 if (readtoken() == TIN) {
10410 app = &ap;
10411 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010412 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010413 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010414 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010415 n2->narg.text = wordtext;
10416 n2->narg.backquote = backquotelist;
10417 *app = n2;
10418 app = &n2->narg.next;
10419 }
10420 *app = NULL;
10421 n1->nfor.args = ap;
10422 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010423 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010424 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010425 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010426 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010427 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010428 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010429 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010430 n1->nfor.args = n2;
10431 /*
10432 * Newline or semicolon here is optional (but note
10433 * that the original Bourne shell only allowed NL).
10434 */
10435 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010436 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010437 }
Eric Andersenc470f442003-07-28 09:56:35 +000010438 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010439 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010440 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010441 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010442 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010443 break;
10444 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010445 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010446 n1->type = NCASE;
10447 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010448 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010449 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010450 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010451 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010452 n2->narg.text = wordtext;
10453 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010454 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010455 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010456 } while (readtoken() == TNL);
10457 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010458 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010459 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010460 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010461 checkkwd = CHKNL | CHKKWD;
10462 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010463 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010464 if (lasttoken == TLP)
10465 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010466 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010467 cp->type = NCLIST;
10468 app = &cp->nclist.pattern;
10469 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010470 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010471 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010472 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010473 ap->narg.text = wordtext;
10474 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010475 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010476 break;
10477 app = &ap->narg.next;
10478 readtoken();
10479 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010480 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010481 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010482 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010483 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010484
Eric Andersenc470f442003-07-28 09:56:35 +000010485 cpp = &cp->nclist.next;
10486
10487 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010488 t = readtoken();
10489 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010490 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491 raise_error_unexpected_syntax(TENDCASE);
10492 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010493 }
Eric Andersenc470f442003-07-28 09:56:35 +000010494 }
Eric Andersencb57d552001-06-28 07:25:16 +000010495 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010496 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010497 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010498 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010499 n1->type = NSUBSHELL;
10500 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010501 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010502 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010503 break;
10504 case TBEGIN:
10505 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010506 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010508 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010509 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010510 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010511 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010512 }
10513
Eric Andersenc470f442003-07-28 09:56:35 +000010514 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010515 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010516
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010517 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010518 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010519 checkkwd = CHKKWD | CHKALIAS;
10520 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010521 while (readtoken() == TREDIR) {
10522 *rpp = n2 = redirnode;
10523 rpp = &n2->nfile.next;
10524 parsefname();
10525 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010526 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010527 *rpp = NULL;
10528 if (redir) {
10529 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010530 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010531 n2->type = NREDIR;
10532 n2->nredir.n = n1;
10533 n1 = n2;
10534 }
10535 n1->nredir.redirect = redir;
10536 }
Eric Andersencb57d552001-06-28 07:25:16 +000010537 return n1;
10538}
10539
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010540#if ENABLE_ASH_BASH_COMPAT
10541static int decode_dollar_squote(void)
10542{
10543 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10544 int c, cnt;
10545 char *p;
10546 char buf[4];
10547
10548 c = pgetc();
10549 p = strchr(C_escapes, c);
10550 if (p) {
10551 buf[0] = c;
10552 p = buf;
10553 cnt = 3;
10554 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10555 do {
10556 c = pgetc();
10557 *++p = c;
10558 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10559 pungetc();
10560 } else if (c == 'x') { /* \xHH */
10561 do {
10562 c = pgetc();
10563 *++p = c;
10564 } while (isxdigit(c) && --cnt);
10565 pungetc();
10566 if (cnt == 3) { /* \x but next char is "bad" */
10567 c = 'x';
10568 goto unrecognized;
10569 }
10570 } else { /* simple seq like \\ or \t */
10571 p++;
10572 }
10573 *p = '\0';
10574 p = buf;
10575 c = bb_process_escape_sequence((void*)&p);
10576 } else { /* unrecognized "\z": print both chars unless ' or " */
10577 if (c != '\'' && c != '"') {
10578 unrecognized:
10579 c |= 0x100; /* "please encode \, then me" */
10580 }
10581 }
10582 return c;
10583}
10584#endif
10585
Eric Andersencb57d552001-06-28 07:25:16 +000010586/*
10587 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10588 * is not NULL, read a here document. In the latter case, eofmark is the
10589 * word which marks the end of the document and striptabs is true if
10590 * leading tabs should be stripped from the document. The argument firstc
10591 * is the first character of the input token or document.
10592 *
10593 * Because C does not have internal subroutines, I have simulated them
10594 * using goto's to implement the subroutine linkage. The following macros
10595 * will run code that appears at the end of readtoken1.
10596 */
Eric Andersen2870d962001-07-02 17:27:21 +000010597#define CHECKEND() {goto checkend; checkend_return:;}
10598#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10599#define PARSESUB() {goto parsesub; parsesub_return:;}
10600#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10601#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10602#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010603static int
Eric Andersenc470f442003-07-28 09:56:35 +000010604readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010605{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010606 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010607 int c = firstc;
10608 char *out;
10609 int len;
10610 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010611 struct nodelist *bqlist;
10612 smallint quotef;
10613 smallint dblquote;
10614 smallint oldstyle;
10615 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010616#if ENABLE_ASH_EXPAND_PRMT
10617 smallint pssyntax; /* we are expanding a prompt string */
10618#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010619 int varnest; /* levels of variables expansion */
10620 int arinest; /* levels of arithmetic expansion */
10621 int parenlevel; /* levels of parens in arithmetic */
10622 int dqvarnest; /* levels of variables expansion within double quotes */
10623
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010624 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10625
Eric Andersencb57d552001-06-28 07:25:16 +000010626#if __GNUC__
10627 /* Avoid longjmp clobbering */
10628 (void) &out;
10629 (void) &quotef;
10630 (void) &dblquote;
10631 (void) &varnest;
10632 (void) &arinest;
10633 (void) &parenlevel;
10634 (void) &dqvarnest;
10635 (void) &oldstyle;
10636 (void) &prevsyntax;
10637 (void) &syntax;
10638#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010639 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010640 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010641 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010642 oldstyle = 0;
10643 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010644#if ENABLE_ASH_EXPAND_PRMT
10645 pssyntax = (syntax == PSSYNTAX);
10646 if (pssyntax)
10647 syntax = DQSYNTAX;
10648#endif
10649 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010650 varnest = 0;
10651 arinest = 0;
10652 parenlevel = 0;
10653 dqvarnest = 0;
10654
10655 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010656 loop:
10657 /* For each line, until end of word */
10658 {
Eric Andersenc470f442003-07-28 09:56:35 +000010659 CHECKEND(); /* set c to PEOF if at end of here document */
10660 for (;;) { /* until end of line or end of word */
10661 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010662 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010663 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010664 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010665 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010666 USTPUTC(c, out);
10667 plinno++;
10668 if (doprompt)
10669 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010670 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010671 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010672 case CWORD:
10673 USTPUTC(c, out);
10674 break;
10675 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010676 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010677 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010678#if ENABLE_ASH_BASH_COMPAT
10679 if (c == '\\' && bash_dollar_squote) {
10680 c = decode_dollar_squote();
10681 if (c & 0x100) {
10682 USTPUTC('\\', out);
10683 c = (unsigned char)c;
10684 }
10685 }
10686#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010687 USTPUTC(c, out);
10688 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010689 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010690 c = pgetc2();
10691 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010692 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010693 USTPUTC('\\', out);
10694 pungetc();
10695 } else if (c == '\n') {
10696 if (doprompt)
10697 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010698 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010699#if ENABLE_ASH_EXPAND_PRMT
10700 if (c == '$' && pssyntax) {
10701 USTPUTC(CTLESC, out);
10702 USTPUTC('\\', out);
10703 }
10704#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010705 if (dblquote && c != '\\'
10706 && c != '`' && c != '$'
10707 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010708 ) {
10709 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010710 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010711 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010712 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010713 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010714 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010715 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010716 }
10717 break;
10718 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010719 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010720 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010721 if (eofmark == NULL) {
10722 USTPUTC(CTLQUOTEMARK, out);
10723 }
Eric Andersencb57d552001-06-28 07:25:16 +000010724 break;
10725 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010726 syntax = DQSYNTAX;
10727 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010728 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010729 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010730 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010731 if (eofmark != NULL && arinest == 0
10732 && varnest == 0
10733 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010734 USTPUTC(c, out);
10735 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010736 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010737 syntax = BASESYNTAX;
10738 dblquote = 0;
10739 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010740 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010741 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010742 }
10743 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010744 case CVAR: /* '$' */
10745 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010746 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010747 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010748 if (varnest > 0) {
10749 varnest--;
10750 if (dqvarnest > 0) {
10751 dqvarnest--;
10752 }
10753 USTPUTC(CTLENDVAR, out);
10754 } else {
10755 USTPUTC(c, out);
10756 }
10757 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010758#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010759 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010760 parenlevel++;
10761 USTPUTC(c, out);
10762 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010763 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010764 if (parenlevel > 0) {
10765 USTPUTC(c, out);
10766 --parenlevel;
10767 } else {
10768 if (pgetc() == ')') {
10769 if (--arinest == 0) {
10770 USTPUTC(CTLENDARI, out);
10771 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010772 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010773 } else
10774 USTPUTC(')', out);
10775 } else {
10776 /*
10777 * unbalanced parens
10778 * (don't 2nd guess - no error)
10779 */
10780 pungetc();
10781 USTPUTC(')', out);
10782 }
10783 }
10784 break;
10785#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010786 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010787 PARSEBACKQOLD();
10788 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010789 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010790 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010791 case CIGN:
10792 break;
10793 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010794 if (varnest == 0) {
10795#if ENABLE_ASH_BASH_COMPAT
10796 if (c == '&') {
10797 if (pgetc() == '>')
10798 c = 0x100 + '>'; /* flag &> */
10799 pungetc();
10800 }
10801#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010802 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010803 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010804#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010805 if (c != PEOA)
10806#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010807 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010808
Eric Andersencb57d552001-06-28 07:25:16 +000010809 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010810 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010811 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010812 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010813 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010814#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010815 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010816 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010817#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010818 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010819 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010820 if (varnest != 0) {
10821 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010822 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010823 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010824 }
10825 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010826 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010827 out = stackblock();
10828 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010829 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10830 && quotef == 0
10831 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010832 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010833 PARSEREDIR(); /* passed as params: out, c */
10834 lasttoken = TREDIR;
10835 return lasttoken;
10836 }
10837 /* else: non-number X seen, interpret it
10838 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010839 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010840 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010841 }
10842 quoteflag = quotef;
10843 backquotelist = bqlist;
10844 grabstackblock(len);
10845 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010846 lasttoken = TWORD;
10847 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010848/* end of readtoken routine */
10849
Eric Andersencb57d552001-06-28 07:25:16 +000010850/*
10851 * Check to see whether we are at the end of the here document. When this
10852 * is called, c is set to the first character of the next input line. If
10853 * we are at the end of the here document, this routine sets the c to PEOF.
10854 */
Eric Andersenc470f442003-07-28 09:56:35 +000010855checkend: {
10856 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010857#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010858 if (c == PEOA) {
10859 c = pgetc2();
10860 }
10861#endif
10862 if (striptabs) {
10863 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010864 c = pgetc2();
10865 }
Eric Andersenc470f442003-07-28 09:56:35 +000010866 }
10867 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010868 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010869 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010870
Eric Andersenc470f442003-07-28 09:56:35 +000010871 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010872 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10873 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010874 if (*p == '\n' && *q == '\0') {
10875 c = PEOF;
10876 plinno++;
10877 needprompt = doprompt;
10878 } else {
10879 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010880 }
10881 }
10882 }
10883 }
Eric Andersenc470f442003-07-28 09:56:35 +000010884 goto checkend_return;
10885}
Eric Andersencb57d552001-06-28 07:25:16 +000010886
Eric Andersencb57d552001-06-28 07:25:16 +000010887/*
10888 * Parse a redirection operator. The variable "out" points to a string
10889 * specifying the fd to be redirected. The variable "c" contains the
10890 * first character of the redirection operator.
10891 */
Eric Andersenc470f442003-07-28 09:56:35 +000010892parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010893 /* out is already checked to be a valid number or "" */
10894 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010895 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010896
Denis Vlasenko597906c2008-02-20 16:38:54 +000010897 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010898 if (c == '>') {
10899 np->nfile.fd = 1;
10900 c = pgetc();
10901 if (c == '>')
10902 np->type = NAPPEND;
10903 else if (c == '|')
10904 np->type = NCLOBBER;
10905 else if (c == '&')
10906 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010907 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010908 else {
10909 np->type = NTO;
10910 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010911 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010912 }
10913#if ENABLE_ASH_BASH_COMPAT
10914 else if (c == 0x100 + '>') { /* this flags &> redirection */
10915 np->nfile.fd = 1;
10916 pgetc(); /* this is '>', no need to check */
10917 np->type = NTO2;
10918 }
10919#endif
10920 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010921 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010922 c = pgetc();
10923 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010924 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010925 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010926 np = stzalloc(sizeof(struct nhere));
10927 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010928 }
10929 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010930 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010931 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010932 c = pgetc();
10933 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010934 heredoc->striptabs = 1;
10935 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010936 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010937 pungetc();
10938 }
10939 break;
10940
10941 case '&':
10942 np->type = NFROMFD;
10943 break;
10944
10945 case '>':
10946 np->type = NFROMTO;
10947 break;
10948
10949 default:
10950 np->type = NFROM;
10951 pungetc();
10952 break;
10953 }
Eric Andersencb57d552001-06-28 07:25:16 +000010954 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010955 if (fd >= 0)
10956 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010957 redirnode = np;
10958 goto parseredir_return;
10959}
Eric Andersencb57d552001-06-28 07:25:16 +000010960
Eric Andersencb57d552001-06-28 07:25:16 +000010961/*
10962 * Parse a substitution. At this point, we have read the dollar sign
10963 * and nothing else.
10964 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010965
10966/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10967 * (assuming ascii char codes, as the original implementation did) */
10968#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010969 (((unsigned)(c) - 33 < 32) \
10970 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010971parsesub: {
10972 int subtype;
10973 int typeloc;
10974 int flags;
10975 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010976 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010977
Eric Andersenc470f442003-07-28 09:56:35 +000010978 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010979 if (c <= PEOA_OR_PEOF
10980 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010981 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010982#if ENABLE_ASH_BASH_COMPAT
10983 if (c == '\'')
10984 bash_dollar_squote = 1;
10985 else
10986#endif
10987 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010988 pungetc();
10989 } else if (c == '(') { /* $(command) or $((arith)) */
10990 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010991#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010992 PARSEARITH();
10993#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010994 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010995#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010996 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010997 pungetc();
10998 PARSEBACKQNEW();
10999 }
11000 } else {
11001 USTPUTC(CTLVAR, out);
11002 typeloc = out - (char *)stackblock();
11003 USTPUTC(VSNORMAL, out);
11004 subtype = VSNORMAL;
11005 if (c == '{') {
11006 c = pgetc();
11007 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011008 c = pgetc();
11009 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011010 c = '#';
11011 else
11012 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011013 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011014 subtype = 0;
11015 }
11016 if (c > PEOA_OR_PEOF && is_name(c)) {
11017 do {
11018 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011019 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011020 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011021 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011022 do {
11023 STPUTC(c, out);
11024 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011025 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011026 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011027 USTPUTC(c, out);
11028 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011029 } else {
11030 badsub:
11031 raise_error_syntax("bad substitution");
11032 }
Eric Andersencb57d552001-06-28 07:25:16 +000011033
Eric Andersenc470f442003-07-28 09:56:35 +000011034 STPUTC('=', out);
11035 flags = 0;
11036 if (subtype == 0) {
11037 switch (c) {
11038 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011039 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011040#if ENABLE_ASH_BASH_COMPAT
11041 if (c == ':' || c == '$' || isdigit(c)) {
11042 pungetc();
11043 subtype = VSSUBSTR;
11044 break;
11045 }
11046#endif
11047 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011048 /*FALLTHROUGH*/
11049 default:
11050 p = strchr(types, c);
11051 if (p == NULL)
11052 goto badsub;
11053 subtype = p - types + VSNORMAL;
11054 break;
11055 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011056 case '#': {
11057 int cc = c;
11058 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11059 c = pgetc();
11060 if (c == cc)
11061 subtype++;
11062 else
11063 pungetc();
11064 break;
11065 }
11066#if ENABLE_ASH_BASH_COMPAT
11067 case '/':
11068 subtype = VSREPLACE;
11069 c = pgetc();
11070 if (c == '/')
11071 subtype++; /* VSREPLACEALL */
11072 else
11073 pungetc();
11074 break;
11075#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011076 }
Eric Andersenc470f442003-07-28 09:56:35 +000011077 } else {
11078 pungetc();
11079 }
11080 if (dblquote || arinest)
11081 flags |= VSQUOTE;
11082 *((char *)stackblock() + typeloc) = subtype | flags;
11083 if (subtype != VSNORMAL) {
11084 varnest++;
11085 if (dblquote || arinest) {
11086 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011087 }
11088 }
11089 }
Eric Andersenc470f442003-07-28 09:56:35 +000011090 goto parsesub_return;
11091}
Eric Andersencb57d552001-06-28 07:25:16 +000011092
Eric Andersencb57d552001-06-28 07:25:16 +000011093/*
11094 * Called to parse command substitutions. Newstyle is set if the command
11095 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11096 * list of commands (passed by reference), and savelen is the number of
11097 * characters on the top of the stack which must be preserved.
11098 */
Eric Andersenc470f442003-07-28 09:56:35 +000011099parsebackq: {
11100 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011101 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011102 union node *n;
11103 char *volatile str;
11104 struct jmploc jmploc;
11105 struct jmploc *volatile savehandler;
11106 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011107 smallint saveprompt = 0;
11108
Eric Andersencb57d552001-06-28 07:25:16 +000011109#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011110 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011111#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011112 savepbq = parsebackquote;
11113 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011114 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011115 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011116 exception_handler = savehandler;
11117 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011118 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011119 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011120 str = NULL;
11121 savelen = out - (char *)stackblock();
11122 if (savelen > 0) {
11123 str = ckmalloc(savelen);
11124 memcpy(str, stackblock(), savelen);
11125 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011126 savehandler = exception_handler;
11127 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011128 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011129 if (oldstyle) {
11130 /* We must read until the closing backquote, giving special
11131 treatment to some slashes, and then push the string and
11132 reread it as input, interpreting it normally. */
11133 char *pout;
11134 int pc;
11135 size_t psavelen;
11136 char *pstr;
11137
11138
11139 STARTSTACKSTR(pout);
11140 for (;;) {
11141 if (needprompt) {
11142 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011143 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011144 pc = pgetc();
11145 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011146 case '`':
11147 goto done;
11148
11149 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011150 pc = pgetc();
11151 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011152 plinno++;
11153 if (doprompt)
11154 setprompt(2);
11155 /*
11156 * If eating a newline, avoid putting
11157 * the newline into the new character
11158 * stream (via the STPUTC after the
11159 * switch).
11160 */
11161 continue;
11162 }
11163 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011164 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011165 STPUTC('\\', pout);
11166 if (pc > PEOA_OR_PEOF) {
11167 break;
11168 }
11169 /* fall through */
11170
11171 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011172#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011173 case PEOA:
11174#endif
11175 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011176 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011177
11178 case '\n':
11179 plinno++;
11180 needprompt = doprompt;
11181 break;
11182
11183 default:
11184 break;
11185 }
11186 STPUTC(pc, pout);
11187 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011188 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011189 STPUTC('\0', pout);
11190 psavelen = pout - (char *)stackblock();
11191 if (psavelen > 0) {
11192 pstr = grabstackstr(pout);
11193 setinputstring(pstr);
11194 }
11195 }
11196 nlpp = &bqlist;
11197 while (*nlpp)
11198 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011199 *nlpp = stzalloc(sizeof(**nlpp));
11200 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011201 parsebackquote = oldstyle;
11202
11203 if (oldstyle) {
11204 saveprompt = doprompt;
11205 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011206 }
11207
Eric Andersenc470f442003-07-28 09:56:35 +000011208 n = list(2);
11209
11210 if (oldstyle)
11211 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011212 else if (readtoken() != TRP)
11213 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011214
11215 (*nlpp)->n = n;
11216 if (oldstyle) {
11217 /*
11218 * Start reading from old file again, ignoring any pushed back
11219 * tokens left from the backquote parsing
11220 */
11221 popfile();
11222 tokpushback = 0;
11223 }
11224 while (stackblocksize() <= savelen)
11225 growstackblock();
11226 STARTSTACKSTR(out);
11227 if (str) {
11228 memcpy(out, str, savelen);
11229 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011230 INT_OFF;
11231 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011232 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011233 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011234 }
11235 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011236 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011237 if (arinest || dblquote)
11238 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11239 else
11240 USTPUTC(CTLBACKQ, out);
11241 if (oldstyle)
11242 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011243 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011244}
11245
Denis Vlasenko131ae172007-02-18 13:00:19 +000011246#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011247/*
11248 * Parse an arithmetic expansion (indicate start of one and set state)
11249 */
Eric Andersenc470f442003-07-28 09:56:35 +000011250parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011251 if (++arinest == 1) {
11252 prevsyntax = syntax;
11253 syntax = ARISYNTAX;
11254 USTPUTC(CTLARI, out);
11255 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011256 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011257 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011258 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011259 } else {
11260 /*
11261 * we collapse embedded arithmetic expansion to
11262 * parenthesis, which should be equivalent
11263 */
11264 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011265 }
Eric Andersenc470f442003-07-28 09:56:35 +000011266 goto parsearith_return;
11267}
11268#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011269
Eric Andersenc470f442003-07-28 09:56:35 +000011270} /* end of readtoken */
11271
Eric Andersencb57d552001-06-28 07:25:16 +000011272/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011273 * Read the next input token.
11274 * If the token is a word, we set backquotelist to the list of cmds in
11275 * backquotes. We set quoteflag to true if any part of the word was
11276 * quoted.
11277 * If the token is TREDIR, then we set redirnode to a structure containing
11278 * the redirection.
11279 * In all cases, the variable startlinno is set to the number of the line
11280 * on which the token starts.
11281 *
11282 * [Change comment: here documents and internal procedures]
11283 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11284 * word parsing code into a separate routine. In this case, readtoken
11285 * doesn't need to have any internal procedures, but parseword does.
11286 * We could also make parseoperator in essence the main routine, and
11287 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011288 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011289#define NEW_xxreadtoken
11290#ifdef NEW_xxreadtoken
11291/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011292static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011293 '\n', '(', ')', /* singles */
11294 '&', '|', ';', /* doubles */
11295 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011296};
Eric Andersencb57d552001-06-28 07:25:16 +000011297
Denis Vlasenko834dee72008-10-07 09:18:30 +000011298#define xxreadtoken_singles 3
11299#define xxreadtoken_doubles 3
11300
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011301static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011302 TNL, TLP, TRP, /* only single occurrence allowed */
11303 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11304 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011305 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011306};
11307
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011308static int
11309xxreadtoken(void)
11310{
11311 int c;
11312
11313 if (tokpushback) {
11314 tokpushback = 0;
11315 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011316 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011317 if (needprompt) {
11318 setprompt(2);
11319 }
11320 startlinno = plinno;
11321 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011322 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011323 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11324 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011325
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011326 if (c == '#') {
11327 while ((c = pgetc()) != '\n' && c != PEOF)
11328 continue;
11329 pungetc();
11330 } else if (c == '\\') {
11331 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011332 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011333 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011334 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011335 startlinno = ++plinno;
11336 if (doprompt)
11337 setprompt(2);
11338 } else {
11339 const char *p;
11340
11341 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11342 if (c != PEOF) {
11343 if (c == '\n') {
11344 plinno++;
11345 needprompt = doprompt;
11346 }
11347
11348 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011349 if (p == NULL)
11350 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011351
Denis Vlasenko834dee72008-10-07 09:18:30 +000011352 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11353 int cc = pgetc();
11354 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011355 p += xxreadtoken_doubles + 1;
11356 } else {
11357 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011358#if ENABLE_ASH_BASH_COMPAT
11359 if (c == '&' && cc == '>') /* &> */
11360 break; /* return readtoken1(...) */
11361#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011362 }
11363 }
11364 }
11365 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11366 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011367 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011368 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011369
11370 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011371}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011372#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011373#define RETURN(token) return lasttoken = token
11374static int
11375xxreadtoken(void)
11376{
11377 int c;
11378
11379 if (tokpushback) {
11380 tokpushback = 0;
11381 return lasttoken;
11382 }
11383 if (needprompt) {
11384 setprompt(2);
11385 }
11386 startlinno = plinno;
11387 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011388 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011389 switch (c) {
11390 case ' ': case '\t':
11391#if ENABLE_ASH_ALIAS
11392 case PEOA:
11393#endif
11394 continue;
11395 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011396 while ((c = pgetc()) != '\n' && c != PEOF)
11397 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011398 pungetc();
11399 continue;
11400 case '\\':
11401 if (pgetc() == '\n') {
11402 startlinno = ++plinno;
11403 if (doprompt)
11404 setprompt(2);
11405 continue;
11406 }
11407 pungetc();
11408 goto breakloop;
11409 case '\n':
11410 plinno++;
11411 needprompt = doprompt;
11412 RETURN(TNL);
11413 case PEOF:
11414 RETURN(TEOF);
11415 case '&':
11416 if (pgetc() == '&')
11417 RETURN(TAND);
11418 pungetc();
11419 RETURN(TBACKGND);
11420 case '|':
11421 if (pgetc() == '|')
11422 RETURN(TOR);
11423 pungetc();
11424 RETURN(TPIPE);
11425 case ';':
11426 if (pgetc() == ';')
11427 RETURN(TENDCASE);
11428 pungetc();
11429 RETURN(TSEMI);
11430 case '(':
11431 RETURN(TLP);
11432 case ')':
11433 RETURN(TRP);
11434 default:
11435 goto breakloop;
11436 }
11437 }
11438 breakloop:
11439 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11440#undef RETURN
11441}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011442#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011443
11444static int
11445readtoken(void)
11446{
11447 int t;
11448#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011449 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011450#endif
11451
11452#if ENABLE_ASH_ALIAS
11453 top:
11454#endif
11455
11456 t = xxreadtoken();
11457
11458 /*
11459 * eat newlines
11460 */
11461 if (checkkwd & CHKNL) {
11462 while (t == TNL) {
11463 parseheredoc();
11464 t = xxreadtoken();
11465 }
11466 }
11467
11468 if (t != TWORD || quoteflag) {
11469 goto out;
11470 }
11471
11472 /*
11473 * check for keywords
11474 */
11475 if (checkkwd & CHKKWD) {
11476 const char *const *pp;
11477
11478 pp = findkwd(wordtext);
11479 if (pp) {
11480 lasttoken = t = pp - tokname_array;
11481 TRACE(("keyword %s recognized\n", tokname(t)));
11482 goto out;
11483 }
11484 }
11485
11486 if (checkkwd & CHKALIAS) {
11487#if ENABLE_ASH_ALIAS
11488 struct alias *ap;
11489 ap = lookupalias(wordtext, 1);
11490 if (ap != NULL) {
11491 if (*ap->val) {
11492 pushstring(ap->val, ap);
11493 }
11494 goto top;
11495 }
11496#endif
11497 }
11498 out:
11499 checkkwd = 0;
11500#if DEBUG
11501 if (!alreadyseen)
11502 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11503 else
11504 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11505#endif
11506 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011507}
11508
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011509static char
11510peektoken(void)
11511{
11512 int t;
11513
11514 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011515 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011516 return tokname_array[t][0];
11517}
Eric Andersencb57d552001-06-28 07:25:16 +000011518
11519/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011520 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11521 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011522 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011523static union node *
11524parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011525{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011526 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011527
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011528 tokpushback = 0;
11529 doprompt = interact;
11530 if (doprompt)
11531 setprompt(doprompt);
11532 needprompt = 0;
11533 t = readtoken();
11534 if (t == TEOF)
11535 return NEOF;
11536 if (t == TNL)
11537 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011538 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011539 return list(1);
11540}
11541
11542/*
11543 * Input any here documents.
11544 */
11545static void
11546parseheredoc(void)
11547{
11548 struct heredoc *here;
11549 union node *n;
11550
11551 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011552 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011553
11554 while (here) {
11555 if (needprompt) {
11556 setprompt(2);
11557 }
11558 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11559 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011560 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011561 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011562 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011563 n->narg.text = wordtext;
11564 n->narg.backquote = backquotelist;
11565 here->here->nhere.doc = n;
11566 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011567 }
Eric Andersencb57d552001-06-28 07:25:16 +000011568}
11569
11570
11571/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011572 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011573 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011574#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011575static const char *
11576expandstr(const char *ps)
11577{
11578 union node n;
11579
11580 /* XXX Fix (char *) cast. */
11581 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011582 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011583 popfile();
11584
11585 n.narg.type = NARG;
11586 n.narg.next = NULL;
11587 n.narg.text = wordtext;
11588 n.narg.backquote = backquotelist;
11589
11590 expandarg(&n, NULL, 0);
11591 return stackblock();
11592}
11593#endif
11594
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011595/*
11596 * Execute a command or commands contained in a string.
11597 */
11598static int
11599evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011600{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011601 union node *n;
11602 struct stackmark smark;
11603 int skip;
11604
11605 setinputstring(s);
11606 setstackmark(&smark);
11607
11608 skip = 0;
11609 while ((n = parsecmd(0)) != NEOF) {
11610 evaltree(n, 0);
11611 popstackmark(&smark);
11612 skip = evalskip;
11613 if (skip)
11614 break;
11615 }
11616 popfile();
11617
11618 skip &= mask;
11619 evalskip = skip;
11620 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011621}
11622
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011623/*
11624 * The eval command.
11625 */
11626static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011627evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011628{
11629 char *p;
11630 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011631
Denis Vlasenko68404f12008-03-17 09:00:54 +000011632 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011633 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011634 argv += 2;
11635 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011636 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011637 for (;;) {
11638 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011639 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011640 if (p == NULL)
11641 break;
11642 STPUTC(' ', concat);
11643 }
11644 STPUTC('\0', concat);
11645 p = grabstackstr(concat);
11646 }
11647 evalstring(p, ~SKIPEVAL);
11648
11649 }
11650 return exitstatus;
11651}
11652
11653/*
11654 * Read and execute commands. "Top" is nonzero for the top level command
11655 * loop; it turns on prompting if the shell is interactive.
11656 */
11657static int
11658cmdloop(int top)
11659{
11660 union node *n;
11661 struct stackmark smark;
11662 int inter;
11663 int numeof = 0;
11664
11665 TRACE(("cmdloop(%d) called\n", top));
11666 for (;;) {
11667 int skip;
11668
11669 setstackmark(&smark);
11670#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011671 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011672 showjobs(stderr, SHOW_CHANGED);
11673#endif
11674 inter = 0;
11675 if (iflag && top) {
11676 inter++;
11677#if ENABLE_ASH_MAIL
11678 chkmail();
11679#endif
11680 }
11681 n = parsecmd(inter);
11682 /* showtree(n); DEBUG */
11683 if (n == NEOF) {
11684 if (!top || numeof >= 50)
11685 break;
11686 if (!stoppedjobs()) {
11687 if (!Iflag)
11688 break;
11689 out2str("\nUse \"exit\" to leave shell.\n");
11690 }
11691 numeof++;
11692 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011693 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11694 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011695 numeof = 0;
11696 evaltree(n, 0);
11697 }
11698 popstackmark(&smark);
11699 skip = evalskip;
11700
11701 if (skip) {
11702 evalskip = 0;
11703 return skip & SKIPEVAL;
11704 }
11705 }
11706 return 0;
11707}
11708
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011709/*
11710 * Take commands from a file. To be compatible we should do a path
11711 * search for the file, which is necessary to find sub-commands.
11712 */
11713static char *
11714find_dot_file(char *name)
11715{
11716 char *fullname;
11717 const char *path = pathval();
11718 struct stat statb;
11719
11720 /* don't try this for absolute or relative paths */
11721 if (strchr(name, '/'))
11722 return name;
11723
11724 while ((fullname = padvance(&path, name)) != NULL) {
11725 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11726 /*
11727 * Don't bother freeing here, since it will
11728 * be freed by the caller.
11729 */
11730 return fullname;
11731 }
11732 stunalloc(fullname);
11733 }
11734
11735 /* not found in the PATH */
11736 ash_msg_and_raise_error("%s: not found", name);
11737 /* NOTREACHED */
11738}
11739
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011740static int
11741dotcmd(int argc, char **argv)
11742{
11743 struct strlist *sp;
11744 volatile struct shparam saveparam;
11745 int status = 0;
11746
11747 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011748 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011749
Denis Vlasenko68404f12008-03-17 09:00:54 +000011750 if (argv[1]) { /* That's what SVR2 does */
11751 char *fullname = find_dot_file(argv[1]);
11752 argv += 2;
11753 argc -= 2;
11754 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011755 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011756 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011757 shellparam.nparam = argc;
11758 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011759 };
11760
11761 setinputfile(fullname, INPUT_PUSH_FILE);
11762 commandname = fullname;
11763 cmdloop(0);
11764 popfile();
11765
Denis Vlasenko68404f12008-03-17 09:00:54 +000011766 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011767 freeparam(&shellparam);
11768 shellparam = saveparam;
11769 };
11770 status = exitstatus;
11771 }
11772 return status;
11773}
11774
11775static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011776exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011777{
11778 if (stoppedjobs())
11779 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011780 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011781 exitstatus = number(argv[1]);
11782 raise_exception(EXEXIT);
11783 /* NOTREACHED */
11784}
11785
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011786/*
11787 * Read a file containing shell functions.
11788 */
11789static void
11790readcmdfile(char *name)
11791{
11792 setinputfile(name, INPUT_PUSH_FILE);
11793 cmdloop(0);
11794 popfile();
11795}
11796
11797
Denis Vlasenkocc571512007-02-23 21:10:35 +000011798/* ============ find_command inplementation */
11799
11800/*
11801 * Resolve a command name. If you change this routine, you may have to
11802 * change the shellexec routine as well.
11803 */
11804static void
11805find_command(char *name, struct cmdentry *entry, int act, const char *path)
11806{
11807 struct tblentry *cmdp;
11808 int idx;
11809 int prev;
11810 char *fullname;
11811 struct stat statb;
11812 int e;
11813 int updatetbl;
11814 struct builtincmd *bcmd;
11815
11816 /* If name contains a slash, don't use PATH or hash table */
11817 if (strchr(name, '/') != NULL) {
11818 entry->u.index = -1;
11819 if (act & DO_ABS) {
11820 while (stat(name, &statb) < 0) {
11821#ifdef SYSV
11822 if (errno == EINTR)
11823 continue;
11824#endif
11825 entry->cmdtype = CMDUNKNOWN;
11826 return;
11827 }
11828 }
11829 entry->cmdtype = CMDNORMAL;
11830 return;
11831 }
11832
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011833/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011834
11835 updatetbl = (path == pathval());
11836 if (!updatetbl) {
11837 act |= DO_ALTPATH;
11838 if (strstr(path, "%builtin") != NULL)
11839 act |= DO_ALTBLTIN;
11840 }
11841
11842 /* If name is in the table, check answer will be ok */
11843 cmdp = cmdlookup(name, 0);
11844 if (cmdp != NULL) {
11845 int bit;
11846
11847 switch (cmdp->cmdtype) {
11848 default:
11849#if DEBUG
11850 abort();
11851#endif
11852 case CMDNORMAL:
11853 bit = DO_ALTPATH;
11854 break;
11855 case CMDFUNCTION:
11856 bit = DO_NOFUNC;
11857 break;
11858 case CMDBUILTIN:
11859 bit = DO_ALTBLTIN;
11860 break;
11861 }
11862 if (act & bit) {
11863 updatetbl = 0;
11864 cmdp = NULL;
11865 } else if (cmdp->rehash == 0)
11866 /* if not invalidated by cd, we're done */
11867 goto success;
11868 }
11869
11870 /* If %builtin not in path, check for builtin next */
11871 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011872 if (bcmd) {
11873 if (IS_BUILTIN_REGULAR(bcmd))
11874 goto builtin_success;
11875 if (act & DO_ALTPATH) {
11876 if (!(act & DO_ALTBLTIN))
11877 goto builtin_success;
11878 } else if (builtinloc <= 0) {
11879 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011880 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011881 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011882
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011883#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011884 {
11885 int applet_no = find_applet_by_name(name);
11886 if (applet_no >= 0) {
11887 entry->cmdtype = CMDNORMAL;
11888 entry->u.index = -2 - applet_no;
11889 return;
11890 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011891 }
11892#endif
11893
Denis Vlasenkocc571512007-02-23 21:10:35 +000011894 /* We have to search path. */
11895 prev = -1; /* where to start */
11896 if (cmdp && cmdp->rehash) { /* doing a rehash */
11897 if (cmdp->cmdtype == CMDBUILTIN)
11898 prev = builtinloc;
11899 else
11900 prev = cmdp->param.index;
11901 }
11902
11903 e = ENOENT;
11904 idx = -1;
11905 loop:
11906 while ((fullname = padvance(&path, name)) != NULL) {
11907 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011908 /* NB: code below will still use fullname
11909 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011910 idx++;
11911 if (pathopt) {
11912 if (prefix(pathopt, "builtin")) {
11913 if (bcmd)
11914 goto builtin_success;
11915 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011916 }
11917 if ((act & DO_NOFUNC)
11918 || !prefix(pathopt, "func")
11919 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011920 continue;
11921 }
11922 }
11923 /* if rehash, don't redo absolute path names */
11924 if (fullname[0] == '/' && idx <= prev) {
11925 if (idx < prev)
11926 continue;
11927 TRACE(("searchexec \"%s\": no change\n", name));
11928 goto success;
11929 }
11930 while (stat(fullname, &statb) < 0) {
11931#ifdef SYSV
11932 if (errno == EINTR)
11933 continue;
11934#endif
11935 if (errno != ENOENT && errno != ENOTDIR)
11936 e = errno;
11937 goto loop;
11938 }
11939 e = EACCES; /* if we fail, this will be the error */
11940 if (!S_ISREG(statb.st_mode))
11941 continue;
11942 if (pathopt) { /* this is a %func directory */
11943 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011944 /* NB: stalloc will return space pointed by fullname
11945 * (because we don't have any intervening allocations
11946 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011947 readcmdfile(fullname);
11948 cmdp = cmdlookup(name, 0);
11949 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11950 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11951 stunalloc(fullname);
11952 goto success;
11953 }
11954 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11955 if (!updatetbl) {
11956 entry->cmdtype = CMDNORMAL;
11957 entry->u.index = idx;
11958 return;
11959 }
11960 INT_OFF;
11961 cmdp = cmdlookup(name, 1);
11962 cmdp->cmdtype = CMDNORMAL;
11963 cmdp->param.index = idx;
11964 INT_ON;
11965 goto success;
11966 }
11967
11968 /* We failed. If there was an entry for this command, delete it */
11969 if (cmdp && updatetbl)
11970 delete_cmd_entry();
11971 if (act & DO_ERR)
11972 ash_msg("%s: %s", name, errmsg(e, "not found"));
11973 entry->cmdtype = CMDUNKNOWN;
11974 return;
11975
11976 builtin_success:
11977 if (!updatetbl) {
11978 entry->cmdtype = CMDBUILTIN;
11979 entry->u.cmd = bcmd;
11980 return;
11981 }
11982 INT_OFF;
11983 cmdp = cmdlookup(name, 1);
11984 cmdp->cmdtype = CMDBUILTIN;
11985 cmdp->param.cmd = bcmd;
11986 INT_ON;
11987 success:
11988 cmdp->rehash = 0;
11989 entry->cmdtype = cmdp->cmdtype;
11990 entry->u = cmdp->param;
11991}
11992
11993
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011994/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011995
Eric Andersencb57d552001-06-28 07:25:16 +000011996/*
Eric Andersencb57d552001-06-28 07:25:16 +000011997 * The trap builtin.
11998 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011999static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012000trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012001{
12002 char *action;
12003 char **ap;
12004 int signo;
12005
Eric Andersenc470f442003-07-28 09:56:35 +000012006 nextopt(nullstr);
12007 ap = argptr;
12008 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012009 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012010 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012011 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012012 single_quote(trap[signo]),
12013 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012014 }
12015 }
12016 return 0;
12017 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012018 action = NULL;
12019 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012020 action = *ap++;
12021 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012022 signo = get_signum(*ap);
12023 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012024 ash_msg_and_raise_error("%s: bad trap", *ap);
12025 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012026 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012027 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012028 action = NULL;
12029 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012030 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012031 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012032 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012033 trap[signo] = action;
12034 if (signo != 0)
12035 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012036 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012037 ap++;
12038 }
12039 return 0;
12040}
12041
Eric Andersenc470f442003-07-28 09:56:35 +000012042
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012043/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012044
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012045#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012046/*
12047 * Lists available builtins
12048 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012049static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012050helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012051{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012052 unsigned col;
12053 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012054
12055 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012056 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012057 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012058 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012059 if (col > 60) {
12060 out1fmt("\n");
12061 col = 0;
12062 }
12063 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012064#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012065 {
12066 const char *a = applet_names;
12067 while (*a) {
12068 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12069 if (col > 60) {
12070 out1fmt("\n");
12071 col = 0;
12072 }
12073 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012074 }
12075 }
12076#endif
12077 out1fmt("\n\n");
12078 return EXIT_SUCCESS;
12079}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012080#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012081
Eric Andersencb57d552001-06-28 07:25:16 +000012082/*
Eric Andersencb57d552001-06-28 07:25:16 +000012083 * The export and readonly commands.
12084 */
Eric Andersenc470f442003-07-28 09:56:35 +000012085static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012086exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012087{
12088 struct var *vp;
12089 char *name;
12090 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012091 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012092 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012093
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012094 if (nextopt("p") != 'p') {
12095 aptr = argptr;
12096 name = *aptr;
12097 if (name) {
12098 do {
12099 p = strchr(name, '=');
12100 if (p != NULL) {
12101 p++;
12102 } else {
12103 vp = *findvar(hashvar(name), name);
12104 if (vp) {
12105 vp->flags |= flag;
12106 continue;
12107 }
Eric Andersencb57d552001-06-28 07:25:16 +000012108 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012109 setvar(name, p, flag);
12110 } while ((name = *++aptr) != NULL);
12111 return 0;
12112 }
Eric Andersencb57d552001-06-28 07:25:16 +000012113 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012114 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012115 return 0;
12116}
12117
Eric Andersencb57d552001-06-28 07:25:16 +000012118/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012119 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012120 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012121static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012122unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012123{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012124 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012125
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012126 cmdp = cmdlookup(name, 0);
12127 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12128 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012129}
12130
Eric Andersencb57d552001-06-28 07:25:16 +000012131/*
Eric Andersencb57d552001-06-28 07:25:16 +000012132 * The unset builtin command. We unset the function before we unset the
12133 * variable to allow a function to be unset when there is a readonly variable
12134 * with the same name.
12135 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012136static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012137unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012138{
12139 char **ap;
12140 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012141 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012142 int ret = 0;
12143
12144 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012145 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012146 }
Eric Andersencb57d552001-06-28 07:25:16 +000012147
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012148 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012149 if (flag != 'f') {
12150 i = unsetvar(*ap);
12151 ret |= i;
12152 if (!(i & 2))
12153 continue;
12154 }
12155 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012156 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012157 }
Eric Andersenc470f442003-07-28 09:56:35 +000012158 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012159}
12160
12161
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012162/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012163
Eric Andersenc470f442003-07-28 09:56:35 +000012164#include <sys/times.h>
12165
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012166static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012167 ' ', offsetof(struct tms, tms_utime),
12168 '\n', offsetof(struct tms, tms_stime),
12169 ' ', offsetof(struct tms, tms_cutime),
12170 '\n', offsetof(struct tms, tms_cstime),
12171 0
12172};
Eric Andersencb57d552001-06-28 07:25:16 +000012173
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012174static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012175timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012176{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012177 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012178 const unsigned char *p;
12179 struct tms buf;
12180
12181 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012182 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012183
12184 p = timescmd_str;
12185 do {
12186 t = *(clock_t *)(((char *) &buf) + p[1]);
12187 s = t / clk_tck;
12188 out1fmt("%ldm%ld.%.3lds%c",
12189 s/60, s%60,
12190 ((t - s * clk_tck) * 1000) / clk_tck,
12191 p[0]);
12192 } while (*(p += 2));
12193
Eric Andersencb57d552001-06-28 07:25:16 +000012194 return 0;
12195}
12196
Denis Vlasenko131ae172007-02-18 13:00:19 +000012197#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012198static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012199dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012200{
Eric Andersened9ecf72004-06-22 08:29:45 +000012201 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012202 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012203
Denis Vlasenkob012b102007-02-19 22:43:01 +000012204 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012205 result = arith(s, &errcode);
12206 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012207 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012208 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012209 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012210 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012211 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012212 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012213 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012214 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012215 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012216
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012217 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012218}
Eric Andersenc470f442003-07-28 09:56:35 +000012219
Eric Andersenc470f442003-07-28 09:56:35 +000012220/*
Eric Andersen90898442003-08-06 11:20:52 +000012221 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12222 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12223 *
12224 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012225 */
12226static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012227letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012228{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012229 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012230
Denis Vlasenko68404f12008-03-17 09:00:54 +000012231 argv++;
12232 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012233 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012234 do {
12235 i = dash_arith(*argv);
12236 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012237
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012238 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012239}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012240#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012241
Eric Andersenc470f442003-07-28 09:56:35 +000012242
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012243/* ============ miscbltin.c
12244 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012245 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012246 */
12247
12248#undef rflag
12249
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012250#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012251typedef enum __rlimit_resource rlim_t;
12252#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012253
Eric Andersenc470f442003-07-28 09:56:35 +000012254/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012255 * The read builtin. Options:
12256 * -r Do not interpret '\' specially
12257 * -s Turn off echo (tty only)
12258 * -n NCHARS Read NCHARS max
12259 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12260 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12261 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012262 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012263 * TODO: bash also has:
12264 * -a ARRAY Read into array[0],[1],etc
12265 * -d DELIM End on DELIM char, not newline
12266 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012267 */
Eric Andersenc470f442003-07-28 09:56:35 +000012268static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012269readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012270{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012271 static const char *const arg_REPLY[] = { "REPLY", NULL };
12272
Eric Andersenc470f442003-07-28 09:56:35 +000012273 char **ap;
12274 int backslash;
12275 char c;
12276 int rflag;
12277 char *prompt;
12278 const char *ifs;
12279 char *p;
12280 int startword;
12281 int status;
12282 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012283 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012284#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012285 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012286 int silent = 0;
12287 struct termios tty, old_tty;
12288#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012289#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012290 unsigned end_ms = 0;
12291 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012292#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012293
12294 rflag = 0;
12295 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012296 while ((i = nextopt("p:u:r"
12297 USE_ASH_READ_TIMEOUT("t:")
12298 USE_ASH_READ_NCHARS("n:s")
12299 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012300 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012301 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012302 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012303 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012304#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012305 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012306 nchars = bb_strtou(optionarg, NULL, 10);
12307 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012308 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012309 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012310 break;
12311 case 's':
12312 silent = 1;
12313 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012314#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012315#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012316 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012317 timeout = bb_strtou(optionarg, NULL, 10);
12318 if (errno || timeout > UINT_MAX / 2048)
12319 ash_msg_and_raise_error("invalid timeout");
12320 timeout *= 1000;
12321#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012322 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012323 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012324 /* EINVAL means number is ok, but not terminated by NUL */
12325 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012326 char *p2;
12327 if (*++p) {
12328 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012329 ts.tv_usec = bb_strtou(p, &p2, 10);
12330 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012331 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012332 scale = p2 - p;
12333 /* normalize to usec */
12334 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012335 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012336 while (scale++ < 6)
12337 ts.tv_usec *= 10;
12338 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012339 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012340 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012341 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012342 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012343 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012344 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012345#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012346 break;
12347#endif
12348 case 'r':
12349 rflag = 1;
12350 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012351 case 'u':
12352 fd = bb_strtou(optionarg, NULL, 10);
12353 if (fd < 0 || errno)
12354 ash_msg_and_raise_error("invalid file descriptor");
12355 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012356 default:
12357 break;
12358 }
Eric Andersenc470f442003-07-28 09:56:35 +000012359 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012360 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012361 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012362 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012363 ap = argptr;
12364 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012365 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012366 ifs = bltinlookup("IFS");
12367 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012368 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012369#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012370 tcgetattr(fd, &tty);
12371 old_tty = tty;
12372 if (nchars || silent) {
12373 if (nchars) {
12374 tty.c_lflag &= ~ICANON;
12375 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012376 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012377 if (silent) {
12378 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12379 }
12380 /* if tcgetattr failed, tcsetattr will fail too.
12381 * Ignoring, it's harmless. */
12382 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012383 }
12384#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012385
Eric Andersenc470f442003-07-28 09:56:35 +000012386 status = 0;
12387 startword = 1;
12388 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012389#if ENABLE_ASH_READ_TIMEOUT
12390 if (timeout) /* NB: ensuring end_ms is nonzero */
12391 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12392#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012393 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012394 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012395#if ENABLE_ASH_READ_TIMEOUT
12396 if (end_ms) {
12397 struct pollfd pfd[1];
12398 pfd[0].fd = fd;
12399 pfd[0].events = POLLIN;
12400 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12401 if ((int)timeout <= 0 /* already late? */
12402 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12403 ) { /* timed out! */
12404#if ENABLE_ASH_READ_NCHARS
12405 tcsetattr(fd, TCSANOW, &old_tty);
12406#endif
12407 return 1;
12408 }
12409 }
12410#endif
12411 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012412 status = 1;
12413 break;
12414 }
12415 if (c == '\0')
12416 continue;
12417 if (backslash) {
12418 backslash = 0;
12419 if (c != '\n')
12420 goto put;
12421 continue;
12422 }
12423 if (!rflag && c == '\\') {
12424 backslash++;
12425 continue;
12426 }
12427 if (c == '\n')
12428 break;
12429 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12430 continue;
12431 }
12432 startword = 0;
12433 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12434 STACKSTRNUL(p);
12435 setvar(*ap, stackblock(), 0);
12436 ap++;
12437 startword = 1;
12438 STARTSTACKSTR(p);
12439 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012440 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012441 STPUTC(c, p);
12442 }
12443 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012444/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012445#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012446 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012447#else
12448 while (1);
12449#endif
12450
12451#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012452 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012453#endif
12454
Eric Andersenc470f442003-07-28 09:56:35 +000012455 STACKSTRNUL(p);
12456 /* Remove trailing blanks */
12457 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12458 *p = '\0';
12459 setvar(*ap, stackblock(), 0);
12460 while (*++ap != NULL)
12461 setvar(*ap, nullstr, 0);
12462 return status;
12463}
12464
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012465static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012466umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012467{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012468 static const char permuser[3] ALIGN1 = "ugo";
12469 static const char permmode[3] ALIGN1 = "rwx";
12470 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012471 S_IRUSR, S_IWUSR, S_IXUSR,
12472 S_IRGRP, S_IWGRP, S_IXGRP,
12473 S_IROTH, S_IWOTH, S_IXOTH
12474 };
12475
12476 char *ap;
12477 mode_t mask;
12478 int i;
12479 int symbolic_mode = 0;
12480
12481 while (nextopt("S") != '\0') {
12482 symbolic_mode = 1;
12483 }
12484
Denis Vlasenkob012b102007-02-19 22:43:01 +000012485 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012486 mask = umask(0);
12487 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012488 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012489
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012490 ap = *argptr;
12491 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012492 if (symbolic_mode) {
12493 char buf[18];
12494 char *p = buf;
12495
12496 for (i = 0; i < 3; i++) {
12497 int j;
12498
12499 *p++ = permuser[i];
12500 *p++ = '=';
12501 for (j = 0; j < 3; j++) {
12502 if ((mask & permmask[3 * i + j]) == 0) {
12503 *p++ = permmode[j];
12504 }
12505 }
12506 *p++ = ',';
12507 }
12508 *--p = 0;
12509 puts(buf);
12510 } else {
12511 out1fmt("%.4o\n", mask);
12512 }
12513 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012514 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012515 mask = 0;
12516 do {
12517 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012518 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012519 mask = (mask << 3) + (*ap - '0');
12520 } while (*++ap != '\0');
12521 umask(mask);
12522 } else {
12523 mask = ~mask & 0777;
12524 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012525 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012526 }
12527 umask(~mask & 0777);
12528 }
12529 }
12530 return 0;
12531}
12532
12533/*
12534 * ulimit builtin
12535 *
12536 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12537 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12538 * ash by J.T. Conklin.
12539 *
12540 * Public domain.
12541 */
12542
12543struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012544 uint8_t cmd; /* RLIMIT_xxx fit into it */
12545 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012546 char option;
12547};
12548
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012549static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012550#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012551 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012552#endif
12553#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012554 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012555#endif
12556#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012557 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012558#endif
12559#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012560 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012561#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012562#ifdef RLIMIT_CORE
12563 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012564#endif
12565#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012566 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012567#endif
12568#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012569 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012570#endif
12571#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012572 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012573#endif
12574#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012575 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012576#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012577#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012578 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012579#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012580#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012581 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012582#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012583};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012584static const char limits_name[] =
12585#ifdef RLIMIT_CPU
12586 "time(seconds)" "\0"
12587#endif
12588#ifdef RLIMIT_FSIZE
12589 "file(blocks)" "\0"
12590#endif
12591#ifdef RLIMIT_DATA
12592 "data(kb)" "\0"
12593#endif
12594#ifdef RLIMIT_STACK
12595 "stack(kb)" "\0"
12596#endif
12597#ifdef RLIMIT_CORE
12598 "coredump(blocks)" "\0"
12599#endif
12600#ifdef RLIMIT_RSS
12601 "memory(kb)" "\0"
12602#endif
12603#ifdef RLIMIT_MEMLOCK
12604 "locked memory(kb)" "\0"
12605#endif
12606#ifdef RLIMIT_NPROC
12607 "process" "\0"
12608#endif
12609#ifdef RLIMIT_NOFILE
12610 "nofiles" "\0"
12611#endif
12612#ifdef RLIMIT_AS
12613 "vmemory(kb)" "\0"
12614#endif
12615#ifdef RLIMIT_LOCKS
12616 "locks" "\0"
12617#endif
12618;
Eric Andersenc470f442003-07-28 09:56:35 +000012619
Glenn L McGrath76620622004-01-13 10:19:37 +000012620enum limtype { SOFT = 0x1, HARD = 0x2 };
12621
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012622static void
12623printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012624 const struct limits *l)
12625{
12626 rlim_t val;
12627
12628 val = limit->rlim_max;
12629 if (how & SOFT)
12630 val = limit->rlim_cur;
12631
12632 if (val == RLIM_INFINITY)
12633 out1fmt("unlimited\n");
12634 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012635 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012636 out1fmt("%lld\n", (long long) val);
12637 }
12638}
12639
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012640static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012641ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012642{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012643 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012644 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012645 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012646 const struct limits *l;
12647 int set, all = 0;
12648 int optc, what;
12649 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012650
12651 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012652 while ((optc = nextopt("HSa"
12653#ifdef RLIMIT_CPU
12654 "t"
12655#endif
12656#ifdef RLIMIT_FSIZE
12657 "f"
12658#endif
12659#ifdef RLIMIT_DATA
12660 "d"
12661#endif
12662#ifdef RLIMIT_STACK
12663 "s"
12664#endif
12665#ifdef RLIMIT_CORE
12666 "c"
12667#endif
12668#ifdef RLIMIT_RSS
12669 "m"
12670#endif
12671#ifdef RLIMIT_MEMLOCK
12672 "l"
12673#endif
12674#ifdef RLIMIT_NPROC
12675 "p"
12676#endif
12677#ifdef RLIMIT_NOFILE
12678 "n"
12679#endif
12680#ifdef RLIMIT_AS
12681 "v"
12682#endif
12683#ifdef RLIMIT_LOCKS
12684 "w"
12685#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012686 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012687 switch (optc) {
12688 case 'H':
12689 how = HARD;
12690 break;
12691 case 'S':
12692 how = SOFT;
12693 break;
12694 case 'a':
12695 all = 1;
12696 break;
12697 default:
12698 what = optc;
12699 }
12700
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012701 for (l = limits_tbl; l->option != what; l++)
12702 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012703
12704 set = *argptr ? 1 : 0;
12705 if (set) {
12706 char *p = *argptr;
12707
12708 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012709 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012710 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012711 val = RLIM_INFINITY;
12712 else {
12713 val = (rlim_t) 0;
12714
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012715 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012716 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012717 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012718 if (val < (rlim_t) 0)
12719 break;
12720 }
12721 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012722 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012723 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012724 }
12725 }
12726 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012727 const char *lname = limits_name;
12728 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012729 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012730 out1fmt("%-20s ", lname);
12731 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012732 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012733 }
12734 return 0;
12735 }
12736
12737 getrlimit(l->cmd, &limit);
12738 if (set) {
12739 if (how & HARD)
12740 limit.rlim_max = val;
12741 if (how & SOFT)
12742 limit.rlim_cur = val;
12743 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012744 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012745 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012746 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012747 }
12748 return 0;
12749}
12750
Eric Andersen90898442003-08-06 11:20:52 +000012751
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012752/* ============ Math support */
12753
Denis Vlasenko131ae172007-02-18 13:00:19 +000012754#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012755
12756/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12757
12758 Permission is hereby granted, free of charge, to any person obtaining
12759 a copy of this software and associated documentation files (the
12760 "Software"), to deal in the Software without restriction, including
12761 without limitation the rights to use, copy, modify, merge, publish,
12762 distribute, sublicense, and/or sell copies of the Software, and to
12763 permit persons to whom the Software is furnished to do so, subject to
12764 the following conditions:
12765
12766 The above copyright notice and this permission notice shall be
12767 included in all copies or substantial portions of the Software.
12768
12769 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12770 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12771 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12772 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12773 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12774 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12775 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12776*/
12777
12778/* This is my infix parser/evaluator. It is optimized for size, intended
12779 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012780 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012781 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012782 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012783 * be that which POSIX specifies for shells. */
12784
12785/* The code uses a simple two-stack algorithm. See
12786 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012787 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012788 * this is based (this code differs in that it applies operators immediately
12789 * to the stack instead of adding them to a queue to end up with an
12790 * expression). */
12791
12792/* To use the routine, call it with an expression string and error return
12793 * pointer */
12794
12795/*
12796 * Aug 24, 2001 Manuel Novoa III
12797 *
12798 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12799 *
12800 * 1) In arith_apply():
12801 * a) Cached values of *numptr and &(numptr[-1]).
12802 * b) Removed redundant test for zero denominator.
12803 *
12804 * 2) In arith():
12805 * a) Eliminated redundant code for processing operator tokens by moving
12806 * to a table-based implementation. Also folded handling of parens
12807 * into the table.
12808 * b) Combined all 3 loops which called arith_apply to reduce generated
12809 * code size at the cost of speed.
12810 *
12811 * 3) The following expressions were treated as valid by the original code:
12812 * 1() , 0! , 1 ( *3 ) .
12813 * These bugs have been fixed by internally enclosing the expression in
12814 * parens and then checking that all binary ops and right parens are
12815 * preceded by a valid expression (NUM_TOKEN).
12816 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012817 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012818 * ctype's isspace() if it is used by another busybox applet or if additional
12819 * whitespace chars should be considered. Look below the "#include"s for a
12820 * precompiler test.
12821 */
12822
12823/*
12824 * Aug 26, 2001 Manuel Novoa III
12825 *
12826 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12827 *
12828 * Merge in Aaron's comments previously posted to the busybox list,
12829 * modified slightly to take account of my changes to the code.
12830 *
12831 */
12832
12833/*
12834 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12835 *
12836 * - allow access to variable,
12837 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12838 * - realize assign syntax (VAR=expr, +=, *= etc)
12839 * - realize exponentiation (** operator)
12840 * - realize comma separated - expr, expr
12841 * - realise ++expr --expr expr++ expr--
12842 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012843 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012844 * - was restored loses XOR operator
12845 * - remove one goto label, added three ;-)
12846 * - protect $((num num)) as true zero expr (Manuel`s error)
12847 * - always use special isspace(), see comment from bash ;-)
12848 */
12849
Eric Andersen90898442003-08-06 11:20:52 +000012850#define arith_isspace(arithval) \
12851 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12852
Eric Andersen90898442003-08-06 11:20:52 +000012853typedef unsigned char operator;
12854
12855/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012856 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012857 * precedence. The ID portion is so that multiple operators can have the
12858 * same precedence, ensuring that the leftmost one is evaluated first.
12859 * Consider * and /. */
12860
12861#define tok_decl(prec,id) (((id)<<5)|(prec))
12862#define PREC(op) ((op) & 0x1F)
12863
12864#define TOK_LPAREN tok_decl(0,0)
12865
12866#define TOK_COMMA tok_decl(1,0)
12867
12868#define TOK_ASSIGN tok_decl(2,0)
12869#define TOK_AND_ASSIGN tok_decl(2,1)
12870#define TOK_OR_ASSIGN tok_decl(2,2)
12871#define TOK_XOR_ASSIGN tok_decl(2,3)
12872#define TOK_PLUS_ASSIGN tok_decl(2,4)
12873#define TOK_MINUS_ASSIGN tok_decl(2,5)
12874#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12875#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12876
12877#define TOK_MUL_ASSIGN tok_decl(3,0)
12878#define TOK_DIV_ASSIGN tok_decl(3,1)
12879#define TOK_REM_ASSIGN tok_decl(3,2)
12880
12881/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012882#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012883
12884/* conditional is right associativity too */
12885#define TOK_CONDITIONAL tok_decl(4,0)
12886#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12887
12888#define TOK_OR tok_decl(5,0)
12889
12890#define TOK_AND tok_decl(6,0)
12891
12892#define TOK_BOR tok_decl(7,0)
12893
12894#define TOK_BXOR tok_decl(8,0)
12895
12896#define TOK_BAND tok_decl(9,0)
12897
12898#define TOK_EQ tok_decl(10,0)
12899#define TOK_NE tok_decl(10,1)
12900
12901#define TOK_LT tok_decl(11,0)
12902#define TOK_GT tok_decl(11,1)
12903#define TOK_GE tok_decl(11,2)
12904#define TOK_LE tok_decl(11,3)
12905
12906#define TOK_LSHIFT tok_decl(12,0)
12907#define TOK_RSHIFT tok_decl(12,1)
12908
12909#define TOK_ADD tok_decl(13,0)
12910#define TOK_SUB tok_decl(13,1)
12911
12912#define TOK_MUL tok_decl(14,0)
12913#define TOK_DIV tok_decl(14,1)
12914#define TOK_REM tok_decl(14,2)
12915
12916/* exponent is right associativity */
12917#define TOK_EXPONENT tok_decl(15,1)
12918
12919/* For now unary operators. */
12920#define UNARYPREC 16
12921#define TOK_BNOT tok_decl(UNARYPREC,0)
12922#define TOK_NOT tok_decl(UNARYPREC,1)
12923
12924#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12925#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12926
12927#define PREC_PRE (UNARYPREC+2)
12928
12929#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12930#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12931
12932#define PREC_POST (UNARYPREC+3)
12933
12934#define TOK_POST_INC tok_decl(PREC_POST, 0)
12935#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12936
12937#define SPEC_PREC (UNARYPREC+4)
12938
12939#define TOK_NUM tok_decl(SPEC_PREC, 0)
12940#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12941
12942#define NUMPTR (*numstackptr)
12943
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012944static int
12945tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012946{
12947 operator prec = PREC(op);
12948
12949 convert_prec_is_assing(prec);
12950 return (prec == PREC(TOK_ASSIGN) ||
12951 prec == PREC_PRE || prec == PREC_POST);
12952}
12953
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012954static int
12955is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012956{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012957 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12958 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012959}
12960
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012961typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012962 arith_t val;
12963 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012964 char contidional_second_val_initialized;
12965 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012966 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012967} v_n_t;
12968
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012969typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012970 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012971 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012972} chk_var_recursive_looped_t;
12973
12974static chk_var_recursive_looped_t *prev_chk_var_recursive;
12975
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012976static int
12977arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012978{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012979 if (t->var) {
12980 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012981
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012982 if (p) {
12983 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012984
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012985 /* recursive try as expression */
12986 chk_var_recursive_looped_t *cur;
12987 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012988
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012989 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12990 if (strcmp(cur->var, t->var) == 0) {
12991 /* expression recursion loop detected */
12992 return -5;
12993 }
12994 }
12995 /* save current lookuped var name */
12996 cur = prev_chk_var_recursive;
12997 cur_save.var = t->var;
12998 cur_save.next = cur;
12999 prev_chk_var_recursive = &cur_save;
13000
13001 t->val = arith (p, &errcode);
13002 /* restore previous ptr after recursiving */
13003 prev_chk_var_recursive = cur;
13004 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013005 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013006 /* allow undefined var as 0 */
13007 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013008 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013010}
13011
13012/* "applying" a token means performing it on the top elements on the integer
13013 * stack. For a unary operator it will only change the top element, but a
13014 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013015static int
13016arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013017{
Eric Andersen90898442003-08-06 11:20:52 +000013018 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013019 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013020 int ret_arith_lookup_val;
13021
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013022 /* There is no operator that can work without arguments */
13023 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013024 numptr_m1 = NUMPTR - 1;
13025
13026 /* check operand is var with noninteger value */
13027 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013028 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013029 return ret_arith_lookup_val;
13030
13031 rez = numptr_m1->val;
13032 if (op == TOK_UMINUS)
13033 rez *= -1;
13034 else if (op == TOK_NOT)
13035 rez = !rez;
13036 else if (op == TOK_BNOT)
13037 rez = ~rez;
13038 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13039 rez++;
13040 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13041 rez--;
13042 else if (op != TOK_UPLUS) {
13043 /* Binary operators */
13044
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013045 /* check and binary operators need two arguments */
13046 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013047
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013048 /* ... and they pop one */
13049 --NUMPTR;
13050 numptr_val = rez;
13051 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013052 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013053 /* protect $((expr1 ? expr2)) without ": expr" */
13054 goto err;
13055 }
13056 rez = numptr_m1->contidional_second_val;
13057 } else if (numptr_m1->contidional_second_val_initialized) {
13058 /* protect $((expr1 : expr2)) without "expr ? " */
13059 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013060 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013061 numptr_m1 = NUMPTR - 1;
13062 if (op != TOK_ASSIGN) {
13063 /* check operand is var with noninteger value for not '=' */
13064 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13065 if (ret_arith_lookup_val)
13066 return ret_arith_lookup_val;
13067 }
13068 if (op == TOK_CONDITIONAL) {
13069 numptr_m1->contidional_second_val = rez;
13070 }
13071 rez = numptr_m1->val;
13072 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013073 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013074 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013075 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013076 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013077 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013078 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013079 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013080 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013081 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013082 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013083 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013084 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013085 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013087 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013089 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013090 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013091 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013092 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013093 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013095 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013097 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013098 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013099 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013100 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013101 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013102 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013103 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013104 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013105 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013106 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013107 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013108 /* protect $((expr : expr)) without "expr ? " */
13109 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013110 }
13111 numptr_m1->contidional_second_val_initialized = op;
13112 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013113 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013114 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013115 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013116 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013117 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013118 return -3; /* exponent less than 0 */
13119 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013120 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013121
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013122 if (numptr_val)
13123 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013124 c *= rez;
13125 rez = c;
13126 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013127 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013128 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013129 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013130 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013131 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013132 rez %= numptr_val;
13133 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013134 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013135 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013136
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013138 /* Hmm, 1=2 ? */
13139 goto err;
13140 }
13141 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013142#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013143 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013144#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013145 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013146#endif
Eric Andersen90898442003-08-06 11:20:52 +000013147 setvar(numptr_m1->var, buf, 0);
13148 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013149 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013150 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013151 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013152 rez++;
13153 }
13154 numptr_m1->val = rez;
13155 /* protect geting var value, is number now */
13156 numptr_m1->var = NULL;
13157 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013158 err:
13159 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013160}
13161
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013162/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013163static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013164 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13165 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13166 '<','<', 0, TOK_LSHIFT,
13167 '>','>', 0, TOK_RSHIFT,
13168 '|','|', 0, TOK_OR,
13169 '&','&', 0, TOK_AND,
13170 '!','=', 0, TOK_NE,
13171 '<','=', 0, TOK_LE,
13172 '>','=', 0, TOK_GE,
13173 '=','=', 0, TOK_EQ,
13174 '|','=', 0, TOK_OR_ASSIGN,
13175 '&','=', 0, TOK_AND_ASSIGN,
13176 '*','=', 0, TOK_MUL_ASSIGN,
13177 '/','=', 0, TOK_DIV_ASSIGN,
13178 '%','=', 0, TOK_REM_ASSIGN,
13179 '+','=', 0, TOK_PLUS_ASSIGN,
13180 '-','=', 0, TOK_MINUS_ASSIGN,
13181 '-','-', 0, TOK_POST_DEC,
13182 '^','=', 0, TOK_XOR_ASSIGN,
13183 '+','+', 0, TOK_POST_INC,
13184 '*','*', 0, TOK_EXPONENT,
13185 '!', 0, TOK_NOT,
13186 '<', 0, TOK_LT,
13187 '>', 0, TOK_GT,
13188 '=', 0, TOK_ASSIGN,
13189 '|', 0, TOK_BOR,
13190 '&', 0, TOK_BAND,
13191 '*', 0, TOK_MUL,
13192 '/', 0, TOK_DIV,
13193 '%', 0, TOK_REM,
13194 '+', 0, TOK_ADD,
13195 '-', 0, TOK_SUB,
13196 '^', 0, TOK_BXOR,
13197 /* uniq */
13198 '~', 0, TOK_BNOT,
13199 ',', 0, TOK_COMMA,
13200 '?', 0, TOK_CONDITIONAL,
13201 ':', 0, TOK_CONDITIONAL_SEP,
13202 ')', 0, TOK_RPAREN,
13203 '(', 0, TOK_LPAREN,
13204 0
13205};
13206/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013207#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013208
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013209static arith_t
13210arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013211{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013212 char arithval; /* Current character under analysis */
13213 operator lasttok, op;
13214 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013215 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013216 const char *p = endexpression;
13217 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013218 v_n_t *numstack, *numstackptr;
13219 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013220
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 /* Stack of integers */
13222 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13223 * in any given correct or incorrect expression is left as an exercise to
13224 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013225 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013226 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013227 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013228
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013229 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13230 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013231
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013232 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013233 arithval = *expr;
13234 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013235 if (p == endexpression) {
13236 /* Null expression. */
13237 return 0;
13238 }
13239
13240 /* This is only reached after all tokens have been extracted from the
13241 * input stream. If there are still tokens on the operator stack, they
13242 * are to be applied in order. At the end, there should be a final
13243 * result on the integer stack */
13244
13245 if (expr != endexpression + 1) {
13246 /* If we haven't done so already, */
13247 /* append a closing right paren */
13248 expr = endexpression;
13249 /* and let the loop process it. */
13250 continue;
13251 }
13252 /* At this point, we're done with the expression. */
13253 if (numstackptr != numstack+1) {
13254 /* ... but if there isn't, it's bad */
13255 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013256 *perrcode = -1;
13257 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013258 }
13259 if (numstack->var) {
13260 /* expression is $((var)) only, lookup now */
13261 errcode = arith_lookup_val(numstack);
13262 }
13263 ret:
13264 *perrcode = errcode;
13265 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013266 }
13267
Eric Andersen90898442003-08-06 11:20:52 +000013268 /* Continue processing the expression. */
13269 if (arith_isspace(arithval)) {
13270 /* Skip whitespace */
13271 goto prologue;
13272 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013273 p = endofname(expr);
13274 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013275 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013276
13277 numstackptr->var = alloca(var_name_size);
13278 safe_strncpy(numstackptr->var, expr, var_name_size);
13279 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013280 num:
Eric Andersen90898442003-08-06 11:20:52 +000013281 numstackptr->contidional_second_val_initialized = 0;
13282 numstackptr++;
13283 lasttok = TOK_NUM;
13284 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013285 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013286 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013287 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013288#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013289 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013290#else
13291 numstackptr->val = strtol(expr, (char **) &expr, 0);
13292#endif
Eric Andersen90898442003-08-06 11:20:52 +000013293 goto num;
13294 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013295 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013296 const char *o;
13297
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013298 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013299 /* strange operator not found */
13300 goto err;
13301 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013302 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013303 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013304 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013305 /* found */
13306 expr = o - 1;
13307 break;
13308 }
13309 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013310 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013311 p++;
13312 /* skip zero delim */
13313 p++;
13314 }
13315 op = p[1];
13316
13317 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013318 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13319 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013320
13321 /* Plus and minus are binary (not unary) _only_ if the last
13322 * token was as number, or a right paren (which pretends to be
13323 * a number, since it evaluates to one). Think about it.
13324 * It makes sense. */
13325 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013326 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013327 case TOK_ADD:
13328 op = TOK_UPLUS;
13329 break;
13330 case TOK_SUB:
13331 op = TOK_UMINUS;
13332 break;
13333 case TOK_POST_INC:
13334 op = TOK_PRE_INC;
13335 break;
13336 case TOK_POST_DEC:
13337 op = TOK_PRE_DEC;
13338 break;
Eric Andersen90898442003-08-06 11:20:52 +000013339 }
13340 }
13341 /* We don't want a unary operator to cause recursive descent on the
13342 * stack, because there can be many in a row and it could cause an
13343 * operator to be evaluated before its argument is pushed onto the
13344 * integer stack. */
13345 /* But for binary operators, "apply" everything on the operator
13346 * stack until we find an operator with a lesser priority than the
13347 * one we have just extracted. */
13348 /* Left paren is given the lowest priority so it will never be
13349 * "applied" in this way.
13350 * if associativity is right and priority eq, applied also skip
13351 */
13352 prec = PREC(op);
13353 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13354 /* not left paren or unary */
13355 if (lasttok != TOK_NUM) {
13356 /* binary op must be preceded by a num */
13357 goto err;
13358 }
13359 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013360 if (op == TOK_RPAREN) {
13361 /* The algorithm employed here is simple: while we don't
13362 * hit an open paren nor the bottom of the stack, pop
13363 * tokens and apply them */
13364 if (stackptr[-1] == TOK_LPAREN) {
13365 --stackptr;
13366 /* Any operator directly after a */
13367 lasttok = TOK_NUM;
13368 /* close paren should consider itself binary */
13369 goto prologue;
13370 }
13371 } else {
13372 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013373
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013374 convert_prec_is_assing(prec);
13375 convert_prec_is_assing(prev_prec);
13376 if (prev_prec < prec)
13377 break;
13378 /* check right assoc */
13379 if (prev_prec == prec && is_right_associativity(prec))
13380 break;
13381 }
13382 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13383 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013384 }
13385 if (op == TOK_RPAREN) {
13386 goto err;
13387 }
13388 }
13389
13390 /* Push this operator to the stack and remember it. */
13391 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013392 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013393 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013394 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013395}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013396#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013397
13398
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013399/* ============ main() and helpers */
13400
13401/*
13402 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013403 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013404static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013405static void
13406exitshell(void)
13407{
13408 struct jmploc loc;
13409 char *p;
13410 int status;
13411
13412 status = exitstatus;
13413 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13414 if (setjmp(loc.loc)) {
13415 if (exception == EXEXIT)
13416/* dash bug: it just does _exit(exitstatus) here
13417 * but we have to do setjobctl(0) first!
13418 * (bug is still not fixed in dash-0.5.3 - if you run dash
13419 * under Midnight Commander, on exit from dash MC is backgrounded) */
13420 status = exitstatus;
13421 goto out;
13422 }
13423 exception_handler = &loc;
13424 p = trap[0];
13425 if (p) {
13426 trap[0] = NULL;
13427 evalstring(p, 0);
13428 }
13429 flush_stdout_stderr();
13430 out:
13431 setjobctl(0);
13432 _exit(status);
13433 /* NOTREACHED */
13434}
13435
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013436static void
13437init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013438{
13439 /* from input.c: */
13440 basepf.nextc = basepf.buf = basebuf;
13441
13442 /* from trap.c: */
13443 signal(SIGCHLD, SIG_DFL);
13444
13445 /* from var.c: */
13446 {
13447 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013448 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013449 const char *p;
13450 struct stat st1, st2;
13451
13452 initvar();
13453 for (envp = environ; envp && *envp; envp++) {
13454 if (strchr(*envp, '=')) {
13455 setvareq(*envp, VEXPORT|VTEXTFIXED);
13456 }
13457 }
13458
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013459 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013460 setvar("PPID", ppid, 0);
13461
13462 p = lookupvar("PWD");
13463 if (p)
13464 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13465 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13466 p = '\0';
13467 setpwd(p, 0);
13468 }
13469}
13470
13471/*
13472 * Process the shell command line arguments.
13473 */
13474static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013475procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013476{
13477 int i;
13478 const char *xminusc;
13479 char **xargv;
13480
13481 xargv = argv;
13482 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013483 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013484 xargv++;
13485 for (i = 0; i < NOPTS; i++)
13486 optlist[i] = 2;
13487 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013488 if (options(1)) {
13489 /* it already printed err message */
13490 raise_exception(EXERROR);
13491 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013492 xargv = argptr;
13493 xminusc = minusc;
13494 if (*xargv == NULL) {
13495 if (xminusc)
13496 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13497 sflag = 1;
13498 }
13499 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13500 iflag = 1;
13501 if (mflag == 2)
13502 mflag = iflag;
13503 for (i = 0; i < NOPTS; i++)
13504 if (optlist[i] == 2)
13505 optlist[i] = 0;
13506#if DEBUG == 2
13507 debug = 1;
13508#endif
13509 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13510 if (xminusc) {
13511 minusc = *xargv++;
13512 if (*xargv)
13513 goto setarg0;
13514 } else if (!sflag) {
13515 setinputfile(*xargv, 0);
13516 setarg0:
13517 arg0 = *xargv++;
13518 commandname = arg0;
13519 }
13520
13521 shellparam.p = xargv;
13522#if ENABLE_ASH_GETOPTS
13523 shellparam.optind = 1;
13524 shellparam.optoff = -1;
13525#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013526 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013527 while (*xargv) {
13528 shellparam.nparam++;
13529 xargv++;
13530 }
13531 optschanged();
13532}
13533
13534/*
13535 * Read /etc/profile or .profile.
13536 */
13537static void
13538read_profile(const char *name)
13539{
13540 int skip;
13541
13542 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13543 return;
13544 skip = cmdloop(0);
13545 popfile();
13546 if (skip)
13547 exitshell();
13548}
13549
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013550/*
13551 * This routine is called when an error or an interrupt occurs in an
13552 * interactive shell and control is returned to the main command loop.
13553 */
13554static void
13555reset(void)
13556{
13557 /* from eval.c: */
13558 evalskip = 0;
13559 loopnest = 0;
13560 /* from input.c: */
13561 parselleft = parsenleft = 0; /* clear input buffer */
13562 popallfiles();
13563 /* from parser.c: */
13564 tokpushback = 0;
13565 checkkwd = 0;
13566 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013567 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013568}
13569
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013570#if PROFILE
13571static short profile_buf[16384];
13572extern int etext();
13573#endif
13574
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013575/*
13576 * Main routine. We initialize things, parse the arguments, execute
13577 * profiles if we're a login shell, and then call cmdloop to execute
13578 * commands. The setjmp call sets up the location to jump to when an
13579 * exception occurs. When an exception occurs the variable "state"
13580 * is used to figure out how far we had gotten.
13581 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013582int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013583int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013584{
13585 char *shinit;
13586 volatile int state;
13587 struct jmploc jmploc;
13588 struct stackmark smark;
13589
Denis Vlasenko01631112007-12-16 17:20:38 +000013590 /* Initialize global data */
13591 INIT_G_misc();
13592 INIT_G_memstack();
13593 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013594#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013595 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013596#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013597 INIT_G_cmdtable();
13598
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013599#if PROFILE
13600 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13601#endif
13602
13603#if ENABLE_FEATURE_EDITING
13604 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13605#endif
13606 state = 0;
13607 if (setjmp(jmploc.loc)) {
13608 int e;
13609 int s;
13610
13611 reset();
13612
13613 e = exception;
13614 if (e == EXERROR)
13615 exitstatus = 2;
13616 s = state;
13617 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13618 exitshell();
13619
13620 if (e == EXINT) {
13621 outcslow('\n', stderr);
13622 }
13623 popstackmark(&smark);
13624 FORCE_INT_ON; /* enable interrupts */
13625 if (s == 1)
13626 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013627 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013628 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013629 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013630 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013631 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013632 }
13633 exception_handler = &jmploc;
13634#if DEBUG
13635 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013636 trace_puts("Shell args: ");
13637 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013638#endif
13639 rootpid = getpid();
13640
13641#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013642 /* Can use monotonic_ns() for better randomness but for now it is
13643 * not used anywhere else in busybox... so avoid bloat */
13644 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013645#endif
13646 init();
13647 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013648 procargs(argv);
13649
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013650#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13651 if (iflag) {
13652 const char *hp = lookupvar("HISTFILE");
13653
13654 if (hp == NULL) {
13655 hp = lookupvar("HOME");
13656 if (hp != NULL) {
13657 char *defhp = concat_path_file(hp, ".ash_history");
13658 setvar("HISTFILE", defhp, 0);
13659 free(defhp);
13660 }
13661 }
13662 }
13663#endif
13664 if (argv[0] && argv[0][0] == '-')
13665 isloginsh = 1;
13666 if (isloginsh) {
13667 state = 1;
13668 read_profile("/etc/profile");
13669 state1:
13670 state = 2;
13671 read_profile(".profile");
13672 }
13673 state2:
13674 state = 3;
13675 if (
13676#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013677 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013678#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013679 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013680 ) {
13681 shinit = lookupvar("ENV");
13682 if (shinit != NULL && *shinit != '\0') {
13683 read_profile(shinit);
13684 }
13685 }
13686 state3:
13687 state = 4;
13688 if (minusc)
13689 evalstring(minusc, 0);
13690
13691 if (sflag || minusc == NULL) {
13692#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013693 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013694 const char *hp = lookupvar("HISTFILE");
13695
13696 if (hp != NULL)
13697 line_input_state->hist_file = hp;
13698 }
13699#endif
13700 state4: /* XXX ??? - why isn't this before the "if" statement */
13701 cmdloop(1);
13702 }
13703#if PROFILE
13704 monitor(0);
13705#endif
13706#ifdef GPROF
13707 {
13708 extern void _mcleanup(void);
13709 _mcleanup();
13710 }
13711#endif
13712 exitshell();
13713 /* NOTREACHED */
13714}
13715
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013716#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013717const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013718int main(int argc, char **argv)
13719{
13720 return ash_main(argc, argv);
13721}
13722#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013723
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013724
Eric Andersendf82f612001-06-28 07:46:40 +000013725/*-
13726 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013727 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013728 *
13729 * This code is derived from software contributed to Berkeley by
13730 * Kenneth Almquist.
13731 *
13732 * Redistribution and use in source and binary forms, with or without
13733 * modification, are permitted provided that the following conditions
13734 * are met:
13735 * 1. Redistributions of source code must retain the above copyright
13736 * notice, this list of conditions and the following disclaimer.
13737 * 2. Redistributions in binary form must reproduce the above copyright
13738 * notice, this list of conditions and the following disclaimer in the
13739 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013740 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013741 * may be used to endorse or promote products derived from this software
13742 * without specific prior written permission.
13743 *
13744 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13745 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13746 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13747 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13748 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13749 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13750 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13751 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13752 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13753 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13754 * SUCH DAMAGE.
13755 */