blob: f93d73735e000e90c1991204d01eba9b42580bbe [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__)
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +000078#error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000079#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
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +000093/* C99 says: "char" declaration may be signed or unsigned by default */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000094#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;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000168 smallint exception_type; /* 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.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000204 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000205 */
206 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +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) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000210#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000211
Denis Vlasenko01631112007-12-16 17:20:38 +0000212 /* indicates specified signal received */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +0000213 char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000214 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000215
216 /* Rarely referenced stuff */
217#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000218 /* Random number generators */
219 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
220 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000221#endif
222 pid_t backgndpid; /* pid of last background process */
223 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000224};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000225extern struct globals_misc *const ash_ptr_to_globals_misc;
226#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000227#define rootpid (G_misc.rootpid )
228#define shlvl (G_misc.shlvl )
229#define minusc (G_misc.minusc )
230#define curdir (G_misc.curdir )
231#define physdir (G_misc.physdir )
232#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000233#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000234#define exception_type (G_misc.exception_type )
Denis Vlasenko01631112007-12-16 17:20:38 +0000235#define suppressint (G_misc.suppressint )
236#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000237//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000238#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000239#define isloginsh (G_misc.isloginsh )
240#define nullstr (G_misc.nullstr )
241#define optlist (G_misc.optlist )
242#define sigmode (G_misc.sigmode )
243#define gotsig (G_misc.gotsig )
244#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000245#define random_galois_LFSR (G_misc.random_galois_LFSR)
246#define random_LCG (G_misc.random_LCG )
247#define backgndpid (G_misc.backgndpid )
248#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000249#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000250 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
251 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000252 curdir = nullstr; \
253 physdir = nullstr; \
254} while (0)
255
256
Denis Vlasenko559691a2008-10-05 18:39:31 +0000257/* ============ Utility functions */
258static int isdigit_str9(const char *str)
259{
260 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
261 while (--maxlen && isdigit(*str))
262 str++;
263 return (*str == '\0');
264}
Denis Vlasenko01631112007-12-16 17:20:38 +0000265
Denis Vlasenko559691a2008-10-05 18:39:31 +0000266
267/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000268/*
Eric Andersen2870d962001-07-02 17:27:21 +0000269 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000270 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000271 * much more efficient and portable. (But hacking the kernel is so much
272 * more fun than worrying about efficiency and portability. :-))
273 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000274#define INT_OFF do { \
275 suppressint++; \
276 xbarrier(); \
277} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000278
279/*
280 * Called to raise an exception. Since C doesn't include exceptions, we
281 * just do a longjmp to the exception handler. The type of exception is
282 * stored in the global variable "exception".
283 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000284static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000285static void
286raise_exception(int e)
287{
288#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000289 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000290 abort();
291#endif
292 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000293 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000294 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295}
296
297/*
298 * Called from trap.c when a SIGINT is received. (If the user specifies
299 * that SIGINT is to be trapped or ignored using the trap builtin, then
300 * this routine is not called.) Suppressint is nonzero when interrupts
301 * are held using the INT_OFF macro. (The test for iflag is just
302 * defensive programming.)
303 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000304static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000305static void
306raise_interrupt(void)
307{
308 int i;
309
310 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000311 /* Signal is not automatically unmasked after it is raised,
312 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000313 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000314 /* pendingsig = 0; - now done in onsig() */
315
Denis Vlasenkob012b102007-02-19 22:43:01 +0000316 i = EXSIG;
317 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
318 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000319 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000320 signal(SIGINT, SIG_DFL);
321 raise(SIGINT);
322 }
323 i = EXINT;
324 }
325 raise_exception(i);
326 /* NOTREACHED */
327}
328
329#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000330static void
331int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000332{
333 if (--suppressint == 0 && intpending) {
334 raise_interrupt();
335 }
336}
337#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000338static void
339force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340{
341 suppressint = 0;
342 if (intpending)
343 raise_interrupt();
344}
345#define FORCE_INT_ON force_int_on()
346#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000347#define INT_ON do { \
348 xbarrier(); \
349 if (--suppressint == 0 && intpending) \
350 raise_interrupt(); \
351} while (0)
352#define FORCE_INT_ON do { \
353 xbarrier(); \
354 suppressint = 0; \
355 if (intpending) \
356 raise_interrupt(); \
357} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000358#endif /* ASH_OPTIMIZE_FOR_SIZE */
359
360#define SAVE_INT(v) ((v) = suppressint)
361
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000362#define RESTORE_INT(v) do { \
363 xbarrier(); \
364 suppressint = (v); \
365 if (suppressint == 0 && intpending) \
366 raise_interrupt(); \
367} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000368
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000369/*
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000370 * Ignore a signal. Avoids unnecessary system calls.
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000371 */
372static void
373ignoresig(int signo)
374{
375 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
376 signal(signo, SIG_IGN);
377 }
378 sigmode[signo - 1] = S_HARD_IGN;
379}
380
381/*
382 * Signal handler. Only one usage site - in setsignal()
383 */
384static void
385onsig(int signo)
386{
387 gotsig[signo - 1] = 1;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000388
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000389 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000390 if (!suppressint) {
391 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000392 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000393 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000394 intpending = 1;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000395 } else {
396 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000397 }
398}
399
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000400
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000401/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000402
Eric Andersenc470f442003-07-28 09:56:35 +0000403static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000404outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000405{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000406 INT_OFF;
407 fputs(p, file);
408 INT_ON;
409}
410
411static void
412flush_stdout_stderr(void)
413{
414 INT_OFF;
415 fflush(stdout);
416 fflush(stderr);
417 INT_ON;
418}
419
420static void
421flush_stderr(void)
422{
423 INT_OFF;
424 fflush(stderr);
425 INT_ON;
426}
427
428static void
429outcslow(int c, FILE *dest)
430{
431 INT_OFF;
432 putc(c, dest);
433 fflush(dest);
434 INT_ON;
435}
436
437static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
438static int
439out1fmt(const char *fmt, ...)
440{
441 va_list ap;
442 int r;
443
444 INT_OFF;
445 va_start(ap, fmt);
446 r = vprintf(fmt, ap);
447 va_end(ap);
448 INT_ON;
449 return r;
450}
451
452static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
453static int
454fmtstr(char *outbuf, size_t length, const char *fmt, ...)
455{
456 va_list ap;
457 int ret;
458
459 va_start(ap, fmt);
460 INT_OFF;
461 ret = vsnprintf(outbuf, length, fmt, ap);
462 va_end(ap);
463 INT_ON;
464 return ret;
465}
466
467static void
468out1str(const char *p)
469{
470 outstr(p, stdout);
471}
472
473static void
474out2str(const char *p)
475{
476 outstr(p, stderr);
477 flush_stderr();
478}
479
480
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000481/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000482
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000483/* control characters in argument strings */
484#define CTLESC '\201' /* escape next character */
485#define CTLVAR '\202' /* variable defn */
486#define CTLENDVAR '\203'
487#define CTLBACKQ '\204'
488#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
489/* CTLBACKQ | CTLQUOTE == '\205' */
490#define CTLARI '\206' /* arithmetic expression */
491#define CTLENDARI '\207'
492#define CTLQUOTEMARK '\210'
493
494/* variable substitution byte (follows CTLVAR) */
495#define VSTYPE 0x0f /* type of variable substitution */
496#define VSNUL 0x10 /* colon--treat the empty string as unset */
497#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
498
499/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000500#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
501#define VSMINUS 0x2 /* ${var-text} */
502#define VSPLUS 0x3 /* ${var+text} */
503#define VSQUESTION 0x4 /* ${var?message} */
504#define VSASSIGN 0x5 /* ${var=text} */
505#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
506#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
507#define VSTRIMLEFT 0x8 /* ${var#pattern} */
508#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
509#define VSLENGTH 0xa /* ${#var} */
510#if ENABLE_ASH_BASH_COMPAT
511#define VSSUBSTR 0xc /* ${var:position:length} */
512#define VSREPLACE 0xd /* ${var/pattern/replacement} */
513#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
514#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000515
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000516static const char dolatstr[] ALIGN1 = {
517 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
518};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000519
Denis Vlasenko559691a2008-10-05 18:39:31 +0000520#define NCMD 0
521#define NPIPE 1
522#define NREDIR 2
523#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000524#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000525#define NAND 5
526#define NOR 6
527#define NSEMI 7
528#define NIF 8
529#define NWHILE 9
530#define NUNTIL 10
531#define NFOR 11
532#define NCASE 12
533#define NCLIST 13
534#define NDEFUN 14
535#define NARG 15
536#define NTO 16
537#if ENABLE_ASH_BASH_COMPAT
538#define NTO2 17
539#endif
540#define NCLOBBER 18
541#define NFROM 19
542#define NFROMTO 20
543#define NAPPEND 21
544#define NTOFD 22
545#define NFROMFD 23
546#define NHERE 24
547#define NXHERE 25
548#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000549#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000550
551union node;
552
553struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000554 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000555 union node *assign;
556 union node *args;
557 union node *redirect;
558};
559
560struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000561 smallint type;
562 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000563 struct nodelist *cmdlist;
564};
565
566struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000567 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000568 union node *n;
569 union node *redirect;
570};
571
572struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000573 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000574 union node *ch1;
575 union node *ch2;
576};
577
578struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000579 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000580 union node *test;
581 union node *ifpart;
582 union node *elsepart;
583};
584
585struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000586 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000587 union node *args;
588 union node *body;
589 char *var;
590};
591
592struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000593 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000594 union node *expr;
595 union node *cases;
596};
597
598struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000599 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000600 union node *next;
601 union node *pattern;
602 union node *body;
603};
604
605struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000606 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607 union node *next;
608 char *text;
609 struct nodelist *backquote;
610};
611
Denis Vlasenko559691a2008-10-05 18:39:31 +0000612/* nfile and ndup layout must match!
613 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
614 * that it is actually NTO2 (>&file), and change its type.
615 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000616struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000617 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618 union node *next;
619 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621 union node *fname;
622 char *expfname;
623};
624
625struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000626 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627 union node *next;
628 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000629 int dupfd;
630 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000631 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000632};
633
634struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000635 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000636 union node *next;
637 int fd;
638 union node *doc;
639};
640
641struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000642 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000643 union node *com;
644};
645
646union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000647 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000648 struct ncmd ncmd;
649 struct npipe npipe;
650 struct nredir nredir;
651 struct nbinary nbinary;
652 struct nif nif;
653 struct nfor nfor;
654 struct ncase ncase;
655 struct nclist nclist;
656 struct narg narg;
657 struct nfile nfile;
658 struct ndup ndup;
659 struct nhere nhere;
660 struct nnot nnot;
661};
662
663struct nodelist {
664 struct nodelist *next;
665 union node *n;
666};
667
668struct funcnode {
669 int count;
670 union node n;
671};
672
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000673/*
674 * Free a parse tree.
675 */
676static void
677freefunc(struct funcnode *f)
678{
679 if (f && --f->count < 0)
680 free(f);
681}
682
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000683
684/* ============ Debugging output */
685
686#if DEBUG
687
688static FILE *tracefile;
689
690static void
691trace_printf(const char *fmt, ...)
692{
693 va_list va;
694
695 if (debug != 1)
696 return;
697 va_start(va, fmt);
698 vfprintf(tracefile, fmt, va);
699 va_end(va);
700}
701
702static void
703trace_vprintf(const char *fmt, va_list va)
704{
705 if (debug != 1)
706 return;
707 vfprintf(tracefile, fmt, va);
708}
709
710static void
711trace_puts(const char *s)
712{
713 if (debug != 1)
714 return;
715 fputs(s, tracefile);
716}
717
718static void
719trace_puts_quoted(char *s)
720{
721 char *p;
722 char c;
723
724 if (debug != 1)
725 return;
726 putc('"', tracefile);
727 for (p = s; *p; p++) {
728 switch (*p) {
729 case '\n': c = 'n'; goto backslash;
730 case '\t': c = 't'; goto backslash;
731 case '\r': c = 'r'; goto backslash;
732 case '"': c = '"'; goto backslash;
733 case '\\': c = '\\'; goto backslash;
734 case CTLESC: c = 'e'; goto backslash;
735 case CTLVAR: c = 'v'; goto backslash;
736 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
737 case CTLBACKQ: c = 'q'; goto backslash;
738 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
739 backslash:
740 putc('\\', tracefile);
741 putc(c, tracefile);
742 break;
743 default:
744 if (*p >= ' ' && *p <= '~')
745 putc(*p, tracefile);
746 else {
747 putc('\\', tracefile);
748 putc(*p >> 6 & 03, tracefile);
749 putc(*p >> 3 & 07, tracefile);
750 putc(*p & 07, tracefile);
751 }
752 break;
753 }
754 }
755 putc('"', tracefile);
756}
757
758static void
759trace_puts_args(char **ap)
760{
761 if (debug != 1)
762 return;
763 if (!*ap)
764 return;
765 while (1) {
766 trace_puts_quoted(*ap);
767 if (!*++ap) {
768 putc('\n', tracefile);
769 break;
770 }
771 putc(' ', tracefile);
772 }
773}
774
775static void
776opentrace(void)
777{
778 char s[100];
779#ifdef O_APPEND
780 int flags;
781#endif
782
783 if (debug != 1) {
784 if (tracefile)
785 fflush(tracefile);
786 /* leave open because libedit might be using it */
787 return;
788 }
789 strcpy(s, "./trace");
790 if (tracefile) {
791 if (!freopen(s, "a", tracefile)) {
792 fprintf(stderr, "Can't re-open %s\n", s);
793 debug = 0;
794 return;
795 }
796 } else {
797 tracefile = fopen(s, "a");
798 if (tracefile == NULL) {
799 fprintf(stderr, "Can't open %s\n", s);
800 debug = 0;
801 return;
802 }
803 }
804#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000805 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000806 if (flags >= 0)
807 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
808#endif
809 setlinebuf(tracefile);
810 fputs("\nTracing started.\n", tracefile);
811}
812
813static void
814indent(int amount, char *pfx, FILE *fp)
815{
816 int i;
817
818 for (i = 0; i < amount; i++) {
819 if (pfx && i == amount - 1)
820 fputs(pfx, fp);
821 putc('\t', fp);
822 }
823}
824
825/* little circular references here... */
826static void shtree(union node *n, int ind, char *pfx, FILE *fp);
827
828static void
829sharg(union node *arg, FILE *fp)
830{
831 char *p;
832 struct nodelist *bqlist;
833 int subtype;
834
835 if (arg->type != NARG) {
836 out1fmt("<node type %d>\n", arg->type);
837 abort();
838 }
839 bqlist = arg->narg.backquote;
840 for (p = arg->narg.text; *p; p++) {
841 switch (*p) {
842 case CTLESC:
843 putc(*++p, fp);
844 break;
845 case CTLVAR:
846 putc('$', fp);
847 putc('{', fp);
848 subtype = *++p;
849 if (subtype == VSLENGTH)
850 putc('#', fp);
851
852 while (*p != '=')
853 putc(*p++, fp);
854
855 if (subtype & VSNUL)
856 putc(':', fp);
857
858 switch (subtype & VSTYPE) {
859 case VSNORMAL:
860 putc('}', fp);
861 break;
862 case VSMINUS:
863 putc('-', fp);
864 break;
865 case VSPLUS:
866 putc('+', fp);
867 break;
868 case VSQUESTION:
869 putc('?', fp);
870 break;
871 case VSASSIGN:
872 putc('=', fp);
873 break;
874 case VSTRIMLEFT:
875 putc('#', fp);
876 break;
877 case VSTRIMLEFTMAX:
878 putc('#', fp);
879 putc('#', fp);
880 break;
881 case VSTRIMRIGHT:
882 putc('%', fp);
883 break;
884 case VSTRIMRIGHTMAX:
885 putc('%', fp);
886 putc('%', fp);
887 break;
888 case VSLENGTH:
889 break;
890 default:
891 out1fmt("<subtype %d>", subtype);
892 }
893 break;
894 case CTLENDVAR:
895 putc('}', fp);
896 break;
897 case CTLBACKQ:
898 case CTLBACKQ|CTLQUOTE:
899 putc('$', fp);
900 putc('(', fp);
901 shtree(bqlist->n, -1, NULL, fp);
902 putc(')', fp);
903 break;
904 default:
905 putc(*p, fp);
906 break;
907 }
908 }
909}
910
911static void
912shcmd(union node *cmd, FILE *fp)
913{
914 union node *np;
915 int first;
916 const char *s;
917 int dftfd;
918
919 first = 1;
920 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000921 if (!first)
922 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000923 sharg(np, fp);
924 first = 0;
925 }
926 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000927 if (!first)
928 putc(' ', fp);
929 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000930 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000931 case NTO: s = ">>"+1; dftfd = 1; break;
932 case NCLOBBER: s = ">|"; dftfd = 1; break;
933 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000934#if ENABLE_ASH_BASH_COMPAT
935 case NTO2:
936#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000937 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000938 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000939 case NFROMFD: s = "<&"; break;
940 case NFROMTO: s = "<>"; break;
941 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000942 }
943 if (np->nfile.fd != dftfd)
944 fprintf(fp, "%d", np->nfile.fd);
945 fputs(s, fp);
946 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
947 fprintf(fp, "%d", np->ndup.dupfd);
948 } else {
949 sharg(np->nfile.fname, fp);
950 }
951 first = 0;
952 }
953}
954
955static void
956shtree(union node *n, int ind, char *pfx, FILE *fp)
957{
958 struct nodelist *lp;
959 const char *s;
960
961 if (n == NULL)
962 return;
963
964 indent(ind, pfx, fp);
965 switch (n->type) {
966 case NSEMI:
967 s = "; ";
968 goto binop;
969 case NAND:
970 s = " && ";
971 goto binop;
972 case NOR:
973 s = " || ";
974 binop:
975 shtree(n->nbinary.ch1, ind, NULL, fp);
976 /* if (ind < 0) */
977 fputs(s, fp);
978 shtree(n->nbinary.ch2, ind, NULL, fp);
979 break;
980 case NCMD:
981 shcmd(n, fp);
982 if (ind >= 0)
983 putc('\n', fp);
984 break;
985 case NPIPE:
986 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
987 shcmd(lp->n, fp);
988 if (lp->next)
989 fputs(" | ", fp);
990 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000991 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000992 fputs(" &", fp);
993 if (ind >= 0)
994 putc('\n', fp);
995 break;
996 default:
997 fprintf(fp, "<node type %d>", n->type);
998 if (ind >= 0)
999 putc('\n', fp);
1000 break;
1001 }
1002}
1003
1004static void
1005showtree(union node *n)
1006{
1007 trace_puts("showtree called\n");
1008 shtree(n, 1, NULL, stdout);
1009}
1010
1011#define TRACE(param) trace_printf param
1012#define TRACEV(param) trace_vprintf param
1013
1014#else
1015
1016#define TRACE(param)
1017#define TRACEV(param)
1018
1019#endif /* DEBUG */
1020
1021
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001022/* ============ Parser data */
1023
1024/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001025 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1026 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001027struct strlist {
1028 struct strlist *next;
1029 char *text;
1030};
1031
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001032struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001033
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034struct strpush {
1035 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001036 char *prev_string;
1037 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001038#if ENABLE_ASH_ALIAS
1039 struct alias *ap; /* if push was associated with an alias */
1040#endif
1041 char *string; /* remember the string since it may change */
1042};
1043
1044struct parsefile {
1045 struct parsefile *prev; /* preceding file on stack */
1046 int linno; /* current line */
1047 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001048 int left_in_line; /* number of chars left in this line */
1049 int left_in_buffer; /* number of chars left in this buffer past the line */
1050 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001051 char *buf; /* input buffer */
1052 struct strpush *strpush; /* for pushing strings at this level */
1053 struct strpush basestrpush; /* so pushing one is fast */
1054};
1055
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001056static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001057static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001058static int startlinno; /* line # where last token started */
1059static char *commandname; /* currently executing command */
1060static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001061static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001062
1063
1064/* ============ Message printing */
1065
1066static void
1067ash_vmsg(const char *msg, va_list ap)
1068{
1069 fprintf(stderr, "%s: ", arg0);
1070 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001071 if (strcmp(arg0, commandname))
1072 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001073 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001074 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001075 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001076 vfprintf(stderr, msg, ap);
1077 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001078}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001079
1080/*
1081 * Exverror is called to raise the error exception. If the second argument
1082 * is not NULL then error prints an error message using printf style
1083 * formatting. It then raises the error exception.
1084 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001085static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001086static void
1087ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001088{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001089#if DEBUG
1090 if (msg) {
1091 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1092 TRACEV((msg, ap));
1093 TRACE(("\") pid=%d\n", getpid()));
1094 } else
1095 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1096 if (msg)
1097#endif
1098 ash_vmsg(msg, ap);
1099
1100 flush_stdout_stderr();
1101 raise_exception(cond);
1102 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001103}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001104
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001105static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001106static void
1107ash_msg_and_raise_error(const char *msg, ...)
1108{
1109 va_list ap;
1110
1111 va_start(ap, msg);
1112 ash_vmsg_and_raise(EXERROR, msg, ap);
1113 /* NOTREACHED */
1114 va_end(ap);
1115}
1116
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001117static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001118static void
1119ash_msg_and_raise(int cond, const char *msg, ...)
1120{
1121 va_list ap;
1122
1123 va_start(ap, msg);
1124 ash_vmsg_and_raise(cond, msg, ap);
1125 /* NOTREACHED */
1126 va_end(ap);
1127}
1128
1129/*
1130 * error/warning routines for external builtins
1131 */
1132static void
1133ash_msg(const char *fmt, ...)
1134{
1135 va_list ap;
1136
1137 va_start(ap, fmt);
1138 ash_vmsg(fmt, ap);
1139 va_end(ap);
1140}
1141
1142/*
1143 * Return a string describing an error. The returned string may be a
1144 * pointer to a static buffer that will be overwritten on the next call.
1145 * Action describes the operation that got the error.
1146 */
1147static const char *
1148errmsg(int e, const char *em)
1149{
1150 if (e == ENOENT || e == ENOTDIR) {
1151 return em;
1152 }
1153 return strerror(e);
1154}
1155
1156
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001157/* ============ Memory allocation */
1158
1159/*
1160 * It appears that grabstackstr() will barf with such alignments
1161 * because stalloc() will return a string allocated in a new stackblock.
1162 */
1163#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1164enum {
1165 /* Most machines require the value returned from malloc to be aligned
1166 * in some way. The following macro will get this right
1167 * on many machines. */
1168 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1169 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001170 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001171};
1172
1173struct stack_block {
1174 struct stack_block *prev;
1175 char space[MINSIZE];
1176};
1177
1178struct stackmark {
1179 struct stack_block *stackp;
1180 char *stacknxt;
1181 size_t stacknleft;
1182 struct stackmark *marknext;
1183};
1184
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001185
Denis Vlasenko01631112007-12-16 17:20:38 +00001186struct globals_memstack {
1187 struct stack_block *g_stackp; // = &stackbase;
1188 struct stackmark *markp;
1189 char *g_stacknxt; // = stackbase.space;
1190 char *sstrend; // = stackbase.space + MINSIZE;
1191 size_t g_stacknleft; // = MINSIZE;
1192 int herefd; // = -1;
1193 struct stack_block stackbase;
1194};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001195extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1196#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001197#define g_stackp (G_memstack.g_stackp )
1198#define markp (G_memstack.markp )
1199#define g_stacknxt (G_memstack.g_stacknxt )
1200#define sstrend (G_memstack.sstrend )
1201#define g_stacknleft (G_memstack.g_stacknleft)
1202#define herefd (G_memstack.herefd )
1203#define stackbase (G_memstack.stackbase )
1204#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001205 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1206 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001207 g_stackp = &stackbase; \
1208 g_stacknxt = stackbase.space; \
1209 g_stacknleft = MINSIZE; \
1210 sstrend = stackbase.space + MINSIZE; \
1211 herefd = -1; \
1212} while (0)
1213
1214#define stackblock() ((void *)g_stacknxt)
1215#define stackblocksize() g_stacknleft
1216
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001217
1218static void *
1219ckrealloc(void * p, size_t nbytes)
1220{
1221 p = realloc(p, nbytes);
1222 if (!p)
1223 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1224 return p;
1225}
1226
1227static void *
1228ckmalloc(size_t nbytes)
1229{
1230 return ckrealloc(NULL, nbytes);
1231}
1232
Denis Vlasenko597906c2008-02-20 16:38:54 +00001233static void *
1234ckzalloc(size_t nbytes)
1235{
1236 return memset(ckmalloc(nbytes), 0, nbytes);
1237}
1238
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001239/*
1240 * Make a copy of a string in safe storage.
1241 */
1242static char *
1243ckstrdup(const char *s)
1244{
1245 char *p = strdup(s);
1246 if (!p)
1247 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1248 return p;
1249}
1250
1251/*
1252 * Parse trees for commands are allocated in lifo order, so we use a stack
1253 * to make this more efficient, and also to avoid all sorts of exception
1254 * handling code to handle interrupts in the middle of a parse.
1255 *
1256 * The size 504 was chosen because the Ultrix malloc handles that size
1257 * well.
1258 */
1259static void *
1260stalloc(size_t nbytes)
1261{
1262 char *p;
1263 size_t aligned;
1264
1265 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001266 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001267 size_t len;
1268 size_t blocksize;
1269 struct stack_block *sp;
1270
1271 blocksize = aligned;
1272 if (blocksize < MINSIZE)
1273 blocksize = MINSIZE;
1274 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1275 if (len < blocksize)
1276 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1277 INT_OFF;
1278 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001279 sp->prev = g_stackp;
1280 g_stacknxt = sp->space;
1281 g_stacknleft = blocksize;
1282 sstrend = g_stacknxt + blocksize;
1283 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001284 INT_ON;
1285 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001286 p = g_stacknxt;
1287 g_stacknxt += aligned;
1288 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001289 return p;
1290}
1291
Denis Vlasenko597906c2008-02-20 16:38:54 +00001292static void *
1293stzalloc(size_t nbytes)
1294{
1295 return memset(stalloc(nbytes), 0, nbytes);
1296}
1297
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001298static void
1299stunalloc(void *p)
1300{
1301#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001302 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001303 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001304 abort();
1305 }
1306#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001307 g_stacknleft += g_stacknxt - (char *)p;
1308 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001309}
1310
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001311/*
1312 * Like strdup but works with the ash stack.
1313 */
1314static char *
1315ststrdup(const char *p)
1316{
1317 size_t len = strlen(p) + 1;
1318 return memcpy(stalloc(len), p, len);
1319}
1320
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001321static void
1322setstackmark(struct stackmark *mark)
1323{
Denis Vlasenko01631112007-12-16 17:20:38 +00001324 mark->stackp = g_stackp;
1325 mark->stacknxt = g_stacknxt;
1326 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001327 mark->marknext = markp;
1328 markp = mark;
1329}
1330
1331static void
1332popstackmark(struct stackmark *mark)
1333{
1334 struct stack_block *sp;
1335
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001336 if (!mark->stackp)
1337 return;
1338
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001339 INT_OFF;
1340 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001341 while (g_stackp != mark->stackp) {
1342 sp = g_stackp;
1343 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001344 free(sp);
1345 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001346 g_stacknxt = mark->stacknxt;
1347 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348 sstrend = mark->stacknxt + mark->stacknleft;
1349 INT_ON;
1350}
1351
1352/*
1353 * When the parser reads in a string, it wants to stick the string on the
1354 * stack and only adjust the stack pointer when it knows how big the
1355 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1356 * of space on top of the stack and stackblocklen returns the length of
1357 * this block. Growstackblock will grow this space by at least one byte,
1358 * possibly moving it (like realloc). Grabstackblock actually allocates the
1359 * part of the block that has been used.
1360 */
1361static void
1362growstackblock(void)
1363{
1364 size_t newlen;
1365
Denis Vlasenko01631112007-12-16 17:20:38 +00001366 newlen = g_stacknleft * 2;
1367 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001368 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1369 if (newlen < 128)
1370 newlen += 128;
1371
Denis Vlasenko01631112007-12-16 17:20:38 +00001372 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001373 struct stack_block *oldstackp;
1374 struct stackmark *xmark;
1375 struct stack_block *sp;
1376 struct stack_block *prevstackp;
1377 size_t grosslen;
1378
1379 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001380 oldstackp = g_stackp;
1381 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001382 prevstackp = sp->prev;
1383 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1384 sp = ckrealloc(sp, grosslen);
1385 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001386 g_stackp = sp;
1387 g_stacknxt = sp->space;
1388 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001389 sstrend = sp->space + newlen;
1390
1391 /*
1392 * Stack marks pointing to the start of the old block
1393 * must be relocated to point to the new block
1394 */
1395 xmark = markp;
1396 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001397 xmark->stackp = g_stackp;
1398 xmark->stacknxt = g_stacknxt;
1399 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001400 xmark = xmark->marknext;
1401 }
1402 INT_ON;
1403 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001404 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001405 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001406 char *p = stalloc(newlen);
1407
1408 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001409 g_stacknxt = memcpy(p, oldspace, oldlen);
1410 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001411 }
1412}
1413
1414static void
1415grabstackblock(size_t len)
1416{
1417 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001418 g_stacknxt += len;
1419 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001420}
1421
1422/*
1423 * The following routines are somewhat easier to use than the above.
1424 * The user declares a variable of type STACKSTR, which may be declared
1425 * to be a register. The macro STARTSTACKSTR initializes things. Then
1426 * the user uses the macro STPUTC to add characters to the string. In
1427 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1428 * grown as necessary. When the user is done, she can just leave the
1429 * string there and refer to it using stackblock(). Or she can allocate
1430 * the space for it using grabstackstr(). If it is necessary to allow
1431 * someone else to use the stack temporarily and then continue to grow
1432 * the string, the user should use grabstack to allocate the space, and
1433 * then call ungrabstr(p) to return to the previous mode of operation.
1434 *
1435 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1436 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1437 * is space for at least one character.
1438 */
1439static void *
1440growstackstr(void)
1441{
1442 size_t len = stackblocksize();
1443 if (herefd >= 0 && len >= 1024) {
1444 full_write(herefd, stackblock(), len);
1445 return stackblock();
1446 }
1447 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001448 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001449}
1450
1451/*
1452 * Called from CHECKSTRSPACE.
1453 */
1454static char *
1455makestrspace(size_t newlen, char *p)
1456{
Denis Vlasenko01631112007-12-16 17:20:38 +00001457 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001458 size_t size = stackblocksize();
1459
1460 for (;;) {
1461 size_t nleft;
1462
1463 size = stackblocksize();
1464 nleft = size - len;
1465 if (nleft >= newlen)
1466 break;
1467 growstackblock();
1468 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001469 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001470}
1471
1472static char *
1473stack_nputstr(const char *s, size_t n, char *p)
1474{
1475 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001476 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001477 return p;
1478}
1479
1480static char *
1481stack_putstr(const char *s, char *p)
1482{
1483 return stack_nputstr(s, strlen(s), p);
1484}
1485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001486static char *
1487_STPUTC(int c, char *p)
1488{
1489 if (p == sstrend)
1490 p = growstackstr();
1491 *p++ = c;
1492 return p;
1493}
1494
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001495#define STARTSTACKSTR(p) ((p) = stackblock())
1496#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001497#define CHECKSTRSPACE(n, p) do { \
1498 char *q = (p); \
1499 size_t l = (n); \
1500 size_t m = sstrend - q; \
1501 if (l > m) \
1502 (p) = makestrspace(l, q); \
1503} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001504#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001505#define STACKSTRNUL(p) do { \
1506 if ((p) == sstrend) \
1507 (p) = growstackstr(); \
1508 *(p) = '\0'; \
1509} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001510#define STUNPUTC(p) (--(p))
1511#define STTOPC(p) ((p)[-1])
1512#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001513
1514#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001515#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001516#define stackstrend() ((void *)sstrend)
1517
1518
1519/* ============ String helpers */
1520
1521/*
1522 * prefix -- see if pfx is a prefix of string.
1523 */
1524static char *
1525prefix(const char *string, const char *pfx)
1526{
1527 while (*pfx) {
1528 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001529 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001530 }
1531 return (char *) string;
1532}
1533
1534/*
1535 * Check for a valid number. This should be elsewhere.
1536 */
1537static int
1538is_number(const char *p)
1539{
1540 do {
1541 if (!isdigit(*p))
1542 return 0;
1543 } while (*++p != '\0');
1544 return 1;
1545}
1546
1547/*
1548 * Convert a string of digits to an integer, printing an error message on
1549 * failure.
1550 */
1551static int
1552number(const char *s)
1553{
1554 if (!is_number(s))
1555 ash_msg_and_raise_error(illnum, s);
1556 return atoi(s);
1557}
1558
1559/*
1560 * Produce a possibly single quoted string suitable as input to the shell.
1561 * The return string is allocated on the stack.
1562 */
1563static char *
1564single_quote(const char *s)
1565{
1566 char *p;
1567
1568 STARTSTACKSTR(p);
1569
1570 do {
1571 char *q;
1572 size_t len;
1573
1574 len = strchrnul(s, '\'') - s;
1575
1576 q = p = makestrspace(len + 3, p);
1577
1578 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001579 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001580 *q++ = '\'';
1581 s += len;
1582
1583 STADJUST(q - p, p);
1584
1585 len = strspn(s, "'");
1586 if (!len)
1587 break;
1588
1589 q = p = makestrspace(len + 3, p);
1590
1591 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001592 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001593 *q++ = '"';
1594 s += len;
1595
1596 STADJUST(q - p, p);
1597 } while (*s);
1598
1599 USTPUTC(0, p);
1600
1601 return stackblock();
1602}
1603
1604
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001605/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001606
1607static char **argptr; /* argument list for builtin commands */
1608static char *optionarg; /* set by nextopt (like getopt) */
1609static char *optptr; /* used by nextopt */
1610
1611/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001612 * XXX - should get rid of. Have all builtins use getopt(3).
1613 * The library getopt must have the BSD extension static variable
1614 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001615 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001616 * Standard option processing (a la getopt) for builtin routines.
1617 * The only argument that is passed to nextopt is the option string;
1618 * the other arguments are unnecessary. It returns the character,
1619 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620 */
1621static int
1622nextopt(const char *optstring)
1623{
1624 char *p;
1625 const char *q;
1626 char c;
1627
1628 p = optptr;
1629 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001630 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001631 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001632 if (p == NULL)
1633 return '\0';
1634 if (*p != '-')
1635 return '\0';
1636 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001637 return '\0';
1638 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001639 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001640 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001641 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001642 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001643 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001644 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001645 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001646 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001647 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001648 if (*++q == ':')
1649 q++;
1650 }
1651 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001652 if (*p == '\0') {
1653 p = *argptr++;
1654 if (p == NULL)
1655 ash_msg_and_raise_error("no arg for -%c option", c);
1656 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001657 optionarg = p;
1658 p = NULL;
1659 }
1660 optptr = p;
1661 return c;
1662}
1663
1664
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001665/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001666
Denis Vlasenko01631112007-12-16 17:20:38 +00001667/*
1668 * The parsefile structure pointed to by the global variable parsefile
1669 * contains information about the current file being read.
1670 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001671struct shparam {
1672 int nparam; /* # of positional parameters (without $0) */
1673#if ENABLE_ASH_GETOPTS
1674 int optind; /* next parameter to be processed by getopts */
1675 int optoff; /* used by getopts */
1676#endif
1677 unsigned char malloced; /* if parameter list dynamically allocated */
1678 char **p; /* parameter list */
1679};
1680
1681/*
1682 * Free the list of positional parameters.
1683 */
1684static void
1685freeparam(volatile struct shparam *param)
1686{
Denis Vlasenko01631112007-12-16 17:20:38 +00001687 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001688 char **ap, **ap1;
1689 ap = ap1 = param->p;
1690 while (*ap)
1691 free(*ap++);
1692 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001693 }
1694}
1695
1696#if ENABLE_ASH_GETOPTS
1697static void getoptsreset(const char *value);
1698#endif
1699
1700struct var {
1701 struct var *next; /* next entry in hash list */
1702 int flags; /* flags are defined above */
1703 const char *text; /* name=value */
1704 void (*func)(const char *); /* function to be called when */
1705 /* the variable gets set/unset */
1706};
1707
1708struct localvar {
1709 struct localvar *next; /* next local variable in list */
1710 struct var *vp; /* the variable that was made local */
1711 int flags; /* saved flags */
1712 const char *text; /* saved text */
1713};
1714
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001715/* flags */
1716#define VEXPORT 0x01 /* variable is exported */
1717#define VREADONLY 0x02 /* variable cannot be modified */
1718#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1719#define VTEXTFIXED 0x08 /* text is statically allocated */
1720#define VSTACK 0x10 /* text is allocated on the stack */
1721#define VUNSET 0x20 /* the variable is not set */
1722#define VNOFUNC 0x40 /* don't call the callback function */
1723#define VNOSET 0x80 /* do not set variable - just readonly test */
1724#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001725#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001726# define VDYNAMIC 0x200 /* dynamic variable */
1727#else
1728# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729#endif
1730
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001732static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#define defifs (defifsvar + 4)
1734#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001735static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001736#endif
1737
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001738
Denis Vlasenko01631112007-12-16 17:20:38 +00001739/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001740#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001741static void
1742change_lc_all(const char *value)
1743{
1744 if (value && *value != '\0')
1745 setlocale(LC_ALL, value);
1746}
1747static void
1748change_lc_ctype(const char *value)
1749{
1750 if (value && *value != '\0')
1751 setlocale(LC_CTYPE, value);
1752}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001753#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754#if ENABLE_ASH_MAIL
1755static void chkmail(void);
1756static void changemail(const char *);
1757#endif
1758static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759#if ENABLE_ASH_RANDOM_SUPPORT
1760static void change_random(const char *);
1761#endif
1762
Denis Vlasenko01631112007-12-16 17:20:38 +00001763static const struct {
1764 int flags;
1765 const char *text;
1766 void (*func)(const char *);
1767} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001768#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001769 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001773#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001774 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1775 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001777 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1778 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1779 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1780 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001782 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783#endif
1784#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001785 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786#endif
1787#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001788 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1789 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790#endif
1791#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001792 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#endif
1794};
1795
Denis Vlasenko0b769642008-07-24 07:54:57 +00001796struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001797
1798struct globals_var {
1799 struct shparam shellparam; /* $@ current positional parameters */
1800 struct redirtab *redirlist;
1801 int g_nullredirs;
1802 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1803 struct var *vartab[VTABSIZE];
1804 struct var varinit[ARRAY_SIZE(varinit_data)];
1805};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001806extern struct globals_var *const ash_ptr_to_globals_var;
1807#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001808#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001809//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001810#define g_nullredirs (G_var.g_nullredirs )
1811#define preverrout_fd (G_var.preverrout_fd)
1812#define vartab (G_var.vartab )
1813#define varinit (G_var.varinit )
1814#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001815 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001816 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1817 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001818 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1819 varinit[i].flags = varinit_data[i].flags; \
1820 varinit[i].text = varinit_data[i].text; \
1821 varinit[i].func = varinit_data[i].func; \
1822 } \
1823} while (0)
1824
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001825#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001826#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001827# define vmail (&vifs)[1]
1828# define vmpath (&vmail)[1]
1829# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001831# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001832#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001833#define vps1 (&vpath)[1]
1834#define vps2 (&vps1)[1]
1835#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001837# define voptind (&vps4)[1]
1838# if ENABLE_ASH_RANDOM_SUPPORT
1839# define vrandom (&voptind)[1]
1840# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001841#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001842# if ENABLE_ASH_RANDOM_SUPPORT
1843# define vrandom (&vps4)[1]
1844# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001846
1847/*
1848 * The following macros access the values of the above variables.
1849 * They have to skip over the name. They return the null string
1850 * for unset variables.
1851 */
1852#define ifsval() (vifs.text + 4)
1853#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001854#if ENABLE_ASH_MAIL
1855# define mailval() (vmail.text + 5)
1856# define mpathval() (vmpath.text + 9)
1857# define mpathset() ((vmpath.flags & VUNSET) == 0)
1858#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001859#define pathval() (vpath.text + 5)
1860#define ps1val() (vps1.text + 4)
1861#define ps2val() (vps2.text + 4)
1862#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001863#if ENABLE_ASH_GETOPTS
1864# define optindval() (voptind.text + 7)
1865#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001866
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001867
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001868#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1869#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1870
Denis Vlasenko01631112007-12-16 17:20:38 +00001871#if ENABLE_ASH_GETOPTS
1872static void
1873getoptsreset(const char *value)
1874{
1875 shellparam.optind = number(value);
1876 shellparam.optoff = -1;
1877}
1878#endif
1879
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001880/*
1881 * Return of a legal variable name (a letter or underscore followed by zero or
1882 * more letters, underscores, and digits).
1883 */
1884static char *
1885endofname(const char *name)
1886{
1887 char *p;
1888
1889 p = (char *) name;
1890 if (!is_name(*p))
1891 return p;
1892 while (*++p) {
1893 if (!is_in_name(*p))
1894 break;
1895 }
1896 return p;
1897}
1898
1899/*
1900 * Compares two strings up to the first = or '\0'. The first
1901 * string must be terminated by '='; the second may be terminated by
1902 * either '=' or '\0'.
1903 */
1904static int
1905varcmp(const char *p, const char *q)
1906{
1907 int c, d;
1908
1909 while ((c = *p) == (d = *q)) {
1910 if (!c || c == '=')
1911 goto out;
1912 p++;
1913 q++;
1914 }
1915 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001916 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001917 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001918 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001919 out:
1920 return c - d;
1921}
1922
1923static int
1924varequal(const char *a, const char *b)
1925{
1926 return !varcmp(a, b);
1927}
1928
1929/*
1930 * Find the appropriate entry in the hash table from the name.
1931 */
1932static struct var **
1933hashvar(const char *p)
1934{
1935 unsigned hashval;
1936
1937 hashval = ((unsigned char) *p) << 4;
1938 while (*p && *p != '=')
1939 hashval += (unsigned char) *p++;
1940 return &vartab[hashval % VTABSIZE];
1941}
1942
1943static int
1944vpcmp(const void *a, const void *b)
1945{
1946 return varcmp(*(const char **)a, *(const char **)b);
1947}
1948
1949/*
1950 * This routine initializes the builtin variables.
1951 */
1952static void
1953initvar(void)
1954{
1955 struct var *vp;
1956 struct var *end;
1957 struct var **vpp;
1958
1959 /*
1960 * PS1 depends on uid
1961 */
1962#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1963 vps1.text = "PS1=\\w \\$ ";
1964#else
1965 if (!geteuid())
1966 vps1.text = "PS1=# ";
1967#endif
1968 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001969 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970 do {
1971 vpp = hashvar(vp->text);
1972 vp->next = *vpp;
1973 *vpp = vp;
1974 } while (++vp < end);
1975}
1976
1977static struct var **
1978findvar(struct var **vpp, const char *name)
1979{
1980 for (; *vpp; vpp = &(*vpp)->next) {
1981 if (varequal((*vpp)->text, name)) {
1982 break;
1983 }
1984 }
1985 return vpp;
1986}
1987
1988/*
1989 * Find the value of a variable. Returns NULL if not set.
1990 */
1991static char *
1992lookupvar(const char *name)
1993{
1994 struct var *v;
1995
1996 v = *findvar(hashvar(name), name);
1997 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001998#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001999 /*
2000 * Dynamic variables are implemented roughly the same way they are
2001 * in bash. Namely, they're "special" so long as they aren't unset.
2002 * As soon as they're unset, they're no longer dynamic, and dynamic
2003 * lookup will no longer happen at that point. -- PFM.
2004 */
2005 if ((v->flags & VDYNAMIC))
2006 (*v->func)(NULL);
2007#endif
2008 if (!(v->flags & VUNSET))
2009 return strchrnul(v->text, '=') + 1;
2010 }
2011 return NULL;
2012}
2013
2014/*
2015 * Search the environment of a builtin command.
2016 */
2017static char *
2018bltinlookup(const char *name)
2019{
2020 struct strlist *sp;
2021
2022 for (sp = cmdenviron; sp; sp = sp->next) {
2023 if (varequal(sp->text, name))
2024 return strchrnul(sp->text, '=') + 1;
2025 }
2026 return lookupvar(name);
2027}
2028
2029/*
2030 * Same as setvar except that the variable and value are passed in
2031 * the first argument as name=value. Since the first argument will
2032 * be actually stored in the table, it should not be a string that
2033 * will go away.
2034 * Called with interrupts off.
2035 */
2036static void
2037setvareq(char *s, int flags)
2038{
2039 struct var *vp, **vpp;
2040
2041 vpp = hashvar(s);
2042 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2043 vp = *findvar(vpp, s);
2044 if (vp) {
2045 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2046 const char *n;
2047
2048 if (flags & VNOSAVE)
2049 free(s);
2050 n = vp->text;
2051 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2052 }
2053
2054 if (flags & VNOSET)
2055 return;
2056
2057 if (vp->func && (flags & VNOFUNC) == 0)
2058 (*vp->func)(strchrnul(s, '=') + 1);
2059
2060 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2061 free((char*)vp->text);
2062
2063 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2064 } else {
2065 if (flags & VNOSET)
2066 return;
2067 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002068 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002069 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002070 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002071 *vpp = vp;
2072 }
2073 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2074 s = ckstrdup(s);
2075 vp->text = s;
2076 vp->flags = flags;
2077}
2078
2079/*
2080 * Set the value of a variable. The flags argument is ored with the
2081 * flags of the variable. If val is NULL, the variable is unset.
2082 */
2083static void
2084setvar(const char *name, const char *val, int flags)
2085{
2086 char *p, *q;
2087 size_t namelen;
2088 char *nameeq;
2089 size_t vallen;
2090
2091 q = endofname(name);
2092 p = strchrnul(q, '=');
2093 namelen = p - name;
2094 if (!namelen || p != q)
2095 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2096 vallen = 0;
2097 if (val == NULL) {
2098 flags |= VUNSET;
2099 } else {
2100 vallen = strlen(val);
2101 }
2102 INT_OFF;
2103 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002104 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002105 if (val) {
2106 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002107 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002108 }
2109 *p = '\0';
2110 setvareq(nameeq, flags | VNOSAVE);
2111 INT_ON;
2112}
2113
2114#if ENABLE_ASH_GETOPTS
2115/*
2116 * Safe version of setvar, returns 1 on success 0 on failure.
2117 */
2118static int
2119setvarsafe(const char *name, const char *val, int flags)
2120{
2121 int err;
2122 volatile int saveint;
2123 struct jmploc *volatile savehandler = exception_handler;
2124 struct jmploc jmploc;
2125
2126 SAVE_INT(saveint);
2127 if (setjmp(jmploc.loc))
2128 err = 1;
2129 else {
2130 exception_handler = &jmploc;
2131 setvar(name, val, flags);
2132 err = 0;
2133 }
2134 exception_handler = savehandler;
2135 RESTORE_INT(saveint);
2136 return err;
2137}
2138#endif
2139
2140/*
2141 * Unset the specified variable.
2142 */
2143static int
2144unsetvar(const char *s)
2145{
2146 struct var **vpp;
2147 struct var *vp;
2148 int retval;
2149
2150 vpp = findvar(hashvar(s), s);
2151 vp = *vpp;
2152 retval = 2;
2153 if (vp) {
2154 int flags = vp->flags;
2155
2156 retval = 1;
2157 if (flags & VREADONLY)
2158 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002159#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002160 vp->flags &= ~VDYNAMIC;
2161#endif
2162 if (flags & VUNSET)
2163 goto ok;
2164 if ((flags & VSTRFIXED) == 0) {
2165 INT_OFF;
2166 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2167 free((char*)vp->text);
2168 *vpp = vp->next;
2169 free(vp);
2170 INT_ON;
2171 } else {
2172 setvar(s, 0, 0);
2173 vp->flags &= ~VEXPORT;
2174 }
2175 ok:
2176 retval = 0;
2177 }
2178 out:
2179 return retval;
2180}
2181
2182/*
2183 * Process a linked list of variable assignments.
2184 */
2185static void
2186listsetvar(struct strlist *list_set_var, int flags)
2187{
2188 struct strlist *lp = list_set_var;
2189
2190 if (!lp)
2191 return;
2192 INT_OFF;
2193 do {
2194 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002195 lp = lp->next;
2196 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002197 INT_ON;
2198}
2199
2200/*
2201 * Generate a list of variables satisfying the given conditions.
2202 */
2203static char **
2204listvars(int on, int off, char ***end)
2205{
2206 struct var **vpp;
2207 struct var *vp;
2208 char **ep;
2209 int mask;
2210
2211 STARTSTACKSTR(ep);
2212 vpp = vartab;
2213 mask = on | off;
2214 do {
2215 for (vp = *vpp; vp; vp = vp->next) {
2216 if ((vp->flags & mask) == on) {
2217 if (ep == stackstrend())
2218 ep = growstackstr();
2219 *ep++ = (char *) vp->text;
2220 }
2221 }
2222 } while (++vpp < vartab + VTABSIZE);
2223 if (ep == stackstrend())
2224 ep = growstackstr();
2225 if (end)
2226 *end = ep;
2227 *ep++ = NULL;
2228 return grabstackstr(ep);
2229}
2230
2231
2232/* ============ Path search helper
2233 *
2234 * The variable path (passed by reference) should be set to the start
2235 * of the path before the first call; padvance will update
2236 * this value as it proceeds. Successive calls to padvance will return
2237 * the possible path expansions in sequence. If an option (indicated by
2238 * a percent sign) appears in the path entry then the global variable
2239 * pathopt will be set to point to it; otherwise pathopt will be set to
2240 * NULL.
2241 */
2242static const char *pathopt; /* set by padvance */
2243
2244static char *
2245padvance(const char **path, const char *name)
2246{
2247 const char *p;
2248 char *q;
2249 const char *start;
2250 size_t len;
2251
2252 if (*path == NULL)
2253 return NULL;
2254 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002255 for (p = start; *p && *p != ':' && *p != '%'; p++)
2256 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002257 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2258 while (stackblocksize() < len)
2259 growstackblock();
2260 q = stackblock();
2261 if (p != start) {
2262 memcpy(q, start, p - start);
2263 q += p - start;
2264 *q++ = '/';
2265 }
2266 strcpy(q, name);
2267 pathopt = NULL;
2268 if (*p == '%') {
2269 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002270 while (*p && *p != ':')
2271 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002272 }
2273 if (*p == ':')
2274 *path = p + 1;
2275 else
2276 *path = NULL;
2277 return stalloc(len);
2278}
2279
2280
2281/* ============ Prompt */
2282
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002283static smallint doprompt; /* if set, prompt the user */
2284static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285
2286#if ENABLE_FEATURE_EDITING
2287static line_input_t *line_input_state;
2288static const char *cmdedit_prompt;
2289static void
2290putprompt(const char *s)
2291{
2292 if (ENABLE_ASH_EXPAND_PRMT) {
2293 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002294 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002295 return;
2296 }
2297 cmdedit_prompt = s;
2298}
2299#else
2300static void
2301putprompt(const char *s)
2302{
2303 out2str(s);
2304}
2305#endif
2306
2307#if ENABLE_ASH_EXPAND_PRMT
2308/* expandstr() needs parsing machinery, so it is far away ahead... */
2309static const char *expandstr(const char *ps);
2310#else
2311#define expandstr(s) s
2312#endif
2313
2314static void
2315setprompt(int whichprompt)
2316{
2317 const char *prompt;
2318#if ENABLE_ASH_EXPAND_PRMT
2319 struct stackmark smark;
2320#endif
2321
2322 needprompt = 0;
2323
2324 switch (whichprompt) {
2325 case 1:
2326 prompt = ps1val();
2327 break;
2328 case 2:
2329 prompt = ps2val();
2330 break;
2331 default: /* 0 */
2332 prompt = nullstr;
2333 }
2334#if ENABLE_ASH_EXPAND_PRMT
2335 setstackmark(&smark);
2336 stalloc(stackblocksize());
2337#endif
2338 putprompt(expandstr(prompt));
2339#if ENABLE_ASH_EXPAND_PRMT
2340 popstackmark(&smark);
2341#endif
2342}
2343
2344
2345/* ============ The cd and pwd commands */
2346
2347#define CD_PHYSICAL 1
2348#define CD_PRINT 2
2349
2350static int docd(const char *, int);
2351
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002352static int
2353cdopt(void)
2354{
2355 int flags = 0;
2356 int i, j;
2357
2358 j = 'L';
2359 while ((i = nextopt("LP"))) {
2360 if (i != j) {
2361 flags ^= CD_PHYSICAL;
2362 j = i;
2363 }
2364 }
2365
2366 return flags;
2367}
2368
2369/*
2370 * Update curdir (the name of the current directory) in response to a
2371 * cd command.
2372 */
2373static const char *
2374updatepwd(const char *dir)
2375{
2376 char *new;
2377 char *p;
2378 char *cdcomppath;
2379 const char *lim;
2380
2381 cdcomppath = ststrdup(dir);
2382 STARTSTACKSTR(new);
2383 if (*dir != '/') {
2384 if (curdir == nullstr)
2385 return 0;
2386 new = stack_putstr(curdir, new);
2387 }
2388 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002389 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390 if (*dir != '/') {
2391 if (new[-1] != '/')
2392 USTPUTC('/', new);
2393 if (new > lim && *lim == '/')
2394 lim++;
2395 } else {
2396 USTPUTC('/', new);
2397 cdcomppath++;
2398 if (dir[1] == '/' && dir[2] != '/') {
2399 USTPUTC('/', new);
2400 cdcomppath++;
2401 lim++;
2402 }
2403 }
2404 p = strtok(cdcomppath, "/");
2405 while (p) {
2406 switch (*p) {
2407 case '.':
2408 if (p[1] == '.' && p[2] == '\0') {
2409 while (new > lim) {
2410 STUNPUTC(new);
2411 if (new[-1] == '/')
2412 break;
2413 }
2414 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002415 }
2416 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002417 break;
2418 /* fall through */
2419 default:
2420 new = stack_putstr(p, new);
2421 USTPUTC('/', new);
2422 }
2423 p = strtok(0, "/");
2424 }
2425 if (new > lim)
2426 STUNPUTC(new);
2427 *new = 0;
2428 return stackblock();
2429}
2430
2431/*
2432 * Find out what the current directory is. If we already know the current
2433 * directory, this routine returns immediately.
2434 */
2435static char *
2436getpwd(void)
2437{
Denis Vlasenko01631112007-12-16 17:20:38 +00002438 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002439 return dir ? dir : nullstr;
2440}
2441
2442static void
2443setpwd(const char *val, int setold)
2444{
2445 char *oldcur, *dir;
2446
2447 oldcur = dir = curdir;
2448
2449 if (setold) {
2450 setvar("OLDPWD", oldcur, VEXPORT);
2451 }
2452 INT_OFF;
2453 if (physdir != nullstr) {
2454 if (physdir != oldcur)
2455 free(physdir);
2456 physdir = nullstr;
2457 }
2458 if (oldcur == val || !val) {
2459 char *s = getpwd();
2460 physdir = s;
2461 if (!val)
2462 dir = s;
2463 } else
2464 dir = ckstrdup(val);
2465 if (oldcur != dir && oldcur != nullstr) {
2466 free(oldcur);
2467 }
2468 curdir = dir;
2469 INT_ON;
2470 setvar("PWD", dir, VEXPORT);
2471}
2472
2473static void hashcd(void);
2474
2475/*
2476 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2477 * know that the current directory has changed.
2478 */
2479static int
2480docd(const char *dest, int flags)
2481{
2482 const char *dir = 0;
2483 int err;
2484
2485 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2486
2487 INT_OFF;
2488 if (!(flags & CD_PHYSICAL)) {
2489 dir = updatepwd(dest);
2490 if (dir)
2491 dest = dir;
2492 }
2493 err = chdir(dest);
2494 if (err)
2495 goto out;
2496 setpwd(dir, 1);
2497 hashcd();
2498 out:
2499 INT_ON;
2500 return err;
2501}
2502
2503static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002504cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002505{
2506 const char *dest;
2507 const char *path;
2508 const char *p;
2509 char c;
2510 struct stat statb;
2511 int flags;
2512
2513 flags = cdopt();
2514 dest = *argptr;
2515 if (!dest)
2516 dest = bltinlookup(homestr);
2517 else if (LONE_DASH(dest)) {
2518 dest = bltinlookup("OLDPWD");
2519 flags |= CD_PRINT;
2520 }
2521 if (!dest)
2522 dest = nullstr;
2523 if (*dest == '/')
2524 goto step7;
2525 if (*dest == '.') {
2526 c = dest[1];
2527 dotdot:
2528 switch (c) {
2529 case '\0':
2530 case '/':
2531 goto step6;
2532 case '.':
2533 c = dest[2];
2534 if (c != '.')
2535 goto dotdot;
2536 }
2537 }
2538 if (!*dest)
2539 dest = ".";
2540 path = bltinlookup("CDPATH");
2541 if (!path) {
2542 step6:
2543 step7:
2544 p = dest;
2545 goto docd;
2546 }
2547 do {
2548 c = *path;
2549 p = padvance(&path, dest);
2550 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2551 if (c && c != ':')
2552 flags |= CD_PRINT;
2553 docd:
2554 if (!docd(p, flags))
2555 goto out;
2556 break;
2557 }
2558 } while (path);
2559 ash_msg_and_raise_error("can't cd to %s", dest);
2560 /* NOTREACHED */
2561 out:
2562 if (flags & CD_PRINT)
2563 out1fmt(snlfmt, curdir);
2564 return 0;
2565}
2566
2567static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002568pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002569{
2570 int flags;
2571 const char *dir = curdir;
2572
2573 flags = cdopt();
2574 if (flags) {
2575 if (physdir == nullstr)
2576 setpwd(dir, 0);
2577 dir = physdir;
2578 }
2579 out1fmt(snlfmt, dir);
2580 return 0;
2581}
2582
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002583
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002584/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002585
Denis Vlasenko834dee72008-10-07 09:18:30 +00002586
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002587#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002588/* buffer for top level input file */
2589#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002590
Eric Andersenc470f442003-07-28 09:56:35 +00002591/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002592#define CWORD 0 /* character is nothing special */
2593#define CNL 1 /* newline character */
2594#define CBACK 2 /* a backslash character */
2595#define CSQUOTE 3 /* single quote */
2596#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002597#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002598#define CBQUOTE 6 /* backwards single quote */
2599#define CVAR 7 /* a dollar sign */
2600#define CENDVAR 8 /* a '}' character */
2601#define CLP 9 /* a left paren in arithmetic */
2602#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002603#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002604#define CCTL 12 /* like CWORD, except it must be escaped */
2605#define CSPCL 13 /* these terminate a word */
2606#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002607
Denis Vlasenko131ae172007-02-18 13:00:19 +00002608#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002609#define SYNBASE 130
2610#define PEOF -130
2611#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002612#define PEOA_OR_PEOF PEOA
2613#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002614#define SYNBASE 129
2615#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002616#define PEOA_OR_PEOF PEOF
2617#endif
2618
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002619/* number syntax index */
2620#define BASESYNTAX 0 /* not in quotes */
2621#define DQSYNTAX 1 /* in double quotes */
2622#define SQSYNTAX 2 /* in single quotes */
2623#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002624#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002625
Denis Vlasenko131ae172007-02-18 13:00:19 +00002626#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002627#define USE_SIT_FUNCTION
2628#endif
2629
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002631static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002632#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002633 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002634#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002635 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2636 { CNL, CNL, CNL, CNL }, /* 2, \n */
2637 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2638 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2639 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2640 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2641 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2642 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2643 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2644 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2645 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002646#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002647 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2648 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2649 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002650#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002651};
Eric Andersenc470f442003-07-28 09:56:35 +00002652#else
2653static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002654#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002655 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002656#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002657 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2658 { CNL, CNL, CNL }, /* 2, \n */
2659 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2660 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2661 { CVAR, CVAR, CWORD }, /* 5, $ */
2662 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2663 { CSPCL, CWORD, CWORD }, /* 7, ( */
2664 { CSPCL, CWORD, CWORD }, /* 8, ) */
2665 { CBACK, CBACK, CCTL }, /* 9, \ */
2666 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2667 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002668#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002669 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2670 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2671 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002672#endif
2673};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002674#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002675
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002676#ifdef USE_SIT_FUNCTION
2677
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002678static int
2679SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002680{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002681 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002682#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002683 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002684 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2685 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2686 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2687 11, 3 /* "}~" */
2688 };
2689#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002690 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002691 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2692 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2693 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2694 10, 2 /* "}~" */
2695 };
2696#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002697 const char *s;
2698 int indx;
2699
Eric Andersenc470f442003-07-28 09:56:35 +00002700 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002701 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002702#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002703 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002704 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002705 else
2706#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002707
2708 if ((unsigned char)c >= (unsigned char)(CTLESC)
2709 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2710 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002711 return CCTL;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002712 }
Denis Vlasenko68819d12008-12-15 11:26:36 +00002713 s = strchrnul(spec_symbls, c);
2714 if (*s == '\0')
2715 return CWORD;
2716 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002717 return S_I_T[indx][syntax];
2718}
2719
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002720#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002721
Denis Vlasenko131ae172007-02-18 13:00:19 +00002722#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002723#define CSPCL_CIGN_CIGN_CIGN 0
2724#define CSPCL_CWORD_CWORD_CWORD 1
2725#define CNL_CNL_CNL_CNL 2
2726#define CWORD_CCTL_CCTL_CWORD 3
2727#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2728#define CVAR_CVAR_CWORD_CVAR 5
2729#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2730#define CSPCL_CWORD_CWORD_CLP 7
2731#define CSPCL_CWORD_CWORD_CRP 8
2732#define CBACK_CBACK_CCTL_CBACK 9
2733#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2734#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2735#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2736#define CWORD_CWORD_CWORD_CWORD 13
2737#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002738#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002739#define CSPCL_CWORD_CWORD_CWORD 0
2740#define CNL_CNL_CNL_CNL 1
2741#define CWORD_CCTL_CCTL_CWORD 2
2742#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2743#define CVAR_CVAR_CWORD_CVAR 4
2744#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2745#define CSPCL_CWORD_CWORD_CLP 6
2746#define CSPCL_CWORD_CWORD_CRP 7
2747#define CBACK_CBACK_CCTL_CBACK 8
2748#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2749#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2750#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2751#define CWORD_CWORD_CWORD_CWORD 12
2752#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002753#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002754
2755static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002756 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002757 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002758#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002759 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2760#endif
2761 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2763 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2764 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2765 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2766 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2767 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2768 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2769 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002770 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2899 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2900 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2913 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2914 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2915 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2916 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2917 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2918 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2919 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2920 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2921 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2922 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002923 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002924 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2926 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002928 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002929 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2930 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2931 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2932 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2934 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2935 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2936 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2937 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2948 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2949 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2950 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2951 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2952 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2953 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2981 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2982 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2983 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2986 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3005 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3006 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3007 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3008 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3009 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3010 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3011 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3012 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3013 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3014 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3015 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3016 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003017};
3018
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003019#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3020
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003021#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003022
Eric Andersen2870d962001-07-02 17:27:21 +00003023
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003024/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003025
Denis Vlasenko131ae172007-02-18 13:00:19 +00003026#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003027
3028#define ALIASINUSE 1
3029#define ALIASDEAD 2
3030
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003031struct alias {
3032 struct alias *next;
3033 char *name;
3034 char *val;
3035 int flag;
3036};
3037
Denis Vlasenko01631112007-12-16 17:20:38 +00003038
3039static struct alias **atab; // [ATABSIZE];
3040#define INIT_G_alias() do { \
3041 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3042} while (0)
3043
Eric Andersen2870d962001-07-02 17:27:21 +00003044
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003045static struct alias **
3046__lookupalias(const char *name) {
3047 unsigned int hashval;
3048 struct alias **app;
3049 const char *p;
3050 unsigned int ch;
3051
3052 p = name;
3053
3054 ch = (unsigned char)*p;
3055 hashval = ch << 4;
3056 while (ch) {
3057 hashval += ch;
3058 ch = (unsigned char)*++p;
3059 }
3060 app = &atab[hashval % ATABSIZE];
3061
3062 for (; *app; app = &(*app)->next) {
3063 if (strcmp(name, (*app)->name) == 0) {
3064 break;
3065 }
3066 }
3067
3068 return app;
3069}
3070
3071static struct alias *
3072lookupalias(const char *name, int check)
3073{
3074 struct alias *ap = *__lookupalias(name);
3075
3076 if (check && ap && (ap->flag & ALIASINUSE))
3077 return NULL;
3078 return ap;
3079}
3080
3081static struct alias *
3082freealias(struct alias *ap)
3083{
3084 struct alias *next;
3085
3086 if (ap->flag & ALIASINUSE) {
3087 ap->flag |= ALIASDEAD;
3088 return ap;
3089 }
3090
3091 next = ap->next;
3092 free(ap->name);
3093 free(ap->val);
3094 free(ap);
3095 return next;
3096}
Eric Andersencb57d552001-06-28 07:25:16 +00003097
Eric Andersenc470f442003-07-28 09:56:35 +00003098static void
3099setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003100{
3101 struct alias *ap, **app;
3102
3103 app = __lookupalias(name);
3104 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003106 if (ap) {
3107 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003108 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003109 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003110 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003111 ap->flag &= ~ALIASDEAD;
3112 } else {
3113 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003114 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003115 ap->name = ckstrdup(name);
3116 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003117 /*ap->flag = 0; - ckzalloc did it */
3118 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003119 *app = ap;
3120 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003121 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003122}
3123
Eric Andersenc470f442003-07-28 09:56:35 +00003124static int
3125unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003126{
Eric Andersencb57d552001-06-28 07:25:16 +00003127 struct alias **app;
3128
3129 app = __lookupalias(name);
3130
3131 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003132 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003133 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003134 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003135 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003136 }
3137
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003138 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003139}
3140
Eric Andersenc470f442003-07-28 09:56:35 +00003141static void
3142rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003143{
Eric Andersencb57d552001-06-28 07:25:16 +00003144 struct alias *ap, **app;
3145 int i;
3146
Denis Vlasenkob012b102007-02-19 22:43:01 +00003147 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003148 for (i = 0; i < ATABSIZE; i++) {
3149 app = &atab[i];
3150 for (ap = *app; ap; ap = *app) {
3151 *app = freealias(*app);
3152 if (ap == *app) {
3153 app = &ap->next;
3154 }
3155 }
3156 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003157 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003158}
3159
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003160static void
3161printalias(const struct alias *ap)
3162{
3163 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3164}
3165
Eric Andersencb57d552001-06-28 07:25:16 +00003166/*
3167 * TODO - sort output
3168 */
Eric Andersenc470f442003-07-28 09:56:35 +00003169static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003170aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003171{
3172 char *n, *v;
3173 int ret = 0;
3174 struct alias *ap;
3175
Denis Vlasenko68404f12008-03-17 09:00:54 +00003176 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003177 int i;
3178
Denis Vlasenko68404f12008-03-17 09:00:54 +00003179 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003180 for (ap = atab[i]; ap; ap = ap->next) {
3181 printalias(ap);
3182 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003183 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003184 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003185 }
3186 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003187 v = strchr(n+1, '=');
3188 if (v == NULL) { /* n+1: funny ksh stuff */
3189 ap = *__lookupalias(n);
3190 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003191 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003192 ret = 1;
3193 } else
3194 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003195 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003196 *v++ = '\0';
3197 setalias(n, v);
3198 }
3199 }
3200
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003201 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003202}
3203
Eric Andersenc470f442003-07-28 09:56:35 +00003204static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003205unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003206{
3207 int i;
3208
3209 while ((i = nextopt("a")) != '\0') {
3210 if (i == 'a') {
3211 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003212 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003213 }
3214 }
3215 for (i = 0; *argptr; argptr++) {
3216 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003217 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003218 i = 1;
3219 }
3220 }
3221
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003222 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003223}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003224
Denis Vlasenko131ae172007-02-18 13:00:19 +00003225#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003226
Eric Andersenc470f442003-07-28 09:56:35 +00003227
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003228/* ============ jobs.c */
3229
3230/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3231#define FORK_FG 0
3232#define FORK_BG 1
3233#define FORK_NOJOB 2
3234
3235/* mode flags for showjob(s) */
3236#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3237#define SHOW_PID 0x04 /* include process pid */
3238#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3239
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003240/*
3241 * A job structure contains information about a job. A job is either a
3242 * single process or a set of processes contained in a pipeline. In the
3243 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3244 * array of pids.
3245 */
3246
3247struct procstat {
3248 pid_t pid; /* process id */
3249 int status; /* last process status from wait() */
3250 char *cmd; /* text of command being run */
3251};
3252
3253struct job {
3254 struct procstat ps0; /* status of process */
3255 struct procstat *ps; /* status or processes when more than one */
3256#if JOBS
3257 int stopstatus; /* status of a stopped job */
3258#endif
3259 uint32_t
3260 nprocs: 16, /* number of processes */
3261 state: 8,
3262#define JOBRUNNING 0 /* at least one proc running */
3263#define JOBSTOPPED 1 /* all procs are stopped */
3264#define JOBDONE 2 /* all procs are completed */
3265#if JOBS
3266 sigint: 1, /* job was killed by SIGINT */
3267 jobctl: 1, /* job running under job control */
3268#endif
3269 waited: 1, /* true if this entry has been waited for */
3270 used: 1, /* true if this entry is in used */
3271 changed: 1; /* true if status has changed */
3272 struct job *prev_job; /* previous job */
3273};
3274
Denis Vlasenko68404f12008-03-17 09:00:54 +00003275static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003276#if !JOBS
3277#define forkshell(job, node, mode) forkshell(job, mode)
3278#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003279static int forkshell(struct job *, union node *, int);
3280static int waitforjob(struct job *);
3281
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003282#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003283enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003284#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003285#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003286static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003287static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003288#endif
3289
3290/*
3291 * Set the signal handler for the specified signal. The routine figures
3292 * out what it should be set to.
3293 */
3294static void
3295setsignal(int signo)
3296{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003297 char *t;
3298 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003299 struct sigaction act;
3300
3301 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003302 new_act = S_DFL;
3303 if (t != NULL) { /* trap for this sig is set */
3304 new_act = S_CATCH;
3305 if (t[0] == '\0') /* trap is "": ignore this sig */
3306 new_act = S_IGN;
3307 }
3308
3309 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003310 switch (signo) {
3311 case SIGINT:
3312 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003313 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314 break;
3315 case SIGQUIT:
3316#if DEBUG
3317 if (debug)
3318 break;
3319#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003320 /* man bash:
3321 * "In all cases, bash ignores SIGQUIT. Non-builtin
3322 * commands run by bash have signal handlers
3323 * set to the values inherited by the shell
3324 * from its parent". */
3325 new_act = S_IGN;
3326 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327 case SIGTERM:
3328 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003329 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003330 break;
3331#if JOBS
3332 case SIGTSTP:
3333 case SIGTTOU:
3334 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003335 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336 break;
3337#endif
3338 }
3339 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003340//TODO: if !rootshell, we reset SIGQUIT to DFL,
3341//whereas we have to restore it to what shell got on entry
3342//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003343
3344 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003345 cur_act = *t;
3346 if (cur_act == 0) {
3347 /* current setting is not yet known */
3348 if (sigaction(signo, NULL, &act)) {
3349 /* pretend it worked; maybe we should give a warning,
3350 * but other shells don't. We don't alter sigmode,
3351 * so we retry every time.
3352 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353 return;
3354 }
3355 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003356 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357 if (mflag
3358 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3359 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003360 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003361 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362 }
3363 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003364 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003365 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003366
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003367 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003368 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003369 case S_CATCH:
3370 act.sa_handler = onsig;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003371 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3372 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373 break;
3374 case S_IGN:
3375 act.sa_handler = SIG_IGN;
3376 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003377 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003378 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003379
3380 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003381}
3382
3383/* mode flags for set_curjob */
3384#define CUR_DELETE 2
3385#define CUR_RUNNING 1
3386#define CUR_STOPPED 0
3387
3388/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003389#define DOWAIT_NONBLOCK WNOHANG
3390#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003391
3392#if JOBS
3393/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003394static int initialpgrp; //references:2
3395static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003396#endif
3397/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003398static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003399/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003400static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003401/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003402static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003403/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003404static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003405
3406static void
3407set_curjob(struct job *jp, unsigned mode)
3408{
3409 struct job *jp1;
3410 struct job **jpp, **curp;
3411
3412 /* first remove from list */
3413 jpp = curp = &curjob;
3414 do {
3415 jp1 = *jpp;
3416 if (jp1 == jp)
3417 break;
3418 jpp = &jp1->prev_job;
3419 } while (1);
3420 *jpp = jp1->prev_job;
3421
3422 /* Then re-insert in correct position */
3423 jpp = curp;
3424 switch (mode) {
3425 default:
3426#if DEBUG
3427 abort();
3428#endif
3429 case CUR_DELETE:
3430 /* job being deleted */
3431 break;
3432 case CUR_RUNNING:
3433 /* newly created job or backgrounded job,
3434 put after all stopped jobs. */
3435 do {
3436 jp1 = *jpp;
3437#if JOBS
3438 if (!jp1 || jp1->state != JOBSTOPPED)
3439#endif
3440 break;
3441 jpp = &jp1->prev_job;
3442 } while (1);
3443 /* FALLTHROUGH */
3444#if JOBS
3445 case CUR_STOPPED:
3446#endif
3447 /* newly stopped job - becomes curjob */
3448 jp->prev_job = *jpp;
3449 *jpp = jp;
3450 break;
3451 }
3452}
3453
3454#if JOBS || DEBUG
3455static int
3456jobno(const struct job *jp)
3457{
3458 return jp - jobtab + 1;
3459}
3460#endif
3461
3462/*
3463 * Convert a job name to a job structure.
3464 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003465#if !JOBS
3466#define getjob(name, getctl) getjob(name)
3467#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003468static struct job *
3469getjob(const char *name, int getctl)
3470{
3471 struct job *jp;
3472 struct job *found;
3473 const char *err_msg = "No such job: %s";
3474 unsigned num;
3475 int c;
3476 const char *p;
3477 char *(*match)(const char *, const char *);
3478
3479 jp = curjob;
3480 p = name;
3481 if (!p)
3482 goto currentjob;
3483
3484 if (*p != '%')
3485 goto err;
3486
3487 c = *++p;
3488 if (!c)
3489 goto currentjob;
3490
3491 if (!p[1]) {
3492 if (c == '+' || c == '%') {
3493 currentjob:
3494 err_msg = "No current job";
3495 goto check;
3496 }
3497 if (c == '-') {
3498 if (jp)
3499 jp = jp->prev_job;
3500 err_msg = "No previous job";
3501 check:
3502 if (!jp)
3503 goto err;
3504 goto gotit;
3505 }
3506 }
3507
3508 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003509// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003510 num = atoi(p);
3511 if (num < njobs) {
3512 jp = jobtab + num - 1;
3513 if (jp->used)
3514 goto gotit;
3515 goto err;
3516 }
3517 }
3518
3519 match = prefix;
3520 if (*p == '?') {
3521 match = strstr;
3522 p++;
3523 }
3524
3525 found = 0;
3526 while (1) {
3527 if (!jp)
3528 goto err;
3529 if (match(jp->ps[0].cmd, p)) {
3530 if (found)
3531 goto err;
3532 found = jp;
3533 err_msg = "%s: ambiguous";
3534 }
3535 jp = jp->prev_job;
3536 }
3537
3538 gotit:
3539#if JOBS
3540 err_msg = "job %s not created under job control";
3541 if (getctl && jp->jobctl == 0)
3542 goto err;
3543#endif
3544 return jp;
3545 err:
3546 ash_msg_and_raise_error(err_msg, name);
3547}
3548
3549/*
3550 * Mark a job structure as unused.
3551 */
3552static void
3553freejob(struct job *jp)
3554{
3555 struct procstat *ps;
3556 int i;
3557
3558 INT_OFF;
3559 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3560 if (ps->cmd != nullstr)
3561 free(ps->cmd);
3562 }
3563 if (jp->ps != &jp->ps0)
3564 free(jp->ps);
3565 jp->used = 0;
3566 set_curjob(jp, CUR_DELETE);
3567 INT_ON;
3568}
3569
3570#if JOBS
3571static void
3572xtcsetpgrp(int fd, pid_t pgrp)
3573{
3574 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003575 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003576}
3577
3578/*
3579 * Turn job control on and off.
3580 *
3581 * Note: This code assumes that the third arg to ioctl is a character
3582 * pointer, which is true on Berkeley systems but not System V. Since
3583 * System V doesn't have job control yet, this isn't a problem now.
3584 *
3585 * Called with interrupts off.
3586 */
3587static void
3588setjobctl(int on)
3589{
3590 int fd;
3591 int pgrp;
3592
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003593 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003594 return;
3595 if (on) {
3596 int ofd;
3597 ofd = fd = open(_PATH_TTY, O_RDWR);
3598 if (fd < 0) {
3599 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3600 * That sometimes helps to acquire controlling tty.
3601 * Obviously, a workaround for bugs when someone
3602 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003603 fd = 2;
3604 while (!isatty(fd))
3605 if (--fd < 0)
3606 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607 }
3608 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003609 if (ofd >= 0)
3610 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003611 if (fd < 0)
3612 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003613 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003614 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003615 do { /* while we are in the background */
3616 pgrp = tcgetpgrp(fd);
3617 if (pgrp < 0) {
3618 out:
3619 ash_msg("can't access tty; job control turned off");
3620 mflag = on = 0;
3621 goto close;
3622 }
3623 if (pgrp == getpgrp())
3624 break;
3625 killpg(0, SIGTTIN);
3626 } while (1);
3627 initialpgrp = pgrp;
3628
3629 setsignal(SIGTSTP);
3630 setsignal(SIGTTOU);
3631 setsignal(SIGTTIN);
3632 pgrp = rootpid;
3633 setpgid(0, pgrp);
3634 xtcsetpgrp(fd, pgrp);
3635 } else {
3636 /* turning job control off */
3637 fd = ttyfd;
3638 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003639 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003640 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003641 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003642 setpgid(0, pgrp);
3643 setsignal(SIGTSTP);
3644 setsignal(SIGTTOU);
3645 setsignal(SIGTTIN);
3646 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003647 if (fd >= 0)
3648 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003649 fd = -1;
3650 }
3651 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003652 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653}
3654
3655static int
3656killcmd(int argc, char **argv)
3657{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003658 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003659 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003660 do {
3661 if (argv[i][0] == '%') {
3662 struct job *jp = getjob(argv[i], 0);
3663 unsigned pid = jp->ps[0].pid;
3664 /* Enough space for ' -NNN<nul>' */
3665 argv[i] = alloca(sizeof(int)*3 + 3);
3666 /* kill_main has matching code to expect
3667 * leading space. Needed to not confuse
3668 * negative pids with "kill -SIGNAL_NO" syntax */
3669 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003670 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003671 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003672 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003673 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003674}
3675
3676static void
3677showpipe(struct job *jp, FILE *out)
3678{
3679 struct procstat *sp;
3680 struct procstat *spend;
3681
3682 spend = jp->ps + jp->nprocs;
3683 for (sp = jp->ps + 1; sp < spend; sp++)
3684 fprintf(out, " | %s", sp->cmd);
3685 outcslow('\n', out);
3686 flush_stdout_stderr();
3687}
3688
3689
3690static int
3691restartjob(struct job *jp, int mode)
3692{
3693 struct procstat *ps;
3694 int i;
3695 int status;
3696 pid_t pgid;
3697
3698 INT_OFF;
3699 if (jp->state == JOBDONE)
3700 goto out;
3701 jp->state = JOBRUNNING;
3702 pgid = jp->ps->pid;
3703 if (mode == FORK_FG)
3704 xtcsetpgrp(ttyfd, pgid);
3705 killpg(pgid, SIGCONT);
3706 ps = jp->ps;
3707 i = jp->nprocs;
3708 do {
3709 if (WIFSTOPPED(ps->status)) {
3710 ps->status = -1;
3711 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003712 ps++;
3713 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003714 out:
3715 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3716 INT_ON;
3717 return status;
3718}
3719
3720static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003721fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003722{
3723 struct job *jp;
3724 FILE *out;
3725 int mode;
3726 int retval;
3727
3728 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3729 nextopt(nullstr);
3730 argv = argptr;
3731 out = stdout;
3732 do {
3733 jp = getjob(*argv, 1);
3734 if (mode == FORK_BG) {
3735 set_curjob(jp, CUR_RUNNING);
3736 fprintf(out, "[%d] ", jobno(jp));
3737 }
3738 outstr(jp->ps->cmd, out);
3739 showpipe(jp, out);
3740 retval = restartjob(jp, mode);
3741 } while (*argv && *++argv);
3742 return retval;
3743}
3744#endif
3745
3746static int
3747sprint_status(char *s, int status, int sigonly)
3748{
3749 int col;
3750 int st;
3751
3752 col = 0;
3753 if (!WIFEXITED(status)) {
3754#if JOBS
3755 if (WIFSTOPPED(status))
3756 st = WSTOPSIG(status);
3757 else
3758#endif
3759 st = WTERMSIG(status);
3760 if (sigonly) {
3761 if (st == SIGINT || st == SIGPIPE)
3762 goto out;
3763#if JOBS
3764 if (WIFSTOPPED(status))
3765 goto out;
3766#endif
3767 }
3768 st &= 0x7f;
3769 col = fmtstr(s, 32, strsignal(st));
3770 if (WCOREDUMP(status)) {
3771 col += fmtstr(s + col, 16, " (core dumped)");
3772 }
3773 } else if (!sigonly) {
3774 st = WEXITSTATUS(status);
3775 if (st)
3776 col = fmtstr(s, 16, "Done(%d)", st);
3777 else
3778 col = fmtstr(s, 16, "Done");
3779 }
3780 out:
3781 return col;
3782}
3783
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003785dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786{
3787 int pid;
3788 int status;
3789 struct job *jp;
3790 struct job *thisjob;
3791 int state;
3792
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003793 TRACE(("dowait(0x%x) called\n", wait_flags));
3794
3795 /* Do a wait system call. If job control is compiled in, we accept
3796 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3797 * NB: _not_ safe_waitpid, we need to detect EINTR */
3798 pid = waitpid(-1, &status,
3799 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3800 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003801 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003803
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003804 INT_OFF;
3805 thisjob = NULL;
3806 for (jp = curjob; jp; jp = jp->prev_job) {
3807 struct procstat *sp;
3808 struct procstat *spend;
3809 if (jp->state == JOBDONE)
3810 continue;
3811 state = JOBDONE;
3812 spend = jp->ps + jp->nprocs;
3813 sp = jp->ps;
3814 do {
3815 if (sp->pid == pid) {
3816 TRACE(("Job %d: changing status of proc %d "
3817 "from 0x%x to 0x%x\n",
3818 jobno(jp), pid, sp->status, status));
3819 sp->status = status;
3820 thisjob = jp;
3821 }
3822 if (sp->status == -1)
3823 state = JOBRUNNING;
3824#if JOBS
3825 if (state == JOBRUNNING)
3826 continue;
3827 if (WIFSTOPPED(sp->status)) {
3828 jp->stopstatus = sp->status;
3829 state = JOBSTOPPED;
3830 }
3831#endif
3832 } while (++sp < spend);
3833 if (thisjob)
3834 goto gotjob;
3835 }
3836#if JOBS
3837 if (!WIFSTOPPED(status))
3838#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839 jobless--;
3840 goto out;
3841
3842 gotjob:
3843 if (state != JOBRUNNING) {
3844 thisjob->changed = 1;
3845
3846 if (thisjob->state != state) {
3847 TRACE(("Job %d: changing state from %d to %d\n",
3848 jobno(thisjob), thisjob->state, state));
3849 thisjob->state = state;
3850#if JOBS
3851 if (state == JOBSTOPPED) {
3852 set_curjob(thisjob, CUR_STOPPED);
3853 }
3854#endif
3855 }
3856 }
3857
3858 out:
3859 INT_ON;
3860
3861 if (thisjob && thisjob == job) {
3862 char s[48 + 1];
3863 int len;
3864
3865 len = sprint_status(s, status, 1);
3866 if (len) {
3867 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003868 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003869 out2str(s);
3870 }
3871 }
3872 return pid;
3873}
3874
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003875static int
3876blocking_wait_with_raise_on_sig(struct job *job)
3877{
3878 pid_t pid = dowait(DOWAIT_BLOCK, job);
3879 if (pid <= 0 && pendingsig)
3880 raise_exception(EXSIG);
3881 return pid;
3882}
3883
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884#if JOBS
3885static void
3886showjob(FILE *out, struct job *jp, int mode)
3887{
3888 struct procstat *ps;
3889 struct procstat *psend;
3890 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003891 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892 char s[80];
3893
3894 ps = jp->ps;
3895
3896 if (mode & SHOW_PGID) {
3897 /* just output process (group) id of pipeline */
3898 fprintf(out, "%d\n", ps->pid);
3899 return;
3900 }
3901
3902 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003903 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003904
3905 if (jp == curjob)
3906 s[col - 2] = '+';
3907 else if (curjob && jp == curjob->prev_job)
3908 s[col - 2] = '-';
3909
3910 if (mode & SHOW_PID)
3911 col += fmtstr(s + col, 16, "%d ", ps->pid);
3912
3913 psend = ps + jp->nprocs;
3914
3915 if (jp->state == JOBRUNNING) {
3916 strcpy(s + col, "Running");
3917 col += sizeof("Running") - 1;
3918 } else {
3919 int status = psend[-1].status;
3920 if (jp->state == JOBSTOPPED)
3921 status = jp->stopstatus;
3922 col += sprint_status(s + col, status, 0);
3923 }
3924
3925 goto start;
3926
3927 do {
3928 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003929 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 start:
3931 fprintf(out, "%s%*c%s",
3932 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3933 );
3934 if (!(mode & SHOW_PID)) {
3935 showpipe(jp, out);
3936 break;
3937 }
3938 if (++ps == psend) {
3939 outcslow('\n', out);
3940 break;
3941 }
3942 } while (1);
3943
3944 jp->changed = 0;
3945
3946 if (jp->state == JOBDONE) {
3947 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3948 freejob(jp);
3949 }
3950}
3951
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003952/*
3953 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3954 * statuses have changed since the last call to showjobs.
3955 */
3956static void
3957showjobs(FILE *out, int mode)
3958{
3959 struct job *jp;
3960
3961 TRACE(("showjobs(%x) called\n", mode));
3962
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003963 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003964 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003965 continue;
3966
3967 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003968 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003970 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 }
3972}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973
3974static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003975jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003976{
3977 int mode, m;
3978
3979 mode = 0;
3980 while ((m = nextopt("lp"))) {
3981 if (m == 'l')
3982 mode = SHOW_PID;
3983 else
3984 mode = SHOW_PGID;
3985 }
3986
3987 argv = argptr;
3988 if (*argv) {
3989 do
3990 showjob(stdout, getjob(*argv,0), mode);
3991 while (*++argv);
3992 } else
3993 showjobs(stdout, mode);
3994
3995 return 0;
3996}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003997#endif /* JOBS */
3998
3999static int
4000getstatus(struct job *job)
4001{
4002 int status;
4003 int retval;
4004
4005 status = job->ps[job->nprocs - 1].status;
4006 retval = WEXITSTATUS(status);
4007 if (!WIFEXITED(status)) {
4008#if JOBS
4009 retval = WSTOPSIG(status);
4010 if (!WIFSTOPPED(status))
4011#endif
4012 {
4013 /* XXX: limits number of signals */
4014 retval = WTERMSIG(status);
4015#if JOBS
4016 if (retval == SIGINT)
4017 job->sigint = 1;
4018#endif
4019 }
4020 retval += 128;
4021 }
4022 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4023 jobno(job), job->nprocs, status, retval));
4024 return retval;
4025}
4026
4027static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004028waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004029{
4030 struct job *job;
4031 int retval;
4032 struct job *jp;
4033
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004034// exsig++;
4035// xbarrier();
4036 if (pendingsig)
4037 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004038
4039 nextopt(nullstr);
4040 retval = 0;
4041
4042 argv = argptr;
4043 if (!*argv) {
4044 /* wait for all jobs */
4045 for (;;) {
4046 jp = curjob;
4047 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004048 if (!jp) /* no running procs */
4049 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 if (jp->state == JOBRUNNING)
4051 break;
4052 jp->waited = 1;
4053 jp = jp->prev_job;
4054 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004055 /* man bash:
4056 * "When bash is waiting for an asynchronous command via
4057 * the wait builtin, the reception of a signal for which a trap
4058 * has been set will cause the wait builtin to return immediately
4059 * with an exit status greater than 128, immediately after which
4060 * the trap is executed."
4061 * Do we do it that way? */
4062 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 }
4064 }
4065
4066 retval = 127;
4067 do {
4068 if (**argv != '%') {
4069 pid_t pid = number(*argv);
4070 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004071 while (1) {
4072 if (!job)
4073 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074 if (job->ps[job->nprocs - 1].pid == pid)
4075 break;
4076 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004077 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004078 } else
4079 job = getjob(*argv, 0);
4080 /* loop until process terminated or stopped */
4081 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004082 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004083 job->waited = 1;
4084 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004085 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 } while (*++argv);
4087
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004088 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004089 return retval;
4090}
4091
4092static struct job *
4093growjobtab(void)
4094{
4095 size_t len;
4096 ptrdiff_t offset;
4097 struct job *jp, *jq;
4098
4099 len = njobs * sizeof(*jp);
4100 jq = jobtab;
4101 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4102
4103 offset = (char *)jp - (char *)jq;
4104 if (offset) {
4105 /* Relocate pointers */
4106 size_t l = len;
4107
4108 jq = (struct job *)((char *)jq + l);
4109 while (l) {
4110 l -= sizeof(*jp);
4111 jq--;
4112#define joff(p) ((struct job *)((char *)(p) + l))
4113#define jmove(p) (p) = (void *)((char *)(p) + offset)
4114 if (joff(jp)->ps == &jq->ps0)
4115 jmove(joff(jp)->ps);
4116 if (joff(jp)->prev_job)
4117 jmove(joff(jp)->prev_job);
4118 }
4119 if (curjob)
4120 jmove(curjob);
4121#undef joff
4122#undef jmove
4123 }
4124
4125 njobs += 4;
4126 jobtab = jp;
4127 jp = (struct job *)((char *)jp + len);
4128 jq = jp + 3;
4129 do {
4130 jq->used = 0;
4131 } while (--jq >= jp);
4132 return jp;
4133}
4134
4135/*
4136 * Return a new job structure.
4137 * Called with interrupts off.
4138 */
4139static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004140makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004141{
4142 int i;
4143 struct job *jp;
4144
4145 for (i = njobs, jp = jobtab; ; jp++) {
4146 if (--i < 0) {
4147 jp = growjobtab();
4148 break;
4149 }
4150 if (jp->used == 0)
4151 break;
4152 if (jp->state != JOBDONE || !jp->waited)
4153 continue;
4154#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004155 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004156 continue;
4157#endif
4158 freejob(jp);
4159 break;
4160 }
4161 memset(jp, 0, sizeof(*jp));
4162#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004163 /* jp->jobctl is a bitfield.
4164 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004165 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004166 jp->jobctl = 1;
4167#endif
4168 jp->prev_job = curjob;
4169 curjob = jp;
4170 jp->used = 1;
4171 jp->ps = &jp->ps0;
4172 if (nprocs > 1) {
4173 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4174 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004175 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176 jobno(jp)));
4177 return jp;
4178}
4179
4180#if JOBS
4181/*
4182 * Return a string identifying a command (to be printed by the
4183 * jobs command).
4184 */
4185static char *cmdnextc;
4186
4187static void
4188cmdputs(const char *s)
4189{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004190 static const char vstype[VSTYPE + 1][3] = {
4191 "", "}", "-", "+", "?", "=",
4192 "%", "%%", "#", "##"
4193 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4194 };
4195
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196 const char *p, *str;
4197 char c, cc[2] = " ";
4198 char *nextc;
4199 int subtype = 0;
4200 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004201
4202 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4203 p = s;
4204 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004205 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004206 switch (c) {
4207 case CTLESC:
4208 c = *p++;
4209 break;
4210 case CTLVAR:
4211 subtype = *p++;
4212 if ((subtype & VSTYPE) == VSLENGTH)
4213 str = "${#";
4214 else
4215 str = "${";
4216 if (!(subtype & VSQUOTE) == !(quoted & 1))
4217 goto dostr;
4218 quoted ^= 1;
4219 c = '"';
4220 break;
4221 case CTLENDVAR:
4222 str = "\"}" + !(quoted & 1);
4223 quoted >>= 1;
4224 subtype = 0;
4225 goto dostr;
4226 case CTLBACKQ:
4227 str = "$(...)";
4228 goto dostr;
4229 case CTLBACKQ+CTLQUOTE:
4230 str = "\"$(...)\"";
4231 goto dostr;
4232#if ENABLE_ASH_MATH_SUPPORT
4233 case CTLARI:
4234 str = "$((";
4235 goto dostr;
4236 case CTLENDARI:
4237 str = "))";
4238 goto dostr;
4239#endif
4240 case CTLQUOTEMARK:
4241 quoted ^= 1;
4242 c = '"';
4243 break;
4244 case '=':
4245 if (subtype == 0)
4246 break;
4247 if ((subtype & VSTYPE) != VSNORMAL)
4248 quoted <<= 1;
4249 str = vstype[subtype & VSTYPE];
4250 if (subtype & VSNUL)
4251 c = ':';
4252 else
4253 goto checkstr;
4254 break;
4255 case '\'':
4256 case '\\':
4257 case '"':
4258 case '$':
4259 /* These can only happen inside quotes */
4260 cc[0] = c;
4261 str = cc;
4262 c = '\\';
4263 break;
4264 default:
4265 break;
4266 }
4267 USTPUTC(c, nextc);
4268 checkstr:
4269 if (!str)
4270 continue;
4271 dostr:
4272 while ((c = *str++)) {
4273 USTPUTC(c, nextc);
4274 }
4275 }
4276 if (quoted & 1) {
4277 USTPUTC('"', nextc);
4278 }
4279 *nextc = 0;
4280 cmdnextc = nextc;
4281}
4282
4283/* cmdtxt() and cmdlist() call each other */
4284static void cmdtxt(union node *n);
4285
4286static void
4287cmdlist(union node *np, int sep)
4288{
4289 for (; np; np = np->narg.next) {
4290 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004291 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004292 cmdtxt(np);
4293 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004294 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004295 }
4296}
4297
4298static void
4299cmdtxt(union node *n)
4300{
4301 union node *np;
4302 struct nodelist *lp;
4303 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004304
4305 if (!n)
4306 return;
4307 switch (n->type) {
4308 default:
4309#if DEBUG
4310 abort();
4311#endif
4312 case NPIPE:
4313 lp = n->npipe.cmdlist;
4314 for (;;) {
4315 cmdtxt(lp->n);
4316 lp = lp->next;
4317 if (!lp)
4318 break;
4319 cmdputs(" | ");
4320 }
4321 break;
4322 case NSEMI:
4323 p = "; ";
4324 goto binop;
4325 case NAND:
4326 p = " && ";
4327 goto binop;
4328 case NOR:
4329 p = " || ";
4330 binop:
4331 cmdtxt(n->nbinary.ch1);
4332 cmdputs(p);
4333 n = n->nbinary.ch2;
4334 goto donode;
4335 case NREDIR:
4336 case NBACKGND:
4337 n = n->nredir.n;
4338 goto donode;
4339 case NNOT:
4340 cmdputs("!");
4341 n = n->nnot.com;
4342 donode:
4343 cmdtxt(n);
4344 break;
4345 case NIF:
4346 cmdputs("if ");
4347 cmdtxt(n->nif.test);
4348 cmdputs("; then ");
4349 n = n->nif.ifpart;
4350 if (n->nif.elsepart) {
4351 cmdtxt(n);
4352 cmdputs("; else ");
4353 n = n->nif.elsepart;
4354 }
4355 p = "; fi";
4356 goto dotail;
4357 case NSUBSHELL:
4358 cmdputs("(");
4359 n = n->nredir.n;
4360 p = ")";
4361 goto dotail;
4362 case NWHILE:
4363 p = "while ";
4364 goto until;
4365 case NUNTIL:
4366 p = "until ";
4367 until:
4368 cmdputs(p);
4369 cmdtxt(n->nbinary.ch1);
4370 n = n->nbinary.ch2;
4371 p = "; done";
4372 dodo:
4373 cmdputs("; do ");
4374 dotail:
4375 cmdtxt(n);
4376 goto dotail2;
4377 case NFOR:
4378 cmdputs("for ");
4379 cmdputs(n->nfor.var);
4380 cmdputs(" in ");
4381 cmdlist(n->nfor.args, 1);
4382 n = n->nfor.body;
4383 p = "; done";
4384 goto dodo;
4385 case NDEFUN:
4386 cmdputs(n->narg.text);
4387 p = "() { ... }";
4388 goto dotail2;
4389 case NCMD:
4390 cmdlist(n->ncmd.args, 1);
4391 cmdlist(n->ncmd.redirect, 0);
4392 break;
4393 case NARG:
4394 p = n->narg.text;
4395 dotail2:
4396 cmdputs(p);
4397 break;
4398 case NHERE:
4399 case NXHERE:
4400 p = "<<...";
4401 goto dotail2;
4402 case NCASE:
4403 cmdputs("case ");
4404 cmdputs(n->ncase.expr->narg.text);
4405 cmdputs(" in ");
4406 for (np = n->ncase.cases; np; np = np->nclist.next) {
4407 cmdtxt(np->nclist.pattern);
4408 cmdputs(") ");
4409 cmdtxt(np->nclist.body);
4410 cmdputs(";; ");
4411 }
4412 p = "esac";
4413 goto dotail2;
4414 case NTO:
4415 p = ">";
4416 goto redir;
4417 case NCLOBBER:
4418 p = ">|";
4419 goto redir;
4420 case NAPPEND:
4421 p = ">>";
4422 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004423#if ENABLE_ASH_BASH_COMPAT
4424 case NTO2:
4425#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004426 case NTOFD:
4427 p = ">&";
4428 goto redir;
4429 case NFROM:
4430 p = "<";
4431 goto redir;
4432 case NFROMFD:
4433 p = "<&";
4434 goto redir;
4435 case NFROMTO:
4436 p = "<>";
4437 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004438 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004439 cmdputs(p);
4440 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004441 cmdputs(utoa(n->ndup.dupfd));
4442 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004443 }
4444 n = n->nfile.fname;
4445 goto donode;
4446 }
4447}
4448
4449static char *
4450commandtext(union node *n)
4451{
4452 char *name;
4453
4454 STARTSTACKSTR(cmdnextc);
4455 cmdtxt(n);
4456 name = stackblock();
4457 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4458 name, cmdnextc, cmdnextc));
4459 return ckstrdup(name);
4460}
4461#endif /* JOBS */
4462
4463/*
4464 * Fork off a subshell. If we are doing job control, give the subshell its
4465 * own process group. Jp is a job structure that the job is to be added to.
4466 * N is the command that will be evaluated by the child. Both jp and n may
4467 * be NULL. The mode parameter can be one of the following:
4468 * FORK_FG - Fork off a foreground process.
4469 * FORK_BG - Fork off a background process.
4470 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4471 * process group even if job control is on.
4472 *
4473 * When job control is turned off, background processes have their standard
4474 * input redirected to /dev/null (except for the second and later processes
4475 * in a pipeline).
4476 *
4477 * Called with interrupts off.
4478 */
4479/*
4480 * Clear traps on a fork.
4481 */
4482static void
4483clear_traps(void)
4484{
4485 char **tp;
4486
4487 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004488 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 INT_OFF;
4490 free(*tp);
4491 *tp = NULL;
4492 if (tp != &trap[0])
4493 setsignal(tp - trap);
4494 INT_ON;
4495 }
4496 }
4497}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004498
4499/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004501
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004502/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004504forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505{
4506 int oldlvl;
4507
4508 TRACE(("Child shell %d\n", getpid()));
4509 oldlvl = shlvl;
4510 shlvl++;
4511
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004512 /* man bash: "Non-builtin commands run by bash have signal handlers
4513 * set to the values inherited by the shell from its parent".
4514 * Do we do it correctly? */
4515
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004516 closescript();
4517 clear_traps();
4518#if JOBS
4519 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004520 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004521 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4522 pid_t pgrp;
4523
4524 if (jp->nprocs == 0)
4525 pgrp = getpid();
4526 else
4527 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004528 /* this can fail because we are doing it in the parent also */
4529 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004530 if (mode == FORK_FG)
4531 xtcsetpgrp(ttyfd, pgrp);
4532 setsignal(SIGTSTP);
4533 setsignal(SIGTTOU);
4534 } else
4535#endif
4536 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004537 /* man bash: "When job control is not in effect,
4538 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004539 ignoresig(SIGINT);
4540 ignoresig(SIGQUIT);
4541 if (jp->nprocs == 0) {
4542 close(0);
4543 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004544 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545 }
4546 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004547 if (!oldlvl) {
4548 if (iflag) { /* why if iflag only? */
4549 setsignal(SIGINT);
4550 setsignal(SIGTERM);
4551 }
4552 /* man bash:
4553 * "In all cases, bash ignores SIGQUIT. Non-builtin
4554 * commands run by bash have signal handlers
4555 * set to the values inherited by the shell
4556 * from its parent".
4557 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004558 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004559 }
4560 for (jp = curjob; jp; jp = jp->prev_job)
4561 freejob(jp);
4562 jobless = 0;
4563}
4564
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004565/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004566#if !JOBS
4567#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4568#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004569static void
4570forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4571{
4572 TRACE(("In parent shell: child = %d\n", pid));
4573 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004574 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4575 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004576 jobless++;
4577 return;
4578 }
4579#if JOBS
4580 if (mode != FORK_NOJOB && jp->jobctl) {
4581 int pgrp;
4582
4583 if (jp->nprocs == 0)
4584 pgrp = pid;
4585 else
4586 pgrp = jp->ps[0].pid;
4587 /* This can fail because we are doing it in the child also */
4588 setpgid(pid, pgrp);
4589 }
4590#endif
4591 if (mode == FORK_BG) {
4592 backgndpid = pid; /* set $! */
4593 set_curjob(jp, CUR_RUNNING);
4594 }
4595 if (jp) {
4596 struct procstat *ps = &jp->ps[jp->nprocs++];
4597 ps->pid = pid;
4598 ps->status = -1;
4599 ps->cmd = nullstr;
4600#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004601 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004602 ps->cmd = commandtext(n);
4603#endif
4604 }
4605}
4606
4607static int
4608forkshell(struct job *jp, union node *n, int mode)
4609{
4610 int pid;
4611
4612 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4613 pid = fork();
4614 if (pid < 0) {
4615 TRACE(("Fork failed, errno=%d", errno));
4616 if (jp)
4617 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004618 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004619 }
4620 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004621 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004622 else
4623 forkparent(jp, n, mode, pid);
4624 return pid;
4625}
4626
4627/*
4628 * Wait for job to finish.
4629 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004630 * Under job control we have the problem that while a child process
4631 * is running interrupts generated by the user are sent to the child
4632 * but not to the shell. This means that an infinite loop started by
4633 * an interactive user may be hard to kill. With job control turned off,
4634 * an interactive user may place an interactive program inside a loop.
4635 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004636 * these interrupts to also abort the loop. The approach we take here
4637 * is to have the shell ignore interrupt signals while waiting for a
4638 * foreground process to terminate, and then send itself an interrupt
4639 * signal if the child process was terminated by an interrupt signal.
4640 * Unfortunately, some programs want to do a bit of cleanup and then
4641 * exit on interrupt; unless these processes terminate themselves by
4642 * sending a signal to themselves (instead of calling exit) they will
4643 * confuse this approach.
4644 *
4645 * Called with interrupts off.
4646 */
4647static int
4648waitforjob(struct job *jp)
4649{
4650 int st;
4651
4652 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004653
4654 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004655 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004656 /* In non-interactive shells, we _can_ get
4657 * a keyboard signal here and be EINTRed,
4658 * but we just loop back, waiting for command to complete.
4659 *
4660 * man bash:
4661 * "If bash is waiting for a command to complete and receives
4662 * a signal for which a trap has been set, the trap
4663 * will not be executed until the command completes."
4664 *
4665 * Reality is that even if trap is not set, bash
4666 * will not act on the signal until command completes.
4667 * Try this. sleep5intoff.c:
4668 * #include <signal.h>
4669 * #include <unistd.h>
4670 * int main() {
4671 * sigset_t set;
4672 * sigemptyset(&set);
4673 * sigaddset(&set, SIGINT);
4674 * sigaddset(&set, SIGQUIT);
4675 * sigprocmask(SIG_BLOCK, &set, NULL);
4676 * sleep(5);
4677 * return 0;
4678 * }
4679 * $ bash -c './sleep5intoff; echo hi'
4680 * ^C^C^C^C <--- pressing ^C once a second
4681 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004682 * $ bash -c './sleep5intoff; echo hi'
4683 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4684 * $ _
4685 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686 dowait(DOWAIT_BLOCK, jp);
4687 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004688 INT_ON;
4689
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004690 st = getstatus(jp);
4691#if JOBS
4692 if (jp->jobctl) {
4693 xtcsetpgrp(ttyfd, rootpid);
4694 /*
4695 * This is truly gross.
4696 * If we're doing job control, then we did a TIOCSPGRP which
4697 * caused us (the shell) to no longer be in the controlling
4698 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4699 * intuit from the subprocess exit status whether a SIGINT
4700 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4701 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004702 if (jp->sigint) /* TODO: do the same with all signals */
4703 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004704 }
4705 if (jp->state == JOBDONE)
4706#endif
4707 freejob(jp);
4708 return st;
4709}
4710
4711/*
4712 * return 1 if there are stopped jobs, otherwise 0
4713 */
4714static int
4715stoppedjobs(void)
4716{
4717 struct job *jp;
4718 int retval;
4719
4720 retval = 0;
4721 if (job_warning)
4722 goto out;
4723 jp = curjob;
4724 if (jp && jp->state == JOBSTOPPED) {
4725 out2str("You have stopped jobs.\n");
4726 job_warning = 2;
4727 retval++;
4728 }
4729 out:
4730 return retval;
4731}
4732
4733
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004734/* ============ redir.c
4735 *
4736 * Code for dealing with input/output redirection.
4737 */
4738
4739#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004740#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004741
4742/*
4743 * Open a file in noclobber mode.
4744 * The code was copied from bash.
4745 */
4746static int
4747noclobberopen(const char *fname)
4748{
4749 int r, fd;
4750 struct stat finfo, finfo2;
4751
4752 /*
4753 * If the file exists and is a regular file, return an error
4754 * immediately.
4755 */
4756 r = stat(fname, &finfo);
4757 if (r == 0 && S_ISREG(finfo.st_mode)) {
4758 errno = EEXIST;
4759 return -1;
4760 }
4761
4762 /*
4763 * If the file was not present (r != 0), make sure we open it
4764 * exclusively so that if it is created before we open it, our open
4765 * will fail. Make sure that we do not truncate an existing file.
4766 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4767 * file was not a regular file, we leave O_EXCL off.
4768 */
4769 if (r != 0)
4770 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4771 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4772
4773 /* If the open failed, return the file descriptor right away. */
4774 if (fd < 0)
4775 return fd;
4776
4777 /*
4778 * OK, the open succeeded, but the file may have been changed from a
4779 * non-regular file to a regular file between the stat and the open.
4780 * We are assuming that the O_EXCL open handles the case where FILENAME
4781 * did not exist and is symlinked to an existing file between the stat
4782 * and open.
4783 */
4784
4785 /*
4786 * If we can open it and fstat the file descriptor, and neither check
4787 * revealed that it was a regular file, and the file has not been
4788 * replaced, return the file descriptor.
4789 */
4790 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4791 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4792 return fd;
4793
4794 /* The file has been replaced. badness. */
4795 close(fd);
4796 errno = EEXIST;
4797 return -1;
4798}
4799
4800/*
4801 * Handle here documents. Normally we fork off a process to write the
4802 * data to a pipe. If the document is short, we can stuff the data in
4803 * the pipe without forking.
4804 */
4805/* openhere needs this forward reference */
4806static void expandhere(union node *arg, int fd);
4807static int
4808openhere(union node *redir)
4809{
4810 int pip[2];
4811 size_t len = 0;
4812
4813 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004814 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004815 if (redir->type == NHERE) {
4816 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004817 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004818 full_write(pip[1], redir->nhere.doc->narg.text, len);
4819 goto out;
4820 }
4821 }
4822 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004823 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004824 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004825 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4826 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4827 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4828 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004829 signal(SIGPIPE, SIG_DFL);
4830 if (redir->type == NHERE)
4831 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004832 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004833 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004834 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004835 }
4836 out:
4837 close(pip[1]);
4838 return pip[0];
4839}
4840
4841static int
4842openredirect(union node *redir)
4843{
4844 char *fname;
4845 int f;
4846
4847 switch (redir->nfile.type) {
4848 case NFROM:
4849 fname = redir->nfile.expfname;
4850 f = open(fname, O_RDONLY);
4851 if (f < 0)
4852 goto eopen;
4853 break;
4854 case NFROMTO:
4855 fname = redir->nfile.expfname;
4856 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4857 if (f < 0)
4858 goto ecreate;
4859 break;
4860 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004861#if ENABLE_ASH_BASH_COMPAT
4862 case NTO2:
4863#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004864 /* Take care of noclobber mode. */
4865 if (Cflag) {
4866 fname = redir->nfile.expfname;
4867 f = noclobberopen(fname);
4868 if (f < 0)
4869 goto ecreate;
4870 break;
4871 }
4872 /* FALLTHROUGH */
4873 case NCLOBBER:
4874 fname = redir->nfile.expfname;
4875 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4876 if (f < 0)
4877 goto ecreate;
4878 break;
4879 case NAPPEND:
4880 fname = redir->nfile.expfname;
4881 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4882 if (f < 0)
4883 goto ecreate;
4884 break;
4885 default:
4886#if DEBUG
4887 abort();
4888#endif
4889 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004890/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004891// case NTOFD:
4892// case NFROMFD:
4893// f = -1;
4894// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004895 case NHERE:
4896 case NXHERE:
4897 f = openhere(redir);
4898 break;
4899 }
4900
4901 return f;
4902 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004903 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004904 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004905 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004906}
4907
4908/*
4909 * Copy a file descriptor to be >= to. Returns -1
4910 * if the source file descriptor is closed, EMPTY if there are no unused
4911 * file descriptors left.
4912 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004913/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4914 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004915enum {
4916 COPYFD_EXACT = (int)~(INT_MAX),
4917 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4918};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919static int
4920copyfd(int from, int to)
4921{
4922 int newfd;
4923
Denis Vlasenko5a867312008-07-24 19:46:38 +00004924 if (to & COPYFD_EXACT) {
4925 to &= ~COPYFD_EXACT;
4926 /*if (from != to)*/
4927 newfd = dup2(from, to);
4928 } else {
4929 newfd = fcntl(from, F_DUPFD, to);
4930 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004931 if (newfd < 0) {
4932 if (errno == EMFILE)
4933 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004934 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004935 ash_msg_and_raise_error("%d: %m", from);
4936 }
4937 return newfd;
4938}
4939
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004940/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004941struct two_fd_t {
4942 int orig, copy;
4943};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004944struct redirtab {
4945 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004946 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004947 int pair_count;
4948 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004949};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004950#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004951
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004952static int need_to_remember(struct redirtab *rp, int fd)
4953{
4954 int i;
4955
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004956 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004957 return 0;
4958
4959 for (i = 0; i < rp->pair_count; i++) {
4960 if (rp->two_fd[i].orig == fd) {
4961 /* already remembered */
4962 return 0;
4963 }
4964 }
4965 return 1;
4966}
4967
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004968/* "hidden" fd is a fd used to read scripts, or a copy of such */
4969static int is_hidden_fd(struct redirtab *rp, int fd)
4970{
4971 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004972 struct parsefile *pf;
4973
4974 if (fd == -1)
4975 return 0;
4976 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004977 while (pf) {
4978 if (fd == pf->fd) {
4979 return 1;
4980 }
4981 pf = pf->prev;
4982 }
4983 if (!rp)
4984 return 0;
4985 fd |= COPYFD_RESTORE;
4986 for (i = 0; i < rp->pair_count; i++) {
4987 if (rp->two_fd[i].copy == fd) {
4988 return 1;
4989 }
4990 }
4991 return 0;
4992}
4993
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004994/*
4995 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4996 * old file descriptors are stashed away so that the redirection can be
4997 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4998 * standard output, and the standard error if it becomes a duplicate of
4999 * stdout, is saved in memory.
5000 */
5001/* flags passed to redirect */
5002#define REDIR_PUSH 01 /* save previous values of file descriptors */
5003#define REDIR_SAVEFD2 03 /* set preverrout */
5004static void
5005redirect(union node *redir, int flags)
5006{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005007 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005008 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005009 int i;
5010 int fd;
5011 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005012 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005013
Denis Vlasenko01631112007-12-16 17:20:38 +00005014 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005015 if (!redir) {
5016 return;
5017 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005018
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005019 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005020 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005021 INT_OFF;
5022 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005023 union node *tmp = redir;
5024 do {
5025 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005026#if ENABLE_ASH_BASH_COMPAT
5027 if (redir->nfile.type == NTO2)
5028 sv_pos++;
5029#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005030 tmp = tmp->nfile.next;
5031 } while (tmp);
5032 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005033 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005034 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005035 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005036 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005037 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005038 while (sv_pos > 0) {
5039 sv_pos--;
5040 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5041 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005042 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005043
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005044 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005045 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005046 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005047 int right_fd = redir->ndup.dupfd;
5048 /* redirect from/to same file descriptor? */
5049 if (right_fd == fd)
5050 continue;
5051 /* echo >&10 and 10 is a fd opened to the sh script? */
5052 if (is_hidden_fd(sv, right_fd)) {
5053 errno = EBADF; /* as if it is closed */
5054 ash_msg_and_raise_error("%d: %m", right_fd);
5055 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005056 newfd = -1;
5057 } else {
5058 newfd = openredirect(redir); /* always >= 0 */
5059 if (fd == newfd) {
5060 /* Descriptor wasn't open before redirect.
5061 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005062 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005063 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005064 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005065 continue;
5066 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005067 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005068#if ENABLE_ASH_BASH_COMPAT
5069 redirect_more:
5070#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005071 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005072 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005073 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005074/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5075 * are closed in popredir() in the child, preventing them from leaking
5076 * into child. (popredir() also cleans up the mess in case of failures)
5077 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 if (i == -1) {
5079 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005080 if (i != EBADF) {
5081 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005082 if (newfd >= 0)
5083 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005084 errno = i;
5085 ash_msg_and_raise_error("%d: %m", fd);
5086 /* NOTREACHED */
5087 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005088 /* EBADF: it is not open - good, remember to close it */
5089 remember_to_close:
5090 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005091 } else { /* fd is open, save its copy */
5092 /* "exec fd>&-" should not close fds
5093 * which point to script file(s).
5094 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005095 if (is_hidden_fd(sv, fd))
5096 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005097 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005098 if (fd == 2)
5099 copied_fd2 = i;
5100 sv->two_fd[sv_pos].orig = fd;
5101 sv->two_fd[sv_pos].copy = i;
5102 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005103 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005104 if (newfd < 0) {
5105 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005106 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005107 close(fd);
5108 } else {
5109 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005110 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005111 } else if (fd != newfd) { /* move newfd to fd */
5112 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005113#if ENABLE_ASH_BASH_COMPAT
5114 if (!(redir->nfile.type == NTO2 && fd == 2))
5115#endif
5116 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005117 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005118#if ENABLE_ASH_BASH_COMPAT
5119 if (redir->nfile.type == NTO2 && fd == 1) {
5120 /* We already redirected it to fd 1, now copy it to 2 */
5121 newfd = 1;
5122 fd = 2;
5123 goto redirect_more;
5124 }
5125#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005126 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005127
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005128 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005129 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5130 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005131}
5132
5133/*
5134 * Undo the effects of the last redirection.
5135 */
5136static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005137popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005138{
5139 struct redirtab *rp;
5140 int i;
5141
Denis Vlasenko01631112007-12-16 17:20:38 +00005142 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005143 return;
5144 INT_OFF;
5145 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005146 for (i = 0; i < rp->pair_count; i++) {
5147 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005148 int copy = rp->two_fd[i].copy;
5149 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005150 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005151 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005152 continue;
5153 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005154 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005155 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005156 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005157 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005158 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005159 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005160 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005161 }
5162 }
5163 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005164 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005165 free(rp);
5166 INT_ON;
5167}
5168
5169/*
5170 * Undo all redirections. Called on error or interrupt.
5171 */
5172
5173/*
5174 * Discard all saved file descriptors.
5175 */
5176static void
5177clearredir(int drop)
5178{
5179 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005180 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005181 if (!redirlist)
5182 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005183 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005184 }
5185}
5186
5187static int
5188redirectsafe(union node *redir, int flags)
5189{
5190 int err;
5191 volatile int saveint;
5192 struct jmploc *volatile savehandler = exception_handler;
5193 struct jmploc jmploc;
5194
5195 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005196 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5197 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005198 if (!err) {
5199 exception_handler = &jmploc;
5200 redirect(redir, flags);
5201 }
5202 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005203 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005204 longjmp(exception_handler->loc, 1);
5205 RESTORE_INT(saveint);
5206 return err;
5207}
5208
5209
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005210/* ============ Routines to expand arguments to commands
5211 *
5212 * We have to deal with backquotes, shell variables, and file metacharacters.
5213 */
5214
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005215#if ENABLE_ASH_MATH_SUPPORT_64
5216typedef int64_t arith_t;
5217#define arith_t_type long long
5218#else
5219typedef long arith_t;
5220#define arith_t_type long
5221#endif
5222
5223#if ENABLE_ASH_MATH_SUPPORT
5224static arith_t dash_arith(const char *);
5225static arith_t arith(const char *expr, int *perrcode);
5226#endif
5227
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005228/*
5229 * expandarg flags
5230 */
5231#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5232#define EXP_TILDE 0x2 /* do normal tilde expansion */
5233#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5234#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5235#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5236#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5237#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5238#define EXP_WORD 0x80 /* expand word in parameter expansion */
5239#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5240/*
5241 * _rmescape() flags
5242 */
5243#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5244#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5245#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5246#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5247#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5248
5249/*
5250 * Structure specifying which parts of the string should be searched
5251 * for IFS characters.
5252 */
5253struct ifsregion {
5254 struct ifsregion *next; /* next region in list */
5255 int begoff; /* offset of start of region */
5256 int endoff; /* offset of end of region */
5257 int nulonly; /* search for nul bytes only */
5258};
5259
5260struct arglist {
5261 struct strlist *list;
5262 struct strlist **lastp;
5263};
5264
5265/* output of current string */
5266static char *expdest;
5267/* list of back quote expressions */
5268static struct nodelist *argbackq;
5269/* first struct in list of ifs regions */
5270static struct ifsregion ifsfirst;
5271/* last struct in list */
5272static struct ifsregion *ifslastp;
5273/* holds expanded arg list */
5274static struct arglist exparg;
5275
5276/*
5277 * Our own itoa().
5278 */
5279static int
5280cvtnum(arith_t num)
5281{
5282 int len;
5283
5284 expdest = makestrspace(32, expdest);
5285#if ENABLE_ASH_MATH_SUPPORT_64
5286 len = fmtstr(expdest, 32, "%lld", (long long) num);
5287#else
5288 len = fmtstr(expdest, 32, "%ld", num);
5289#endif
5290 STADJUST(len, expdest);
5291 return len;
5292}
5293
5294static size_t
5295esclen(const char *start, const char *p)
5296{
5297 size_t esc = 0;
5298
5299 while (p > start && *--p == CTLESC) {
5300 esc++;
5301 }
5302 return esc;
5303}
5304
5305/*
5306 * Remove any CTLESC characters from a string.
5307 */
5308static char *
5309_rmescapes(char *str, int flag)
5310{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005311 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005312
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005313 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005314 unsigned inquotes;
5315 int notescaped;
5316 int globbing;
5317
5318 p = strpbrk(str, qchars);
5319 if (!p) {
5320 return str;
5321 }
5322 q = p;
5323 r = str;
5324 if (flag & RMESCAPE_ALLOC) {
5325 size_t len = p - str;
5326 size_t fulllen = len + strlen(p) + 1;
5327
5328 if (flag & RMESCAPE_GROW) {
5329 r = makestrspace(fulllen, expdest);
5330 } else if (flag & RMESCAPE_HEAP) {
5331 r = ckmalloc(fulllen);
5332 } else {
5333 r = stalloc(fulllen);
5334 }
5335 q = r;
5336 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005337 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005338 }
5339 }
5340 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5341 globbing = flag & RMESCAPE_GLOB;
5342 notescaped = globbing;
5343 while (*p) {
5344 if (*p == CTLQUOTEMARK) {
5345 inquotes = ~inquotes;
5346 p++;
5347 notescaped = globbing;
5348 continue;
5349 }
5350 if (*p == '\\') {
5351 /* naked back slash */
5352 notescaped = 0;
5353 goto copy;
5354 }
5355 if (*p == CTLESC) {
5356 p++;
5357 if (notescaped && inquotes && *p != '/') {
5358 *q++ = '\\';
5359 }
5360 }
5361 notescaped = globbing;
5362 copy:
5363 *q++ = *p++;
5364 }
5365 *q = '\0';
5366 if (flag & RMESCAPE_GROW) {
5367 expdest = r;
5368 STADJUST(q - r + 1, expdest);
5369 }
5370 return r;
5371}
5372#define rmescapes(p) _rmescapes((p), 0)
5373
5374#define pmatch(a, b) !fnmatch((a), (b), 0)
5375
5376/*
5377 * Prepare a pattern for a expmeta (internal glob(3)) call.
5378 *
5379 * Returns an stalloced string.
5380 */
5381static char *
5382preglob(const char *pattern, int quoted, int flag)
5383{
5384 flag |= RMESCAPE_GLOB;
5385 if (quoted) {
5386 flag |= RMESCAPE_QUOTED;
5387 }
5388 return _rmescapes((char *)pattern, flag);
5389}
5390
5391/*
5392 * Put a string on the stack.
5393 */
5394static void
5395memtodest(const char *p, size_t len, int syntax, int quotes)
5396{
5397 char *q = expdest;
5398
5399 q = makestrspace(len * 2, q);
5400
5401 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005402 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005403 if (!c)
5404 continue;
5405 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5406 USTPUTC(CTLESC, q);
5407 USTPUTC(c, q);
5408 }
5409
5410 expdest = q;
5411}
5412
5413static void
5414strtodest(const char *p, int syntax, int quotes)
5415{
5416 memtodest(p, strlen(p), syntax, quotes);
5417}
5418
5419/*
5420 * Record the fact that we have to scan this region of the
5421 * string for IFS characters.
5422 */
5423static void
5424recordregion(int start, int end, int nulonly)
5425{
5426 struct ifsregion *ifsp;
5427
5428 if (ifslastp == NULL) {
5429 ifsp = &ifsfirst;
5430 } else {
5431 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005432 ifsp = ckzalloc(sizeof(*ifsp));
5433 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005434 ifslastp->next = ifsp;
5435 INT_ON;
5436 }
5437 ifslastp = ifsp;
5438 ifslastp->begoff = start;
5439 ifslastp->endoff = end;
5440 ifslastp->nulonly = nulonly;
5441}
5442
5443static void
5444removerecordregions(int endoff)
5445{
5446 if (ifslastp == NULL)
5447 return;
5448
5449 if (ifsfirst.endoff > endoff) {
5450 while (ifsfirst.next != NULL) {
5451 struct ifsregion *ifsp;
5452 INT_OFF;
5453 ifsp = ifsfirst.next->next;
5454 free(ifsfirst.next);
5455 ifsfirst.next = ifsp;
5456 INT_ON;
5457 }
5458 if (ifsfirst.begoff > endoff)
5459 ifslastp = NULL;
5460 else {
5461 ifslastp = &ifsfirst;
5462 ifsfirst.endoff = endoff;
5463 }
5464 return;
5465 }
5466
5467 ifslastp = &ifsfirst;
5468 while (ifslastp->next && ifslastp->next->begoff < endoff)
5469 ifslastp=ifslastp->next;
5470 while (ifslastp->next != NULL) {
5471 struct ifsregion *ifsp;
5472 INT_OFF;
5473 ifsp = ifslastp->next->next;
5474 free(ifslastp->next);
5475 ifslastp->next = ifsp;
5476 INT_ON;
5477 }
5478 if (ifslastp->endoff > endoff)
5479 ifslastp->endoff = endoff;
5480}
5481
5482static char *
5483exptilde(char *startp, char *p, int flag)
5484{
5485 char c;
5486 char *name;
5487 struct passwd *pw;
5488 const char *home;
5489 int quotes = flag & (EXP_FULL | EXP_CASE);
5490 int startloc;
5491
5492 name = p + 1;
5493
5494 while ((c = *++p) != '\0') {
5495 switch (c) {
5496 case CTLESC:
5497 return startp;
5498 case CTLQUOTEMARK:
5499 return startp;
5500 case ':':
5501 if (flag & EXP_VARTILDE)
5502 goto done;
5503 break;
5504 case '/':
5505 case CTLENDVAR:
5506 goto done;
5507 }
5508 }
5509 done:
5510 *p = '\0';
5511 if (*name == '\0') {
5512 home = lookupvar(homestr);
5513 } else {
5514 pw = getpwnam(name);
5515 if (pw == NULL)
5516 goto lose;
5517 home = pw->pw_dir;
5518 }
5519 if (!home || !*home)
5520 goto lose;
5521 *p = c;
5522 startloc = expdest - (char *)stackblock();
5523 strtodest(home, SQSYNTAX, quotes);
5524 recordregion(startloc, expdest - (char *)stackblock(), 0);
5525 return p;
5526 lose:
5527 *p = c;
5528 return startp;
5529}
5530
5531/*
5532 * Execute a command inside back quotes. If it's a builtin command, we
5533 * want to save its output in a block obtained from malloc. Otherwise
5534 * we fork off a subprocess and get the output of the command via a pipe.
5535 * Should be called with interrupts off.
5536 */
5537struct backcmd { /* result of evalbackcmd */
5538 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005539 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005540 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541 struct job *jp; /* job structure for command */
5542};
5543
5544/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005545static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005546#define EV_EXIT 01 /* exit after evaluating tree */
5547static void evaltree(union node *, int);
5548
5549static void
5550evalbackcmd(union node *n, struct backcmd *result)
5551{
5552 int saveherefd;
5553
5554 result->fd = -1;
5555 result->buf = NULL;
5556 result->nleft = 0;
5557 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005558 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005559 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005560
5561 saveherefd = herefd;
5562 herefd = -1;
5563
5564 {
5565 int pip[2];
5566 struct job *jp;
5567
5568 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005569 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005570 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005571 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5572 FORCE_INT_ON;
5573 close(pip[0]);
5574 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005575 /*close(1);*/
5576 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005577 close(pip[1]);
5578 }
5579 eflag = 0;
5580 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5581 /* NOTREACHED */
5582 }
5583 close(pip[1]);
5584 result->fd = pip[0];
5585 result->jp = jp;
5586 }
5587 herefd = saveherefd;
5588 out:
5589 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5590 result->fd, result->buf, result->nleft, result->jp));
5591}
5592
5593/*
5594 * Expand stuff in backwards quotes.
5595 */
5596static void
5597expbackq(union node *cmd, int quoted, int quotes)
5598{
5599 struct backcmd in;
5600 int i;
5601 char buf[128];
5602 char *p;
5603 char *dest;
5604 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005605 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 struct stackmark smark;
5607
5608 INT_OFF;
5609 setstackmark(&smark);
5610 dest = expdest;
5611 startloc = dest - (char *)stackblock();
5612 grabstackstr(dest);
5613 evalbackcmd(cmd, &in);
5614 popstackmark(&smark);
5615
5616 p = in.buf;
5617 i = in.nleft;
5618 if (i == 0)
5619 goto read;
5620 for (;;) {
5621 memtodest(p, i, syntax, quotes);
5622 read:
5623 if (in.fd < 0)
5624 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005625 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005626 TRACE(("expbackq: read returns %d\n", i));
5627 if (i <= 0)
5628 break;
5629 p = buf;
5630 }
5631
Denis Vlasenko60818682007-09-28 22:07:23 +00005632 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005633 if (in.fd >= 0) {
5634 close(in.fd);
5635 back_exitstatus = waitforjob(in.jp);
5636 }
5637 INT_ON;
5638
5639 /* Eat all trailing newlines */
5640 dest = expdest;
5641 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5642 STUNPUTC(dest);
5643 expdest = dest;
5644
5645 if (quoted == 0)
5646 recordregion(startloc, dest - (char *)stackblock(), 0);
5647 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5648 (dest - (char *)stackblock()) - startloc,
5649 (dest - (char *)stackblock()) - startloc,
5650 stackblock() + startloc));
5651}
5652
5653#if ENABLE_ASH_MATH_SUPPORT
5654/*
5655 * Expand arithmetic expression. Backup to start of expression,
5656 * evaluate, place result in (backed up) result, adjust string position.
5657 */
5658static void
5659expari(int quotes)
5660{
5661 char *p, *start;
5662 int begoff;
5663 int flag;
5664 int len;
5665
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005666 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005667
5668 /*
5669 * This routine is slightly over-complicated for
5670 * efficiency. Next we scan backwards looking for the
5671 * start of arithmetic.
5672 */
5673 start = stackblock();
5674 p = expdest - 1;
5675 *p = '\0';
5676 p--;
5677 do {
5678 int esc;
5679
5680 while (*p != CTLARI) {
5681 p--;
5682#if DEBUG
5683 if (p < start) {
5684 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5685 }
5686#endif
5687 }
5688
5689 esc = esclen(start, p);
5690 if (!(esc % 2)) {
5691 break;
5692 }
5693
5694 p -= esc + 1;
5695 } while (1);
5696
5697 begoff = p - start;
5698
5699 removerecordregions(begoff);
5700
5701 flag = p[1];
5702
5703 expdest = p;
5704
5705 if (quotes)
5706 rmescapes(p + 2);
5707
5708 len = cvtnum(dash_arith(p + 2));
5709
5710 if (flag != '"')
5711 recordregion(begoff, begoff + len, 0);
5712}
5713#endif
5714
5715/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005716static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005717
5718/*
5719 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5720 * characters to allow for further processing. Otherwise treat
5721 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005722 *
5723 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5724 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5725 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005726 */
5727static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005728argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005729{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005730 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005731 '=',
5732 ':',
5733 CTLQUOTEMARK,
5734 CTLENDVAR,
5735 CTLESC,
5736 CTLVAR,
5737 CTLBACKQ,
5738 CTLBACKQ | CTLQUOTE,
5739#if ENABLE_ASH_MATH_SUPPORT
5740 CTLENDARI,
5741#endif
5742 0
5743 };
5744 const char *reject = spclchars;
5745 int c;
5746 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5747 int breakall = flag & EXP_WORD;
5748 int inquotes;
5749 size_t length;
5750 int startloc;
5751
5752 if (!(flag & EXP_VARTILDE)) {
5753 reject += 2;
5754 } else if (flag & EXP_VARTILDE2) {
5755 reject++;
5756 }
5757 inquotes = 0;
5758 length = 0;
5759 if (flag & EXP_TILDE) {
5760 char *q;
5761
5762 flag &= ~EXP_TILDE;
5763 tilde:
5764 q = p;
5765 if (*q == CTLESC && (flag & EXP_QWORD))
5766 q++;
5767 if (*q == '~')
5768 p = exptilde(p, q, flag);
5769 }
5770 start:
5771 startloc = expdest - (char *)stackblock();
5772 for (;;) {
5773 length += strcspn(p + length, reject);
5774 c = p[length];
5775 if (c && (!(c & 0x80)
5776#if ENABLE_ASH_MATH_SUPPORT
5777 || c == CTLENDARI
5778#endif
5779 )) {
5780 /* c == '=' || c == ':' || c == CTLENDARI */
5781 length++;
5782 }
5783 if (length > 0) {
5784 int newloc;
5785 expdest = stack_nputstr(p, length, expdest);
5786 newloc = expdest - (char *)stackblock();
5787 if (breakall && !inquotes && newloc > startloc) {
5788 recordregion(startloc, newloc, 0);
5789 }
5790 startloc = newloc;
5791 }
5792 p += length + 1;
5793 length = 0;
5794
5795 switch (c) {
5796 case '\0':
5797 goto breakloop;
5798 case '=':
5799 if (flag & EXP_VARTILDE2) {
5800 p--;
5801 continue;
5802 }
5803 flag |= EXP_VARTILDE2;
5804 reject++;
5805 /* fall through */
5806 case ':':
5807 /*
5808 * sort of a hack - expand tildes in variable
5809 * assignments (after the first '=' and after ':'s).
5810 */
5811 if (*--p == '~') {
5812 goto tilde;
5813 }
5814 continue;
5815 }
5816
5817 switch (c) {
5818 case CTLENDVAR: /* ??? */
5819 goto breakloop;
5820 case CTLQUOTEMARK:
5821 /* "$@" syntax adherence hack */
5822 if (
5823 !inquotes &&
5824 !memcmp(p, dolatstr, 4) &&
5825 (p[4] == CTLQUOTEMARK || (
5826 p[4] == CTLENDVAR &&
5827 p[5] == CTLQUOTEMARK
5828 ))
5829 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005830 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005831 goto start;
5832 }
5833 inquotes = !inquotes;
5834 addquote:
5835 if (quotes) {
5836 p--;
5837 length++;
5838 startloc++;
5839 }
5840 break;
5841 case CTLESC:
5842 startloc++;
5843 length++;
5844 goto addquote;
5845 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005846 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005847 goto start;
5848 case CTLBACKQ:
5849 c = 0;
5850 case CTLBACKQ|CTLQUOTE:
5851 expbackq(argbackq->n, c, quotes);
5852 argbackq = argbackq->next;
5853 goto start;
5854#if ENABLE_ASH_MATH_SUPPORT
5855 case CTLENDARI:
5856 p--;
5857 expari(quotes);
5858 goto start;
5859#endif
5860 }
5861 }
5862 breakloop:
5863 ;
5864}
5865
5866static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005867scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 int zero)
5869{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005870// This commented out code was added by James Simmons <jsimmons@infradead.org>
5871// as part of a larger change when he added support for ${var/a/b}.
5872// However, it broke # and % operators:
5873//
5874//var=ababcdcd
5875// ok bad
5876//echo ${var#ab} abcdcd abcdcd
5877//echo ${var##ab} abcdcd abcdcd
5878//echo ${var#a*b} abcdcd ababcdcd (!)
5879//echo ${var##a*b} cdcd cdcd
5880//echo ${var#?} babcdcd ababcdcd (!)
5881//echo ${var##?} babcdcd babcdcd
5882//echo ${var#*} ababcdcd babcdcd (!)
5883//echo ${var##*}
5884//echo ${var%cd} ababcd ababcd
5885//echo ${var%%cd} ababcd abab (!)
5886//echo ${var%c*d} ababcd ababcd
5887//echo ${var%%c*d} abab ababcdcd (!)
5888//echo ${var%?} ababcdc ababcdc
5889//echo ${var%%?} ababcdc ababcdcd (!)
5890//echo ${var%*} ababcdcd ababcdcd
5891//echo ${var%%*}
5892//
5893// Commenting it back out helped. Remove it completely if it really
5894// is not needed.
5895
5896 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005897 char c;
5898
5899 loc = startp;
5900 loc2 = rmesc;
5901 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005902 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005903 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005904
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005905 c = *loc2;
5906 if (zero) {
5907 *loc2 = '\0';
5908 s = rmesc;
5909 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005910 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005911
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005912// // chop off end if its '*'
5913// full = strrchr(str, '*');
5914// if (full && full != str)
5915// match--;
5916//
5917// // If str starts with '*' replace with s.
5918// if ((*str == '*') && strlen(s) >= match) {
5919// full = xstrdup(s);
5920// strncpy(full+strlen(s)-match+1, str+1, match-1);
5921// } else
5922// full = xstrndup(str, match);
5923// match = strncmp(s, full, strlen(full));
5924// free(full);
5925//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005926 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005927 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005928 return loc;
5929 if (quotes && *loc == CTLESC)
5930 loc++;
5931 loc++;
5932 loc2++;
5933 } while (c);
5934 return 0;
5935}
5936
5937static char *
5938scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5939 int zero)
5940{
5941 int esc = 0;
5942 char *loc;
5943 char *loc2;
5944
5945 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5946 int match;
5947 char c = *loc2;
5948 const char *s = loc2;
5949 if (zero) {
5950 *loc2 = '\0';
5951 s = rmesc;
5952 }
5953 match = pmatch(str, s);
5954 *loc2 = c;
5955 if (match)
5956 return loc;
5957 loc--;
5958 if (quotes) {
5959 if (--esc < 0) {
5960 esc = esclen(startp, loc);
5961 }
5962 if (esc % 2) {
5963 esc--;
5964 loc--;
5965 }
5966 }
5967 }
5968 return 0;
5969}
5970
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005971static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005972static void
5973varunset(const char *end, const char *var, const char *umsg, int varflags)
5974{
5975 const char *msg;
5976 const char *tail;
5977
5978 tail = nullstr;
5979 msg = "parameter not set";
5980 if (umsg) {
5981 if (*end == CTLENDVAR) {
5982 if (varflags & VSNUL)
5983 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005984 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005985 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005986 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005987 }
5988 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5989}
5990
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005991#if ENABLE_ASH_BASH_COMPAT
5992static char *
5993parse_sub_pattern(char *arg, int inquotes)
5994{
5995 char *idx, *repl = NULL;
5996 unsigned char c;
5997
Denis Vlasenko2659c632008-06-14 06:04:59 +00005998 idx = arg;
5999 while (1) {
6000 c = *arg;
6001 if (!c)
6002 break;
6003 if (c == '/') {
6004 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006005 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006006 repl = idx + 1;
6007 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006008 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006009 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006010 *idx++ = c;
6011 if (!inquotes && c == '\\' && arg[1] == '\\')
6012 arg++; /* skip both \\, not just first one */
6013 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006014 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006015 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006016
6017 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006018}
6019#endif /* ENABLE_ASH_BASH_COMPAT */
6020
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006021static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006022subevalvar(char *p, char *str, int strloc, int subtype,
6023 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006024{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006025 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006026 char *startp;
6027 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006028 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006029 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6030 USE_ASH_BASH_COMPAT(char null = '\0';)
6031 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6032 int saveherefd = herefd;
6033 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006034 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006035 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036
6037 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006038 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6039 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006040 STPUTC('\0', expdest);
6041 herefd = saveherefd;
6042 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006043 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006044
6045 switch (subtype) {
6046 case VSASSIGN:
6047 setvar(str, startp, 0);
6048 amount = startp - expdest;
6049 STADJUST(amount, expdest);
6050 return startp;
6051
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006052#if ENABLE_ASH_BASH_COMPAT
6053 case VSSUBSTR:
6054 loc = str = stackblock() + strloc;
6055// TODO: number() instead? It does error checking...
6056 pos = atoi(loc);
6057 len = str - startp - 1;
6058
6059 /* *loc != '\0', guaranteed by parser */
6060 if (quotes) {
6061 char *ptr;
6062
6063 /* We must adjust the length by the number of escapes we find. */
6064 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006065 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006066 len--;
6067 ptr++;
6068 }
6069 }
6070 }
6071 orig_len = len;
6072
6073 if (*loc++ == ':') {
6074// TODO: number() instead? It does error checking...
6075 len = atoi(loc);
6076 } else {
6077 len = orig_len;
6078 while (*loc && *loc != ':')
6079 loc++;
6080 if (*loc++ == ':')
6081// TODO: number() instead? It does error checking...
6082 len = atoi(loc);
6083 }
6084 if (pos >= orig_len) {
6085 pos = 0;
6086 len = 0;
6087 }
6088 if (len > (orig_len - pos))
6089 len = orig_len - pos;
6090
6091 for (str = startp; pos; str++, pos--) {
6092 if (quotes && *str == CTLESC)
6093 str++;
6094 }
6095 for (loc = startp; len; len--) {
6096 if (quotes && *str == CTLESC)
6097 *loc++ = *str++;
6098 *loc++ = *str++;
6099 }
6100 *loc = '\0';
6101 amount = loc - expdest;
6102 STADJUST(amount, expdest);
6103 return loc;
6104#endif
6105
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006106 case VSQUESTION:
6107 varunset(p, str, startp, varflags);
6108 /* NOTREACHED */
6109 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006110 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006112 /* We'll comeback here if we grow the stack while handling
6113 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6114 * stack will need rebasing, and we'll need to remove our work
6115 * areas each time
6116 */
6117 USE_ASH_BASH_COMPAT(restart:)
6118
6119 amount = expdest - ((char *)stackblock() + resetloc);
6120 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006121 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006122
6123 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006124 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006125 if (quotes) {
6126 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6127 if (rmesc != startp) {
6128 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006129 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006130 }
6131 }
6132 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006133 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006134 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006135 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006137#if ENABLE_ASH_BASH_COMPAT
6138 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6139 char *idx, *end, *restart_detect;
6140
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006141 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006142 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6143 if (!repl)
6144 repl = &null;
6145 }
6146
6147 /* If there's no pattern to match, return the expansion unmolested */
6148 if (*str == '\0')
6149 return 0;
6150
6151 len = 0;
6152 idx = startp;
6153 end = str - 1;
6154 while (idx < end) {
6155 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6156 if (!loc) {
6157 /* No match, advance */
6158 restart_detect = stackblock();
6159 STPUTC(*idx, expdest);
6160 if (quotes && *idx == CTLESC) {
6161 idx++;
6162 len++;
6163 STPUTC(*idx, expdest);
6164 }
6165 if (stackblock() != restart_detect)
6166 goto restart;
6167 idx++;
6168 len++;
6169 rmesc++;
6170 continue;
6171 }
6172
6173 if (subtype == VSREPLACEALL) {
6174 while (idx < loc) {
6175 if (quotes && *idx == CTLESC)
6176 idx++;
6177 idx++;
6178 rmesc++;
6179 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006180 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006181 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006182 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006183
6184 for (loc = repl; *loc; loc++) {
6185 restart_detect = stackblock();
6186 STPUTC(*loc, expdest);
6187 if (stackblock() != restart_detect)
6188 goto restart;
6189 len++;
6190 }
6191
6192 if (subtype == VSREPLACE) {
6193 while (*idx) {
6194 restart_detect = stackblock();
6195 STPUTC(*idx, expdest);
6196 if (stackblock() != restart_detect)
6197 goto restart;
6198 len++;
6199 idx++;
6200 }
6201 break;
6202 }
6203 }
6204
6205 /* We've put the replaced text into a buffer at workloc, now
6206 * move it to the right place and adjust the stack.
6207 */
6208 startp = stackblock() + startloc;
6209 STPUTC('\0', expdest);
6210 memmove(startp, stackblock() + workloc, len);
6211 startp[len++] = '\0';
6212 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6213 STADJUST(-amount, expdest);
6214 return startp;
6215 }
6216#endif /* ENABLE_ASH_BASH_COMPAT */
6217
6218 subtype -= VSTRIMRIGHT;
6219#if DEBUG
6220 if (subtype < 0 || subtype > 7)
6221 abort();
6222#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006223 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6224 zero = subtype >> 1;
6225 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6226 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6227
6228 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6229 if (loc) {
6230 if (zero) {
6231 memmove(startp, loc, str - loc);
6232 loc = startp + (str - loc) - 1;
6233 }
6234 *loc = '\0';
6235 amount = loc - expdest;
6236 STADJUST(amount, expdest);
6237 }
6238 return loc;
6239}
6240
6241/*
6242 * Add the value of a specialized variable to the stack string.
6243 */
6244static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006245varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006246{
6247 int num;
6248 char *p;
6249 int i;
6250 int sep = 0;
6251 int sepq = 0;
6252 ssize_t len = 0;
6253 char **ap;
6254 int syntax;
6255 int quoted = varflags & VSQUOTE;
6256 int subtype = varflags & VSTYPE;
6257 int quotes = flags & (EXP_FULL | EXP_CASE);
6258
6259 if (quoted && (flags & EXP_FULL))
6260 sep = 1 << CHAR_BIT;
6261
6262 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6263 switch (*name) {
6264 case '$':
6265 num = rootpid;
6266 goto numvar;
6267 case '?':
6268 num = exitstatus;
6269 goto numvar;
6270 case '#':
6271 num = shellparam.nparam;
6272 goto numvar;
6273 case '!':
6274 num = backgndpid;
6275 if (num == 0)
6276 return -1;
6277 numvar:
6278 len = cvtnum(num);
6279 break;
6280 case '-':
6281 p = makestrspace(NOPTS, expdest);
6282 for (i = NOPTS - 1; i >= 0; i--) {
6283 if (optlist[i]) {
6284 USTPUTC(optletters(i), p);
6285 len++;
6286 }
6287 }
6288 expdest = p;
6289 break;
6290 case '@':
6291 if (sep)
6292 goto param;
6293 /* fall through */
6294 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006295 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6297 sepq = 1;
6298 param:
6299 ap = shellparam.p;
6300 if (!ap)
6301 return -1;
6302 while ((p = *ap++)) {
6303 size_t partlen;
6304
6305 partlen = strlen(p);
6306 len += partlen;
6307
6308 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6309 memtodest(p, partlen, syntax, quotes);
6310
6311 if (*ap && sep) {
6312 char *q;
6313
6314 len++;
6315 if (subtype == VSPLUS || subtype == VSLENGTH) {
6316 continue;
6317 }
6318 q = expdest;
6319 if (sepq)
6320 STPUTC(CTLESC, q);
6321 STPUTC(sep, q);
6322 expdest = q;
6323 }
6324 }
6325 return len;
6326 case '0':
6327 case '1':
6328 case '2':
6329 case '3':
6330 case '4':
6331 case '5':
6332 case '6':
6333 case '7':
6334 case '8':
6335 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006336// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006337 num = atoi(name);
6338 if (num < 0 || num > shellparam.nparam)
6339 return -1;
6340 p = num ? shellparam.p[num - 1] : arg0;
6341 goto value;
6342 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006343 /* NB: name has form "VAR=..." */
6344
6345 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6346 * which should be considered before we check variables. */
6347 if (var_str_list) {
6348 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6349 p = NULL;
6350 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006351 char *str, *eq;
6352 str = var_str_list->text;
6353 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006354 if (!eq) /* stop at first non-assignment */
6355 break;
6356 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006357 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006358 && strncmp(str, name, name_len) == 0) {
6359 p = eq;
6360 /* goto value; - WRONG! */
6361 /* think "A=1 A=2 B=$A" */
6362 }
6363 var_str_list = var_str_list->next;
6364 } while (var_str_list);
6365 if (p)
6366 goto value;
6367 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006368 p = lookupvar(name);
6369 value:
6370 if (!p)
6371 return -1;
6372
6373 len = strlen(p);
6374 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6375 memtodest(p, len, syntax, quotes);
6376 return len;
6377 }
6378
6379 if (subtype == VSPLUS || subtype == VSLENGTH)
6380 STADJUST(-len, expdest);
6381 return len;
6382}
6383
6384/*
6385 * Expand a variable, and return a pointer to the next character in the
6386 * input string.
6387 */
6388static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006389evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006390{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006391 char varflags;
6392 char subtype;
6393 char quoted;
6394 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006395 char *var;
6396 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006397 int startloc;
6398 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006400 varflags = *p++;
6401 subtype = varflags & VSTYPE;
6402 quoted = varflags & VSQUOTE;
6403 var = p;
6404 easy = (!quoted || (*var == '@' && shellparam.nparam));
6405 startloc = expdest - (char *)stackblock();
6406 p = strchr(p, '=') + 1;
6407
6408 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006409 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006410 if (varflags & VSNUL)
6411 varlen--;
6412
6413 if (subtype == VSPLUS) {
6414 varlen = -1 - varlen;
6415 goto vsplus;
6416 }
6417
6418 if (subtype == VSMINUS) {
6419 vsplus:
6420 if (varlen < 0) {
6421 argstr(
6422 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006423 (quoted ? EXP_QWORD : EXP_WORD),
6424 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006425 );
6426 goto end;
6427 }
6428 if (easy)
6429 goto record;
6430 goto end;
6431 }
6432
6433 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6434 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006435 if (subevalvar(p, var, /* strloc: */ 0,
6436 subtype, startloc, varflags,
6437 /* quotes: */ 0,
6438 var_str_list)
6439 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006440 varflags &= ~VSNUL;
6441 /*
6442 * Remove any recorded regions beyond
6443 * start of variable
6444 */
6445 removerecordregions(startloc);
6446 goto again;
6447 }
6448 goto end;
6449 }
6450 if (easy)
6451 goto record;
6452 goto end;
6453 }
6454
6455 if (varlen < 0 && uflag)
6456 varunset(p, var, 0, 0);
6457
6458 if (subtype == VSLENGTH) {
6459 cvtnum(varlen > 0 ? varlen : 0);
6460 goto record;
6461 }
6462
6463 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006464 if (easy)
6465 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006466 goto end;
6467 }
6468
6469#if DEBUG
6470 switch (subtype) {
6471 case VSTRIMLEFT:
6472 case VSTRIMLEFTMAX:
6473 case VSTRIMRIGHT:
6474 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475#if ENABLE_ASH_BASH_COMPAT
6476 case VSSUBSTR:
6477 case VSREPLACE:
6478 case VSREPLACEALL:
6479#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006480 break;
6481 default:
6482 abort();
6483 }
6484#endif
6485
6486 if (varlen >= 0) {
6487 /*
6488 * Terminate the string and start recording the pattern
6489 * right after it
6490 */
6491 STPUTC('\0', expdest);
6492 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006493 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6494 startloc, varflags,
6495 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6496 var_str_list)
6497 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006498 int amount = expdest - (
6499 (char *)stackblock() + patloc - 1
6500 );
6501 STADJUST(-amount, expdest);
6502 }
6503 /* Remove any recorded regions beyond start of variable */
6504 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006505 record:
6506 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006507 }
6508
6509 end:
6510 if (subtype != VSNORMAL) { /* skip to end of alternative */
6511 int nesting = 1;
6512 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006513 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006514 if (c == CTLESC)
6515 p++;
6516 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6517 if (varlen >= 0)
6518 argbackq = argbackq->next;
6519 } else if (c == CTLVAR) {
6520 if ((*p++ & VSTYPE) != VSNORMAL)
6521 nesting++;
6522 } else if (c == CTLENDVAR) {
6523 if (--nesting == 0)
6524 break;
6525 }
6526 }
6527 }
6528 return p;
6529}
6530
6531/*
6532 * Break the argument string into pieces based upon IFS and add the
6533 * strings to the argument list. The regions of the string to be
6534 * searched for IFS characters have been stored by recordregion.
6535 */
6536static void
6537ifsbreakup(char *string, struct arglist *arglist)
6538{
6539 struct ifsregion *ifsp;
6540 struct strlist *sp;
6541 char *start;
6542 char *p;
6543 char *q;
6544 const char *ifs, *realifs;
6545 int ifsspc;
6546 int nulonly;
6547
6548 start = string;
6549 if (ifslastp != NULL) {
6550 ifsspc = 0;
6551 nulonly = 0;
6552 realifs = ifsset() ? ifsval() : defifs;
6553 ifsp = &ifsfirst;
6554 do {
6555 p = string + ifsp->begoff;
6556 nulonly = ifsp->nulonly;
6557 ifs = nulonly ? nullstr : realifs;
6558 ifsspc = 0;
6559 while (p < string + ifsp->endoff) {
6560 q = p;
6561 if (*p == CTLESC)
6562 p++;
6563 if (!strchr(ifs, *p)) {
6564 p++;
6565 continue;
6566 }
6567 if (!nulonly)
6568 ifsspc = (strchr(defifs, *p) != NULL);
6569 /* Ignore IFS whitespace at start */
6570 if (q == start && ifsspc) {
6571 p++;
6572 start = p;
6573 continue;
6574 }
6575 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006576 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006577 sp->text = start;
6578 *arglist->lastp = sp;
6579 arglist->lastp = &sp->next;
6580 p++;
6581 if (!nulonly) {
6582 for (;;) {
6583 if (p >= string + ifsp->endoff) {
6584 break;
6585 }
6586 q = p;
6587 if (*p == CTLESC)
6588 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006589 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 p = q;
6591 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006592 }
6593 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594 if (ifsspc) {
6595 p++;
6596 ifsspc = 0;
6597 } else {
6598 p = q;
6599 break;
6600 }
6601 } else
6602 p++;
6603 }
6604 }
6605 start = p;
6606 } /* while */
6607 ifsp = ifsp->next;
6608 } while (ifsp != NULL);
6609 if (nulonly)
6610 goto add;
6611 }
6612
6613 if (!*start)
6614 return;
6615
6616 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006617 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006618 sp->text = start;
6619 *arglist->lastp = sp;
6620 arglist->lastp = &sp->next;
6621}
6622
6623static void
6624ifsfree(void)
6625{
6626 struct ifsregion *p;
6627
6628 INT_OFF;
6629 p = ifsfirst.next;
6630 do {
6631 struct ifsregion *ifsp;
6632 ifsp = p->next;
6633 free(p);
6634 p = ifsp;
6635 } while (p);
6636 ifslastp = NULL;
6637 ifsfirst.next = NULL;
6638 INT_ON;
6639}
6640
6641/*
6642 * Add a file name to the list.
6643 */
6644static void
6645addfname(const char *name)
6646{
6647 struct strlist *sp;
6648
Denis Vlasenko597906c2008-02-20 16:38:54 +00006649 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006650 sp->text = ststrdup(name);
6651 *exparg.lastp = sp;
6652 exparg.lastp = &sp->next;
6653}
6654
6655static char *expdir;
6656
6657/*
6658 * Do metacharacter (i.e. *, ?, [...]) expansion.
6659 */
6660static void
6661expmeta(char *enddir, char *name)
6662{
6663 char *p;
6664 const char *cp;
6665 char *start;
6666 char *endname;
6667 int metaflag;
6668 struct stat statb;
6669 DIR *dirp;
6670 struct dirent *dp;
6671 int atend;
6672 int matchdot;
6673
6674 metaflag = 0;
6675 start = name;
6676 for (p = name; *p; p++) {
6677 if (*p == '*' || *p == '?')
6678 metaflag = 1;
6679 else if (*p == '[') {
6680 char *q = p + 1;
6681 if (*q == '!')
6682 q++;
6683 for (;;) {
6684 if (*q == '\\')
6685 q++;
6686 if (*q == '/' || *q == '\0')
6687 break;
6688 if (*++q == ']') {
6689 metaflag = 1;
6690 break;
6691 }
6692 }
6693 } else if (*p == '\\')
6694 p++;
6695 else if (*p == '/') {
6696 if (metaflag)
6697 goto out;
6698 start = p + 1;
6699 }
6700 }
6701 out:
6702 if (metaflag == 0) { /* we've reached the end of the file name */
6703 if (enddir != expdir)
6704 metaflag++;
6705 p = name;
6706 do {
6707 if (*p == '\\')
6708 p++;
6709 *enddir++ = *p;
6710 } while (*p++);
6711 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6712 addfname(expdir);
6713 return;
6714 }
6715 endname = p;
6716 if (name < start) {
6717 p = name;
6718 do {
6719 if (*p == '\\')
6720 p++;
6721 *enddir++ = *p++;
6722 } while (p < start);
6723 }
6724 if (enddir == expdir) {
6725 cp = ".";
6726 } else if (enddir == expdir + 1 && *expdir == '/') {
6727 cp = "/";
6728 } else {
6729 cp = expdir;
6730 enddir[-1] = '\0';
6731 }
6732 dirp = opendir(cp);
6733 if (dirp == NULL)
6734 return;
6735 if (enddir != expdir)
6736 enddir[-1] = '/';
6737 if (*endname == 0) {
6738 atend = 1;
6739 } else {
6740 atend = 0;
6741 *endname++ = '\0';
6742 }
6743 matchdot = 0;
6744 p = start;
6745 if (*p == '\\')
6746 p++;
6747 if (*p == '.')
6748 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006749 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006750 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006751 continue;
6752 if (pmatch(start, dp->d_name)) {
6753 if (atend) {
6754 strcpy(enddir, dp->d_name);
6755 addfname(expdir);
6756 } else {
6757 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6758 continue;
6759 p[-1] = '/';
6760 expmeta(p, endname);
6761 }
6762 }
6763 }
6764 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006765 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006766 endname[-1] = '/';
6767}
6768
6769static struct strlist *
6770msort(struct strlist *list, int len)
6771{
6772 struct strlist *p, *q = NULL;
6773 struct strlist **lpp;
6774 int half;
6775 int n;
6776
6777 if (len <= 1)
6778 return list;
6779 half = len >> 1;
6780 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006781 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006782 q = p;
6783 p = p->next;
6784 }
6785 q->next = NULL; /* terminate first half of list */
6786 q = msort(list, half); /* sort first half of list */
6787 p = msort(p, len - half); /* sort second half */
6788 lpp = &list;
6789 for (;;) {
6790#if ENABLE_LOCALE_SUPPORT
6791 if (strcoll(p->text, q->text) < 0)
6792#else
6793 if (strcmp(p->text, q->text) < 0)
6794#endif
6795 {
6796 *lpp = p;
6797 lpp = &p->next;
6798 p = *lpp;
6799 if (p == NULL) {
6800 *lpp = q;
6801 break;
6802 }
6803 } else {
6804 *lpp = q;
6805 lpp = &q->next;
6806 q = *lpp;
6807 if (q == NULL) {
6808 *lpp = p;
6809 break;
6810 }
6811 }
6812 }
6813 return list;
6814}
6815
6816/*
6817 * Sort the results of file name expansion. It calculates the number of
6818 * strings to sort and then calls msort (short for merge sort) to do the
6819 * work.
6820 */
6821static struct strlist *
6822expsort(struct strlist *str)
6823{
6824 int len;
6825 struct strlist *sp;
6826
6827 len = 0;
6828 for (sp = str; sp; sp = sp->next)
6829 len++;
6830 return msort(str, len);
6831}
6832
6833static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006834expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006835{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006836 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006837 '*', '?', '[', 0
6838 };
6839 /* TODO - EXP_REDIR */
6840
6841 while (str) {
6842 struct strlist **savelastp;
6843 struct strlist *sp;
6844 char *p;
6845
6846 if (fflag)
6847 goto nometa;
6848 if (!strpbrk(str->text, metachars))
6849 goto nometa;
6850 savelastp = exparg.lastp;
6851
6852 INT_OFF;
6853 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6854 {
6855 int i = strlen(str->text);
6856 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6857 }
6858
6859 expmeta(expdir, p);
6860 free(expdir);
6861 if (p != str->text)
6862 free(p);
6863 INT_ON;
6864 if (exparg.lastp == savelastp) {
6865 /*
6866 * no matches
6867 */
6868 nometa:
6869 *exparg.lastp = str;
6870 rmescapes(str->text);
6871 exparg.lastp = &str->next;
6872 } else {
6873 *exparg.lastp = NULL;
6874 *savelastp = sp = expsort(*savelastp);
6875 while (sp->next != NULL)
6876 sp = sp->next;
6877 exparg.lastp = &sp->next;
6878 }
6879 str = str->next;
6880 }
6881}
6882
6883/*
6884 * Perform variable substitution and command substitution on an argument,
6885 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6886 * perform splitting and file name expansion. When arglist is NULL, perform
6887 * here document expansion.
6888 */
6889static void
6890expandarg(union node *arg, struct arglist *arglist, int flag)
6891{
6892 struct strlist *sp;
6893 char *p;
6894
6895 argbackq = arg->narg.backquote;
6896 STARTSTACKSTR(expdest);
6897 ifsfirst.next = NULL;
6898 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006899 argstr(arg->narg.text, flag,
6900 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006901 p = _STPUTC('\0', expdest);
6902 expdest = p - 1;
6903 if (arglist == NULL) {
6904 return; /* here document expanded */
6905 }
6906 p = grabstackstr(p);
6907 exparg.lastp = &exparg.list;
6908 /*
6909 * TODO - EXP_REDIR
6910 */
6911 if (flag & EXP_FULL) {
6912 ifsbreakup(p, &exparg);
6913 *exparg.lastp = NULL;
6914 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006915 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006916 } else {
6917 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6918 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006919 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006920 sp->text = p;
6921 *exparg.lastp = sp;
6922 exparg.lastp = &sp->next;
6923 }
6924 if (ifsfirst.next)
6925 ifsfree();
6926 *exparg.lastp = NULL;
6927 if (exparg.list) {
6928 *arglist->lastp = exparg.list;
6929 arglist->lastp = exparg.lastp;
6930 }
6931}
6932
6933/*
6934 * Expand shell variables and backquotes inside a here document.
6935 */
6936static void
6937expandhere(union node *arg, int fd)
6938{
6939 herefd = fd;
6940 expandarg(arg, (struct arglist *)NULL, 0);
6941 full_write(fd, stackblock(), expdest - (char *)stackblock());
6942}
6943
6944/*
6945 * Returns true if the pattern matches the string.
6946 */
6947static int
6948patmatch(char *pattern, const char *string)
6949{
6950 return pmatch(preglob(pattern, 0, 0), string);
6951}
6952
6953/*
6954 * See if a pattern matches in a case statement.
6955 */
6956static int
6957casematch(union node *pattern, char *val)
6958{
6959 struct stackmark smark;
6960 int result;
6961
6962 setstackmark(&smark);
6963 argbackq = pattern->narg.backquote;
6964 STARTSTACKSTR(expdest);
6965 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006966 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6967 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006968 STACKSTRNUL(expdest);
6969 result = patmatch(stackblock(), val);
6970 popstackmark(&smark);
6971 return result;
6972}
6973
6974
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006975/* ============ find_command */
6976
6977struct builtincmd {
6978 const char *name;
6979 int (*builtin)(int, char **);
6980 /* unsigned flags; */
6981};
6982#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006983/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006984 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006986#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987
6988struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006989 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006990 union param {
6991 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006992 /* index >= 0 for commands without path (slashes) */
6993 /* (TODO: what exactly does the value mean? PATH position?) */
6994 /* index == -1 for commands with slashes */
6995 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006996 const struct builtincmd *cmd;
6997 struct funcnode *func;
6998 } u;
6999};
7000/* values of cmdtype */
7001#define CMDUNKNOWN -1 /* no entry in table for command */
7002#define CMDNORMAL 0 /* command is an executable program */
7003#define CMDFUNCTION 1 /* command is a shell function */
7004#define CMDBUILTIN 2 /* command is a shell builtin */
7005
7006/* action to find_command() */
7007#define DO_ERR 0x01 /* prints errors */
7008#define DO_ABS 0x02 /* checks absolute paths */
7009#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7010#define DO_ALTPATH 0x08 /* using alternate path */
7011#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7012
7013static void find_command(char *, struct cmdentry *, int, const char *);
7014
7015
7016/* ============ Hashing commands */
7017
7018/*
7019 * When commands are first encountered, they are entered in a hash table.
7020 * This ensures that a full path search will not have to be done for them
7021 * on each invocation.
7022 *
7023 * We should investigate converting to a linear search, even though that
7024 * would make the command name "hash" a misnomer.
7025 */
7026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007027struct tblentry {
7028 struct tblentry *next; /* next entry in hash chain */
7029 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007030 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007031 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007032 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007033};
7034
Denis Vlasenko01631112007-12-16 17:20:38 +00007035static struct tblentry **cmdtable;
7036#define INIT_G_cmdtable() do { \
7037 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7038} while (0)
7039
7040static int builtinloc = -1; /* index in path of %builtin, or -1 */
7041
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007042
7043static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007044tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007045{
7046 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007047
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007048#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007049 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007050 if (APPLET_IS_NOEXEC(applet_no)) {
7051 while (*envp)
7052 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007053 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007054 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007055 /* re-exec ourselves with the new arguments */
7056 execve(bb_busybox_exec_path, argv, envp);
7057 /* If they called chroot or otherwise made the binary no longer
7058 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007059 }
7060#endif
7061
7062 repeat:
7063#ifdef SYSV
7064 do {
7065 execve(cmd, argv, envp);
7066 } while (errno == EINTR);
7067#else
7068 execve(cmd, argv, envp);
7069#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007070 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007071 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007072 return;
7073 }
7074 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007075 char **ap;
7076 char **new;
7077
7078 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007079 continue;
7080 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007081 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007082 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007083 ap += 2;
7084 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007085 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007086 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007087 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007088 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007089 goto repeat;
7090 }
7091}
7092
7093/*
7094 * Exec a program. Never returns. If you change this routine, you may
7095 * have to change the find_command routine as well.
7096 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007097static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007098static void
7099shellexec(char **argv, const char *path, int idx)
7100{
7101 char *cmdname;
7102 int e;
7103 char **envp;
7104 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007105#if ENABLE_FEATURE_SH_STANDALONE
7106 int applet_no = -1;
7107#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007108
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007109 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007110 envp = listvars(VEXPORT, VUNSET, 0);
7111 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007112#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007113 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007114#endif
7115 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007116 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007117 e = errno;
7118 } else {
7119 e = ENOENT;
7120 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7121 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007122 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007123 if (errno != ENOENT && errno != ENOTDIR)
7124 e = errno;
7125 }
7126 stunalloc(cmdname);
7127 }
7128 }
7129
7130 /* Map to POSIX errors */
7131 switch (e) {
7132 case EACCES:
7133 exerrno = 126;
7134 break;
7135 case ENOENT:
7136 exerrno = 127;
7137 break;
7138 default:
7139 exerrno = 2;
7140 break;
7141 }
7142 exitstatus = exerrno;
7143 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007144 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007145 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7146 /* NOTREACHED */
7147}
7148
7149static void
7150printentry(struct tblentry *cmdp)
7151{
7152 int idx;
7153 const char *path;
7154 char *name;
7155
7156 idx = cmdp->param.index;
7157 path = pathval();
7158 do {
7159 name = padvance(&path, cmdp->cmdname);
7160 stunalloc(name);
7161 } while (--idx >= 0);
7162 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7163}
7164
7165/*
7166 * Clear out command entries. The argument specifies the first entry in
7167 * PATH which has changed.
7168 */
7169static void
7170clearcmdentry(int firstchange)
7171{
7172 struct tblentry **tblp;
7173 struct tblentry **pp;
7174 struct tblentry *cmdp;
7175
7176 INT_OFF;
7177 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7178 pp = tblp;
7179 while ((cmdp = *pp) != NULL) {
7180 if ((cmdp->cmdtype == CMDNORMAL &&
7181 cmdp->param.index >= firstchange)
7182 || (cmdp->cmdtype == CMDBUILTIN &&
7183 builtinloc >= firstchange)
7184 ) {
7185 *pp = cmdp->next;
7186 free(cmdp);
7187 } else {
7188 pp = &cmdp->next;
7189 }
7190 }
7191 }
7192 INT_ON;
7193}
7194
7195/*
7196 * Locate a command in the command hash table. If "add" is nonzero,
7197 * add the command to the table if it is not already present. The
7198 * variable "lastcmdentry" is set to point to the address of the link
7199 * pointing to the entry, so that delete_cmd_entry can delete the
7200 * entry.
7201 *
7202 * Interrupts must be off if called with add != 0.
7203 */
7204static struct tblentry **lastcmdentry;
7205
7206static struct tblentry *
7207cmdlookup(const char *name, int add)
7208{
7209 unsigned int hashval;
7210 const char *p;
7211 struct tblentry *cmdp;
7212 struct tblentry **pp;
7213
7214 p = name;
7215 hashval = (unsigned char)*p << 4;
7216 while (*p)
7217 hashval += (unsigned char)*p++;
7218 hashval &= 0x7FFF;
7219 pp = &cmdtable[hashval % CMDTABLESIZE];
7220 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7221 if (strcmp(cmdp->cmdname, name) == 0)
7222 break;
7223 pp = &cmdp->next;
7224 }
7225 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007226 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7227 + strlen(name)
7228 /* + 1 - already done because
7229 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007230 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007231 cmdp->cmdtype = CMDUNKNOWN;
7232 strcpy(cmdp->cmdname, name);
7233 }
7234 lastcmdentry = pp;
7235 return cmdp;
7236}
7237
7238/*
7239 * Delete the command entry returned on the last lookup.
7240 */
7241static void
7242delete_cmd_entry(void)
7243{
7244 struct tblentry *cmdp;
7245
7246 INT_OFF;
7247 cmdp = *lastcmdentry;
7248 *lastcmdentry = cmdp->next;
7249 if (cmdp->cmdtype == CMDFUNCTION)
7250 freefunc(cmdp->param.func);
7251 free(cmdp);
7252 INT_ON;
7253}
7254
7255/*
7256 * Add a new command entry, replacing any existing command entry for
7257 * the same name - except special builtins.
7258 */
7259static void
7260addcmdentry(char *name, struct cmdentry *entry)
7261{
7262 struct tblentry *cmdp;
7263
7264 cmdp = cmdlookup(name, 1);
7265 if (cmdp->cmdtype == CMDFUNCTION) {
7266 freefunc(cmdp->param.func);
7267 }
7268 cmdp->cmdtype = entry->cmdtype;
7269 cmdp->param = entry->u;
7270 cmdp->rehash = 0;
7271}
7272
7273static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007274hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007275{
7276 struct tblentry **pp;
7277 struct tblentry *cmdp;
7278 int c;
7279 struct cmdentry entry;
7280 char *name;
7281
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007282 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007283 clearcmdentry(0);
7284 return 0;
7285 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007286
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287 if (*argptr == NULL) {
7288 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7289 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7290 if (cmdp->cmdtype == CMDNORMAL)
7291 printentry(cmdp);
7292 }
7293 }
7294 return 0;
7295 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007296
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007297 c = 0;
7298 while ((name = *argptr) != NULL) {
7299 cmdp = cmdlookup(name, 0);
7300 if (cmdp != NULL
7301 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007302 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7303 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007305 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007306 find_command(name, &entry, DO_ERR, pathval());
7307 if (entry.cmdtype == CMDUNKNOWN)
7308 c = 1;
7309 argptr++;
7310 }
7311 return c;
7312}
7313
7314/*
7315 * Called when a cd is done. Marks all commands so the next time they
7316 * are executed they will be rehashed.
7317 */
7318static void
7319hashcd(void)
7320{
7321 struct tblentry **pp;
7322 struct tblentry *cmdp;
7323
7324 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7325 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007326 if (cmdp->cmdtype == CMDNORMAL
7327 || (cmdp->cmdtype == CMDBUILTIN
7328 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7329 && builtinloc > 0)
7330 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007331 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007332 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333 }
7334 }
7335}
7336
7337/*
7338 * Fix command hash table when PATH changed.
7339 * Called before PATH is changed. The argument is the new value of PATH;
7340 * pathval() still returns the old value at this point.
7341 * Called with interrupts off.
7342 */
7343static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007344changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007345{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007346 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007347 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007348 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007349 int idx_bltin;
7350
7351 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007352 firstchange = 9999; /* assume no change */
7353 idx = 0;
7354 idx_bltin = -1;
7355 for (;;) {
7356 if (*old != *new) {
7357 firstchange = idx;
7358 if ((*old == '\0' && *new == ':')
7359 || (*old == ':' && *new == '\0'))
7360 firstchange++;
7361 old = new; /* ignore subsequent differences */
7362 }
7363 if (*new == '\0')
7364 break;
7365 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7366 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007367 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007368 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007369 new++, old++;
7370 }
7371 if (builtinloc < 0 && idx_bltin >= 0)
7372 builtinloc = idx_bltin; /* zap builtins */
7373 if (builtinloc >= 0 && idx_bltin < 0)
7374 firstchange = 0;
7375 clearcmdentry(firstchange);
7376 builtinloc = idx_bltin;
7377}
7378
7379#define TEOF 0
7380#define TNL 1
7381#define TREDIR 2
7382#define TWORD 3
7383#define TSEMI 4
7384#define TBACKGND 5
7385#define TAND 6
7386#define TOR 7
7387#define TPIPE 8
7388#define TLP 9
7389#define TRP 10
7390#define TENDCASE 11
7391#define TENDBQUOTE 12
7392#define TNOT 13
7393#define TCASE 14
7394#define TDO 15
7395#define TDONE 16
7396#define TELIF 17
7397#define TELSE 18
7398#define TESAC 19
7399#define TFI 20
7400#define TFOR 21
7401#define TIF 22
7402#define TIN 23
7403#define TTHEN 24
7404#define TUNTIL 25
7405#define TWHILE 26
7406#define TBEGIN 27
7407#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007408typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409
7410/* first char is indicating which tokens mark the end of a list */
7411static const char *const tokname_array[] = {
7412 "\1end of file",
7413 "\0newline",
7414 "\0redirection",
7415 "\0word",
7416 "\0;",
7417 "\0&",
7418 "\0&&",
7419 "\0||",
7420 "\0|",
7421 "\0(",
7422 "\1)",
7423 "\1;;",
7424 "\1`",
7425#define KWDOFFSET 13
7426 /* the following are keywords */
7427 "\0!",
7428 "\0case",
7429 "\1do",
7430 "\1done",
7431 "\1elif",
7432 "\1else",
7433 "\1esac",
7434 "\1fi",
7435 "\0for",
7436 "\0if",
7437 "\0in",
7438 "\1then",
7439 "\0until",
7440 "\0while",
7441 "\0{",
7442 "\1}",
7443};
7444
7445static const char *
7446tokname(int tok)
7447{
7448 static char buf[16];
7449
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007450//try this:
7451//if (tok < TSEMI) return tokname_array[tok] + 1;
7452//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7453//return buf;
7454
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007455 if (tok >= TSEMI)
7456 buf[0] = '"';
7457 sprintf(buf + (tok >= TSEMI), "%s%c",
7458 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7459 return buf;
7460}
7461
7462/* Wrapper around strcmp for qsort/bsearch/... */
7463static int
7464pstrcmp(const void *a, const void *b)
7465{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007466 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007467}
7468
7469static const char *const *
7470findkwd(const char *s)
7471{
7472 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007473 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7474 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007475}
7476
7477/*
7478 * Locate and print what a word is...
7479 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007480static int
7481describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007482{
7483 struct cmdentry entry;
7484 struct tblentry *cmdp;
7485#if ENABLE_ASH_ALIAS
7486 const struct alias *ap;
7487#endif
7488 const char *path = pathval();
7489
7490 if (describe_command_verbose) {
7491 out1str(command);
7492 }
7493
7494 /* First look at the keywords */
7495 if (findkwd(command)) {
7496 out1str(describe_command_verbose ? " is a shell keyword" : command);
7497 goto out;
7498 }
7499
7500#if ENABLE_ASH_ALIAS
7501 /* Then look at the aliases */
7502 ap = lookupalias(command, 0);
7503 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007504 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007505 out1str("alias ");
7506 printalias(ap);
7507 return 0;
7508 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007509 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007510 goto out;
7511 }
7512#endif
7513 /* Then check if it is a tracked alias */
7514 cmdp = cmdlookup(command, 0);
7515 if (cmdp != NULL) {
7516 entry.cmdtype = cmdp->cmdtype;
7517 entry.u = cmdp->param;
7518 } else {
7519 /* Finally use brute force */
7520 find_command(command, &entry, DO_ABS, path);
7521 }
7522
7523 switch (entry.cmdtype) {
7524 case CMDNORMAL: {
7525 int j = entry.u.index;
7526 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007527 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007528 p = command;
7529 } else {
7530 do {
7531 p = padvance(&path, command);
7532 stunalloc(p);
7533 } while (--j >= 0);
7534 }
7535 if (describe_command_verbose) {
7536 out1fmt(" is%s %s",
7537 (cmdp ? " a tracked alias for" : nullstr), p
7538 );
7539 } else {
7540 out1str(p);
7541 }
7542 break;
7543 }
7544
7545 case CMDFUNCTION:
7546 if (describe_command_verbose) {
7547 out1str(" is a shell function");
7548 } else {
7549 out1str(command);
7550 }
7551 break;
7552
7553 case CMDBUILTIN:
7554 if (describe_command_verbose) {
7555 out1fmt(" is a %sshell builtin",
7556 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7557 "special " : nullstr
7558 );
7559 } else {
7560 out1str(command);
7561 }
7562 break;
7563
7564 default:
7565 if (describe_command_verbose) {
7566 out1str(": not found\n");
7567 }
7568 return 127;
7569 }
7570 out:
7571 outstr("\n", stdout);
7572 return 0;
7573}
7574
7575static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007576typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007577{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007578 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007579 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007580 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007581
Denis Vlasenko46846e22007-05-20 13:08:31 +00007582 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007583 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007584 i++;
7585 verbose = 0;
7586 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007587 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007588 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007589 }
7590 return err;
7591}
7592
7593#if ENABLE_ASH_CMDCMD
7594static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007595commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007596{
7597 int c;
7598 enum {
7599 VERIFY_BRIEF = 1,
7600 VERIFY_VERBOSE = 2,
7601 } verify = 0;
7602
7603 while ((c = nextopt("pvV")) != '\0')
7604 if (c == 'V')
7605 verify |= VERIFY_VERBOSE;
7606 else if (c == 'v')
7607 verify |= VERIFY_BRIEF;
7608#if DEBUG
7609 else if (c != 'p')
7610 abort();
7611#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007612 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7613 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007614 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007615 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007616
7617 return 0;
7618}
7619#endif
7620
7621
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007622/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007623
Denis Vlasenko340299a2008-11-21 10:36:36 +00007624static int funcblocksize; /* size of structures in function */
7625static int funcstringsize; /* size of strings in node */
7626static void *funcblock; /* block to allocate function from */
7627static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007628
Eric Andersencb57d552001-06-28 07:25:16 +00007629/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007630#define EV_EXIT 01 /* exit after evaluating tree */
7631#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007632#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007633
Denis Vlasenko340299a2008-11-21 10:36:36 +00007634static const short nodesize[N_NUMBER] = {
7635 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7636 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7637 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7638 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7639 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7640 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7641 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7642 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7643 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7644 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7645 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7646 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7647 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7648 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7649 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7650 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7651 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007652#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007653 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007654#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007655 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7656 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7657 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7658 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7659 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7660 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7661 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7662 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7663 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007664};
7665
7666static void calcsize(union node *n);
7667
7668static void
7669sizenodelist(struct nodelist *lp)
7670{
7671 while (lp) {
7672 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7673 calcsize(lp->n);
7674 lp = lp->next;
7675 }
7676}
7677
7678static void
7679calcsize(union node *n)
7680{
7681 if (n == NULL)
7682 return;
7683 funcblocksize += nodesize[n->type];
7684 switch (n->type) {
7685 case NCMD:
7686 calcsize(n->ncmd.redirect);
7687 calcsize(n->ncmd.args);
7688 calcsize(n->ncmd.assign);
7689 break;
7690 case NPIPE:
7691 sizenodelist(n->npipe.cmdlist);
7692 break;
7693 case NREDIR:
7694 case NBACKGND:
7695 case NSUBSHELL:
7696 calcsize(n->nredir.redirect);
7697 calcsize(n->nredir.n);
7698 break;
7699 case NAND:
7700 case NOR:
7701 case NSEMI:
7702 case NWHILE:
7703 case NUNTIL:
7704 calcsize(n->nbinary.ch2);
7705 calcsize(n->nbinary.ch1);
7706 break;
7707 case NIF:
7708 calcsize(n->nif.elsepart);
7709 calcsize(n->nif.ifpart);
7710 calcsize(n->nif.test);
7711 break;
7712 case NFOR:
7713 funcstringsize += strlen(n->nfor.var) + 1;
7714 calcsize(n->nfor.body);
7715 calcsize(n->nfor.args);
7716 break;
7717 case NCASE:
7718 calcsize(n->ncase.cases);
7719 calcsize(n->ncase.expr);
7720 break;
7721 case NCLIST:
7722 calcsize(n->nclist.body);
7723 calcsize(n->nclist.pattern);
7724 calcsize(n->nclist.next);
7725 break;
7726 case NDEFUN:
7727 case NARG:
7728 sizenodelist(n->narg.backquote);
7729 funcstringsize += strlen(n->narg.text) + 1;
7730 calcsize(n->narg.next);
7731 break;
7732 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007733#if ENABLE_ASH_BASH_COMPAT
7734 case NTO2:
7735#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007736 case NCLOBBER:
7737 case NFROM:
7738 case NFROMTO:
7739 case NAPPEND:
7740 calcsize(n->nfile.fname);
7741 calcsize(n->nfile.next);
7742 break;
7743 case NTOFD:
7744 case NFROMFD:
7745 calcsize(n->ndup.vname);
7746 calcsize(n->ndup.next);
7747 break;
7748 case NHERE:
7749 case NXHERE:
7750 calcsize(n->nhere.doc);
7751 calcsize(n->nhere.next);
7752 break;
7753 case NNOT:
7754 calcsize(n->nnot.com);
7755 break;
7756 };
7757}
7758
7759static char *
7760nodeckstrdup(char *s)
7761{
7762 char *rtn = funcstring;
7763
7764 strcpy(funcstring, s);
7765 funcstring += strlen(s) + 1;
7766 return rtn;
7767}
7768
7769static union node *copynode(union node *);
7770
7771static struct nodelist *
7772copynodelist(struct nodelist *lp)
7773{
7774 struct nodelist *start;
7775 struct nodelist **lpp;
7776
7777 lpp = &start;
7778 while (lp) {
7779 *lpp = funcblock;
7780 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7781 (*lpp)->n = copynode(lp->n);
7782 lp = lp->next;
7783 lpp = &(*lpp)->next;
7784 }
7785 *lpp = NULL;
7786 return start;
7787}
7788
7789static union node *
7790copynode(union node *n)
7791{
7792 union node *new;
7793
7794 if (n == NULL)
7795 return NULL;
7796 new = funcblock;
7797 funcblock = (char *) funcblock + nodesize[n->type];
7798
7799 switch (n->type) {
7800 case NCMD:
7801 new->ncmd.redirect = copynode(n->ncmd.redirect);
7802 new->ncmd.args = copynode(n->ncmd.args);
7803 new->ncmd.assign = copynode(n->ncmd.assign);
7804 break;
7805 case NPIPE:
7806 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007807 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007808 break;
7809 case NREDIR:
7810 case NBACKGND:
7811 case NSUBSHELL:
7812 new->nredir.redirect = copynode(n->nredir.redirect);
7813 new->nredir.n = copynode(n->nredir.n);
7814 break;
7815 case NAND:
7816 case NOR:
7817 case NSEMI:
7818 case NWHILE:
7819 case NUNTIL:
7820 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7821 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7822 break;
7823 case NIF:
7824 new->nif.elsepart = copynode(n->nif.elsepart);
7825 new->nif.ifpart = copynode(n->nif.ifpart);
7826 new->nif.test = copynode(n->nif.test);
7827 break;
7828 case NFOR:
7829 new->nfor.var = nodeckstrdup(n->nfor.var);
7830 new->nfor.body = copynode(n->nfor.body);
7831 new->nfor.args = copynode(n->nfor.args);
7832 break;
7833 case NCASE:
7834 new->ncase.cases = copynode(n->ncase.cases);
7835 new->ncase.expr = copynode(n->ncase.expr);
7836 break;
7837 case NCLIST:
7838 new->nclist.body = copynode(n->nclist.body);
7839 new->nclist.pattern = copynode(n->nclist.pattern);
7840 new->nclist.next = copynode(n->nclist.next);
7841 break;
7842 case NDEFUN:
7843 case NARG:
7844 new->narg.backquote = copynodelist(n->narg.backquote);
7845 new->narg.text = nodeckstrdup(n->narg.text);
7846 new->narg.next = copynode(n->narg.next);
7847 break;
7848 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007849#if ENABLE_ASH_BASH_COMPAT
7850 case NTO2:
7851#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007852 case NCLOBBER:
7853 case NFROM:
7854 case NFROMTO:
7855 case NAPPEND:
7856 new->nfile.fname = copynode(n->nfile.fname);
7857 new->nfile.fd = n->nfile.fd;
7858 new->nfile.next = copynode(n->nfile.next);
7859 break;
7860 case NTOFD:
7861 case NFROMFD:
7862 new->ndup.vname = copynode(n->ndup.vname);
7863 new->ndup.dupfd = n->ndup.dupfd;
7864 new->ndup.fd = n->ndup.fd;
7865 new->ndup.next = copynode(n->ndup.next);
7866 break;
7867 case NHERE:
7868 case NXHERE:
7869 new->nhere.doc = copynode(n->nhere.doc);
7870 new->nhere.fd = n->nhere.fd;
7871 new->nhere.next = copynode(n->nhere.next);
7872 break;
7873 case NNOT:
7874 new->nnot.com = copynode(n->nnot.com);
7875 break;
7876 };
7877 new->type = n->type;
7878 return new;
7879}
7880
7881/*
7882 * Make a copy of a parse tree.
7883 */
7884static struct funcnode *
7885copyfunc(union node *n)
7886{
7887 struct funcnode *f;
7888 size_t blocksize;
7889
7890 funcblocksize = offsetof(struct funcnode, n);
7891 funcstringsize = 0;
7892 calcsize(n);
7893 blocksize = funcblocksize;
7894 f = ckmalloc(blocksize + funcstringsize);
7895 funcblock = (char *) f + offsetof(struct funcnode, n);
7896 funcstring = (char *) f + blocksize;
7897 copynode(n);
7898 f->count = 0;
7899 return f;
7900}
7901
7902/*
7903 * Define a shell function.
7904 */
7905static void
7906defun(char *name, union node *func)
7907{
7908 struct cmdentry entry;
7909
7910 INT_OFF;
7911 entry.cmdtype = CMDFUNCTION;
7912 entry.u.func = copyfunc(func);
7913 addcmdentry(name, &entry);
7914 INT_ON;
7915}
7916
7917static int evalskip; /* set if we are skipping commands */
7918/* reasons for skipping commands (see comment on breakcmd routine) */
7919#define SKIPBREAK (1 << 0)
7920#define SKIPCONT (1 << 1)
7921#define SKIPFUNC (1 << 2)
7922#define SKIPFILE (1 << 3)
7923#define SKIPEVAL (1 << 4)
7924static int skipcount; /* number of levels to skip */
7925static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007926static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007927
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007928/* forward decl way out to parsing code - dotrap needs it */
7929static int evalstring(char *s, int mask);
7930
7931/*
7932 * Called to execute a trap. Perhaps we should avoid entering new trap
7933 * handlers while we are executing a trap handler.
7934 */
7935static int
7936dotrap(void)
7937{
7938 char *p;
7939 char *q;
7940 int i;
7941 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007942 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007943
7944 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007945 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007946 xbarrier();
7947
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007948 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007949 if (!*q)
7950 continue;
7951 *q = '\0';
7952
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007953 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007954 if (!p)
7955 continue;
7956 skip = evalstring(p, SKIPEVAL);
7957 exitstatus = savestatus;
7958 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007959 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007960 }
7961
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007962 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007963}
7964
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007965/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007966static void evalloop(union node *, int);
7967static void evalfor(union node *, int);
7968static void evalcase(union node *, int);
7969static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007970static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007971static void evalpipe(union node *, int);
7972static void evalcommand(union node *, int);
7973static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007974static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007975
Eric Andersen62483552001-07-10 06:09:16 +00007976/*
Eric Andersenc470f442003-07-28 09:56:35 +00007977 * Evaluate a parse tree. The value is left in the global variable
7978 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007979 */
Eric Andersenc470f442003-07-28 09:56:35 +00007980static void
7981evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007982{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007983 struct jmploc *volatile savehandler = exception_handler;
7984 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007985 int checkexit = 0;
7986 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007988
Eric Andersenc470f442003-07-28 09:56:35 +00007989 if (n == NULL) {
7990 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007991 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007992 }
Eric Andersenc470f442003-07-28 09:56:35 +00007993 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007994 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007995
7996 exception_handler = &jmploc;
7997 {
7998 int err = setjmp(jmploc.loc);
7999 if (err) {
8000 /* if it was a signal, check for trap handlers */
Denis Vlasenko7f88e342009-03-19 03:36:18 +00008001 if (exception_type == EXSIG)
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008002 goto out;
8003 /* continue on the way out */
8004 exception_handler = savehandler;
8005 longjmp(exception_handler->loc, err);
8006 }
8007 }
8008
Eric Andersenc470f442003-07-28 09:56:35 +00008009 switch (n->type) {
8010 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008011#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008012 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008013 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008014 break;
8015#endif
8016 case NNOT:
8017 evaltree(n->nnot.com, EV_TESTED);
8018 status = !exitstatus;
8019 goto setstatus;
8020 case NREDIR:
8021 expredir(n->nredir.redirect);
8022 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8023 if (!status) {
8024 evaltree(n->nredir.n, flags & EV_TESTED);
8025 status = exitstatus;
8026 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008027 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008028 goto setstatus;
8029 case NCMD:
8030 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008031 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008032 if (eflag && !(flags & EV_TESTED))
8033 checkexit = ~0;
8034 goto calleval;
8035 case NFOR:
8036 evalfn = evalfor;
8037 goto calleval;
8038 case NWHILE:
8039 case NUNTIL:
8040 evalfn = evalloop;
8041 goto calleval;
8042 case NSUBSHELL:
8043 case NBACKGND:
8044 evalfn = evalsubshell;
8045 goto calleval;
8046 case NPIPE:
8047 evalfn = evalpipe;
8048 goto checkexit;
8049 case NCASE:
8050 evalfn = evalcase;
8051 goto calleval;
8052 case NAND:
8053 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008054 case NSEMI: {
8055
Eric Andersenc470f442003-07-28 09:56:35 +00008056#if NAND + 1 != NOR
8057#error NAND + 1 != NOR
8058#endif
8059#if NOR + 1 != NSEMI
8060#error NOR + 1 != NSEMI
8061#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008062 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008063 evaltree(
8064 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008065 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008066 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008067 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008068 break;
8069 if (!evalskip) {
8070 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008071 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008072 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008073 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008074 evalfn(n, flags);
8075 break;
8076 }
8077 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008078 }
Eric Andersenc470f442003-07-28 09:56:35 +00008079 case NIF:
8080 evaltree(n->nif.test, EV_TESTED);
8081 if (evalskip)
8082 break;
8083 if (exitstatus == 0) {
8084 n = n->nif.ifpart;
8085 goto evaln;
8086 } else if (n->nif.elsepart) {
8087 n = n->nif.elsepart;
8088 goto evaln;
8089 }
8090 goto success;
8091 case NDEFUN:
8092 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008093 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008094 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008095 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008096 exitstatus = status;
8097 break;
8098 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008099
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008100 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008101 exception_handler = savehandler;
8102 out1:
8103 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008104 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008105 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008106 goto exexit;
8107
8108 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008109 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008110 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008111 }
Eric Andersen62483552001-07-10 06:09:16 +00008112}
8113
Eric Andersenc470f442003-07-28 09:56:35 +00008114#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8115static
8116#endif
8117void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8118
Eric Andersenc470f442003-07-28 09:56:35 +00008119static void
8120evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008121{
8122 int status;
8123
8124 loopnest++;
8125 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008126 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008127 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008128 int i;
8129
Eric Andersencb57d552001-06-28 07:25:16 +00008130 evaltree(n->nbinary.ch1, EV_TESTED);
8131 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008132 skipping:
8133 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008134 evalskip = 0;
8135 continue;
8136 }
8137 if (evalskip == SKIPBREAK && --skipcount <= 0)
8138 evalskip = 0;
8139 break;
8140 }
Eric Andersenc470f442003-07-28 09:56:35 +00008141 i = exitstatus;
8142 if (n->type != NWHILE)
8143 i = !i;
8144 if (i != 0)
8145 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008146 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008147 status = exitstatus;
8148 if (evalskip)
8149 goto skipping;
8150 }
8151 loopnest--;
8152 exitstatus = status;
8153}
8154
Eric Andersenc470f442003-07-28 09:56:35 +00008155static void
8156evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008157{
8158 struct arglist arglist;
8159 union node *argp;
8160 struct strlist *sp;
8161 struct stackmark smark;
8162
8163 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008164 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008165 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008166 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008167 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008168 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008169 if (evalskip)
8170 goto out;
8171 }
8172 *arglist.lastp = NULL;
8173
8174 exitstatus = 0;
8175 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008176 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008177 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008178 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008179 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008180 if (evalskip) {
8181 if (evalskip == SKIPCONT && --skipcount <= 0) {
8182 evalskip = 0;
8183 continue;
8184 }
8185 if (evalskip == SKIPBREAK && --skipcount <= 0)
8186 evalskip = 0;
8187 break;
8188 }
8189 }
8190 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008191 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008192 popstackmark(&smark);
8193}
8194
Eric Andersenc470f442003-07-28 09:56:35 +00008195static void
8196evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008197{
8198 union node *cp;
8199 union node *patp;
8200 struct arglist arglist;
8201 struct stackmark smark;
8202
8203 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008204 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008205 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008206 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008207 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008208 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8209 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008210 if (casematch(patp, arglist.list->text)) {
8211 if (evalskip == 0) {
8212 evaltree(cp->nclist.body, flags);
8213 }
8214 goto out;
8215 }
8216 }
8217 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008218 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008219 popstackmark(&smark);
8220}
8221
Eric Andersenc470f442003-07-28 09:56:35 +00008222/*
8223 * Kick off a subshell to evaluate a tree.
8224 */
Eric Andersenc470f442003-07-28 09:56:35 +00008225static void
8226evalsubshell(union node *n, int flags)
8227{
8228 struct job *jp;
8229 int backgnd = (n->type == NBACKGND);
8230 int status;
8231
8232 expredir(n->nredir.redirect);
8233 if (!backgnd && flags & EV_EXIT && !trap[0])
8234 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008235 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008236 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008237 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008238 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008239 flags |= EV_EXIT;
8240 if (backgnd)
8241 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008242 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008243 redirect(n->nredir.redirect, 0);
8244 evaltreenr(n->nredir.n, flags);
8245 /* never returns */
8246 }
8247 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008248 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008249 status = waitforjob(jp);
8250 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008251 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008252}
8253
Eric Andersenc470f442003-07-28 09:56:35 +00008254/*
8255 * Compute the names of the files in a redirection list.
8256 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008257static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008258static void
8259expredir(union node *n)
8260{
8261 union node *redir;
8262
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008263 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008264 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008265
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008266 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008267 fn.lastp = &fn.list;
8268 switch (redir->type) {
8269 case NFROMTO:
8270 case NFROM:
8271 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008272#if ENABLE_ASH_BASH_COMPAT
8273 case NTO2:
8274#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008275 case NCLOBBER:
8276 case NAPPEND:
8277 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008278#if ENABLE_ASH_BASH_COMPAT
8279 store_expfname:
8280#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008281 redir->nfile.expfname = fn.list->text;
8282 break;
8283 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008284 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008285 if (redir->ndup.vname) {
8286 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008287 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008288 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008289#if ENABLE_ASH_BASH_COMPAT
8290//FIXME: we used expandarg with different args!
8291 if (!isdigit_str9(fn.list->text)) {
8292 /* >&file, not >&fd */
8293 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8294 ash_msg_and_raise_error("redir error");
8295 redir->type = NTO2;
8296 goto store_expfname;
8297 }
8298#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008299 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008300 }
8301 break;
8302 }
8303 }
8304}
8305
Eric Andersencb57d552001-06-28 07:25:16 +00008306/*
Eric Andersencb57d552001-06-28 07:25:16 +00008307 * Evaluate a pipeline. All the processes in the pipeline are children
8308 * of the process creating the pipeline. (This differs from some versions
8309 * of the shell, which make the last process in a pipeline the parent
8310 * of all the rest.)
8311 */
Eric Andersenc470f442003-07-28 09:56:35 +00008312static void
8313evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008314{
8315 struct job *jp;
8316 struct nodelist *lp;
8317 int pipelen;
8318 int prevfd;
8319 int pip[2];
8320
Eric Andersenc470f442003-07-28 09:56:35 +00008321 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008322 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008323 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008324 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008325 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008326 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008327 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008328 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008329 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008330 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008331 pip[1] = -1;
8332 if (lp->next) {
8333 if (pipe(pip) < 0) {
8334 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008335 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008336 }
8337 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008338 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008339 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008340 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008341 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008342 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008343 if (prevfd > 0) {
8344 dup2(prevfd, 0);
8345 close(prevfd);
8346 }
8347 if (pip[1] > 1) {
8348 dup2(pip[1], 1);
8349 close(pip[1]);
8350 }
Eric Andersenc470f442003-07-28 09:56:35 +00008351 evaltreenr(lp->n, flags);
8352 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008353 }
8354 if (prevfd >= 0)
8355 close(prevfd);
8356 prevfd = pip[0];
8357 close(pip[1]);
8358 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008359 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008360 exitstatus = waitforjob(jp);
8361 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008362 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008363 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008364}
8365
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008366/*
8367 * Controls whether the shell is interactive or not.
8368 */
8369static void
8370setinteractive(int on)
8371{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008372 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008373
8374 if (++on == is_interactive)
8375 return;
8376 is_interactive = on;
8377 setsignal(SIGINT);
8378 setsignal(SIGQUIT);
8379 setsignal(SIGTERM);
8380#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8381 if (is_interactive > 1) {
8382 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008383 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008384
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008385 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008386 out1fmt(
8387 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008388 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008389 "Enter 'help' for a list of built-in commands."
8390 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008391 bb_banner);
8392 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008393 }
8394 }
8395#endif
8396}
8397
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008398static void
8399optschanged(void)
8400{
8401#if DEBUG
8402 opentrace();
8403#endif
8404 setinteractive(iflag);
8405 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008406#if ENABLE_FEATURE_EDITING_VI
8407 if (viflag)
8408 line_input_state->flags |= VI_MODE;
8409 else
8410 line_input_state->flags &= ~VI_MODE;
8411#else
8412 viflag = 0; /* forcibly keep the option off */
8413#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008414}
8415
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008416static struct localvar *localvars;
8417
8418/*
8419 * Called after a function returns.
8420 * Interrupts must be off.
8421 */
8422static void
8423poplocalvars(void)
8424{
8425 struct localvar *lvp;
8426 struct var *vp;
8427
8428 while ((lvp = localvars) != NULL) {
8429 localvars = lvp->next;
8430 vp = lvp->vp;
8431 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8432 if (vp == NULL) { /* $- saved */
8433 memcpy(optlist, lvp->text, sizeof(optlist));
8434 free((char*)lvp->text);
8435 optschanged();
8436 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8437 unsetvar(vp->text);
8438 } else {
8439 if (vp->func)
8440 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8441 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8442 free((char*)vp->text);
8443 vp->flags = lvp->flags;
8444 vp->text = lvp->text;
8445 }
8446 free(lvp);
8447 }
8448}
8449
8450static int
8451evalfun(struct funcnode *func, int argc, char **argv, int flags)
8452{
8453 volatile struct shparam saveparam;
8454 struct localvar *volatile savelocalvars;
8455 struct jmploc *volatile savehandler;
8456 struct jmploc jmploc;
8457 int e;
8458
8459 saveparam = shellparam;
8460 savelocalvars = localvars;
8461 e = setjmp(jmploc.loc);
8462 if (e) {
8463 goto funcdone;
8464 }
8465 INT_OFF;
8466 savehandler = exception_handler;
8467 exception_handler = &jmploc;
8468 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008469 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008470 func->count++;
8471 funcnest++;
8472 INT_ON;
8473 shellparam.nparam = argc - 1;
8474 shellparam.p = argv + 1;
8475#if ENABLE_ASH_GETOPTS
8476 shellparam.optind = 1;
8477 shellparam.optoff = -1;
8478#endif
8479 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008480 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008481 INT_OFF;
8482 funcnest--;
8483 freefunc(func);
8484 poplocalvars();
8485 localvars = savelocalvars;
8486 freeparam(&shellparam);
8487 shellparam = saveparam;
8488 exception_handler = savehandler;
8489 INT_ON;
8490 evalskip &= ~SKIPFUNC;
8491 return e;
8492}
8493
Denis Vlasenko131ae172007-02-18 13:00:19 +00008494#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008495static char **
8496parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008497{
8498 char *cp, c;
8499
8500 for (;;) {
8501 cp = *++argv;
8502 if (!cp)
8503 return 0;
8504 if (*cp++ != '-')
8505 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008506 c = *cp++;
8507 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008508 break;
8509 if (c == '-' && !*cp) {
8510 argv++;
8511 break;
8512 }
8513 do {
8514 switch (c) {
8515 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008516 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008517 break;
8518 default:
8519 /* run 'typecmd' for other options */
8520 return 0;
8521 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008522 c = *cp++;
8523 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008524 }
8525 return argv;
8526}
8527#endif
8528
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008529/*
8530 * Make a variable a local variable. When a variable is made local, it's
8531 * value and flags are saved in a localvar structure. The saved values
8532 * will be restored when the shell function returns. We handle the name
8533 * "-" as a special case.
8534 */
8535static void
8536mklocal(char *name)
8537{
8538 struct localvar *lvp;
8539 struct var **vpp;
8540 struct var *vp;
8541
8542 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008543 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008544 if (LONE_DASH(name)) {
8545 char *p;
8546 p = ckmalloc(sizeof(optlist));
8547 lvp->text = memcpy(p, optlist, sizeof(optlist));
8548 vp = NULL;
8549 } else {
8550 char *eq;
8551
8552 vpp = hashvar(name);
8553 vp = *findvar(vpp, name);
8554 eq = strchr(name, '=');
8555 if (vp == NULL) {
8556 if (eq)
8557 setvareq(name, VSTRFIXED);
8558 else
8559 setvar(name, NULL, VSTRFIXED);
8560 vp = *vpp; /* the new variable */
8561 lvp->flags = VUNSET;
8562 } else {
8563 lvp->text = vp->text;
8564 lvp->flags = vp->flags;
8565 vp->flags |= VSTRFIXED|VTEXTFIXED;
8566 if (eq)
8567 setvareq(name, 0);
8568 }
8569 }
8570 lvp->vp = vp;
8571 lvp->next = localvars;
8572 localvars = lvp;
8573 INT_ON;
8574}
8575
8576/*
8577 * The "local" command.
8578 */
8579static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008580localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008581{
8582 char *name;
8583
8584 argv = argptr;
8585 while ((name = *argv++) != NULL) {
8586 mklocal(name);
8587 }
8588 return 0;
8589}
8590
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008591static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008592falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008593{
8594 return 1;
8595}
8596
8597static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008598truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008599{
8600 return 0;
8601}
8602
8603static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008604execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008605{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008606 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008607 iflag = 0; /* exit on error */
8608 mflag = 0;
8609 optschanged();
8610 shellexec(argv + 1, pathval(), 0);
8611 }
8612 return 0;
8613}
8614
8615/*
8616 * The return command.
8617 */
8618static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008619returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008620{
8621 /*
8622 * If called outside a function, do what ksh does;
8623 * skip the rest of the file.
8624 */
8625 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8626 return argv[1] ? number(argv[1]) : exitstatus;
8627}
8628
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008629/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008630static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008631static int dotcmd(int, char **);
8632static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008633static int exitcmd(int, char **);
8634static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008635#if ENABLE_ASH_GETOPTS
8636static int getoptscmd(int, char **);
8637#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008638#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008639static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008640#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008641#if ENABLE_ASH_MATH_SUPPORT
8642static int letcmd(int, char **);
8643#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008644static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008645static int setcmd(int, char **);
8646static int shiftcmd(int, char **);
8647static int timescmd(int, char **);
8648static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008649static int umaskcmd(int, char **);
8650static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008651static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008652
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008653#define BUILTIN_NOSPEC "0"
8654#define BUILTIN_SPECIAL "1"
8655#define BUILTIN_REGULAR "2"
8656#define BUILTIN_SPEC_REG "3"
8657#define BUILTIN_ASSIGN "4"
8658#define BUILTIN_SPEC_ASSG "5"
8659#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008660#define BUILTIN_SPEC_REG_ASSG "7"
8661
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008662/* We do not handle [[ expr ]] bashism bash-compatibly,
8663 * we make it a synonym of [ expr ].
8664 * Basically, word splitting and pathname expansion should NOT be performed
8665 * Examples:
8666 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8667 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8668 * Additional operators:
8669 * || and && should work as -o and -a
8670 * =~ regexp match
8671 * Apart from the above, [[ expr ]] should work as [ expr ]
8672 */
8673
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008674#define echocmd echo_main
8675#define printfcmd printf_main
8676#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008677
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008678/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008679static const struct builtincmd builtintab[] = {
8680 { BUILTIN_SPEC_REG ".", dotcmd },
8681 { BUILTIN_SPEC_REG ":", truecmd },
8682#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008683 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008684#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008685 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008686#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008687#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008688#if ENABLE_ASH_ALIAS
8689 { BUILTIN_REG_ASSG "alias", aliascmd },
8690#endif
8691#if JOBS
8692 { BUILTIN_REGULAR "bg", fg_bgcmd },
8693#endif
8694 { BUILTIN_SPEC_REG "break", breakcmd },
8695 { BUILTIN_REGULAR "cd", cdcmd },
8696 { BUILTIN_NOSPEC "chdir", cdcmd },
8697#if ENABLE_ASH_CMDCMD
8698 { BUILTIN_REGULAR "command", commandcmd },
8699#endif
8700 { BUILTIN_SPEC_REG "continue", breakcmd },
8701#if ENABLE_ASH_BUILTIN_ECHO
8702 { BUILTIN_REGULAR "echo", echocmd },
8703#endif
8704 { BUILTIN_SPEC_REG "eval", evalcmd },
8705 { BUILTIN_SPEC_REG "exec", execcmd },
8706 { BUILTIN_SPEC_REG "exit", exitcmd },
8707 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8708 { BUILTIN_REGULAR "false", falsecmd },
8709#if JOBS
8710 { BUILTIN_REGULAR "fg", fg_bgcmd },
8711#endif
8712#if ENABLE_ASH_GETOPTS
8713 { BUILTIN_REGULAR "getopts", getoptscmd },
8714#endif
8715 { BUILTIN_NOSPEC "hash", hashcmd },
8716#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8717 { BUILTIN_NOSPEC "help", helpcmd },
8718#endif
8719#if JOBS
8720 { BUILTIN_REGULAR "jobs", jobscmd },
8721 { BUILTIN_REGULAR "kill", killcmd },
8722#endif
8723#if ENABLE_ASH_MATH_SUPPORT
8724 { BUILTIN_NOSPEC "let", letcmd },
8725#endif
8726 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008727#if ENABLE_ASH_BUILTIN_PRINTF
8728 { BUILTIN_REGULAR "printf", printfcmd },
8729#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008730 { BUILTIN_NOSPEC "pwd", pwdcmd },
8731 { BUILTIN_REGULAR "read", readcmd },
8732 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8733 { BUILTIN_SPEC_REG "return", returncmd },
8734 { BUILTIN_SPEC_REG "set", setcmd },
8735 { BUILTIN_SPEC_REG "shift", shiftcmd },
8736 { BUILTIN_SPEC_REG "source", dotcmd },
8737#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008738 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008739#endif
8740 { BUILTIN_SPEC_REG "times", timescmd },
8741 { BUILTIN_SPEC_REG "trap", trapcmd },
8742 { BUILTIN_REGULAR "true", truecmd },
8743 { BUILTIN_NOSPEC "type", typecmd },
8744 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8745 { BUILTIN_REGULAR "umask", umaskcmd },
8746#if ENABLE_ASH_ALIAS
8747 { BUILTIN_REGULAR "unalias", unaliascmd },
8748#endif
8749 { BUILTIN_SPEC_REG "unset", unsetcmd },
8750 { BUILTIN_REGULAR "wait", waitcmd },
8751};
8752
Denis Vlasenko80591b02008-03-25 07:49:43 +00008753/* Should match the above table! */
8754#define COMMANDCMD (builtintab + \
8755 2 + \
8756 1 * ENABLE_ASH_BUILTIN_TEST + \
8757 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8758 1 * ENABLE_ASH_ALIAS + \
8759 1 * ENABLE_ASH_JOB_CONTROL + \
8760 3)
8761#define EXECCMD (builtintab + \
8762 2 + \
8763 1 * ENABLE_ASH_BUILTIN_TEST + \
8764 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8765 1 * ENABLE_ASH_ALIAS + \
8766 1 * ENABLE_ASH_JOB_CONTROL + \
8767 3 + \
8768 1 * ENABLE_ASH_CMDCMD + \
8769 1 + \
8770 ENABLE_ASH_BUILTIN_ECHO + \
8771 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008772
8773/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008774 * Search the table of builtin commands.
8775 */
8776static struct builtincmd *
8777find_builtin(const char *name)
8778{
8779 struct builtincmd *bp;
8780
8781 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008782 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008783 pstrcmp
8784 );
8785 return bp;
8786}
8787
8788/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008789 * Execute a simple command.
8790 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008791static int
8792isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008793{
8794 const char *q = endofname(p);
8795 if (p == q)
8796 return 0;
8797 return *q == '=';
8798}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008799static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008800bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008801{
8802 /* Preserve exitstatus of a previous possible redirection
8803 * as POSIX mandates */
8804 return back_exitstatus;
8805}
Eric Andersenc470f442003-07-28 09:56:35 +00008806static void
8807evalcommand(union node *cmd, int flags)
8808{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008809 static const struct builtincmd null_bltin = {
8810 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008811 };
Eric Andersenc470f442003-07-28 09:56:35 +00008812 struct stackmark smark;
8813 union node *argp;
8814 struct arglist arglist;
8815 struct arglist varlist;
8816 char **argv;
8817 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008818 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008819 struct cmdentry cmdentry;
8820 struct job *jp;
8821 char *lastarg;
8822 const char *path;
8823 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008824 int status;
8825 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008826 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008827 smallint cmd_is_exec;
8828 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008829
8830 /* First expand the arguments. */
8831 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8832 setstackmark(&smark);
8833 back_exitstatus = 0;
8834
8835 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008836 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008837 varlist.lastp = &varlist.list;
8838 *varlist.lastp = NULL;
8839 arglist.lastp = &arglist.list;
8840 *arglist.lastp = NULL;
8841
8842 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008843 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008844 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8845 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8846 }
8847
Eric Andersenc470f442003-07-28 09:56:35 +00008848 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8849 struct strlist **spp;
8850
8851 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008852 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008853 expandarg(argp, &arglist, EXP_VARTILDE);
8854 else
8855 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8856
Eric Andersenc470f442003-07-28 09:56:35 +00008857 for (sp = *spp; sp; sp = sp->next)
8858 argc++;
8859 }
8860
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008861 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008862 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008863 TRACE(("evalcommand arg: %s\n", sp->text));
8864 *nargv++ = sp->text;
8865 }
8866 *nargv = NULL;
8867
8868 lastarg = NULL;
8869 if (iflag && funcnest == 0 && argc > 0)
8870 lastarg = nargv[-1];
8871
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008872 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008873 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008874 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008875
8876 path = vpath.text;
8877 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8878 struct strlist **spp;
8879 char *p;
8880
8881 spp = varlist.lastp;
8882 expandarg(argp, &varlist, EXP_VARTILDE);
8883
8884 /*
8885 * Modify the command lookup path, if a PATH= assignment
8886 * is present
8887 */
8888 p = (*spp)->text;
8889 if (varequal(p, path))
8890 path = p;
8891 }
8892
8893 /* Print the command if xflag is set. */
8894 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008895 int n;
8896 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008897
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008898 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008899 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008900
8901 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008902 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008903 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008904 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008905 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008906 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008907 p--;
8908 }
8909 }
8910 sp = arglist.list;
8911 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008912 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008913 }
8914
8915 cmd_is_exec = 0;
8916 spclbltin = -1;
8917
8918 /* Now locate the command. */
8919 if (argc) {
8920 const char *oldpath;
8921 int cmd_flag = DO_ERR;
8922
8923 path += 5;
8924 oldpath = path;
8925 for (;;) {
8926 find_command(argv[0], &cmdentry, cmd_flag, path);
8927 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008928 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008929 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008930 goto bail;
8931 }
8932
8933 /* implement bltin and command here */
8934 if (cmdentry.cmdtype != CMDBUILTIN)
8935 break;
8936 if (spclbltin < 0)
8937 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8938 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008939 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008940#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008941 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008942 path = oldpath;
8943 nargv = parse_command_args(argv, &path);
8944 if (!nargv)
8945 break;
8946 argc -= nargv - argv;
8947 argv = nargv;
8948 cmd_flag |= DO_NOFUNC;
8949 } else
8950#endif
8951 break;
8952 }
8953 }
8954
8955 if (status) {
8956 /* We have a redirection error. */
8957 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008958 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008959 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008960 exitstatus = status;
8961 goto out;
8962 }
8963
8964 /* Execute the command. */
8965 switch (cmdentry.cmdtype) {
8966 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008967
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008968#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008969/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8970 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008971 {
8972 /* find_command() encodes applet_no as (-2 - applet_no) */
8973 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008974 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008975 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008976 /* run <applet>_main() */
8977 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008978 break;
8979 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008980 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008981#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008982 /* Fork off a child process if necessary. */
8983 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008984 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008985 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008986 if (forkshell(jp, cmd, FORK_FG) != 0) {
8987 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008988 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008989 break;
8990 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008991 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008992 }
8993 listsetvar(varlist.list, VEXPORT|VSTACK);
8994 shellexec(argv, path, cmdentry.u.index);
8995 /* NOTREACHED */
8996
8997 case CMDBUILTIN:
8998 cmdenviron = varlist.list;
8999 if (cmdenviron) {
9000 struct strlist *list = cmdenviron;
9001 int i = VNOSET;
9002 if (spclbltin > 0 || argc == 0) {
9003 i = 0;
9004 if (cmd_is_exec && argc > 1)
9005 i = VEXPORT;
9006 }
9007 listsetvar(list, i);
9008 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009009 /* Tight loop with builtins only:
9010 * "while kill -0 $child; do true; done"
9011 * will never exit even if $child died, unless we do this
9012 * to reap the zombie and make kill detect that it's gone: */
9013 dowait(DOWAIT_NONBLOCK, NULL);
9014
Eric Andersenc470f442003-07-28 09:56:35 +00009015 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9016 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009017 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009018 if (i == EXEXIT)
9019 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009020 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009021 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009022 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009023 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009024 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009025 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009026 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009027 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009028 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009029 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009030 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009031 }
9032 break;
9033
9034 case CMDFUNCTION:
9035 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009036 /* See above for the rationale */
9037 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009038 if (evalfun(cmdentry.u.func, argc, argv, flags))
9039 goto raise;
9040 break;
9041 }
9042
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009043 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009044 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009045 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009046 /* dsl: I think this is intended to be used to support
9047 * '_' in 'vi' command mode during line editing...
9048 * However I implemented that within libedit itself.
9049 */
9050 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009051 }
Eric Andersenc470f442003-07-28 09:56:35 +00009052 popstackmark(&smark);
9053}
9054
9055static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009056evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9057{
Eric Andersenc470f442003-07-28 09:56:35 +00009058 char *volatile savecmdname;
9059 struct jmploc *volatile savehandler;
9060 struct jmploc jmploc;
9061 int i;
9062
9063 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009064 i = setjmp(jmploc.loc);
9065 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009066 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009067 savehandler = exception_handler;
9068 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009069 commandname = argv[0];
9070 argptr = argv + 1;
9071 optptr = NULL; /* initialize nextopt */
9072 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009073 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009074 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009075 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009076 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009077 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009078// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009079 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009080
9081 return i;
9082}
9083
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009084static int
9085goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009086{
9087 return !*endofname(p);
9088}
9089
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009090
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009091/*
9092 * Search for a command. This is called before we fork so that the
9093 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009094 * the child. The check for "goodname" is an overly conservative
9095 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009096 */
Eric Andersenc470f442003-07-28 09:56:35 +00009097static void
9098prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009099{
9100 struct cmdentry entry;
9101
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009102 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9103 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009104}
9105
Eric Andersencb57d552001-06-28 07:25:16 +00009106
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009107/* ============ Builtin commands
9108 *
9109 * Builtin commands whose functions are closely tied to evaluation
9110 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009111 */
9112
9113/*
Eric Andersencb57d552001-06-28 07:25:16 +00009114 * Handle break and continue commands. Break, continue, and return are
9115 * all handled by setting the evalskip flag. The evaluation routines
9116 * above all check this flag, and if it is set they start skipping
9117 * commands rather than executing them. The variable skipcount is
9118 * the number of loops to break/continue, or the number of function
9119 * levels to return. (The latter is always 1.) It should probably
9120 * be an error to break out of more loops than exist, but it isn't
9121 * in the standard shell so we don't make it one here.
9122 */
Eric Andersenc470f442003-07-28 09:56:35 +00009123static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009124breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009125{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009126 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009127
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009128 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009129 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009130 if (n > loopnest)
9131 n = loopnest;
9132 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009133 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009134 skipcount = n;
9135 }
9136 return 0;
9137}
9138
Eric Andersenc470f442003-07-28 09:56:35 +00009139
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009140/* ============ input.c
9141 *
Eric Andersen90898442003-08-06 11:20:52 +00009142 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009143 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009144
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009145enum {
9146 INPUT_PUSH_FILE = 1,
9147 INPUT_NOFILE_OK = 2,
9148};
Eric Andersencb57d552001-06-28 07:25:16 +00009149
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009150static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009151/* values of checkkwd variable */
9152#define CHKALIAS 0x1
9153#define CHKKWD 0x2
9154#define CHKNL 0x4
9155
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009156/*
9157 * Push a string back onto the input at this current parsefile level.
9158 * We handle aliases this way.
9159 */
9160#if !ENABLE_ASH_ALIAS
9161#define pushstring(s, ap) pushstring(s)
9162#endif
9163static void
9164pushstring(char *s, struct alias *ap)
9165{
9166 struct strpush *sp;
9167 int len;
9168
9169 len = strlen(s);
9170 INT_OFF;
9171 if (g_parsefile->strpush) {
9172 sp = ckzalloc(sizeof(*sp));
9173 sp->prev = g_parsefile->strpush;
9174 } else {
9175 sp = &(g_parsefile->basestrpush);
9176 }
9177 g_parsefile->strpush = sp;
9178 sp->prev_string = g_parsefile->next_to_pgetc;
9179 sp->prev_left_in_line = g_parsefile->left_in_line;
9180#if ENABLE_ASH_ALIAS
9181 sp->ap = ap;
9182 if (ap) {
9183 ap->flag |= ALIASINUSE;
9184 sp->string = s;
9185 }
9186#endif
9187 g_parsefile->next_to_pgetc = s;
9188 g_parsefile->left_in_line = len;
9189 INT_ON;
9190}
9191
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009192static void
9193popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009194{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009195 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009196
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009197 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009198#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009199 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009200 if (g_parsefile->next_to_pgetc[-1] == ' '
9201 || g_parsefile->next_to_pgetc[-1] == '\t'
9202 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009204 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205 if (sp->string != sp->ap->val) {
9206 free(sp->string);
9207 }
9208 sp->ap->flag &= ~ALIASINUSE;
9209 if (sp->ap->flag & ALIASDEAD) {
9210 unalias(sp->ap->name);
9211 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009212 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009213#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009214 g_parsefile->next_to_pgetc = sp->prev_string;
9215 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009216 g_parsefile->strpush = sp->prev;
9217 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218 free(sp);
9219 INT_ON;
9220}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009221
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009222//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9223//it peeks whether it is &>, and then pushes back both chars.
9224//This function needs to save last *next_to_pgetc to buf[0]
9225//to make two pungetc() reliable. Currently,
9226// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009227static int
9228preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009229{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009230 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009231 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009232
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009233 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009234#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009235 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009236 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009237 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009238 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009239#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009240 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009241#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009242 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9243 if (nr == 0) {
9244 /* Ctrl+C pressed */
9245 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009246 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009247 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009248 raise(SIGINT);
9249 return 1;
9250 }
Eric Andersenc470f442003-07-28 09:56:35 +00009251 goto retry;
9252 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009253 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009254 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009255 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009256 }
Eric Andersencb57d552001-06-28 07:25:16 +00009257 }
9258#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009259 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009260#endif
9261
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009262#if 0
9263/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009264 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009265 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009266 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009267 if (flags >= 0 && (flags & O_NONBLOCK)) {
9268 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009269 if (fcntl(0, F_SETFL, flags) >= 0) {
9270 out2str("sh: turning off NDELAY mode\n");
9271 goto retry;
9272 }
9273 }
9274 }
9275 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009276#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009277 return nr;
9278}
9279
9280/*
9281 * Refill the input buffer and return the next input character:
9282 *
9283 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009284 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9285 * or we are reading from a string so we can't refill the buffer,
9286 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009287 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9288 * 4) Process input up to the next newline, deleting nul characters.
9289 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009290//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9291#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009292/*
9293 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9294 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9295 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9296 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9297 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009298static int
Eric Andersenc470f442003-07-28 09:56:35 +00009299preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009300{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009301 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009302 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009303
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009304 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009305#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009306 if (g_parsefile->left_in_line == -1
9307 && g_parsefile->strpush->ap
9308 && g_parsefile->next_to_pgetc[-1] != ' '
9309 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009310 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009311 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009312 return PEOA;
9313 }
Eric Andersen2870d962001-07-02 17:27:21 +00009314#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009315 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009316 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009317 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9318 g_parsefile->left_in_line,
9319 g_parsefile->next_to_pgetc,
9320 g_parsefile->next_to_pgetc);
9321 if (--g_parsefile->left_in_line >= 0)
9322 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009323 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009324 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009325 * "pgetc" needs refilling.
9326 */
9327
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009328 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009329 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009330 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009331 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009332 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009333 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009334 /* even in failure keep left_in_line and next_to_pgetc
9335 * in lock step, for correct multi-layer pungetc.
9336 * left_in_line was decremented before preadbuffer(),
9337 * must inc next_to_pgetc: */
9338 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009339 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009340 }
Eric Andersencb57d552001-06-28 07:25:16 +00009341
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009342 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009343 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009344 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009345 again:
9346 more = preadfd();
9347 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009348 /* don't try reading again */
9349 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009350 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009351 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009352 return PEOF;
9353 }
9354 }
9355
Denis Vlasenko727752d2008-11-28 03:41:47 +00009356 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009357 * Set g_parsefile->left_in_line
9358 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009359 * NUL chars are deleted.
9360 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009361 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009362 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009363 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009364
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009365 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009366
Denis Vlasenko727752d2008-11-28 03:41:47 +00009367 c = *q;
9368 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009369 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009370 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009371 q++;
9372 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009373 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009374 break;
9375 }
Eric Andersencb57d552001-06-28 07:25:16 +00009376 }
9377
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009378 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009379 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9380 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009381 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009382 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009383 }
9384 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009385 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009386
Eric Andersencb57d552001-06-28 07:25:16 +00009387 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009388 char save = *q;
9389 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009390 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009391 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009392 }
9393
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009394 pgetc_debug("preadbuffer at %d:%p'%s'",
9395 g_parsefile->left_in_line,
9396 g_parsefile->next_to_pgetc,
9397 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009398 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009399}
9400
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009401#define pgetc_as_macro() \
9402 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009403 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009404 : preadbuffer() \
9405 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009406
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009407static int
9408pgetc(void)
9409{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009410 pgetc_debug("pgetc_fast at %d:%p'%s'",
9411 g_parsefile->left_in_line,
9412 g_parsefile->next_to_pgetc,
9413 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009414 return pgetc_as_macro();
9415}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009416
9417#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009418#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009419#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009420#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009421#endif
9422
9423/*
9424 * Same as pgetc(), but ignores PEOA.
9425 */
9426#if ENABLE_ASH_ALIAS
9427static int
9428pgetc2(void)
9429{
9430 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009431 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009432 pgetc_debug("pgetc_fast at %d:%p'%s'",
9433 g_parsefile->left_in_line,
9434 g_parsefile->next_to_pgetc,
9435 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009436 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009437 } while (c == PEOA);
9438 return c;
9439}
9440#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009441#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009442#endif
9443
9444/*
9445 * Read a line from the script.
9446 */
9447static char *
9448pfgets(char *line, int len)
9449{
9450 char *p = line;
9451 int nleft = len;
9452 int c;
9453
9454 while (--nleft > 0) {
9455 c = pgetc2();
9456 if (c == PEOF) {
9457 if (p == line)
9458 return NULL;
9459 break;
9460 }
9461 *p++ = c;
9462 if (c == '\n')
9463 break;
9464 }
9465 *p = '\0';
9466 return line;
9467}
9468
Eric Andersenc470f442003-07-28 09:56:35 +00009469/*
9470 * Undo the last call to pgetc. Only one character may be pushed back.
9471 * PEOF may be pushed back.
9472 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009473static void
Eric Andersenc470f442003-07-28 09:56:35 +00009474pungetc(void)
9475{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009476 g_parsefile->left_in_line++;
9477 g_parsefile->next_to_pgetc--;
9478 pgetc_debug("pushed back to %d:%p'%s'",
9479 g_parsefile->left_in_line,
9480 g_parsefile->next_to_pgetc,
9481 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009482}
9483
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009484/*
9485 * To handle the "." command, a stack of input files is used. Pushfile
9486 * adds a new entry to the stack and popfile restores the previous level.
9487 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009489pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009490{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009491 struct parsefile *pf;
9492
Denis Vlasenko597906c2008-02-20 16:38:54 +00009493 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009494 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009495 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009496 /*pf->strpush = NULL; - ckzalloc did it */
9497 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009498 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009499}
9500
9501static void
9502popfile(void)
9503{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009504 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009505
Denis Vlasenkob012b102007-02-19 22:43:01 +00009506 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009507 if (pf->fd >= 0)
9508 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009509 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009510 while (pf->strpush)
9511 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009512 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009513 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009514 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009515}
9516
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009517/*
9518 * Return to top level.
9519 */
9520static void
9521popallfiles(void)
9522{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009523 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009524 popfile();
9525}
9526
9527/*
9528 * Close the file(s) that the shell is reading commands from. Called
9529 * after a fork is done.
9530 */
9531static void
9532closescript(void)
9533{
9534 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009535 if (g_parsefile->fd > 0) {
9536 close(g_parsefile->fd);
9537 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009538 }
9539}
9540
9541/*
9542 * Like setinputfile, but takes an open file descriptor. Call this with
9543 * interrupts off.
9544 */
9545static void
9546setinputfd(int fd, int push)
9547{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009548 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009549 if (push) {
9550 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009551 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009552 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009553 g_parsefile->fd = fd;
9554 if (g_parsefile->buf == NULL)
9555 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009556 g_parsefile->left_in_buffer = 0;
9557 g_parsefile->left_in_line = 0;
9558 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009559}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009560
Eric Andersenc470f442003-07-28 09:56:35 +00009561/*
9562 * Set the input to take input from a file. If push is set, push the
9563 * old input onto the stack first.
9564 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009565static int
9566setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009567{
9568 int fd;
9569 int fd2;
9570
Denis Vlasenkob012b102007-02-19 22:43:01 +00009571 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009572 fd = open(fname, O_RDONLY);
9573 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009574 if (flags & INPUT_NOFILE_OK)
9575 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009576 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009577 }
Eric Andersenc470f442003-07-28 09:56:35 +00009578 if (fd < 10) {
9579 fd2 = copyfd(fd, 10);
9580 close(fd);
9581 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009582 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009583 fd = fd2;
9584 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009585 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009586 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009587 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009588 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009589}
9590
Eric Andersencb57d552001-06-28 07:25:16 +00009591/*
9592 * Like setinputfile, but takes input from a string.
9593 */
Eric Andersenc470f442003-07-28 09:56:35 +00009594static void
9595setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009596{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009597 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009598 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009599 g_parsefile->next_to_pgetc = string;
9600 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009601 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009602 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009603 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009604}
9605
9606
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009607/* ============ mail.c
9608 *
9609 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009610 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009611
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009612#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009613
Eric Andersencb57d552001-06-28 07:25:16 +00009614#define MAXMBOXES 10
9615
Eric Andersenc470f442003-07-28 09:56:35 +00009616/* times of mailboxes */
9617static time_t mailtime[MAXMBOXES];
9618/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009619static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009620
Eric Andersencb57d552001-06-28 07:25:16 +00009621/*
Eric Andersenc470f442003-07-28 09:56:35 +00009622 * Print appropriate message(s) if mail has arrived.
9623 * If mail_var_path_changed is set,
9624 * then the value of MAIL has mail_var_path_changed,
9625 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009626 */
Eric Andersenc470f442003-07-28 09:56:35 +00009627static void
9628chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009629{
Eric Andersencb57d552001-06-28 07:25:16 +00009630 const char *mpath;
9631 char *p;
9632 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009633 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009634 struct stackmark smark;
9635 struct stat statb;
9636
Eric Andersencb57d552001-06-28 07:25:16 +00009637 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009638 mpath = mpathset() ? mpathval() : mailval();
9639 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009640 p = padvance(&mpath, nullstr);
9641 if (p == NULL)
9642 break;
9643 if (*p == '\0')
9644 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009645 for (q = p; *q; q++)
9646 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009647#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009648 if (q[-1] != '/')
9649 abort();
9650#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009651 q[-1] = '\0'; /* delete trailing '/' */
9652 if (stat(p, &statb) < 0) {
9653 *mtp = 0;
9654 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009655 }
Eric Andersenc470f442003-07-28 09:56:35 +00009656 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9657 fprintf(
9658 stderr, snlfmt,
9659 pathopt ? pathopt : "you have mail"
9660 );
9661 }
9662 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009663 }
Eric Andersenc470f442003-07-28 09:56:35 +00009664 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009665 popstackmark(&smark);
9666}
Eric Andersencb57d552001-06-28 07:25:16 +00009667
Eric Andersenc470f442003-07-28 09:56:35 +00009668static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009669changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009670{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009671 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009672}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009673
Denis Vlasenko131ae172007-02-18 13:00:19 +00009674#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009675
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009676
9677/* ============ ??? */
9678
Eric Andersencb57d552001-06-28 07:25:16 +00009679/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009680 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009681 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009682static void
9683setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009684{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009685 char **newparam;
9686 char **ap;
9687 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009688
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009689 for (nparam = 0; argv[nparam]; nparam++)
9690 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009691 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9692 while (*argv) {
9693 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009694 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009695 *ap = NULL;
9696 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009697 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009698 shellparam.nparam = nparam;
9699 shellparam.p = newparam;
9700#if ENABLE_ASH_GETOPTS
9701 shellparam.optind = 1;
9702 shellparam.optoff = -1;
9703#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009704}
9705
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009706/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009707 * Process shell options. The global variable argptr contains a pointer
9708 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009709 *
9710 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9711 * For a non-interactive shell, an error condition encountered
9712 * by a special built-in ... shall cause the shell to write a diagnostic message
9713 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009714 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009715 * ...
9716 * Utility syntax error (option or operand error) Shall exit
9717 * ...
9718 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9719 * we see that bash does not do that (set "finishes" with error code 1 instead,
9720 * and shell continues), and people rely on this behavior!
9721 * Testcase:
9722 * set -o barfoo 2>/dev/null
9723 * echo $?
9724 *
9725 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009726 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009727static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009728plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009729{
9730 int i;
9731
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009732 if (name) {
9733 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009734 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009735 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009736 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009737 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009738 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009739 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009740 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009741 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009742 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009743 if (val) {
9744 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9745 } else {
9746 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9747 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009748 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009749 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009750}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009751static void
9752setoption(int flag, int val)
9753{
9754 int i;
9755
9756 for (i = 0; i < NOPTS; i++) {
9757 if (optletters(i) == flag) {
9758 optlist[i] = val;
9759 return;
9760 }
9761 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009762 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009763 /* NOTREACHED */
9764}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009765static int
Eric Andersenc470f442003-07-28 09:56:35 +00009766options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009767{
9768 char *p;
9769 int val;
9770 int c;
9771
9772 if (cmdline)
9773 minusc = NULL;
9774 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009775 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009776 if (c != '-' && c != '+')
9777 break;
9778 argptr++;
9779 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009780 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009781 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009782 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009783 if (!cmdline) {
9784 /* "-" means turn off -x and -v */
9785 if (p[0] == '\0')
9786 xflag = vflag = 0;
9787 /* "--" means reset params */
9788 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009789 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009790 }
Eric Andersenc470f442003-07-28 09:56:35 +00009791 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009792 }
Eric Andersencb57d552001-06-28 07:25:16 +00009793 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009794 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009795 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009796 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009797 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009798 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009799 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009800 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009801 /* it already printed err message */
9802 return 1; /* error */
9803 }
Eric Andersencb57d552001-06-28 07:25:16 +00009804 if (*argptr)
9805 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009806 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9807 isloginsh = 1;
9808 /* bash does not accept +-login, we also won't */
9809 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009810 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009811 isloginsh = 1;
9812 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 } else {
9814 setoption(c, val);
9815 }
9816 }
9817 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009818 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009819}
9820
Eric Andersencb57d552001-06-28 07:25:16 +00009821/*
Eric Andersencb57d552001-06-28 07:25:16 +00009822 * The shift builtin command.
9823 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009824static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009825shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009826{
9827 int n;
9828 char **ap1, **ap2;
9829
9830 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009831 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009832 n = number(argv[1]);
9833 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009834 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009835 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009836 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009837 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009838 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009839 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009840 }
9841 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009842 while ((*ap2++ = *ap1++) != NULL)
9843 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009844#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009845 shellparam.optind = 1;
9846 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009847#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009848 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009849 return 0;
9850}
9851
Eric Andersencb57d552001-06-28 07:25:16 +00009852/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009853 * POSIX requires that 'set' (but not export or readonly) output the
9854 * variables in lexicographic order - by the locale's collating order (sigh).
9855 * Maybe we could keep them in an ordered balanced binary tree
9856 * instead of hashed lists.
9857 * For now just roll 'em through qsort for printing...
9858 */
9859static int
9860showvars(const char *sep_prefix, int on, int off)
9861{
9862 const char *sep;
9863 char **ep, **epend;
9864
9865 ep = listvars(on, off, &epend);
9866 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9867
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009868 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009869
9870 for (; ep < epend; ep++) {
9871 const char *p;
9872 const char *q;
9873
9874 p = strchrnul(*ep, '=');
9875 q = nullstr;
9876 if (*p)
9877 q = single_quote(++p);
9878 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9879 }
9880 return 0;
9881}
9882
9883/*
Eric Andersencb57d552001-06-28 07:25:16 +00009884 * The set command builtin.
9885 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009886static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009887setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009888{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009889 int retval;
9890
Denis Vlasenko68404f12008-03-17 09:00:54 +00009891 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009892 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009893 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009894 retval = 1;
9895 if (!options(0)) { /* if no parse error... */
9896 retval = 0;
9897 optschanged();
9898 if (*argptr != NULL) {
9899 setparam(argptr);
9900 }
Eric Andersencb57d552001-06-28 07:25:16 +00009901 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009902 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009903 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009904}
9905
Denis Vlasenko131ae172007-02-18 13:00:19 +00009906#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009907static void
9908change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009909{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009910 /* Galois LFSR parameter */
9911 /* Taps at 32 31 29 1: */
9912 enum { MASK = 0x8000000b };
9913 /* Another example - taps at 32 31 30 10: */
9914 /* MASK = 0x00400007 */
9915
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009916 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009917 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009918 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009919
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009920 /* LCG has period of 2^32 and alternating lowest bit */
9921 random_LCG = 1664525 * random_LCG + 1013904223;
9922 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9923 t = (random_galois_LFSR << 1);
9924 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9925 t ^= MASK;
9926 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009927 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009928 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009929 * for $RANDOM range. Combining with subtraction is
9930 * just for fun. + and ^ would work equally well. */
9931 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009932 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009933 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009934 vrandom.flags &= ~VNOFUNC;
9935 } else {
9936 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009937 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009938 }
Eric Andersenef02f822004-03-11 13:34:24 +00009939}
Eric Andersen16767e22004-03-16 05:14:10 +00009940#endif
9941
Denis Vlasenko131ae172007-02-18 13:00:19 +00009942#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009943static int
Eric Andersenc470f442003-07-28 09:56:35 +00009944getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009945{
9946 char *p, *q;
9947 char c = '?';
9948 int done = 0;
9949 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009950 char s[12];
9951 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009952
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009953 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009954 return 1;
9955 optnext = optfirst + *param_optind - 1;
9956
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009957 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009958 p = NULL;
9959 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009960 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009961 if (p == NULL || *p == '\0') {
9962 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009963 p = *optnext;
9964 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009965 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009966 p = NULL;
9967 done = 1;
9968 goto out;
9969 }
9970 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009971 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009972 goto atend;
9973 }
9974
9975 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009976 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009977 if (*q == '\0') {
9978 if (optstr[0] == ':') {
9979 s[0] = c;
9980 s[1] = '\0';
9981 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009982 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009983 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009984 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009985 }
9986 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009987 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009988 }
9989 if (*++q == ':')
9990 q++;
9991 }
9992
9993 if (*++q == ':') {
9994 if (*p == '\0' && (p = *optnext) == NULL) {
9995 if (optstr[0] == ':') {
9996 s[0] = c;
9997 s[1] = '\0';
9998 err |= setvarsafe("OPTARG", s, 0);
9999 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010000 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010001 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010002 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010003 c = '?';
10004 }
Eric Andersenc470f442003-07-28 09:56:35 +000010005 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010006 }
10007
10008 if (p == *optnext)
10009 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010010 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010011 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010012 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010013 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010014 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010015 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010016 *param_optind = optnext - optfirst + 1;
10017 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010018 err |= setvarsafe("OPTIND", s, VNOFUNC);
10019 s[0] = c;
10020 s[1] = '\0';
10021 err |= setvarsafe(optvar, s, 0);
10022 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010023 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010024 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010025 flush_stdout_stderr();
10026 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010027 }
10028 return done;
10029}
Eric Andersenc470f442003-07-28 09:56:35 +000010030
10031/*
10032 * The getopts builtin. Shellparam.optnext points to the next argument
10033 * to be processed. Shellparam.optptr points to the next character to
10034 * be processed in the current argument. If shellparam.optnext is NULL,
10035 * then it's the first time getopts has been called.
10036 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010037static int
Eric Andersenc470f442003-07-28 09:56:35 +000010038getoptscmd(int argc, char **argv)
10039{
10040 char **optbase;
10041
10042 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010043 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010044 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010045 optbase = shellparam.p;
10046 if (shellparam.optind > shellparam.nparam + 1) {
10047 shellparam.optind = 1;
10048 shellparam.optoff = -1;
10049 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010050 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010051 optbase = &argv[3];
10052 if (shellparam.optind > argc - 2) {
10053 shellparam.optind = 1;
10054 shellparam.optoff = -1;
10055 }
10056 }
10057
10058 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010059 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010060}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010061#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010062
Eric Andersencb57d552001-06-28 07:25:16 +000010063
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010064/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010065
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010066struct heredoc {
10067 struct heredoc *next; /* next here document in list */
10068 union node *here; /* redirection node */
10069 char *eofmark; /* string indicating end of input */
10070 smallint striptabs; /* if set, strip leading tabs */
10071};
10072
10073static smallint tokpushback; /* last token pushed back */
10074static smallint parsebackquote; /* nonzero if we are inside backquotes */
10075static smallint quoteflag; /* set if (part of) last token was quoted */
10076static token_id_t lasttoken; /* last token read (integer id Txxx) */
10077static struct heredoc *heredoclist; /* list of here documents to read */
10078static char *wordtext; /* text of last word returned by readtoken */
10079static struct nodelist *backquotelist;
10080static union node *redirnode;
10081static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010082/*
10083 * NEOF is returned by parsecmd when it encounters an end of file. It
10084 * must be distinct from NULL, so we use the address of a variable that
10085 * happens to be handy.
10086 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010087#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010088
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010089static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010090static void
10091raise_error_syntax(const char *msg)
10092{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010093 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010094 /* NOTREACHED */
10095}
10096
10097/*
10098 * Called when an unexpected token is read during the parse. The argument
10099 * is the token that is expected, or -1 if more than one type of token can
10100 * occur at this point.
10101 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010102static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010103static void
10104raise_error_unexpected_syntax(int token)
10105{
10106 char msg[64];
10107 int l;
10108
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010109 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010110 if (token >= 0)
10111 sprintf(msg + l, " (expecting %s)", tokname(token));
10112 raise_error_syntax(msg);
10113 /* NOTREACHED */
10114}
Eric Andersencb57d552001-06-28 07:25:16 +000010115
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010116#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010117
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010118/* parsing is heavily cross-recursive, need these forward decls */
10119static union node *andor(void);
10120static union node *pipeline(void);
10121static union node *parse_command(void);
10122static void parseheredoc(void);
10123static char peektoken(void);
10124static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010125
Eric Andersenc470f442003-07-28 09:56:35 +000010126static union node *
10127list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010128{
10129 union node *n1, *n2, *n3;
10130 int tok;
10131
Eric Andersenc470f442003-07-28 09:56:35 +000010132 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10133 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010134 return NULL;
10135 n1 = NULL;
10136 for (;;) {
10137 n2 = andor();
10138 tok = readtoken();
10139 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010140 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010141 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010142 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010143 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010144 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010145 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010146 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010147 n2 = n3;
10148 }
10149 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010150 }
10151 }
10152 if (n1 == NULL) {
10153 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010154 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010155 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010156 n3->type = NSEMI;
10157 n3->nbinary.ch1 = n1;
10158 n3->nbinary.ch2 = n2;
10159 n1 = n3;
10160 }
10161 switch (tok) {
10162 case TBACKGND:
10163 case TSEMI:
10164 tok = readtoken();
10165 /* fall through */
10166 case TNL:
10167 if (tok == TNL) {
10168 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010169 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010170 return n1;
10171 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010172 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010173 }
Eric Andersenc470f442003-07-28 09:56:35 +000010174 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010175 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010176 return n1;
10177 break;
10178 case TEOF:
10179 if (heredoclist)
10180 parseheredoc();
10181 else
Eric Andersenc470f442003-07-28 09:56:35 +000010182 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010183 return n1;
10184 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010185 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010186 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010187 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010188 return n1;
10189 }
10190 }
10191}
10192
Eric Andersenc470f442003-07-28 09:56:35 +000010193static union node *
10194andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010195{
Eric Andersencb57d552001-06-28 07:25:16 +000010196 union node *n1, *n2, *n3;
10197 int t;
10198
Eric Andersencb57d552001-06-28 07:25:16 +000010199 n1 = pipeline();
10200 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010201 t = readtoken();
10202 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010203 t = NAND;
10204 } else if (t == TOR) {
10205 t = NOR;
10206 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010207 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010208 return n1;
10209 }
Eric Andersenc470f442003-07-28 09:56:35 +000010210 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010211 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010212 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010213 n3->type = t;
10214 n3->nbinary.ch1 = n1;
10215 n3->nbinary.ch2 = n2;
10216 n1 = n3;
10217 }
10218}
10219
Eric Andersenc470f442003-07-28 09:56:35 +000010220static union node *
10221pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010222{
Eric Andersencb57d552001-06-28 07:25:16 +000010223 union node *n1, *n2, *pipenode;
10224 struct nodelist *lp, *prev;
10225 int negate;
10226
10227 negate = 0;
10228 TRACE(("pipeline: entered\n"));
10229 if (readtoken() == TNOT) {
10230 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010231 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010232 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010233 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010234 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010235 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010236 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010237 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010238 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010239 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010240 pipenode->npipe.cmdlist = lp;
10241 lp->n = n1;
10242 do {
10243 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010244 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010245 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010247 prev->next = lp;
10248 } while (readtoken() == TPIPE);
10249 lp->next = NULL;
10250 n1 = pipenode;
10251 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010252 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010253 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010254 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010255 n2->type = NNOT;
10256 n2->nnot.com = n1;
10257 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010258 }
10259 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010260}
10261
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010262static union node *
10263makename(void)
10264{
10265 union node *n;
10266
Denis Vlasenko597906c2008-02-20 16:38:54 +000010267 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010268 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010269 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010270 n->narg.text = wordtext;
10271 n->narg.backquote = backquotelist;
10272 return n;
10273}
10274
10275static void
10276fixredir(union node *n, const char *text, int err)
10277{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010278 int fd;
10279
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010280 TRACE(("Fix redir %s %d\n", text, err));
10281 if (!err)
10282 n->ndup.vname = NULL;
10283
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010284 fd = bb_strtou(text, NULL, 10);
10285 if (!errno && fd >= 0)
10286 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010287 else if (LONE_DASH(text))
10288 n->ndup.dupfd = -1;
10289 else {
10290 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010291 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010292 n->ndup.vname = makename();
10293 }
10294}
10295
10296/*
10297 * Returns true if the text contains nothing to expand (no dollar signs
10298 * or backquotes).
10299 */
10300static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010301noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010302{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010303 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010304 char c;
10305
10306 p = text;
10307 while ((c = *p++) != '\0') {
10308 if (c == CTLQUOTEMARK)
10309 continue;
10310 if (c == CTLESC)
10311 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010312 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010313 return 0;
10314 }
10315 return 1;
10316}
10317
10318static void
10319parsefname(void)
10320{
10321 union node *n = redirnode;
10322
10323 if (readtoken() != TWORD)
10324 raise_error_unexpected_syntax(-1);
10325 if (n->type == NHERE) {
10326 struct heredoc *here = heredoc;
10327 struct heredoc *p;
10328 int i;
10329
10330 if (quoteflag == 0)
10331 n->type = NXHERE;
10332 TRACE(("Here document %d\n", n->type));
10333 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010334 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010335 rmescapes(wordtext);
10336 here->eofmark = wordtext;
10337 here->next = NULL;
10338 if (heredoclist == NULL)
10339 heredoclist = here;
10340 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010341 for (p = heredoclist; p->next; p = p->next)
10342 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010343 p->next = here;
10344 }
10345 } else if (n->type == NTOFD || n->type == NFROMFD) {
10346 fixredir(n, wordtext, 0);
10347 } else {
10348 n->nfile.fname = makename();
10349 }
10350}
Eric Andersencb57d552001-06-28 07:25:16 +000010351
Eric Andersenc470f442003-07-28 09:56:35 +000010352static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353simplecmd(void)
10354{
10355 union node *args, **app;
10356 union node *n = NULL;
10357 union node *vars, **vpp;
10358 union node **rpp, *redir;
10359 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010360#if ENABLE_ASH_BASH_COMPAT
10361 smallint double_brackets_flag = 0;
10362#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010363
10364 args = NULL;
10365 app = &args;
10366 vars = NULL;
10367 vpp = &vars;
10368 redir = NULL;
10369 rpp = &redir;
10370
10371 savecheckkwd = CHKALIAS;
10372 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010373 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010374 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010375 t = readtoken();
10376 switch (t) {
10377#if ENABLE_ASH_BASH_COMPAT
10378 case TAND: /* "&&" */
10379 case TOR: /* "||" */
10380 if (!double_brackets_flag) {
10381 tokpushback = 1;
10382 goto out;
10383 }
10384 wordtext = (char *) (t == TAND ? "-a" : "-o");
10385#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010386 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010387 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010388 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010389 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010390 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010391#if ENABLE_ASH_BASH_COMPAT
10392 if (strcmp("[[", wordtext) == 0)
10393 double_brackets_flag = 1;
10394 else if (strcmp("]]", wordtext) == 0)
10395 double_brackets_flag = 0;
10396#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010397 n->narg.backquote = backquotelist;
10398 if (savecheckkwd && isassignment(wordtext)) {
10399 *vpp = n;
10400 vpp = &n->narg.next;
10401 } else {
10402 *app = n;
10403 app = &n->narg.next;
10404 savecheckkwd = 0;
10405 }
10406 break;
10407 case TREDIR:
10408 *rpp = n = redirnode;
10409 rpp = &n->nfile.next;
10410 parsefname(); /* read name of redirection file */
10411 break;
10412 case TLP:
10413 if (args && app == &args->narg.next
10414 && !vars && !redir
10415 ) {
10416 struct builtincmd *bcmd;
10417 const char *name;
10418
10419 /* We have a function */
10420 if (readtoken() != TRP)
10421 raise_error_unexpected_syntax(TRP);
10422 name = n->narg.text;
10423 if (!goodname(name)
10424 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10425 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010426 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010427 }
10428 n->type = NDEFUN;
10429 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10430 n->narg.next = parse_command();
10431 return n;
10432 }
10433 /* fall through */
10434 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010435 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010436 goto out;
10437 }
10438 }
10439 out:
10440 *app = NULL;
10441 *vpp = NULL;
10442 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010443 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010444 n->type = NCMD;
10445 n->ncmd.args = args;
10446 n->ncmd.assign = vars;
10447 n->ncmd.redirect = redir;
10448 return n;
10449}
10450
10451static union node *
10452parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010453{
Eric Andersencb57d552001-06-28 07:25:16 +000010454 union node *n1, *n2;
10455 union node *ap, **app;
10456 union node *cp, **cpp;
10457 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010458 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010459 int t;
10460
10461 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010462 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010463
Eric Andersencb57d552001-06-28 07:25:16 +000010464 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010465 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010466 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010467 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010468 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010469 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010470 n1->type = NIF;
10471 n1->nif.test = list(0);
10472 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010474 n1->nif.ifpart = list(0);
10475 n2 = n1;
10476 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010477 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010478 n2 = n2->nif.elsepart;
10479 n2->type = NIF;
10480 n2->nif.test = list(0);
10481 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010482 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010483 n2->nif.ifpart = list(0);
10484 }
10485 if (lasttoken == TELSE)
10486 n2->nif.elsepart = list(0);
10487 else {
10488 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010489 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010490 }
Eric Andersenc470f442003-07-28 09:56:35 +000010491 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010492 break;
10493 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010494 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010495 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010496 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010497 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010498 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010499 got = readtoken();
10500 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010501 TRACE(("expecting DO got %s %s\n", tokname(got),
10502 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010503 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010504 }
10505 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010506 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 break;
10508 }
10509 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010510 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010511 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010512 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010513 n1->type = NFOR;
10514 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010515 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 if (readtoken() == TIN) {
10517 app = &ap;
10518 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010519 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010520 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010521 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010522 n2->narg.text = wordtext;
10523 n2->narg.backquote = backquotelist;
10524 *app = n2;
10525 app = &n2->narg.next;
10526 }
10527 *app = NULL;
10528 n1->nfor.args = ap;
10529 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010530 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010531 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010532 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010533 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010534 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010535 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010536 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010537 n1->nfor.args = n2;
10538 /*
10539 * Newline or semicolon here is optional (but note
10540 * that the original Bourne shell only allowed NL).
10541 */
10542 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010543 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010544 }
Eric Andersenc470f442003-07-28 09:56:35 +000010545 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010546 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010547 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010548 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010549 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010550 break;
10551 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010552 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010553 n1->type = NCASE;
10554 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010555 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010556 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010557 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010558 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010559 n2->narg.text = wordtext;
10560 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010561 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010562 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010563 } while (readtoken() == TNL);
10564 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010565 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010566 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010567 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010568 checkkwd = CHKNL | CHKKWD;
10569 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010570 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010571 if (lasttoken == TLP)
10572 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010573 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010574 cp->type = NCLIST;
10575 app = &cp->nclist.pattern;
10576 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010577 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010578 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010579 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010580 ap->narg.text = wordtext;
10581 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010582 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010583 break;
10584 app = &ap->narg.next;
10585 readtoken();
10586 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010587 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010588 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010589 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010590 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010591
Eric Andersenc470f442003-07-28 09:56:35 +000010592 cpp = &cp->nclist.next;
10593
10594 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010595 t = readtoken();
10596 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010597 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010598 raise_error_unexpected_syntax(TENDCASE);
10599 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010600 }
Eric Andersenc470f442003-07-28 09:56:35 +000010601 }
Eric Andersencb57d552001-06-28 07:25:16 +000010602 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010603 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010604 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010605 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010606 n1->type = NSUBSHELL;
10607 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010608 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010609 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010610 break;
10611 case TBEGIN:
10612 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010613 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010614 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010615 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010616 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010617 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010618 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010619 }
10620
Eric Andersenc470f442003-07-28 09:56:35 +000010621 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010622 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010623
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010624 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010625 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010626 checkkwd = CHKKWD | CHKALIAS;
10627 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010628 while (readtoken() == TREDIR) {
10629 *rpp = n2 = redirnode;
10630 rpp = &n2->nfile.next;
10631 parsefname();
10632 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010633 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010634 *rpp = NULL;
10635 if (redir) {
10636 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010637 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010638 n2->type = NREDIR;
10639 n2->nredir.n = n1;
10640 n1 = n2;
10641 }
10642 n1->nredir.redirect = redir;
10643 }
Eric Andersencb57d552001-06-28 07:25:16 +000010644 return n1;
10645}
10646
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010647#if ENABLE_ASH_BASH_COMPAT
10648static int decode_dollar_squote(void)
10649{
10650 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10651 int c, cnt;
10652 char *p;
10653 char buf[4];
10654
10655 c = pgetc();
10656 p = strchr(C_escapes, c);
10657 if (p) {
10658 buf[0] = c;
10659 p = buf;
10660 cnt = 3;
10661 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10662 do {
10663 c = pgetc();
10664 *++p = c;
10665 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10666 pungetc();
10667 } else if (c == 'x') { /* \xHH */
10668 do {
10669 c = pgetc();
10670 *++p = c;
10671 } while (isxdigit(c) && --cnt);
10672 pungetc();
10673 if (cnt == 3) { /* \x but next char is "bad" */
10674 c = 'x';
10675 goto unrecognized;
10676 }
10677 } else { /* simple seq like \\ or \t */
10678 p++;
10679 }
10680 *p = '\0';
10681 p = buf;
10682 c = bb_process_escape_sequence((void*)&p);
10683 } else { /* unrecognized "\z": print both chars unless ' or " */
10684 if (c != '\'' && c != '"') {
10685 unrecognized:
10686 c |= 0x100; /* "please encode \, then me" */
10687 }
10688 }
10689 return c;
10690}
10691#endif
10692
Eric Andersencb57d552001-06-28 07:25:16 +000010693/*
10694 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10695 * is not NULL, read a here document. In the latter case, eofmark is the
10696 * word which marks the end of the document and striptabs is true if
10697 * leading tabs should be stripped from the document. The argument firstc
10698 * is the first character of the input token or document.
10699 *
10700 * Because C does not have internal subroutines, I have simulated them
10701 * using goto's to implement the subroutine linkage. The following macros
10702 * will run code that appears at the end of readtoken1.
10703 */
Eric Andersen2870d962001-07-02 17:27:21 +000010704#define CHECKEND() {goto checkend; checkend_return:;}
10705#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10706#define PARSESUB() {goto parsesub; parsesub_return:;}
10707#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10708#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10709#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010710static int
Eric Andersenc470f442003-07-28 09:56:35 +000010711readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010712{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010713 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010714 int c = firstc;
10715 char *out;
10716 int len;
10717 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010718 struct nodelist *bqlist;
10719 smallint quotef;
10720 smallint dblquote;
10721 smallint oldstyle;
10722 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010723#if ENABLE_ASH_EXPAND_PRMT
10724 smallint pssyntax; /* we are expanding a prompt string */
10725#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010726 int varnest; /* levels of variables expansion */
10727 int arinest; /* levels of arithmetic expansion */
10728 int parenlevel; /* levels of parens in arithmetic */
10729 int dqvarnest; /* levels of variables expansion within double quotes */
10730
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010731 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10732
Eric Andersencb57d552001-06-28 07:25:16 +000010733#if __GNUC__
10734 /* Avoid longjmp clobbering */
10735 (void) &out;
10736 (void) &quotef;
10737 (void) &dblquote;
10738 (void) &varnest;
10739 (void) &arinest;
10740 (void) &parenlevel;
10741 (void) &dqvarnest;
10742 (void) &oldstyle;
10743 (void) &prevsyntax;
10744 (void) &syntax;
10745#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010746 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010747 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010748 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010749 oldstyle = 0;
10750 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010751#if ENABLE_ASH_EXPAND_PRMT
10752 pssyntax = (syntax == PSSYNTAX);
10753 if (pssyntax)
10754 syntax = DQSYNTAX;
10755#endif
10756 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010757 varnest = 0;
10758 arinest = 0;
10759 parenlevel = 0;
10760 dqvarnest = 0;
10761
10762 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010763 loop:
10764 /* For each line, until end of word */
10765 {
Eric Andersenc470f442003-07-28 09:56:35 +000010766 CHECKEND(); /* set c to PEOF if at end of here document */
10767 for (;;) { /* until end of line or end of word */
10768 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010769 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010770 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010771 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010772 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010773 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010774 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010775 if (doprompt)
10776 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010777 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010778 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010779 case CWORD:
10780 USTPUTC(c, out);
10781 break;
10782 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010783 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010784 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010785#if ENABLE_ASH_BASH_COMPAT
10786 if (c == '\\' && bash_dollar_squote) {
10787 c = decode_dollar_squote();
10788 if (c & 0x100) {
10789 USTPUTC('\\', out);
10790 c = (unsigned char)c;
10791 }
10792 }
10793#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010794 USTPUTC(c, out);
10795 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010796 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010797 c = pgetc2();
10798 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010799 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010800 USTPUTC('\\', out);
10801 pungetc();
10802 } else if (c == '\n') {
10803 if (doprompt)
10804 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010805 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010806#if ENABLE_ASH_EXPAND_PRMT
10807 if (c == '$' && pssyntax) {
10808 USTPUTC(CTLESC, out);
10809 USTPUTC('\\', out);
10810 }
10811#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010812 if (dblquote && c != '\\'
10813 && c != '`' && c != '$'
10814 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010815 ) {
10816 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010818 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010819 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010820 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010821 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010822 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010823 }
10824 break;
10825 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010826 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010827 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010828 if (eofmark == NULL) {
10829 USTPUTC(CTLQUOTEMARK, out);
10830 }
Eric Andersencb57d552001-06-28 07:25:16 +000010831 break;
10832 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010833 syntax = DQSYNTAX;
10834 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010835 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010836 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010837 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010838 if (eofmark != NULL && arinest == 0
10839 && varnest == 0
10840 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010841 USTPUTC(c, out);
10842 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010843 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010844 syntax = BASESYNTAX;
10845 dblquote = 0;
10846 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010847 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010848 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010849 }
10850 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010851 case CVAR: /* '$' */
10852 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010853 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010854 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010855 if (varnest > 0) {
10856 varnest--;
10857 if (dqvarnest > 0) {
10858 dqvarnest--;
10859 }
10860 USTPUTC(CTLENDVAR, out);
10861 } else {
10862 USTPUTC(c, out);
10863 }
10864 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010865#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010866 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010867 parenlevel++;
10868 USTPUTC(c, out);
10869 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010870 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010871 if (parenlevel > 0) {
10872 USTPUTC(c, out);
10873 --parenlevel;
10874 } else {
10875 if (pgetc() == ')') {
10876 if (--arinest == 0) {
10877 USTPUTC(CTLENDARI, out);
10878 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010879 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010880 } else
10881 USTPUTC(')', out);
10882 } else {
10883 /*
10884 * unbalanced parens
10885 * (don't 2nd guess - no error)
10886 */
10887 pungetc();
10888 USTPUTC(')', out);
10889 }
10890 }
10891 break;
10892#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010893 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010894 PARSEBACKQOLD();
10895 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010896 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010897 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010898 case CIGN:
10899 break;
10900 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010901 if (varnest == 0) {
10902#if ENABLE_ASH_BASH_COMPAT
10903 if (c == '&') {
10904 if (pgetc() == '>')
10905 c = 0x100 + '>'; /* flag &> */
10906 pungetc();
10907 }
10908#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010909 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010910 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010911#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010912 if (c != PEOA)
10913#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010914 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010915
Eric Andersencb57d552001-06-28 07:25:16 +000010916 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010917 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010918 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010919 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010920 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010921#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010922 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010923 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010924#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010925 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010926 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010927 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010928 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010929 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010930 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010931 }
10932 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010933 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010934 out = stackblock();
10935 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010936 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10937 && quotef == 0
10938 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010939 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010940 PARSEREDIR(); /* passed as params: out, c */
10941 lasttoken = TREDIR;
10942 return lasttoken;
10943 }
10944 /* else: non-number X seen, interpret it
10945 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010946 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010947 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010948 }
10949 quoteflag = quotef;
10950 backquotelist = bqlist;
10951 grabstackblock(len);
10952 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010953 lasttoken = TWORD;
10954 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010955/* end of readtoken routine */
10956
Eric Andersencb57d552001-06-28 07:25:16 +000010957/*
10958 * Check to see whether we are at the end of the here document. When this
10959 * is called, c is set to the first character of the next input line. If
10960 * we are at the end of the here document, this routine sets the c to PEOF.
10961 */
Eric Andersenc470f442003-07-28 09:56:35 +000010962checkend: {
10963 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010964#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010965 if (c == PEOA) {
10966 c = pgetc2();
10967 }
10968#endif
10969 if (striptabs) {
10970 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010971 c = pgetc2();
10972 }
Eric Andersenc470f442003-07-28 09:56:35 +000010973 }
10974 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010975 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010976 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010977
Eric Andersenc470f442003-07-28 09:56:35 +000010978 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010979 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10980 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010981 if (*p == '\n' && *q == '\0') {
10982 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010983 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010984 needprompt = doprompt;
10985 } else {
10986 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010987 }
10988 }
10989 }
10990 }
Eric Andersenc470f442003-07-28 09:56:35 +000010991 goto checkend_return;
10992}
Eric Andersencb57d552001-06-28 07:25:16 +000010993
Eric Andersencb57d552001-06-28 07:25:16 +000010994/*
10995 * Parse a redirection operator. The variable "out" points to a string
10996 * specifying the fd to be redirected. The variable "c" contains the
10997 * first character of the redirection operator.
10998 */
Eric Andersenc470f442003-07-28 09:56:35 +000010999parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011000 /* out is already checked to be a valid number or "" */
11001 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011002 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011003
Denis Vlasenko597906c2008-02-20 16:38:54 +000011004 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011005 if (c == '>') {
11006 np->nfile.fd = 1;
11007 c = pgetc();
11008 if (c == '>')
11009 np->type = NAPPEND;
11010 else if (c == '|')
11011 np->type = NCLOBBER;
11012 else if (c == '&')
11013 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011014 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011015 else {
11016 np->type = NTO;
11017 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011018 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011019 }
11020#if ENABLE_ASH_BASH_COMPAT
11021 else if (c == 0x100 + '>') { /* this flags &> redirection */
11022 np->nfile.fd = 1;
11023 pgetc(); /* this is '>', no need to check */
11024 np->type = NTO2;
11025 }
11026#endif
11027 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011028 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011029 c = pgetc();
11030 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011031 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011032 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011033 np = stzalloc(sizeof(struct nhere));
11034 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011035 }
11036 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011037 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011038 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011039 c = pgetc();
11040 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011041 heredoc->striptabs = 1;
11042 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011043 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011044 pungetc();
11045 }
11046 break;
11047
11048 case '&':
11049 np->type = NFROMFD;
11050 break;
11051
11052 case '>':
11053 np->type = NFROMTO;
11054 break;
11055
11056 default:
11057 np->type = NFROM;
11058 pungetc();
11059 break;
11060 }
Eric Andersencb57d552001-06-28 07:25:16 +000011061 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011062 if (fd >= 0)
11063 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011064 redirnode = np;
11065 goto parseredir_return;
11066}
Eric Andersencb57d552001-06-28 07:25:16 +000011067
Eric Andersencb57d552001-06-28 07:25:16 +000011068/*
11069 * Parse a substitution. At this point, we have read the dollar sign
11070 * and nothing else.
11071 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011072
11073/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11074 * (assuming ascii char codes, as the original implementation did) */
11075#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011076 (((unsigned)(c) - 33 < 32) \
11077 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011078parsesub: {
11079 int subtype;
11080 int typeloc;
11081 int flags;
11082 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011083 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011084
Eric Andersenc470f442003-07-28 09:56:35 +000011085 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011086 if (c <= PEOA_OR_PEOF
11087 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011088 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011089#if ENABLE_ASH_BASH_COMPAT
11090 if (c == '\'')
11091 bash_dollar_squote = 1;
11092 else
11093#endif
11094 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011095 pungetc();
11096 } else if (c == '(') { /* $(command) or $((arith)) */
11097 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011098#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011099 PARSEARITH();
11100#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011101 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011102#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011103 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011104 pungetc();
11105 PARSEBACKQNEW();
11106 }
11107 } else {
11108 USTPUTC(CTLVAR, out);
11109 typeloc = out - (char *)stackblock();
11110 USTPUTC(VSNORMAL, out);
11111 subtype = VSNORMAL;
11112 if (c == '{') {
11113 c = pgetc();
11114 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011115 c = pgetc();
11116 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011117 c = '#';
11118 else
11119 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011120 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011121 subtype = 0;
11122 }
11123 if (c > PEOA_OR_PEOF && is_name(c)) {
11124 do {
11125 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011126 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011127 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011128 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011129 do {
11130 STPUTC(c, out);
11131 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011132 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011133 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011134 USTPUTC(c, out);
11135 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011136 } else {
11137 badsub:
11138 raise_error_syntax("bad substitution");
11139 }
Eric Andersencb57d552001-06-28 07:25:16 +000011140
Eric Andersenc470f442003-07-28 09:56:35 +000011141 STPUTC('=', out);
11142 flags = 0;
11143 if (subtype == 0) {
11144 switch (c) {
11145 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011146 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011147#if ENABLE_ASH_BASH_COMPAT
11148 if (c == ':' || c == '$' || isdigit(c)) {
11149 pungetc();
11150 subtype = VSSUBSTR;
11151 break;
11152 }
11153#endif
11154 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011155 /*FALLTHROUGH*/
11156 default:
11157 p = strchr(types, c);
11158 if (p == NULL)
11159 goto badsub;
11160 subtype = p - types + VSNORMAL;
11161 break;
11162 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011163 case '#': {
11164 int cc = c;
11165 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11166 c = pgetc();
11167 if (c == cc)
11168 subtype++;
11169 else
11170 pungetc();
11171 break;
11172 }
11173#if ENABLE_ASH_BASH_COMPAT
11174 case '/':
11175 subtype = VSREPLACE;
11176 c = pgetc();
11177 if (c == '/')
11178 subtype++; /* VSREPLACEALL */
11179 else
11180 pungetc();
11181 break;
11182#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011183 }
Eric Andersenc470f442003-07-28 09:56:35 +000011184 } else {
11185 pungetc();
11186 }
11187 if (dblquote || arinest)
11188 flags |= VSQUOTE;
11189 *((char *)stackblock() + typeloc) = subtype | flags;
11190 if (subtype != VSNORMAL) {
11191 varnest++;
11192 if (dblquote || arinest) {
11193 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011194 }
11195 }
11196 }
Eric Andersenc470f442003-07-28 09:56:35 +000011197 goto parsesub_return;
11198}
Eric Andersencb57d552001-06-28 07:25:16 +000011199
Eric Andersencb57d552001-06-28 07:25:16 +000011200/*
11201 * Called to parse command substitutions. Newstyle is set if the command
11202 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11203 * list of commands (passed by reference), and savelen is the number of
11204 * characters on the top of the stack which must be preserved.
11205 */
Eric Andersenc470f442003-07-28 09:56:35 +000011206parsebackq: {
11207 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011208 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011209 union node *n;
11210 char *volatile str;
11211 struct jmploc jmploc;
11212 struct jmploc *volatile savehandler;
11213 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011214 smallint saveprompt = 0;
11215
Eric Andersencb57d552001-06-28 07:25:16 +000011216#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011217 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011218#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011219 savepbq = parsebackquote;
11220 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011221 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011222 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011223 exception_handler = savehandler;
11224 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011225 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011226 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011227 str = NULL;
11228 savelen = out - (char *)stackblock();
11229 if (savelen > 0) {
11230 str = ckmalloc(savelen);
11231 memcpy(str, stackblock(), savelen);
11232 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011233 savehandler = exception_handler;
11234 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011235 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011236 if (oldstyle) {
11237 /* We must read until the closing backquote, giving special
11238 treatment to some slashes, and then push the string and
11239 reread it as input, interpreting it normally. */
11240 char *pout;
11241 int pc;
11242 size_t psavelen;
11243 char *pstr;
11244
11245
11246 STARTSTACKSTR(pout);
11247 for (;;) {
11248 if (needprompt) {
11249 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011250 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011251 pc = pgetc();
11252 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011253 case '`':
11254 goto done;
11255
11256 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011257 pc = pgetc();
11258 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011259 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011260 if (doprompt)
11261 setprompt(2);
11262 /*
11263 * If eating a newline, avoid putting
11264 * the newline into the new character
11265 * stream (via the STPUTC after the
11266 * switch).
11267 */
11268 continue;
11269 }
11270 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011271 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011272 STPUTC('\\', pout);
11273 if (pc > PEOA_OR_PEOF) {
11274 break;
11275 }
11276 /* fall through */
11277
11278 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011279#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011280 case PEOA:
11281#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011282 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011283 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011284
11285 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011286 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011287 needprompt = doprompt;
11288 break;
11289
11290 default:
11291 break;
11292 }
11293 STPUTC(pc, pout);
11294 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011295 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011296 STPUTC('\0', pout);
11297 psavelen = pout - (char *)stackblock();
11298 if (psavelen > 0) {
11299 pstr = grabstackstr(pout);
11300 setinputstring(pstr);
11301 }
11302 }
11303 nlpp = &bqlist;
11304 while (*nlpp)
11305 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011306 *nlpp = stzalloc(sizeof(**nlpp));
11307 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011308 parsebackquote = oldstyle;
11309
11310 if (oldstyle) {
11311 saveprompt = doprompt;
11312 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011313 }
11314
Eric Andersenc470f442003-07-28 09:56:35 +000011315 n = list(2);
11316
11317 if (oldstyle)
11318 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011319 else if (readtoken() != TRP)
11320 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011321
11322 (*nlpp)->n = n;
11323 if (oldstyle) {
11324 /*
11325 * Start reading from old file again, ignoring any pushed back
11326 * tokens left from the backquote parsing
11327 */
11328 popfile();
11329 tokpushback = 0;
11330 }
11331 while (stackblocksize() <= savelen)
11332 growstackblock();
11333 STARTSTACKSTR(out);
11334 if (str) {
11335 memcpy(out, str, savelen);
11336 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011337 INT_OFF;
11338 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011339 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011340 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011341 }
11342 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011343 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011344 if (arinest || dblquote)
11345 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11346 else
11347 USTPUTC(CTLBACKQ, out);
11348 if (oldstyle)
11349 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011350 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011351}
11352
Denis Vlasenko131ae172007-02-18 13:00:19 +000011353#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011354/*
11355 * Parse an arithmetic expansion (indicate start of one and set state)
11356 */
Eric Andersenc470f442003-07-28 09:56:35 +000011357parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011358 if (++arinest == 1) {
11359 prevsyntax = syntax;
11360 syntax = ARISYNTAX;
11361 USTPUTC(CTLARI, out);
11362 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011363 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011364 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011365 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011366 } else {
11367 /*
11368 * we collapse embedded arithmetic expansion to
11369 * parenthesis, which should be equivalent
11370 */
11371 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011372 }
Eric Andersenc470f442003-07-28 09:56:35 +000011373 goto parsearith_return;
11374}
11375#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011376
Eric Andersenc470f442003-07-28 09:56:35 +000011377} /* end of readtoken */
11378
Eric Andersencb57d552001-06-28 07:25:16 +000011379/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011380 * Read the next input token.
11381 * If the token is a word, we set backquotelist to the list of cmds in
11382 * backquotes. We set quoteflag to true if any part of the word was
11383 * quoted.
11384 * If the token is TREDIR, then we set redirnode to a structure containing
11385 * the redirection.
11386 * In all cases, the variable startlinno is set to the number of the line
11387 * on which the token starts.
11388 *
11389 * [Change comment: here documents and internal procedures]
11390 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11391 * word parsing code into a separate routine. In this case, readtoken
11392 * doesn't need to have any internal procedures, but parseword does.
11393 * We could also make parseoperator in essence the main routine, and
11394 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011395 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011396#define NEW_xxreadtoken
11397#ifdef NEW_xxreadtoken
11398/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011399static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011400 '\n', '(', ')', /* singles */
11401 '&', '|', ';', /* doubles */
11402 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011403};
Eric Andersencb57d552001-06-28 07:25:16 +000011404
Denis Vlasenko834dee72008-10-07 09:18:30 +000011405#define xxreadtoken_singles 3
11406#define xxreadtoken_doubles 3
11407
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011408static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011409 TNL, TLP, TRP, /* only single occurrence allowed */
11410 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11411 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011412 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011413};
11414
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011415static int
11416xxreadtoken(void)
11417{
11418 int c;
11419
11420 if (tokpushback) {
11421 tokpushback = 0;
11422 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011424 if (needprompt) {
11425 setprompt(2);
11426 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011427 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011428 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011429 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011430 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11431 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011432
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011433 if (c == '#') {
11434 while ((c = pgetc()) != '\n' && c != PEOF)
11435 continue;
11436 pungetc();
11437 } else if (c == '\\') {
11438 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011439 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011440 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011442 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011443 if (doprompt)
11444 setprompt(2);
11445 } else {
11446 const char *p;
11447
11448 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11449 if (c != PEOF) {
11450 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011451 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011452 needprompt = doprompt;
11453 }
11454
11455 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011456 if (p == NULL)
11457 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011458
Denis Vlasenko834dee72008-10-07 09:18:30 +000011459 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11460 int cc = pgetc();
11461 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011462 p += xxreadtoken_doubles + 1;
11463 } else {
11464 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011465#if ENABLE_ASH_BASH_COMPAT
11466 if (c == '&' && cc == '>') /* &> */
11467 break; /* return readtoken1(...) */
11468#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011469 }
11470 }
11471 }
11472 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11473 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011474 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011475 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011476
11477 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011478}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011479#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011480#define RETURN(token) return lasttoken = token
11481static int
11482xxreadtoken(void)
11483{
11484 int c;
11485
11486 if (tokpushback) {
11487 tokpushback = 0;
11488 return lasttoken;
11489 }
11490 if (needprompt) {
11491 setprompt(2);
11492 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011493 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011494 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011495 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011496 switch (c) {
11497 case ' ': case '\t':
11498#if ENABLE_ASH_ALIAS
11499 case PEOA:
11500#endif
11501 continue;
11502 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011503 while ((c = pgetc()) != '\n' && c != PEOF)
11504 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011505 pungetc();
11506 continue;
11507 case '\\':
11508 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011509 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011510 if (doprompt)
11511 setprompt(2);
11512 continue;
11513 }
11514 pungetc();
11515 goto breakloop;
11516 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011517 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011518 needprompt = doprompt;
11519 RETURN(TNL);
11520 case PEOF:
11521 RETURN(TEOF);
11522 case '&':
11523 if (pgetc() == '&')
11524 RETURN(TAND);
11525 pungetc();
11526 RETURN(TBACKGND);
11527 case '|':
11528 if (pgetc() == '|')
11529 RETURN(TOR);
11530 pungetc();
11531 RETURN(TPIPE);
11532 case ';':
11533 if (pgetc() == ';')
11534 RETURN(TENDCASE);
11535 pungetc();
11536 RETURN(TSEMI);
11537 case '(':
11538 RETURN(TLP);
11539 case ')':
11540 RETURN(TRP);
11541 default:
11542 goto breakloop;
11543 }
11544 }
11545 breakloop:
11546 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11547#undef RETURN
11548}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011549#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011550
11551static int
11552readtoken(void)
11553{
11554 int t;
11555#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011556 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011557#endif
11558
11559#if ENABLE_ASH_ALIAS
11560 top:
11561#endif
11562
11563 t = xxreadtoken();
11564
11565 /*
11566 * eat newlines
11567 */
11568 if (checkkwd & CHKNL) {
11569 while (t == TNL) {
11570 parseheredoc();
11571 t = xxreadtoken();
11572 }
11573 }
11574
11575 if (t != TWORD || quoteflag) {
11576 goto out;
11577 }
11578
11579 /*
11580 * check for keywords
11581 */
11582 if (checkkwd & CHKKWD) {
11583 const char *const *pp;
11584
11585 pp = findkwd(wordtext);
11586 if (pp) {
11587 lasttoken = t = pp - tokname_array;
11588 TRACE(("keyword %s recognized\n", tokname(t)));
11589 goto out;
11590 }
11591 }
11592
11593 if (checkkwd & CHKALIAS) {
11594#if ENABLE_ASH_ALIAS
11595 struct alias *ap;
11596 ap = lookupalias(wordtext, 1);
11597 if (ap != NULL) {
11598 if (*ap->val) {
11599 pushstring(ap->val, ap);
11600 }
11601 goto top;
11602 }
11603#endif
11604 }
11605 out:
11606 checkkwd = 0;
11607#if DEBUG
11608 if (!alreadyseen)
11609 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11610 else
11611 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11612#endif
11613 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011614}
11615
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011616static char
11617peektoken(void)
11618{
11619 int t;
11620
11621 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011622 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011623 return tokname_array[t][0];
11624}
Eric Andersencb57d552001-06-28 07:25:16 +000011625
11626/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011627 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11628 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011629 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011630static union node *
11631parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011632{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011633 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011634
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011635 tokpushback = 0;
11636 doprompt = interact;
11637 if (doprompt)
11638 setprompt(doprompt);
11639 needprompt = 0;
11640 t = readtoken();
11641 if (t == TEOF)
11642 return NEOF;
11643 if (t == TNL)
11644 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011645 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011646 return list(1);
11647}
11648
11649/*
11650 * Input any here documents.
11651 */
11652static void
11653parseheredoc(void)
11654{
11655 struct heredoc *here;
11656 union node *n;
11657
11658 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011659 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011660
11661 while (here) {
11662 if (needprompt) {
11663 setprompt(2);
11664 }
11665 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11666 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011667 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011668 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011669 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011670 n->narg.text = wordtext;
11671 n->narg.backquote = backquotelist;
11672 here->here->nhere.doc = n;
11673 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011674 }
Eric Andersencb57d552001-06-28 07:25:16 +000011675}
11676
11677
11678/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011679 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011680 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011681#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011682static const char *
11683expandstr(const char *ps)
11684{
11685 union node n;
11686
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011687 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11688 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011689 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011690 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011691 popfile();
11692
11693 n.narg.type = NARG;
11694 n.narg.next = NULL;
11695 n.narg.text = wordtext;
11696 n.narg.backquote = backquotelist;
11697
11698 expandarg(&n, NULL, 0);
11699 return stackblock();
11700}
11701#endif
11702
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011703/*
11704 * Execute a command or commands contained in a string.
11705 */
11706static int
11707evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011708{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011709 union node *n;
11710 struct stackmark smark;
11711 int skip;
11712
11713 setinputstring(s);
11714 setstackmark(&smark);
11715
11716 skip = 0;
11717 while ((n = parsecmd(0)) != NEOF) {
11718 evaltree(n, 0);
11719 popstackmark(&smark);
11720 skip = evalskip;
11721 if (skip)
11722 break;
11723 }
11724 popfile();
11725
11726 skip &= mask;
11727 evalskip = skip;
11728 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011729}
11730
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011731/*
11732 * The eval command.
11733 */
11734static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011735evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011736{
11737 char *p;
11738 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011739
Denis Vlasenko68404f12008-03-17 09:00:54 +000011740 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011741 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011742 argv += 2;
11743 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011744 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011745 for (;;) {
11746 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011747 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011748 if (p == NULL)
11749 break;
11750 STPUTC(' ', concat);
11751 }
11752 STPUTC('\0', concat);
11753 p = grabstackstr(concat);
11754 }
11755 evalstring(p, ~SKIPEVAL);
11756
11757 }
11758 return exitstatus;
11759}
11760
11761/*
11762 * Read and execute commands. "Top" is nonzero for the top level command
11763 * loop; it turns on prompting if the shell is interactive.
11764 */
11765static int
11766cmdloop(int top)
11767{
11768 union node *n;
11769 struct stackmark smark;
11770 int inter;
11771 int numeof = 0;
11772
11773 TRACE(("cmdloop(%d) called\n", top));
11774 for (;;) {
11775 int skip;
11776
11777 setstackmark(&smark);
11778#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011779 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011780 showjobs(stderr, SHOW_CHANGED);
11781#endif
11782 inter = 0;
11783 if (iflag && top) {
11784 inter++;
11785#if ENABLE_ASH_MAIL
11786 chkmail();
11787#endif
11788 }
11789 n = parsecmd(inter);
11790 /* showtree(n); DEBUG */
11791 if (n == NEOF) {
11792 if (!top || numeof >= 50)
11793 break;
11794 if (!stoppedjobs()) {
11795 if (!Iflag)
11796 break;
11797 out2str("\nUse \"exit\" to leave shell.\n");
11798 }
11799 numeof++;
11800 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011801 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11802 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011803 numeof = 0;
11804 evaltree(n, 0);
11805 }
11806 popstackmark(&smark);
11807 skip = evalskip;
11808
11809 if (skip) {
11810 evalskip = 0;
11811 return skip & SKIPEVAL;
11812 }
11813 }
11814 return 0;
11815}
11816
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011817/*
11818 * Take commands from a file. To be compatible we should do a path
11819 * search for the file, which is necessary to find sub-commands.
11820 */
11821static char *
11822find_dot_file(char *name)
11823{
11824 char *fullname;
11825 const char *path = pathval();
11826 struct stat statb;
11827
11828 /* don't try this for absolute or relative paths */
11829 if (strchr(name, '/'))
11830 return name;
11831
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011832 /* IIRC standards do not say whether . is to be searched.
11833 * And it is even smaller this way, making it unconditional for now:
11834 */
11835 if (1) { /* ENABLE_ASH_BASH_COMPAT */
11836 fullname = name;
11837 goto try_cur_dir;
11838 }
11839
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011840 while ((fullname = padvance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011841 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011842 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11843 /*
11844 * Don't bother freeing here, since it will
11845 * be freed by the caller.
11846 */
11847 return fullname;
11848 }
11849 stunalloc(fullname);
11850 }
11851
11852 /* not found in the PATH */
11853 ash_msg_and_raise_error("%s: not found", name);
11854 /* NOTREACHED */
11855}
11856
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011857static int
11858dotcmd(int argc, char **argv)
11859{
11860 struct strlist *sp;
11861 volatile struct shparam saveparam;
11862 int status = 0;
11863
11864 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011865 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011866
Denis Vlasenko68404f12008-03-17 09:00:54 +000011867 if (argv[1]) { /* That's what SVR2 does */
11868 char *fullname = find_dot_file(argv[1]);
11869 argv += 2;
11870 argc -= 2;
11871 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011872 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011873 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011874 shellparam.nparam = argc;
11875 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011876 };
11877
11878 setinputfile(fullname, INPUT_PUSH_FILE);
11879 commandname = fullname;
11880 cmdloop(0);
11881 popfile();
11882
Denis Vlasenko68404f12008-03-17 09:00:54 +000011883 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011884 freeparam(&shellparam);
11885 shellparam = saveparam;
11886 };
11887 status = exitstatus;
11888 }
11889 return status;
11890}
11891
11892static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011893exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011894{
11895 if (stoppedjobs())
11896 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011897 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011898 exitstatus = number(argv[1]);
11899 raise_exception(EXEXIT);
11900 /* NOTREACHED */
11901}
11902
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011903/*
11904 * Read a file containing shell functions.
11905 */
11906static void
11907readcmdfile(char *name)
11908{
11909 setinputfile(name, INPUT_PUSH_FILE);
11910 cmdloop(0);
11911 popfile();
11912}
11913
11914
Denis Vlasenkocc571512007-02-23 21:10:35 +000011915/* ============ find_command inplementation */
11916
11917/*
11918 * Resolve a command name. If you change this routine, you may have to
11919 * change the shellexec routine as well.
11920 */
11921static void
11922find_command(char *name, struct cmdentry *entry, int act, const char *path)
11923{
11924 struct tblentry *cmdp;
11925 int idx;
11926 int prev;
11927 char *fullname;
11928 struct stat statb;
11929 int e;
11930 int updatetbl;
11931 struct builtincmd *bcmd;
11932
11933 /* If name contains a slash, don't use PATH or hash table */
11934 if (strchr(name, '/') != NULL) {
11935 entry->u.index = -1;
11936 if (act & DO_ABS) {
11937 while (stat(name, &statb) < 0) {
11938#ifdef SYSV
11939 if (errno == EINTR)
11940 continue;
11941#endif
11942 entry->cmdtype = CMDUNKNOWN;
11943 return;
11944 }
11945 }
11946 entry->cmdtype = CMDNORMAL;
11947 return;
11948 }
11949
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011950/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011951
11952 updatetbl = (path == pathval());
11953 if (!updatetbl) {
11954 act |= DO_ALTPATH;
11955 if (strstr(path, "%builtin") != NULL)
11956 act |= DO_ALTBLTIN;
11957 }
11958
11959 /* If name is in the table, check answer will be ok */
11960 cmdp = cmdlookup(name, 0);
11961 if (cmdp != NULL) {
11962 int bit;
11963
11964 switch (cmdp->cmdtype) {
11965 default:
11966#if DEBUG
11967 abort();
11968#endif
11969 case CMDNORMAL:
11970 bit = DO_ALTPATH;
11971 break;
11972 case CMDFUNCTION:
11973 bit = DO_NOFUNC;
11974 break;
11975 case CMDBUILTIN:
11976 bit = DO_ALTBLTIN;
11977 break;
11978 }
11979 if (act & bit) {
11980 updatetbl = 0;
11981 cmdp = NULL;
11982 } else if (cmdp->rehash == 0)
11983 /* if not invalidated by cd, we're done */
11984 goto success;
11985 }
11986
11987 /* If %builtin not in path, check for builtin next */
11988 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011989 if (bcmd) {
11990 if (IS_BUILTIN_REGULAR(bcmd))
11991 goto builtin_success;
11992 if (act & DO_ALTPATH) {
11993 if (!(act & DO_ALTBLTIN))
11994 goto builtin_success;
11995 } else if (builtinloc <= 0) {
11996 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011997 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011998 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011999
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012000#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012001 {
12002 int applet_no = find_applet_by_name(name);
12003 if (applet_no >= 0) {
12004 entry->cmdtype = CMDNORMAL;
12005 entry->u.index = -2 - applet_no;
12006 return;
12007 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012008 }
12009#endif
12010
Denis Vlasenkocc571512007-02-23 21:10:35 +000012011 /* We have to search path. */
12012 prev = -1; /* where to start */
12013 if (cmdp && cmdp->rehash) { /* doing a rehash */
12014 if (cmdp->cmdtype == CMDBUILTIN)
12015 prev = builtinloc;
12016 else
12017 prev = cmdp->param.index;
12018 }
12019
12020 e = ENOENT;
12021 idx = -1;
12022 loop:
12023 while ((fullname = padvance(&path, name)) != NULL) {
12024 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012025 /* NB: code below will still use fullname
12026 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012027 idx++;
12028 if (pathopt) {
12029 if (prefix(pathopt, "builtin")) {
12030 if (bcmd)
12031 goto builtin_success;
12032 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012033 }
12034 if ((act & DO_NOFUNC)
12035 || !prefix(pathopt, "func")
12036 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012037 continue;
12038 }
12039 }
12040 /* if rehash, don't redo absolute path names */
12041 if (fullname[0] == '/' && idx <= prev) {
12042 if (idx < prev)
12043 continue;
12044 TRACE(("searchexec \"%s\": no change\n", name));
12045 goto success;
12046 }
12047 while (stat(fullname, &statb) < 0) {
12048#ifdef SYSV
12049 if (errno == EINTR)
12050 continue;
12051#endif
12052 if (errno != ENOENT && errno != ENOTDIR)
12053 e = errno;
12054 goto loop;
12055 }
12056 e = EACCES; /* if we fail, this will be the error */
12057 if (!S_ISREG(statb.st_mode))
12058 continue;
12059 if (pathopt) { /* this is a %func directory */
12060 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012061 /* NB: stalloc will return space pointed by fullname
12062 * (because we don't have any intervening allocations
12063 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012064 readcmdfile(fullname);
12065 cmdp = cmdlookup(name, 0);
12066 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12067 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12068 stunalloc(fullname);
12069 goto success;
12070 }
12071 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12072 if (!updatetbl) {
12073 entry->cmdtype = CMDNORMAL;
12074 entry->u.index = idx;
12075 return;
12076 }
12077 INT_OFF;
12078 cmdp = cmdlookup(name, 1);
12079 cmdp->cmdtype = CMDNORMAL;
12080 cmdp->param.index = idx;
12081 INT_ON;
12082 goto success;
12083 }
12084
12085 /* We failed. If there was an entry for this command, delete it */
12086 if (cmdp && updatetbl)
12087 delete_cmd_entry();
12088 if (act & DO_ERR)
12089 ash_msg("%s: %s", name, errmsg(e, "not found"));
12090 entry->cmdtype = CMDUNKNOWN;
12091 return;
12092
12093 builtin_success:
12094 if (!updatetbl) {
12095 entry->cmdtype = CMDBUILTIN;
12096 entry->u.cmd = bcmd;
12097 return;
12098 }
12099 INT_OFF;
12100 cmdp = cmdlookup(name, 1);
12101 cmdp->cmdtype = CMDBUILTIN;
12102 cmdp->param.cmd = bcmd;
12103 INT_ON;
12104 success:
12105 cmdp->rehash = 0;
12106 entry->cmdtype = cmdp->cmdtype;
12107 entry->u = cmdp->param;
12108}
12109
12110
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012111/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012112
Eric Andersencb57d552001-06-28 07:25:16 +000012113/*
Eric Andersencb57d552001-06-28 07:25:16 +000012114 * The trap builtin.
12115 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012116static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012117trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012118{
12119 char *action;
12120 char **ap;
12121 int signo;
12122
Eric Andersenc470f442003-07-28 09:56:35 +000012123 nextopt(nullstr);
12124 ap = argptr;
12125 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012126 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012127 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012128 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012129 single_quote(trap[signo]),
12130 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012131 }
12132 }
12133 return 0;
12134 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012135 action = NULL;
12136 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012137 action = *ap++;
12138 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012139 signo = get_signum(*ap);
12140 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012141 ash_msg_and_raise_error("%s: bad trap", *ap);
12142 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012143 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012144 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012145 action = NULL;
12146 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012147 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012148 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012149 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012150 trap[signo] = action;
12151 if (signo != 0)
12152 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012153 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012154 ap++;
12155 }
12156 return 0;
12157}
12158
Eric Andersenc470f442003-07-28 09:56:35 +000012159
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012160/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012161
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012162#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012163/*
12164 * Lists available builtins
12165 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012166static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012167helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012168{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012169 unsigned col;
12170 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012171
12172 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012173 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012174 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012175 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012176 if (col > 60) {
12177 out1fmt("\n");
12178 col = 0;
12179 }
12180 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012181#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012182 {
12183 const char *a = applet_names;
12184 while (*a) {
12185 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12186 if (col > 60) {
12187 out1fmt("\n");
12188 col = 0;
12189 }
12190 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012191 }
12192 }
12193#endif
12194 out1fmt("\n\n");
12195 return EXIT_SUCCESS;
12196}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012197#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012198
Eric Andersencb57d552001-06-28 07:25:16 +000012199/*
Eric Andersencb57d552001-06-28 07:25:16 +000012200 * The export and readonly commands.
12201 */
Eric Andersenc470f442003-07-28 09:56:35 +000012202static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012203exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012204{
12205 struct var *vp;
12206 char *name;
12207 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012208 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012209 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012210
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012211 if (nextopt("p") != 'p') {
12212 aptr = argptr;
12213 name = *aptr;
12214 if (name) {
12215 do {
12216 p = strchr(name, '=');
12217 if (p != NULL) {
12218 p++;
12219 } else {
12220 vp = *findvar(hashvar(name), name);
12221 if (vp) {
12222 vp->flags |= flag;
12223 continue;
12224 }
Eric Andersencb57d552001-06-28 07:25:16 +000012225 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012226 setvar(name, p, flag);
12227 } while ((name = *++aptr) != NULL);
12228 return 0;
12229 }
Eric Andersencb57d552001-06-28 07:25:16 +000012230 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012231 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012232 return 0;
12233}
12234
Eric Andersencb57d552001-06-28 07:25:16 +000012235/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012236 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012237 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012238static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012239unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012240{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012241 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012242
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012243 cmdp = cmdlookup(name, 0);
12244 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12245 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012246}
12247
Eric Andersencb57d552001-06-28 07:25:16 +000012248/*
Eric Andersencb57d552001-06-28 07:25:16 +000012249 * The unset builtin command. We unset the function before we unset the
12250 * variable to allow a function to be unset when there is a readonly variable
12251 * with the same name.
12252 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012253static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012254unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012255{
12256 char **ap;
12257 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012258 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012259 int ret = 0;
12260
12261 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012262 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012263 }
Eric Andersencb57d552001-06-28 07:25:16 +000012264
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012265 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012266 if (flag != 'f') {
12267 i = unsetvar(*ap);
12268 ret |= i;
12269 if (!(i & 2))
12270 continue;
12271 }
12272 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012273 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012274 }
Eric Andersenc470f442003-07-28 09:56:35 +000012275 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012276}
12277
12278
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012279/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012280
Eric Andersenc470f442003-07-28 09:56:35 +000012281#include <sys/times.h>
12282
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012283static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012284 ' ', offsetof(struct tms, tms_utime),
12285 '\n', offsetof(struct tms, tms_stime),
12286 ' ', offsetof(struct tms, tms_cutime),
12287 '\n', offsetof(struct tms, tms_cstime),
12288 0
12289};
Eric Andersencb57d552001-06-28 07:25:16 +000012290
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012291static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012292timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012293{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012294 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012295 const unsigned char *p;
12296 struct tms buf;
12297
12298 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012299 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012300
12301 p = timescmd_str;
12302 do {
12303 t = *(clock_t *)(((char *) &buf) + p[1]);
12304 s = t / clk_tck;
12305 out1fmt("%ldm%ld.%.3lds%c",
12306 s/60, s%60,
12307 ((t - s * clk_tck) * 1000) / clk_tck,
12308 p[0]);
12309 } while (*(p += 2));
12310
Eric Andersencb57d552001-06-28 07:25:16 +000012311 return 0;
12312}
12313
Denis Vlasenko131ae172007-02-18 13:00:19 +000012314#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012315static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012316dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012317{
Eric Andersened9ecf72004-06-22 08:29:45 +000012318 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012319 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012320
Denis Vlasenkob012b102007-02-19 22:43:01 +000012321 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012322 result = arith(s, &errcode);
12323 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012324 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012325 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012326 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012327 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012328 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012329 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012330 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012331 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012332 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012333
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012334 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012335}
Eric Andersenc470f442003-07-28 09:56:35 +000012336
Eric Andersenc470f442003-07-28 09:56:35 +000012337/*
Eric Andersen90898442003-08-06 11:20:52 +000012338 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12339 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12340 *
12341 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012342 */
12343static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012344letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012345{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012346 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012347
Denis Vlasenko68404f12008-03-17 09:00:54 +000012348 argv++;
12349 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012350 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012351 do {
12352 i = dash_arith(*argv);
12353 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012354
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012355 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012356}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012357#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012358
Eric Andersenc470f442003-07-28 09:56:35 +000012359
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012360/* ============ miscbltin.c
12361 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012362 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012363 */
12364
12365#undef rflag
12366
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012367#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012368typedef enum __rlimit_resource rlim_t;
12369#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012370
Eric Andersenc470f442003-07-28 09:56:35 +000012371/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012372 * The read builtin. Options:
12373 * -r Do not interpret '\' specially
12374 * -s Turn off echo (tty only)
12375 * -n NCHARS Read NCHARS max
12376 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12377 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12378 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012379 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012380 * TODO: bash also has:
12381 * -a ARRAY Read into array[0],[1],etc
12382 * -d DELIM End on DELIM char, not newline
12383 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012384 */
Eric Andersenc470f442003-07-28 09:56:35 +000012385static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012386readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012387{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012388 static const char *const arg_REPLY[] = { "REPLY", NULL };
12389
Eric Andersenc470f442003-07-28 09:56:35 +000012390 char **ap;
12391 int backslash;
12392 char c;
12393 int rflag;
12394 char *prompt;
12395 const char *ifs;
12396 char *p;
12397 int startword;
12398 int status;
12399 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012400 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012401#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012402 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012403 int silent = 0;
12404 struct termios tty, old_tty;
12405#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012406#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012407 unsigned end_ms = 0;
12408 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012409#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012410
12411 rflag = 0;
12412 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012413 while ((i = nextopt("p:u:r"
12414 USE_ASH_READ_TIMEOUT("t:")
12415 USE_ASH_READ_NCHARS("n:s")
12416 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012417 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012418 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012419 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012420 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012421#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012422 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012423 nchars = bb_strtou(optionarg, NULL, 10);
12424 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012425 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012426 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012427 break;
12428 case 's':
12429 silent = 1;
12430 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012431#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012432#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012433 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012434 timeout = bb_strtou(optionarg, NULL, 10);
12435 if (errno || timeout > UINT_MAX / 2048)
12436 ash_msg_and_raise_error("invalid timeout");
12437 timeout *= 1000;
12438#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012439 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012440 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012441 /* EINVAL means number is ok, but not terminated by NUL */
12442 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012443 char *p2;
12444 if (*++p) {
12445 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012446 ts.tv_usec = bb_strtou(p, &p2, 10);
12447 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012448 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012449 scale = p2 - p;
12450 /* normalize to usec */
12451 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012452 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012453 while (scale++ < 6)
12454 ts.tv_usec *= 10;
12455 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012456 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012457 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012458 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012459 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012460 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012461 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012462#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012463 break;
12464#endif
12465 case 'r':
12466 rflag = 1;
12467 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012468 case 'u':
12469 fd = bb_strtou(optionarg, NULL, 10);
12470 if (fd < 0 || errno)
12471 ash_msg_and_raise_error("invalid file descriptor");
12472 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012473 default:
12474 break;
12475 }
Eric Andersenc470f442003-07-28 09:56:35 +000012476 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012477 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012478 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012479 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012480 ap = argptr;
12481 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012482 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012483 ifs = bltinlookup("IFS");
12484 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012485 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012486#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012487 tcgetattr(fd, &tty);
12488 old_tty = tty;
12489 if (nchars || silent) {
12490 if (nchars) {
12491 tty.c_lflag &= ~ICANON;
12492 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012493 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012494 if (silent) {
12495 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12496 }
12497 /* if tcgetattr failed, tcsetattr will fail too.
12498 * Ignoring, it's harmless. */
12499 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012500 }
12501#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012502
Eric Andersenc470f442003-07-28 09:56:35 +000012503 status = 0;
12504 startword = 1;
12505 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012506#if ENABLE_ASH_READ_TIMEOUT
12507 if (timeout) /* NB: ensuring end_ms is nonzero */
12508 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12509#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012510 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012511 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012512#if ENABLE_ASH_READ_TIMEOUT
12513 if (end_ms) {
12514 struct pollfd pfd[1];
12515 pfd[0].fd = fd;
12516 pfd[0].events = POLLIN;
12517 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12518 if ((int)timeout <= 0 /* already late? */
12519 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12520 ) { /* timed out! */
12521#if ENABLE_ASH_READ_NCHARS
12522 tcsetattr(fd, TCSANOW, &old_tty);
12523#endif
12524 return 1;
12525 }
12526 }
12527#endif
12528 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012529 status = 1;
12530 break;
12531 }
12532 if (c == '\0')
12533 continue;
12534 if (backslash) {
12535 backslash = 0;
12536 if (c != '\n')
12537 goto put;
12538 continue;
12539 }
12540 if (!rflag && c == '\\') {
12541 backslash++;
12542 continue;
12543 }
12544 if (c == '\n')
12545 break;
12546 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12547 continue;
12548 }
12549 startword = 0;
12550 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12551 STACKSTRNUL(p);
12552 setvar(*ap, stackblock(), 0);
12553 ap++;
12554 startword = 1;
12555 STARTSTACKSTR(p);
12556 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012557 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012558 STPUTC(c, p);
12559 }
12560 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012561/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012562#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012563 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012564#else
12565 while (1);
12566#endif
12567
12568#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012569 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012570#endif
12571
Eric Andersenc470f442003-07-28 09:56:35 +000012572 STACKSTRNUL(p);
12573 /* Remove trailing blanks */
12574 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12575 *p = '\0';
12576 setvar(*ap, stackblock(), 0);
12577 while (*++ap != NULL)
12578 setvar(*ap, nullstr, 0);
12579 return status;
12580}
12581
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012582static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012583umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012584{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012585 static const char permuser[3] ALIGN1 = "ugo";
12586 static const char permmode[3] ALIGN1 = "rwx";
12587 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012588 S_IRUSR, S_IWUSR, S_IXUSR,
12589 S_IRGRP, S_IWGRP, S_IXGRP,
12590 S_IROTH, S_IWOTH, S_IXOTH
12591 };
12592
12593 char *ap;
12594 mode_t mask;
12595 int i;
12596 int symbolic_mode = 0;
12597
12598 while (nextopt("S") != '\0') {
12599 symbolic_mode = 1;
12600 }
12601
Denis Vlasenkob012b102007-02-19 22:43:01 +000012602 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012603 mask = umask(0);
12604 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012605 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012606
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012607 ap = *argptr;
12608 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012609 if (symbolic_mode) {
12610 char buf[18];
12611 char *p = buf;
12612
12613 for (i = 0; i < 3; i++) {
12614 int j;
12615
12616 *p++ = permuser[i];
12617 *p++ = '=';
12618 for (j = 0; j < 3; j++) {
12619 if ((mask & permmask[3 * i + j]) == 0) {
12620 *p++ = permmode[j];
12621 }
12622 }
12623 *p++ = ',';
12624 }
12625 *--p = 0;
12626 puts(buf);
12627 } else {
12628 out1fmt("%.4o\n", mask);
12629 }
12630 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012631 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012632 mask = 0;
12633 do {
12634 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012635 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012636 mask = (mask << 3) + (*ap - '0');
12637 } while (*++ap != '\0');
12638 umask(mask);
12639 } else {
12640 mask = ~mask & 0777;
12641 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012642 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012643 }
12644 umask(~mask & 0777);
12645 }
12646 }
12647 return 0;
12648}
12649
12650/*
12651 * ulimit builtin
12652 *
12653 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12654 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12655 * ash by J.T. Conklin.
12656 *
12657 * Public domain.
12658 */
12659
12660struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012661 uint8_t cmd; /* RLIMIT_xxx fit into it */
12662 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012663 char option;
12664};
12665
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012666static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012667#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012668 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012669#endif
12670#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012671 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012672#endif
12673#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012674 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012675#endif
12676#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012677 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012678#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012679#ifdef RLIMIT_CORE
12680 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012681#endif
12682#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012683 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012684#endif
12685#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012686 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012687#endif
12688#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012689 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012690#endif
12691#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012692 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012693#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012694#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012695 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012696#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012697#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012698 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012699#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012700};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012701static const char limits_name[] =
12702#ifdef RLIMIT_CPU
12703 "time(seconds)" "\0"
12704#endif
12705#ifdef RLIMIT_FSIZE
12706 "file(blocks)" "\0"
12707#endif
12708#ifdef RLIMIT_DATA
12709 "data(kb)" "\0"
12710#endif
12711#ifdef RLIMIT_STACK
12712 "stack(kb)" "\0"
12713#endif
12714#ifdef RLIMIT_CORE
12715 "coredump(blocks)" "\0"
12716#endif
12717#ifdef RLIMIT_RSS
12718 "memory(kb)" "\0"
12719#endif
12720#ifdef RLIMIT_MEMLOCK
12721 "locked memory(kb)" "\0"
12722#endif
12723#ifdef RLIMIT_NPROC
12724 "process" "\0"
12725#endif
12726#ifdef RLIMIT_NOFILE
12727 "nofiles" "\0"
12728#endif
12729#ifdef RLIMIT_AS
12730 "vmemory(kb)" "\0"
12731#endif
12732#ifdef RLIMIT_LOCKS
12733 "locks" "\0"
12734#endif
12735;
Eric Andersenc470f442003-07-28 09:56:35 +000012736
Glenn L McGrath76620622004-01-13 10:19:37 +000012737enum limtype { SOFT = 0x1, HARD = 0x2 };
12738
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012739static void
12740printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012741 const struct limits *l)
12742{
12743 rlim_t val;
12744
12745 val = limit->rlim_max;
12746 if (how & SOFT)
12747 val = limit->rlim_cur;
12748
12749 if (val == RLIM_INFINITY)
12750 out1fmt("unlimited\n");
12751 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012752 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012753 out1fmt("%lld\n", (long long) val);
12754 }
12755}
12756
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012757static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012758ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012759{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012760 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012761 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012762 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012763 const struct limits *l;
12764 int set, all = 0;
12765 int optc, what;
12766 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012767
12768 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012769 while ((optc = nextopt("HSa"
12770#ifdef RLIMIT_CPU
12771 "t"
12772#endif
12773#ifdef RLIMIT_FSIZE
12774 "f"
12775#endif
12776#ifdef RLIMIT_DATA
12777 "d"
12778#endif
12779#ifdef RLIMIT_STACK
12780 "s"
12781#endif
12782#ifdef RLIMIT_CORE
12783 "c"
12784#endif
12785#ifdef RLIMIT_RSS
12786 "m"
12787#endif
12788#ifdef RLIMIT_MEMLOCK
12789 "l"
12790#endif
12791#ifdef RLIMIT_NPROC
12792 "p"
12793#endif
12794#ifdef RLIMIT_NOFILE
12795 "n"
12796#endif
12797#ifdef RLIMIT_AS
12798 "v"
12799#endif
12800#ifdef RLIMIT_LOCKS
12801 "w"
12802#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012803 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012804 switch (optc) {
12805 case 'H':
12806 how = HARD;
12807 break;
12808 case 'S':
12809 how = SOFT;
12810 break;
12811 case 'a':
12812 all = 1;
12813 break;
12814 default:
12815 what = optc;
12816 }
12817
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012818 for (l = limits_tbl; l->option != what; l++)
12819 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012820
12821 set = *argptr ? 1 : 0;
12822 if (set) {
12823 char *p = *argptr;
12824
12825 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012826 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012827 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012828 val = RLIM_INFINITY;
12829 else {
12830 val = (rlim_t) 0;
12831
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012832 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012833 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012834 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012835 if (val < (rlim_t) 0)
12836 break;
12837 }
12838 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012839 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012840 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012841 }
12842 }
12843 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012844 const char *lname = limits_name;
12845 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012846 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012847 out1fmt("%-20s ", lname);
12848 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012849 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012850 }
12851 return 0;
12852 }
12853
12854 getrlimit(l->cmd, &limit);
12855 if (set) {
12856 if (how & HARD)
12857 limit.rlim_max = val;
12858 if (how & SOFT)
12859 limit.rlim_cur = val;
12860 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012861 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012862 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012863 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012864 }
12865 return 0;
12866}
12867
Eric Andersen90898442003-08-06 11:20:52 +000012868
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012869/* ============ Math support */
12870
Denis Vlasenko131ae172007-02-18 13:00:19 +000012871#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012872
12873/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12874
12875 Permission is hereby granted, free of charge, to any person obtaining
12876 a copy of this software and associated documentation files (the
12877 "Software"), to deal in the Software without restriction, including
12878 without limitation the rights to use, copy, modify, merge, publish,
12879 distribute, sublicense, and/or sell copies of the Software, and to
12880 permit persons to whom the Software is furnished to do so, subject to
12881 the following conditions:
12882
12883 The above copyright notice and this permission notice shall be
12884 included in all copies or substantial portions of the Software.
12885
12886 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12887 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12888 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12889 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12890 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12891 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12892 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12893*/
12894
12895/* This is my infix parser/evaluator. It is optimized for size, intended
12896 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012897 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012898 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012899 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012900 * be that which POSIX specifies for shells. */
12901
12902/* The code uses a simple two-stack algorithm. See
12903 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012904 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012905 * this is based (this code differs in that it applies operators immediately
12906 * to the stack instead of adding them to a queue to end up with an
12907 * expression). */
12908
12909/* To use the routine, call it with an expression string and error return
12910 * pointer */
12911
12912/*
12913 * Aug 24, 2001 Manuel Novoa III
12914 *
12915 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12916 *
12917 * 1) In arith_apply():
12918 * a) Cached values of *numptr and &(numptr[-1]).
12919 * b) Removed redundant test for zero denominator.
12920 *
12921 * 2) In arith():
12922 * a) Eliminated redundant code for processing operator tokens by moving
12923 * to a table-based implementation. Also folded handling of parens
12924 * into the table.
12925 * b) Combined all 3 loops which called arith_apply to reduce generated
12926 * code size at the cost of speed.
12927 *
12928 * 3) The following expressions were treated as valid by the original code:
12929 * 1() , 0! , 1 ( *3 ) .
12930 * These bugs have been fixed by internally enclosing the expression in
12931 * parens and then checking that all binary ops and right parens are
12932 * preceded by a valid expression (NUM_TOKEN).
12933 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012934 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012935 * ctype's isspace() if it is used by another busybox applet or if additional
12936 * whitespace chars should be considered. Look below the "#include"s for a
12937 * precompiler test.
12938 */
12939
12940/*
12941 * Aug 26, 2001 Manuel Novoa III
12942 *
12943 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12944 *
12945 * Merge in Aaron's comments previously posted to the busybox list,
12946 * modified slightly to take account of my changes to the code.
12947 *
12948 */
12949
12950/*
12951 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12952 *
12953 * - allow access to variable,
12954 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12955 * - realize assign syntax (VAR=expr, +=, *= etc)
12956 * - realize exponentiation (** operator)
12957 * - realize comma separated - expr, expr
12958 * - realise ++expr --expr expr++ expr--
12959 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012960 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012961 * - was restored loses XOR operator
12962 * - remove one goto label, added three ;-)
12963 * - protect $((num num)) as true zero expr (Manuel`s error)
12964 * - always use special isspace(), see comment from bash ;-)
12965 */
12966
Eric Andersen90898442003-08-06 11:20:52 +000012967#define arith_isspace(arithval) \
12968 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12969
Eric Andersen90898442003-08-06 11:20:52 +000012970typedef unsigned char operator;
12971
12972/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012973 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012974 * precedence. The ID portion is so that multiple operators can have the
12975 * same precedence, ensuring that the leftmost one is evaluated first.
12976 * Consider * and /. */
12977
12978#define tok_decl(prec,id) (((id)<<5)|(prec))
12979#define PREC(op) ((op) & 0x1F)
12980
12981#define TOK_LPAREN tok_decl(0,0)
12982
12983#define TOK_COMMA tok_decl(1,0)
12984
12985#define TOK_ASSIGN tok_decl(2,0)
12986#define TOK_AND_ASSIGN tok_decl(2,1)
12987#define TOK_OR_ASSIGN tok_decl(2,2)
12988#define TOK_XOR_ASSIGN tok_decl(2,3)
12989#define TOK_PLUS_ASSIGN tok_decl(2,4)
12990#define TOK_MINUS_ASSIGN tok_decl(2,5)
12991#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12992#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12993
12994#define TOK_MUL_ASSIGN tok_decl(3,0)
12995#define TOK_DIV_ASSIGN tok_decl(3,1)
12996#define TOK_REM_ASSIGN tok_decl(3,2)
12997
12998/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012999#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000013000
13001/* conditional is right associativity too */
13002#define TOK_CONDITIONAL tok_decl(4,0)
13003#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13004
13005#define TOK_OR tok_decl(5,0)
13006
13007#define TOK_AND tok_decl(6,0)
13008
13009#define TOK_BOR tok_decl(7,0)
13010
13011#define TOK_BXOR tok_decl(8,0)
13012
13013#define TOK_BAND tok_decl(9,0)
13014
13015#define TOK_EQ tok_decl(10,0)
13016#define TOK_NE tok_decl(10,1)
13017
13018#define TOK_LT tok_decl(11,0)
13019#define TOK_GT tok_decl(11,1)
13020#define TOK_GE tok_decl(11,2)
13021#define TOK_LE tok_decl(11,3)
13022
13023#define TOK_LSHIFT tok_decl(12,0)
13024#define TOK_RSHIFT tok_decl(12,1)
13025
13026#define TOK_ADD tok_decl(13,0)
13027#define TOK_SUB tok_decl(13,1)
13028
13029#define TOK_MUL tok_decl(14,0)
13030#define TOK_DIV tok_decl(14,1)
13031#define TOK_REM tok_decl(14,2)
13032
13033/* exponent is right associativity */
13034#define TOK_EXPONENT tok_decl(15,1)
13035
13036/* For now unary operators. */
13037#define UNARYPREC 16
13038#define TOK_BNOT tok_decl(UNARYPREC,0)
13039#define TOK_NOT tok_decl(UNARYPREC,1)
13040
13041#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13042#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13043
13044#define PREC_PRE (UNARYPREC+2)
13045
13046#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13047#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13048
13049#define PREC_POST (UNARYPREC+3)
13050
13051#define TOK_POST_INC tok_decl(PREC_POST, 0)
13052#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13053
13054#define SPEC_PREC (UNARYPREC+4)
13055
13056#define TOK_NUM tok_decl(SPEC_PREC, 0)
13057#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13058
13059#define NUMPTR (*numstackptr)
13060
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013061static int
13062tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013063{
13064 operator prec = PREC(op);
13065
13066 convert_prec_is_assing(prec);
13067 return (prec == PREC(TOK_ASSIGN) ||
13068 prec == PREC_PRE || prec == PREC_POST);
13069}
13070
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013071static int
13072is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013073{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013074 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13075 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013076}
13077
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013078typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013079 arith_t val;
13080 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013081 char contidional_second_val_initialized;
13082 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013083 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013084} v_n_t;
13085
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013086typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013087 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013088 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013089} chk_var_recursive_looped_t;
13090
13091static chk_var_recursive_looped_t *prev_chk_var_recursive;
13092
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013093static int
13094arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013095{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 if (t->var) {
13097 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013098
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013099 if (p) {
13100 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013101
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013102 /* recursive try as expression */
13103 chk_var_recursive_looped_t *cur;
13104 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013105
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013106 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13107 if (strcmp(cur->var, t->var) == 0) {
13108 /* expression recursion loop detected */
13109 return -5;
13110 }
13111 }
13112 /* save current lookuped var name */
13113 cur = prev_chk_var_recursive;
13114 cur_save.var = t->var;
13115 cur_save.next = cur;
13116 prev_chk_var_recursive = &cur_save;
13117
13118 t->val = arith (p, &errcode);
13119 /* restore previous ptr after recursiving */
13120 prev_chk_var_recursive = cur;
13121 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013122 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 /* allow undefined var as 0 */
13124 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013125 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013126 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013127}
13128
13129/* "applying" a token means performing it on the top elements on the integer
13130 * stack. For a unary operator it will only change the top element, but a
13131 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013132static int
13133arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013134{
Eric Andersen90898442003-08-06 11:20:52 +000013135 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013136 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013137 int ret_arith_lookup_val;
13138
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013139 /* There is no operator that can work without arguments */
13140 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013141 numptr_m1 = NUMPTR - 1;
13142
13143 /* check operand is var with noninteger value */
13144 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013145 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013146 return ret_arith_lookup_val;
13147
13148 rez = numptr_m1->val;
13149 if (op == TOK_UMINUS)
13150 rez *= -1;
13151 else if (op == TOK_NOT)
13152 rez = !rez;
13153 else if (op == TOK_BNOT)
13154 rez = ~rez;
13155 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13156 rez++;
13157 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13158 rez--;
13159 else if (op != TOK_UPLUS) {
13160 /* Binary operators */
13161
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013162 /* check and binary operators need two arguments */
13163 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013164
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013165 /* ... and they pop one */
13166 --NUMPTR;
13167 numptr_val = rez;
13168 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013169 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013170 /* protect $((expr1 ? expr2)) without ": expr" */
13171 goto err;
13172 }
13173 rez = numptr_m1->contidional_second_val;
13174 } else if (numptr_m1->contidional_second_val_initialized) {
13175 /* protect $((expr1 : expr2)) without "expr ? " */
13176 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013177 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013178 numptr_m1 = NUMPTR - 1;
13179 if (op != TOK_ASSIGN) {
13180 /* check operand is var with noninteger value for not '=' */
13181 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13182 if (ret_arith_lookup_val)
13183 return ret_arith_lookup_val;
13184 }
13185 if (op == TOK_CONDITIONAL) {
13186 numptr_m1->contidional_second_val = rez;
13187 }
13188 rez = numptr_m1->val;
13189 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013190 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013191 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013192 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013193 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013194 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013195 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013196 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013197 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013198 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013199 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013200 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013201 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013202 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013203 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013204 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013206 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013207 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013208 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013209 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013210 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013211 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013212 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013213 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013214 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013215 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013216 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013217 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013218 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013219 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013220 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013222 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013223 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013224 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013225 /* protect $((expr : expr)) without "expr ? " */
13226 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013227 }
13228 numptr_m1->contidional_second_val_initialized = op;
13229 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013230 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013231 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013232 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013233 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013234 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013235 return -3; /* exponent less than 0 */
13236 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013237 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013238
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013239 if (numptr_val)
13240 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013241 c *= rez;
13242 rez = c;
13243 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013244 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013245 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013246 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013247 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013248 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013249 rez %= numptr_val;
13250 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013251 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013252 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013253
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013254 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013255 /* Hmm, 1=2 ? */
13256 goto err;
13257 }
13258 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013259#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013260 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013261#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013262 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013263#endif
Eric Andersen90898442003-08-06 11:20:52 +000013264 setvar(numptr_m1->var, buf, 0);
13265 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013267 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013268 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013269 rez++;
13270 }
13271 numptr_m1->val = rez;
13272 /* protect geting var value, is number now */
13273 numptr_m1->var = NULL;
13274 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013275 err:
13276 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013277}
13278
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013279/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013280static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013281 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13282 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13283 '<','<', 0, TOK_LSHIFT,
13284 '>','>', 0, TOK_RSHIFT,
13285 '|','|', 0, TOK_OR,
13286 '&','&', 0, TOK_AND,
13287 '!','=', 0, TOK_NE,
13288 '<','=', 0, TOK_LE,
13289 '>','=', 0, TOK_GE,
13290 '=','=', 0, TOK_EQ,
13291 '|','=', 0, TOK_OR_ASSIGN,
13292 '&','=', 0, TOK_AND_ASSIGN,
13293 '*','=', 0, TOK_MUL_ASSIGN,
13294 '/','=', 0, TOK_DIV_ASSIGN,
13295 '%','=', 0, TOK_REM_ASSIGN,
13296 '+','=', 0, TOK_PLUS_ASSIGN,
13297 '-','=', 0, TOK_MINUS_ASSIGN,
13298 '-','-', 0, TOK_POST_DEC,
13299 '^','=', 0, TOK_XOR_ASSIGN,
13300 '+','+', 0, TOK_POST_INC,
13301 '*','*', 0, TOK_EXPONENT,
13302 '!', 0, TOK_NOT,
13303 '<', 0, TOK_LT,
13304 '>', 0, TOK_GT,
13305 '=', 0, TOK_ASSIGN,
13306 '|', 0, TOK_BOR,
13307 '&', 0, TOK_BAND,
13308 '*', 0, TOK_MUL,
13309 '/', 0, TOK_DIV,
13310 '%', 0, TOK_REM,
13311 '+', 0, TOK_ADD,
13312 '-', 0, TOK_SUB,
13313 '^', 0, TOK_BXOR,
13314 /* uniq */
13315 '~', 0, TOK_BNOT,
13316 ',', 0, TOK_COMMA,
13317 '?', 0, TOK_CONDITIONAL,
13318 ':', 0, TOK_CONDITIONAL_SEP,
13319 ')', 0, TOK_RPAREN,
13320 '(', 0, TOK_LPAREN,
13321 0
13322};
13323/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013324#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013325
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013326static arith_t
13327arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013328{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013329 char arithval; /* Current character under analysis */
13330 operator lasttok, op;
13331 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013332 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013333 const char *p = endexpression;
13334 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013335 v_n_t *numstack, *numstackptr;
13336 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013337
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013338 /* Stack of integers */
13339 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13340 * in any given correct or incorrect expression is left as an exercise to
13341 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013342 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013343 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013344 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013345
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013346 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13347 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013349 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013350 arithval = *expr;
13351 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013352 if (p == endexpression) {
13353 /* Null expression. */
13354 return 0;
13355 }
13356
13357 /* This is only reached after all tokens have been extracted from the
13358 * input stream. If there are still tokens on the operator stack, they
13359 * are to be applied in order. At the end, there should be a final
13360 * result on the integer stack */
13361
13362 if (expr != endexpression + 1) {
13363 /* If we haven't done so already, */
13364 /* append a closing right paren */
13365 expr = endexpression;
13366 /* and let the loop process it. */
13367 continue;
13368 }
13369 /* At this point, we're done with the expression. */
13370 if (numstackptr != numstack+1) {
13371 /* ... but if there isn't, it's bad */
13372 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013373 *perrcode = -1;
13374 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013375 }
13376 if (numstack->var) {
13377 /* expression is $((var)) only, lookup now */
13378 errcode = arith_lookup_val(numstack);
13379 }
13380 ret:
13381 *perrcode = errcode;
13382 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013383 }
13384
Eric Andersen90898442003-08-06 11:20:52 +000013385 /* Continue processing the expression. */
13386 if (arith_isspace(arithval)) {
13387 /* Skip whitespace */
13388 goto prologue;
13389 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013390 p = endofname(expr);
13391 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013392 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013393
13394 numstackptr->var = alloca(var_name_size);
13395 safe_strncpy(numstackptr->var, expr, var_name_size);
13396 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013397 num:
Eric Andersen90898442003-08-06 11:20:52 +000013398 numstackptr->contidional_second_val_initialized = 0;
13399 numstackptr++;
13400 lasttok = TOK_NUM;
13401 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013402 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013403 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013404 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013405#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013406 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013407#else
13408 numstackptr->val = strtol(expr, (char **) &expr, 0);
13409#endif
Eric Andersen90898442003-08-06 11:20:52 +000013410 goto num;
13411 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013412 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013413 const char *o;
13414
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013415 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013416 /* strange operator not found */
13417 goto err;
13418 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013419 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013420 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013421 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013422 /* found */
13423 expr = o - 1;
13424 break;
13425 }
13426 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013427 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013428 p++;
13429 /* skip zero delim */
13430 p++;
13431 }
13432 op = p[1];
13433
13434 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013435 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13436 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013437
13438 /* Plus and minus are binary (not unary) _only_ if the last
13439 * token was as number, or a right paren (which pretends to be
13440 * a number, since it evaluates to one). Think about it.
13441 * It makes sense. */
13442 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013443 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013444 case TOK_ADD:
13445 op = TOK_UPLUS;
13446 break;
13447 case TOK_SUB:
13448 op = TOK_UMINUS;
13449 break;
13450 case TOK_POST_INC:
13451 op = TOK_PRE_INC;
13452 break;
13453 case TOK_POST_DEC:
13454 op = TOK_PRE_DEC;
13455 break;
Eric Andersen90898442003-08-06 11:20:52 +000013456 }
13457 }
13458 /* We don't want a unary operator to cause recursive descent on the
13459 * stack, because there can be many in a row and it could cause an
13460 * operator to be evaluated before its argument is pushed onto the
13461 * integer stack. */
13462 /* But for binary operators, "apply" everything on the operator
13463 * stack until we find an operator with a lesser priority than the
13464 * one we have just extracted. */
13465 /* Left paren is given the lowest priority so it will never be
13466 * "applied" in this way.
13467 * if associativity is right and priority eq, applied also skip
13468 */
13469 prec = PREC(op);
13470 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13471 /* not left paren or unary */
13472 if (lasttok != TOK_NUM) {
13473 /* binary op must be preceded by a num */
13474 goto err;
13475 }
13476 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013477 if (op == TOK_RPAREN) {
13478 /* The algorithm employed here is simple: while we don't
13479 * hit an open paren nor the bottom of the stack, pop
13480 * tokens and apply them */
13481 if (stackptr[-1] == TOK_LPAREN) {
13482 --stackptr;
13483 /* Any operator directly after a */
13484 lasttok = TOK_NUM;
13485 /* close paren should consider itself binary */
13486 goto prologue;
13487 }
13488 } else {
13489 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013490
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013491 convert_prec_is_assing(prec);
13492 convert_prec_is_assing(prev_prec);
13493 if (prev_prec < prec)
13494 break;
13495 /* check right assoc */
13496 if (prev_prec == prec && is_right_associativity(prec))
13497 break;
13498 }
13499 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13500 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013501 }
13502 if (op == TOK_RPAREN) {
13503 goto err;
13504 }
13505 }
13506
13507 /* Push this operator to the stack and remember it. */
13508 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013509 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013510 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013511 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013512}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013513#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013514
13515
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013516/* ============ main() and helpers */
13517
13518/*
13519 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013520 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013521static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013522static void
13523exitshell(void)
13524{
13525 struct jmploc loc;
13526 char *p;
13527 int status;
13528
13529 status = exitstatus;
13530 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13531 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013532 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013533/* dash bug: it just does _exit(exitstatus) here
13534 * but we have to do setjobctl(0) first!
13535 * (bug is still not fixed in dash-0.5.3 - if you run dash
13536 * under Midnight Commander, on exit from dash MC is backgrounded) */
13537 status = exitstatus;
13538 goto out;
13539 }
13540 exception_handler = &loc;
13541 p = trap[0];
13542 if (p) {
13543 trap[0] = NULL;
13544 evalstring(p, 0);
13545 }
13546 flush_stdout_stderr();
13547 out:
13548 setjobctl(0);
13549 _exit(status);
13550 /* NOTREACHED */
13551}
13552
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013553static void
13554init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013555{
13556 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013557 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013558
13559 /* from trap.c: */
13560 signal(SIGCHLD, SIG_DFL);
13561
13562 /* from var.c: */
13563 {
13564 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013565 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013566 const char *p;
13567 struct stat st1, st2;
13568
13569 initvar();
13570 for (envp = environ; envp && *envp; envp++) {
13571 if (strchr(*envp, '=')) {
13572 setvareq(*envp, VEXPORT|VTEXTFIXED);
13573 }
13574 }
13575
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013576 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013577 setvar("PPID", ppid, 0);
13578
13579 p = lookupvar("PWD");
13580 if (p)
13581 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13582 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13583 p = '\0';
13584 setpwd(p, 0);
13585 }
13586}
13587
13588/*
13589 * Process the shell command line arguments.
13590 */
13591static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013592procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013593{
13594 int i;
13595 const char *xminusc;
13596 char **xargv;
13597
13598 xargv = argv;
13599 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013600 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013601 xargv++;
13602 for (i = 0; i < NOPTS; i++)
13603 optlist[i] = 2;
13604 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013605 if (options(1)) {
13606 /* it already printed err message */
13607 raise_exception(EXERROR);
13608 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013609 xargv = argptr;
13610 xminusc = minusc;
13611 if (*xargv == NULL) {
13612 if (xminusc)
13613 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13614 sflag = 1;
13615 }
13616 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13617 iflag = 1;
13618 if (mflag == 2)
13619 mflag = iflag;
13620 for (i = 0; i < NOPTS; i++)
13621 if (optlist[i] == 2)
13622 optlist[i] = 0;
13623#if DEBUG == 2
13624 debug = 1;
13625#endif
13626 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13627 if (xminusc) {
13628 minusc = *xargv++;
13629 if (*xargv)
13630 goto setarg0;
13631 } else if (!sflag) {
13632 setinputfile(*xargv, 0);
13633 setarg0:
13634 arg0 = *xargv++;
13635 commandname = arg0;
13636 }
13637
13638 shellparam.p = xargv;
13639#if ENABLE_ASH_GETOPTS
13640 shellparam.optind = 1;
13641 shellparam.optoff = -1;
13642#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013643 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013644 while (*xargv) {
13645 shellparam.nparam++;
13646 xargv++;
13647 }
13648 optschanged();
13649}
13650
13651/*
13652 * Read /etc/profile or .profile.
13653 */
13654static void
13655read_profile(const char *name)
13656{
13657 int skip;
13658
13659 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13660 return;
13661 skip = cmdloop(0);
13662 popfile();
13663 if (skip)
13664 exitshell();
13665}
13666
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013667/*
13668 * This routine is called when an error or an interrupt occurs in an
13669 * interactive shell and control is returned to the main command loop.
13670 */
13671static void
13672reset(void)
13673{
13674 /* from eval.c: */
13675 evalskip = 0;
13676 loopnest = 0;
13677 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013678 g_parsefile->left_in_buffer = 0;
13679 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013680 popallfiles();
13681 /* from parser.c: */
13682 tokpushback = 0;
13683 checkkwd = 0;
13684 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013685 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013686}
13687
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013688#if PROFILE
13689static short profile_buf[16384];
13690extern int etext();
13691#endif
13692
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013693/*
13694 * Main routine. We initialize things, parse the arguments, execute
13695 * profiles if we're a login shell, and then call cmdloop to execute
13696 * commands. The setjmp call sets up the location to jump to when an
13697 * exception occurs. When an exception occurs the variable "state"
13698 * is used to figure out how far we had gotten.
13699 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013700int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013701int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013702{
13703 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013704 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013705 struct jmploc jmploc;
13706 struct stackmark smark;
13707
Denis Vlasenko01631112007-12-16 17:20:38 +000013708 /* Initialize global data */
13709 INIT_G_misc();
13710 INIT_G_memstack();
13711 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013712#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013713 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013714#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013715 INIT_G_cmdtable();
13716
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013717#if PROFILE
13718 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13719#endif
13720
13721#if ENABLE_FEATURE_EDITING
13722 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13723#endif
13724 state = 0;
13725 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013726 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013727 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013728
13729 reset();
13730
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013731 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013732 if (e == EXERROR)
13733 exitstatus = 2;
13734 s = state;
13735 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13736 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013737 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013738 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013739
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013740 popstackmark(&smark);
13741 FORCE_INT_ON; /* enable interrupts */
13742 if (s == 1)
13743 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013744 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013745 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013746 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013747 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013748 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013749 }
13750 exception_handler = &jmploc;
13751#if DEBUG
13752 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013753 trace_puts("Shell args: ");
13754 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013755#endif
13756 rootpid = getpid();
13757
13758#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013759 /* Can use monotonic_ns() for better randomness but for now it is
13760 * not used anywhere else in busybox... so avoid bloat */
13761 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013762#endif
13763 init();
13764 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013765 procargs(argv);
13766
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013767#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13768 if (iflag) {
13769 const char *hp = lookupvar("HISTFILE");
13770
13771 if (hp == NULL) {
13772 hp = lookupvar("HOME");
13773 if (hp != NULL) {
13774 char *defhp = concat_path_file(hp, ".ash_history");
13775 setvar("HISTFILE", defhp, 0);
13776 free(defhp);
13777 }
13778 }
13779 }
13780#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013781 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013782 isloginsh = 1;
13783 if (isloginsh) {
13784 state = 1;
13785 read_profile("/etc/profile");
13786 state1:
13787 state = 2;
13788 read_profile(".profile");
13789 }
13790 state2:
13791 state = 3;
13792 if (
13793#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013794 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013795#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013796 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013797 ) {
13798 shinit = lookupvar("ENV");
13799 if (shinit != NULL && *shinit != '\0') {
13800 read_profile(shinit);
13801 }
13802 }
13803 state3:
13804 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013805 if (minusc) {
13806 /* evalstring pushes parsefile stack.
13807 * Ensure we don't falsely claim that 0 (stdin)
13808 * is one of stacked source fds */
13809 if (!sflag)
13810 g_parsefile->fd = -1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013811 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013812 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013813
13814 if (sflag || minusc == NULL) {
13815#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013816 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013817 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013818 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013819 line_input_state->hist_file = hp;
13820 }
13821#endif
13822 state4: /* XXX ??? - why isn't this before the "if" statement */
13823 cmdloop(1);
13824 }
13825#if PROFILE
13826 monitor(0);
13827#endif
13828#ifdef GPROF
13829 {
13830 extern void _mcleanup(void);
13831 _mcleanup();
13832 }
13833#endif
13834 exitshell();
13835 /* NOTREACHED */
13836}
13837
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013838#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013839const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013840int main(int argc, char **argv)
13841{
13842 return ash_main(argc, argv);
13843}
13844#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013845
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013846
Eric Andersendf82f612001-06-28 07:46:40 +000013847/*-
13848 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013849 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013850 *
13851 * This code is derived from software contributed to Berkeley by
13852 * Kenneth Almquist.
13853 *
13854 * Redistribution and use in source and binary forms, with or without
13855 * modification, are permitted provided that the following conditions
13856 * are met:
13857 * 1. Redistributions of source code must retain the above copyright
13858 * notice, this list of conditions and the following disclaimer.
13859 * 2. Redistributions in binary form must reproduce the above copyright
13860 * notice, this list of conditions and the following disclaimer in the
13861 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013862 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013863 * may be used to endorse or promote products derived from this software
13864 * without specific prior written permission.
13865 *
13866 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13867 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13868 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13869 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13870 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13871 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13872 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13873 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13874 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13875 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13876 * SUCH DAMAGE.
13877 */