blob: 913779ab41c2698e2db2365b8e95046901639de9 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
Eric Andersencb57d552001-06-28 07:25:16 +000011 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000014 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000015 *
Eric Andersen81fe1232003-07-29 06:38:40 +000016 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Eric Andersen90898442003-08-06 11:20:52 +000020 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
Eric Andersenef02f822004-03-11 13:34:24 +000023 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000025 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000026 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000027 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
Eric Andersen90898442003-08-06 11:20:52 +000030 */
31
Eric Andersen90898442003-08-06 11:20:52 +000032/*
Eric Andersenc470f442003-07-28 09:56:35 +000033 * The follow should be set to reflect the type of system you have:
34 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V.
36 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
37 * define DEBUG=2 to compile in and turn on debugging.
38 *
39 * When debugging is on, debugging info will be written to ./trace and
40 * a quit signal will generate a core dump.
41 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000042#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000043#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000044
45#define IFS_BROKEN
46
47#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048
Denis Vlasenkob012b102007-02-19 22:43:01 +000049#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000050#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#define _GNU_SOURCE
52#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000053#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000054
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000055#include "busybox.h" /* for applet_names */
Denis Vlasenko61befda2008-11-25 01:36:03 +000056//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
57//#include "applet_tables.h" doesn't work
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko61befda2008-11-25 01:36:03 +000061
62#if defined SINGLE_APPLET_MAIN
63/* STANDALONE does not make sense, and won't compile */
64#undef CONFIG_FEATURE_SH_STANDALONE
65#undef ENABLE_FEATURE_SH_STANDALONE
66#undef USE_FEATURE_SH_STANDALONE
67#undef SKIP_FEATURE_SH_STANDALONE(...)
68#define ENABLE_FEATURE_SH_STANDALONE 0
69#define USE_FEATURE_SH_STANDALONE(...)
70#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000071#endif
72
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000073#ifndef PIPE_BUF
74#define PIPE_BUF 4096 /* amount of buffering in a pipe */
75#endif
76
Denis Vlasenkob012b102007-02-19 22:43:01 +000077#if defined(__uClinux__)
78#error "Do not even bother, ash will not run on uClinux"
79#endif
80
Denis Vlasenkob012b102007-02-19 22:43:01 +000081
Denis Vlasenko01631112007-12-16 17:20:38 +000082/* ============ Hash table sizes. Configurable. */
83
84#define VTABSIZE 39
85#define ATABSIZE 39
86#define CMDTABLESIZE 31 /* should be prime */
87
88
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000089/* ============ Misc helpers */
90
91#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
92
93/* C99 say: "char" declaration may be signed or unsigned default */
94#define signed_char2int(sc) ((int)((signed char)sc))
95
96
Denis Vlasenkob012b102007-02-19 22:43:01 +000097/* ============ Shell options */
98
99static const char *const optletters_optnames[] = {
100 "e" "errexit",
101 "f" "noglob",
102 "I" "ignoreeof",
103 "i" "interactive",
104 "m" "monitor",
105 "n" "noexec",
106 "s" "stdin",
107 "x" "xtrace",
108 "v" "verbose",
109 "C" "noclobber",
110 "a" "allexport",
111 "b" "notify",
112 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000115 ,"\0" "nolog"
116 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000117#endif
118};
119
120#define optletters(n) optletters_optnames[(n)][0]
121#define optnames(n) (&optletters_optnames[(n)][1])
122
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000123enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000124
Eric Andersenc470f442003-07-28 09:56:35 +0000125
Denis Vlasenkob012b102007-02-19 22:43:01 +0000126/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000127
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000128static const char homestr[] ALIGN1 = "HOME";
129static const char snlfmt[] ALIGN1 = "%s\n";
130static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000131
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000132/*
Eric Andersenc470f442003-07-28 09:56:35 +0000133 * We enclose jmp_buf in a structure so that we can declare pointers to
134 * jump locations. The global variable handler contains the location to
135 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000136 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000137 * exception handlers, the user should save the value of handler on entry
138 * to an inner scope, set handler to point to a jmploc structure for the
139 * inner scope, and restore handler on exit from the scope.
140 */
Eric Andersenc470f442003-07-28 09:56:35 +0000141struct jmploc {
142 jmp_buf loc;
143};
Denis Vlasenko01631112007-12-16 17:20:38 +0000144
145struct globals_misc {
146 /* pid of main shell */
147 int rootpid;
148 /* shell level: 0 for the main shell, 1 for its children, and so on */
149 int shlvl;
150#define rootshell (!shlvl)
151 char *minusc; /* argument to -c option */
152
153 char *curdir; // = nullstr; /* current working directory */
154 char *physdir; // = nullstr; /* physical working directory */
155
156 char *arg0; /* value of $0 */
157
158 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000159
160// disabled by vda: cannot understand how it was supposed to work -
161// cannot fix bugs. That's why you have to explain your non-trivial designs!
162// /* do we generate EXSIG events */
163// int exsig; /* counter */
164 volatile int suppressint; /* counter */
165 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
166 /* last pending signal */
167 volatile /*sig_atomic_t*/ smallint pendingsig;
168 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000169 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000170#define EXINT 0 /* SIGINT received */
171#define EXERROR 1 /* a generic error */
172#define EXSHELLPROC 2 /* execute a shell procedure */
173#define EXEXEC 3 /* command execution failed */
174#define EXEXIT 4 /* exit the shell */
175#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000176
Denis Vlasenko01631112007-12-16 17:20:38 +0000177 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000178 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000179
180 char optlist[NOPTS];
181#define eflag optlist[0]
182#define fflag optlist[1]
183#define Iflag optlist[2]
184#define iflag optlist[3]
185#define mflag optlist[4]
186#define nflag optlist[5]
187#define sflag optlist[6]
188#define xflag optlist[7]
189#define vflag optlist[8]
190#define Cflag optlist[9]
191#define aflag optlist[10]
192#define bflag optlist[11]
193#define uflag optlist[12]
194#define viflag optlist[13]
195#if DEBUG
196#define nolog optlist[14]
197#define debug optlist[15]
198#endif
199
200 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /*
202 * Sigmode records the current value of the signal handlers for the various
203 * modes. A value of zero means that the current handler is not known.
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)
234#define exception (G_misc.exception )
235#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;
293 exception = 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 Vlasenko7c139b42007-03-21 20:17:27 +0000388 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000389
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000390 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000391 if (!suppressint) {
392 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000393 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000394 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000395 intpending = 1;
396 }
397}
398
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000399
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000400/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000401
Eric Andersenc470f442003-07-28 09:56:35 +0000402static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000403outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000404{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000405 INT_OFF;
406 fputs(p, file);
407 INT_ON;
408}
409
410static void
411flush_stdout_stderr(void)
412{
413 INT_OFF;
414 fflush(stdout);
415 fflush(stderr);
416 INT_ON;
417}
418
419static void
420flush_stderr(void)
421{
422 INT_OFF;
423 fflush(stderr);
424 INT_ON;
425}
426
427static void
428outcslow(int c, FILE *dest)
429{
430 INT_OFF;
431 putc(c, dest);
432 fflush(dest);
433 INT_ON;
434}
435
436static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
437static int
438out1fmt(const char *fmt, ...)
439{
440 va_list ap;
441 int r;
442
443 INT_OFF;
444 va_start(ap, fmt);
445 r = vprintf(fmt, ap);
446 va_end(ap);
447 INT_ON;
448 return r;
449}
450
451static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
452static int
453fmtstr(char *outbuf, size_t length, const char *fmt, ...)
454{
455 va_list ap;
456 int ret;
457
458 va_start(ap, fmt);
459 INT_OFF;
460 ret = vsnprintf(outbuf, length, fmt, ap);
461 va_end(ap);
462 INT_ON;
463 return ret;
464}
465
466static void
467out1str(const char *p)
468{
469 outstr(p, stdout);
470}
471
472static void
473out2str(const char *p)
474{
475 outstr(p, stderr);
476 flush_stderr();
477}
478
479
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000480/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000481
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000482/* control characters in argument strings */
483#define CTLESC '\201' /* escape next character */
484#define CTLVAR '\202' /* variable defn */
485#define CTLENDVAR '\203'
486#define CTLBACKQ '\204'
487#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
488/* CTLBACKQ | CTLQUOTE == '\205' */
489#define CTLARI '\206' /* arithmetic expression */
490#define CTLENDARI '\207'
491#define CTLQUOTEMARK '\210'
492
493/* variable substitution byte (follows CTLVAR) */
494#define VSTYPE 0x0f /* type of variable substitution */
495#define VSNUL 0x10 /* colon--treat the empty string as unset */
496#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
497
498/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000499#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
500#define VSMINUS 0x2 /* ${var-text} */
501#define VSPLUS 0x3 /* ${var+text} */
502#define VSQUESTION 0x4 /* ${var?message} */
503#define VSASSIGN 0x5 /* ${var=text} */
504#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
505#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
506#define VSTRIMLEFT 0x8 /* ${var#pattern} */
507#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
508#define VSLENGTH 0xa /* ${#var} */
509#if ENABLE_ASH_BASH_COMPAT
510#define VSSUBSTR 0xc /* ${var:position:length} */
511#define VSREPLACE 0xd /* ${var/pattern/replacement} */
512#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
513#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000514
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000515static const char dolatstr[] ALIGN1 = {
516 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
517};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000518
Denis Vlasenko559691a2008-10-05 18:39:31 +0000519#define NCMD 0
520#define NPIPE 1
521#define NREDIR 2
522#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000523#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000524#define NAND 5
525#define NOR 6
526#define NSEMI 7
527#define NIF 8
528#define NWHILE 9
529#define NUNTIL 10
530#define NFOR 11
531#define NCASE 12
532#define NCLIST 13
533#define NDEFUN 14
534#define NARG 15
535#define NTO 16
536#if ENABLE_ASH_BASH_COMPAT
537#define NTO2 17
538#endif
539#define NCLOBBER 18
540#define NFROM 19
541#define NFROMTO 20
542#define NAPPEND 21
543#define NTOFD 22
544#define NFROMFD 23
545#define NHERE 24
546#define NXHERE 25
547#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000548#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000549
550union node;
551
552struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000553 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000554 union node *assign;
555 union node *args;
556 union node *redirect;
557};
558
559struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000560 smallint type;
561 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000562 struct nodelist *cmdlist;
563};
564
565struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000566 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000567 union node *n;
568 union node *redirect;
569};
570
571struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000572 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000573 union node *ch1;
574 union node *ch2;
575};
576
577struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000578 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000579 union node *test;
580 union node *ifpart;
581 union node *elsepart;
582};
583
584struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000585 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000586 union node *args;
587 union node *body;
588 char *var;
589};
590
591struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000592 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000593 union node *expr;
594 union node *cases;
595};
596
597struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000598 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000599 union node *next;
600 union node *pattern;
601 union node *body;
602};
603
604struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000605 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000606 union node *next;
607 char *text;
608 struct nodelist *backquote;
609};
610
Denis Vlasenko559691a2008-10-05 18:39:31 +0000611/* nfile and ndup layout must match!
612 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
613 * that it is actually NTO2 (>&file), and change its type.
614 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000615struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000616 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000617 union node *next;
618 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000619 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000620 union node *fname;
621 char *expfname;
622};
623
624struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000625 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000626 union node *next;
627 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000628 int dupfd;
629 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000630 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000631};
632
633struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000634 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000635 union node *next;
636 int fd;
637 union node *doc;
638};
639
640struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000641 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000642 union node *com;
643};
644
645union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000646 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000647 struct ncmd ncmd;
648 struct npipe npipe;
649 struct nredir nredir;
650 struct nbinary nbinary;
651 struct nif nif;
652 struct nfor nfor;
653 struct ncase ncase;
654 struct nclist nclist;
655 struct narg narg;
656 struct nfile nfile;
657 struct ndup ndup;
658 struct nhere nhere;
659 struct nnot nnot;
660};
661
662struct nodelist {
663 struct nodelist *next;
664 union node *n;
665};
666
667struct funcnode {
668 int count;
669 union node n;
670};
671
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000672/*
673 * Free a parse tree.
674 */
675static void
676freefunc(struct funcnode *f)
677{
678 if (f && --f->count < 0)
679 free(f);
680}
681
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000682
683/* ============ Debugging output */
684
685#if DEBUG
686
687static FILE *tracefile;
688
689static void
690trace_printf(const char *fmt, ...)
691{
692 va_list va;
693
694 if (debug != 1)
695 return;
696 va_start(va, fmt);
697 vfprintf(tracefile, fmt, va);
698 va_end(va);
699}
700
701static void
702trace_vprintf(const char *fmt, va_list va)
703{
704 if (debug != 1)
705 return;
706 vfprintf(tracefile, fmt, va);
707}
708
709static void
710trace_puts(const char *s)
711{
712 if (debug != 1)
713 return;
714 fputs(s, tracefile);
715}
716
717static void
718trace_puts_quoted(char *s)
719{
720 char *p;
721 char c;
722
723 if (debug != 1)
724 return;
725 putc('"', tracefile);
726 for (p = s; *p; p++) {
727 switch (*p) {
728 case '\n': c = 'n'; goto backslash;
729 case '\t': c = 't'; goto backslash;
730 case '\r': c = 'r'; goto backslash;
731 case '"': c = '"'; goto backslash;
732 case '\\': c = '\\'; goto backslash;
733 case CTLESC: c = 'e'; goto backslash;
734 case CTLVAR: c = 'v'; goto backslash;
735 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
736 case CTLBACKQ: c = 'q'; goto backslash;
737 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
738 backslash:
739 putc('\\', tracefile);
740 putc(c, tracefile);
741 break;
742 default:
743 if (*p >= ' ' && *p <= '~')
744 putc(*p, tracefile);
745 else {
746 putc('\\', tracefile);
747 putc(*p >> 6 & 03, tracefile);
748 putc(*p >> 3 & 07, tracefile);
749 putc(*p & 07, tracefile);
750 }
751 break;
752 }
753 }
754 putc('"', tracefile);
755}
756
757static void
758trace_puts_args(char **ap)
759{
760 if (debug != 1)
761 return;
762 if (!*ap)
763 return;
764 while (1) {
765 trace_puts_quoted(*ap);
766 if (!*++ap) {
767 putc('\n', tracefile);
768 break;
769 }
770 putc(' ', tracefile);
771 }
772}
773
774static void
775opentrace(void)
776{
777 char s[100];
778#ifdef O_APPEND
779 int flags;
780#endif
781
782 if (debug != 1) {
783 if (tracefile)
784 fflush(tracefile);
785 /* leave open because libedit might be using it */
786 return;
787 }
788 strcpy(s, "./trace");
789 if (tracefile) {
790 if (!freopen(s, "a", tracefile)) {
791 fprintf(stderr, "Can't re-open %s\n", s);
792 debug = 0;
793 return;
794 }
795 } else {
796 tracefile = fopen(s, "a");
797 if (tracefile == NULL) {
798 fprintf(stderr, "Can't open %s\n", s);
799 debug = 0;
800 return;
801 }
802 }
803#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000804 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000805 if (flags >= 0)
806 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
807#endif
808 setlinebuf(tracefile);
809 fputs("\nTracing started.\n", tracefile);
810}
811
812static void
813indent(int amount, char *pfx, FILE *fp)
814{
815 int i;
816
817 for (i = 0; i < amount; i++) {
818 if (pfx && i == amount - 1)
819 fputs(pfx, fp);
820 putc('\t', fp);
821 }
822}
823
824/* little circular references here... */
825static void shtree(union node *n, int ind, char *pfx, FILE *fp);
826
827static void
828sharg(union node *arg, FILE *fp)
829{
830 char *p;
831 struct nodelist *bqlist;
832 int subtype;
833
834 if (arg->type != NARG) {
835 out1fmt("<node type %d>\n", arg->type);
836 abort();
837 }
838 bqlist = arg->narg.backquote;
839 for (p = arg->narg.text; *p; p++) {
840 switch (*p) {
841 case CTLESC:
842 putc(*++p, fp);
843 break;
844 case CTLVAR:
845 putc('$', fp);
846 putc('{', fp);
847 subtype = *++p;
848 if (subtype == VSLENGTH)
849 putc('#', fp);
850
851 while (*p != '=')
852 putc(*p++, fp);
853
854 if (subtype & VSNUL)
855 putc(':', fp);
856
857 switch (subtype & VSTYPE) {
858 case VSNORMAL:
859 putc('}', fp);
860 break;
861 case VSMINUS:
862 putc('-', fp);
863 break;
864 case VSPLUS:
865 putc('+', fp);
866 break;
867 case VSQUESTION:
868 putc('?', fp);
869 break;
870 case VSASSIGN:
871 putc('=', fp);
872 break;
873 case VSTRIMLEFT:
874 putc('#', fp);
875 break;
876 case VSTRIMLEFTMAX:
877 putc('#', fp);
878 putc('#', fp);
879 break;
880 case VSTRIMRIGHT:
881 putc('%', fp);
882 break;
883 case VSTRIMRIGHTMAX:
884 putc('%', fp);
885 putc('%', fp);
886 break;
887 case VSLENGTH:
888 break;
889 default:
890 out1fmt("<subtype %d>", subtype);
891 }
892 break;
893 case CTLENDVAR:
894 putc('}', fp);
895 break;
896 case CTLBACKQ:
897 case CTLBACKQ|CTLQUOTE:
898 putc('$', fp);
899 putc('(', fp);
900 shtree(bqlist->n, -1, NULL, fp);
901 putc(')', fp);
902 break;
903 default:
904 putc(*p, fp);
905 break;
906 }
907 }
908}
909
910static void
911shcmd(union node *cmd, FILE *fp)
912{
913 union node *np;
914 int first;
915 const char *s;
916 int dftfd;
917
918 first = 1;
919 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000920 if (!first)
921 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000922 sharg(np, fp);
923 first = 0;
924 }
925 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000926 if (!first)
927 putc(' ', fp);
928 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000929 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000930 case NTO: s = ">>"+1; dftfd = 1; break;
931 case NCLOBBER: s = ">|"; dftfd = 1; break;
932 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000933#if ENABLE_ASH_BASH_COMPAT
934 case NTO2:
935#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000936 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000937 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000938 case NFROMFD: s = "<&"; break;
939 case NFROMTO: s = "<>"; break;
940 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000941 }
942 if (np->nfile.fd != dftfd)
943 fprintf(fp, "%d", np->nfile.fd);
944 fputs(s, fp);
945 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
946 fprintf(fp, "%d", np->ndup.dupfd);
947 } else {
948 sharg(np->nfile.fname, fp);
949 }
950 first = 0;
951 }
952}
953
954static void
955shtree(union node *n, int ind, char *pfx, FILE *fp)
956{
957 struct nodelist *lp;
958 const char *s;
959
960 if (n == NULL)
961 return;
962
963 indent(ind, pfx, fp);
964 switch (n->type) {
965 case NSEMI:
966 s = "; ";
967 goto binop;
968 case NAND:
969 s = " && ";
970 goto binop;
971 case NOR:
972 s = " || ";
973 binop:
974 shtree(n->nbinary.ch1, ind, NULL, fp);
975 /* if (ind < 0) */
976 fputs(s, fp);
977 shtree(n->nbinary.ch2, ind, NULL, fp);
978 break;
979 case NCMD:
980 shcmd(n, fp);
981 if (ind >= 0)
982 putc('\n', fp);
983 break;
984 case NPIPE:
985 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
986 shcmd(lp->n, fp);
987 if (lp->next)
988 fputs(" | ", fp);
989 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000990 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000991 fputs(" &", fp);
992 if (ind >= 0)
993 putc('\n', fp);
994 break;
995 default:
996 fprintf(fp, "<node type %d>", n->type);
997 if (ind >= 0)
998 putc('\n', fp);
999 break;
1000 }
1001}
1002
1003static void
1004showtree(union node *n)
1005{
1006 trace_puts("showtree called\n");
1007 shtree(n, 1, NULL, stdout);
1008}
1009
1010#define TRACE(param) trace_printf param
1011#define TRACEV(param) trace_vprintf param
1012
1013#else
1014
1015#define TRACE(param)
1016#define TRACEV(param)
1017
1018#endif /* DEBUG */
1019
1020
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001021/* ============ Parser data */
1022
1023/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001024 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1025 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001026struct strlist {
1027 struct strlist *next;
1028 char *text;
1029};
1030
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001031struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001032
Denis Vlasenkob012b102007-02-19 22:43:01 +00001033struct strpush {
1034 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001035 char *prev_string;
1036 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001037#if ENABLE_ASH_ALIAS
1038 struct alias *ap; /* if push was associated with an alias */
1039#endif
1040 char *string; /* remember the string since it may change */
1041};
1042
1043struct parsefile {
1044 struct parsefile *prev; /* preceding file on stack */
1045 int linno; /* current line */
1046 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001047 int left_in_line; /* number of chars left in this line */
1048 int left_in_buffer; /* number of chars left in this buffer past the line */
1049 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001050 char *buf; /* input buffer */
1051 struct strpush *strpush; /* for pushing strings at this level */
1052 struct strpush basestrpush; /* so pushing one is fast */
1053};
1054
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001055static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001056static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001057static int startlinno; /* line # where last token started */
1058static char *commandname; /* currently executing command */
1059static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001060static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001061
1062
1063/* ============ Message printing */
1064
1065static void
1066ash_vmsg(const char *msg, va_list ap)
1067{
1068 fprintf(stderr, "%s: ", arg0);
1069 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001070 if (strcmp(arg0, commandname))
1071 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001072 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001073 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001074 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001075 vfprintf(stderr, msg, ap);
1076 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001077}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001078
1079/*
1080 * Exverror is called to raise the error exception. If the second argument
1081 * is not NULL then error prints an error message using printf style
1082 * formatting. It then raises the error exception.
1083 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001084static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001085static void
1086ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001087{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001088#if DEBUG
1089 if (msg) {
1090 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1091 TRACEV((msg, ap));
1092 TRACE(("\") pid=%d\n", getpid()));
1093 } else
1094 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1095 if (msg)
1096#endif
1097 ash_vmsg(msg, ap);
1098
1099 flush_stdout_stderr();
1100 raise_exception(cond);
1101 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001102}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001103
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001104static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001105static void
1106ash_msg_and_raise_error(const char *msg, ...)
1107{
1108 va_list ap;
1109
1110 va_start(ap, msg);
1111 ash_vmsg_and_raise(EXERROR, msg, ap);
1112 /* NOTREACHED */
1113 va_end(ap);
1114}
1115
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001116static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001117static void
1118ash_msg_and_raise(int cond, const char *msg, ...)
1119{
1120 va_list ap;
1121
1122 va_start(ap, msg);
1123 ash_vmsg_and_raise(cond, msg, ap);
1124 /* NOTREACHED */
1125 va_end(ap);
1126}
1127
1128/*
1129 * error/warning routines for external builtins
1130 */
1131static void
1132ash_msg(const char *fmt, ...)
1133{
1134 va_list ap;
1135
1136 va_start(ap, fmt);
1137 ash_vmsg(fmt, ap);
1138 va_end(ap);
1139}
1140
1141/*
1142 * Return a string describing an error. The returned string may be a
1143 * pointer to a static buffer that will be overwritten on the next call.
1144 * Action describes the operation that got the error.
1145 */
1146static const char *
1147errmsg(int e, const char *em)
1148{
1149 if (e == ENOENT || e == ENOTDIR) {
1150 return em;
1151 }
1152 return strerror(e);
1153}
1154
1155
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001156/* ============ Memory allocation */
1157
1158/*
1159 * It appears that grabstackstr() will barf with such alignments
1160 * because stalloc() will return a string allocated in a new stackblock.
1161 */
1162#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1163enum {
1164 /* Most machines require the value returned from malloc to be aligned
1165 * in some way. The following macro will get this right
1166 * on many machines. */
1167 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1168 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001169 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001170};
1171
1172struct stack_block {
1173 struct stack_block *prev;
1174 char space[MINSIZE];
1175};
1176
1177struct stackmark {
1178 struct stack_block *stackp;
1179 char *stacknxt;
1180 size_t stacknleft;
1181 struct stackmark *marknext;
1182};
1183
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001184
Denis Vlasenko01631112007-12-16 17:20:38 +00001185struct globals_memstack {
1186 struct stack_block *g_stackp; // = &stackbase;
1187 struct stackmark *markp;
1188 char *g_stacknxt; // = stackbase.space;
1189 char *sstrend; // = stackbase.space + MINSIZE;
1190 size_t g_stacknleft; // = MINSIZE;
1191 int herefd; // = -1;
1192 struct stack_block stackbase;
1193};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001194extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1195#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001196#define g_stackp (G_memstack.g_stackp )
1197#define markp (G_memstack.markp )
1198#define g_stacknxt (G_memstack.g_stacknxt )
1199#define sstrend (G_memstack.sstrend )
1200#define g_stacknleft (G_memstack.g_stacknleft)
1201#define herefd (G_memstack.herefd )
1202#define stackbase (G_memstack.stackbase )
1203#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001204 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1205 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001206 g_stackp = &stackbase; \
1207 g_stacknxt = stackbase.space; \
1208 g_stacknleft = MINSIZE; \
1209 sstrend = stackbase.space + MINSIZE; \
1210 herefd = -1; \
1211} while (0)
1212
1213#define stackblock() ((void *)g_stacknxt)
1214#define stackblocksize() g_stacknleft
1215
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001216
1217static void *
1218ckrealloc(void * p, size_t nbytes)
1219{
1220 p = realloc(p, nbytes);
1221 if (!p)
1222 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1223 return p;
1224}
1225
1226static void *
1227ckmalloc(size_t nbytes)
1228{
1229 return ckrealloc(NULL, nbytes);
1230}
1231
Denis Vlasenko597906c2008-02-20 16:38:54 +00001232static void *
1233ckzalloc(size_t nbytes)
1234{
1235 return memset(ckmalloc(nbytes), 0, nbytes);
1236}
1237
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001238/*
1239 * Make a copy of a string in safe storage.
1240 */
1241static char *
1242ckstrdup(const char *s)
1243{
1244 char *p = strdup(s);
1245 if (!p)
1246 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1247 return p;
1248}
1249
1250/*
1251 * Parse trees for commands are allocated in lifo order, so we use a stack
1252 * to make this more efficient, and also to avoid all sorts of exception
1253 * handling code to handle interrupts in the middle of a parse.
1254 *
1255 * The size 504 was chosen because the Ultrix malloc handles that size
1256 * well.
1257 */
1258static void *
1259stalloc(size_t nbytes)
1260{
1261 char *p;
1262 size_t aligned;
1263
1264 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001265 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001266 size_t len;
1267 size_t blocksize;
1268 struct stack_block *sp;
1269
1270 blocksize = aligned;
1271 if (blocksize < MINSIZE)
1272 blocksize = MINSIZE;
1273 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1274 if (len < blocksize)
1275 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1276 INT_OFF;
1277 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001278 sp->prev = g_stackp;
1279 g_stacknxt = sp->space;
1280 g_stacknleft = blocksize;
1281 sstrend = g_stacknxt + blocksize;
1282 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001283 INT_ON;
1284 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001285 p = g_stacknxt;
1286 g_stacknxt += aligned;
1287 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001288 return p;
1289}
1290
Denis Vlasenko597906c2008-02-20 16:38:54 +00001291static void *
1292stzalloc(size_t nbytes)
1293{
1294 return memset(stalloc(nbytes), 0, nbytes);
1295}
1296
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297static void
1298stunalloc(void *p)
1299{
1300#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001301 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001302 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001303 abort();
1304 }
1305#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001306 g_stacknleft += g_stacknxt - (char *)p;
1307 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001308}
1309
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001310/*
1311 * Like strdup but works with the ash stack.
1312 */
1313static char *
1314ststrdup(const char *p)
1315{
1316 size_t len = strlen(p) + 1;
1317 return memcpy(stalloc(len), p, len);
1318}
1319
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001320static void
1321setstackmark(struct stackmark *mark)
1322{
Denis Vlasenko01631112007-12-16 17:20:38 +00001323 mark->stackp = g_stackp;
1324 mark->stacknxt = g_stacknxt;
1325 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326 mark->marknext = markp;
1327 markp = mark;
1328}
1329
1330static void
1331popstackmark(struct stackmark *mark)
1332{
1333 struct stack_block *sp;
1334
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001335 if (!mark->stackp)
1336 return;
1337
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338 INT_OFF;
1339 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001340 while (g_stackp != mark->stackp) {
1341 sp = g_stackp;
1342 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343 free(sp);
1344 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001345 g_stacknxt = mark->stacknxt;
1346 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347 sstrend = mark->stacknxt + mark->stacknleft;
1348 INT_ON;
1349}
1350
1351/*
1352 * When the parser reads in a string, it wants to stick the string on the
1353 * stack and only adjust the stack pointer when it knows how big the
1354 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1355 * of space on top of the stack and stackblocklen returns the length of
1356 * this block. Growstackblock will grow this space by at least one byte,
1357 * possibly moving it (like realloc). Grabstackblock actually allocates the
1358 * part of the block that has been used.
1359 */
1360static void
1361growstackblock(void)
1362{
1363 size_t newlen;
1364
Denis Vlasenko01631112007-12-16 17:20:38 +00001365 newlen = g_stacknleft * 2;
1366 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001367 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1368 if (newlen < 128)
1369 newlen += 128;
1370
Denis Vlasenko01631112007-12-16 17:20:38 +00001371 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001372 struct stack_block *oldstackp;
1373 struct stackmark *xmark;
1374 struct stack_block *sp;
1375 struct stack_block *prevstackp;
1376 size_t grosslen;
1377
1378 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001379 oldstackp = g_stackp;
1380 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001381 prevstackp = sp->prev;
1382 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1383 sp = ckrealloc(sp, grosslen);
1384 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001385 g_stackp = sp;
1386 g_stacknxt = sp->space;
1387 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001388 sstrend = sp->space + newlen;
1389
1390 /*
1391 * Stack marks pointing to the start of the old block
1392 * must be relocated to point to the new block
1393 */
1394 xmark = markp;
1395 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001396 xmark->stackp = g_stackp;
1397 xmark->stacknxt = g_stacknxt;
1398 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001399 xmark = xmark->marknext;
1400 }
1401 INT_ON;
1402 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001403 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001404 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001405 char *p = stalloc(newlen);
1406
1407 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001408 g_stacknxt = memcpy(p, oldspace, oldlen);
1409 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001410 }
1411}
1412
1413static void
1414grabstackblock(size_t len)
1415{
1416 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001417 g_stacknxt += len;
1418 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001419}
1420
1421/*
1422 * The following routines are somewhat easier to use than the above.
1423 * The user declares a variable of type STACKSTR, which may be declared
1424 * to be a register. The macro STARTSTACKSTR initializes things. Then
1425 * the user uses the macro STPUTC to add characters to the string. In
1426 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1427 * grown as necessary. When the user is done, she can just leave the
1428 * string there and refer to it using stackblock(). Or she can allocate
1429 * the space for it using grabstackstr(). If it is necessary to allow
1430 * someone else to use the stack temporarily and then continue to grow
1431 * the string, the user should use grabstack to allocate the space, and
1432 * then call ungrabstr(p) to return to the previous mode of operation.
1433 *
1434 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1435 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1436 * is space for at least one character.
1437 */
1438static void *
1439growstackstr(void)
1440{
1441 size_t len = stackblocksize();
1442 if (herefd >= 0 && len >= 1024) {
1443 full_write(herefd, stackblock(), len);
1444 return stackblock();
1445 }
1446 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001447 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001448}
1449
1450/*
1451 * Called from CHECKSTRSPACE.
1452 */
1453static char *
1454makestrspace(size_t newlen, char *p)
1455{
Denis Vlasenko01631112007-12-16 17:20:38 +00001456 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001457 size_t size = stackblocksize();
1458
1459 for (;;) {
1460 size_t nleft;
1461
1462 size = stackblocksize();
1463 nleft = size - len;
1464 if (nleft >= newlen)
1465 break;
1466 growstackblock();
1467 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001468 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001469}
1470
1471static char *
1472stack_nputstr(const char *s, size_t n, char *p)
1473{
1474 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001475 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001476 return p;
1477}
1478
1479static char *
1480stack_putstr(const char *s, char *p)
1481{
1482 return stack_nputstr(s, strlen(s), p);
1483}
1484
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001485static char *
1486_STPUTC(int c, char *p)
1487{
1488 if (p == sstrend)
1489 p = growstackstr();
1490 *p++ = c;
1491 return p;
1492}
1493
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001494#define STARTSTACKSTR(p) ((p) = stackblock())
1495#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001496#define CHECKSTRSPACE(n, p) do { \
1497 char *q = (p); \
1498 size_t l = (n); \
1499 size_t m = sstrend - q; \
1500 if (l > m) \
1501 (p) = makestrspace(l, q); \
1502} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001503#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001504#define STACKSTRNUL(p) do { \
1505 if ((p) == sstrend) \
1506 (p) = growstackstr(); \
1507 *(p) = '\0'; \
1508} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001509#define STUNPUTC(p) (--(p))
1510#define STTOPC(p) ((p)[-1])
1511#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001512
1513#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001514#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001515#define stackstrend() ((void *)sstrend)
1516
1517
1518/* ============ String helpers */
1519
1520/*
1521 * prefix -- see if pfx is a prefix of string.
1522 */
1523static char *
1524prefix(const char *string, const char *pfx)
1525{
1526 while (*pfx) {
1527 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001528 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001529 }
1530 return (char *) string;
1531}
1532
1533/*
1534 * Check for a valid number. This should be elsewhere.
1535 */
1536static int
1537is_number(const char *p)
1538{
1539 do {
1540 if (!isdigit(*p))
1541 return 0;
1542 } while (*++p != '\0');
1543 return 1;
1544}
1545
1546/*
1547 * Convert a string of digits to an integer, printing an error message on
1548 * failure.
1549 */
1550static int
1551number(const char *s)
1552{
1553 if (!is_number(s))
1554 ash_msg_and_raise_error(illnum, s);
1555 return atoi(s);
1556}
1557
1558/*
1559 * Produce a possibly single quoted string suitable as input to the shell.
1560 * The return string is allocated on the stack.
1561 */
1562static char *
1563single_quote(const char *s)
1564{
1565 char *p;
1566
1567 STARTSTACKSTR(p);
1568
1569 do {
1570 char *q;
1571 size_t len;
1572
1573 len = strchrnul(s, '\'') - s;
1574
1575 q = p = makestrspace(len + 3, p);
1576
1577 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001578 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001579 *q++ = '\'';
1580 s += len;
1581
1582 STADJUST(q - p, p);
1583
1584 len = strspn(s, "'");
1585 if (!len)
1586 break;
1587
1588 q = p = makestrspace(len + 3, p);
1589
1590 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001591 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001592 *q++ = '"';
1593 s += len;
1594
1595 STADJUST(q - p, p);
1596 } while (*s);
1597
1598 USTPUTC(0, p);
1599
1600 return stackblock();
1601}
1602
1603
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001604/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001605
1606static char **argptr; /* argument list for builtin commands */
1607static char *optionarg; /* set by nextopt (like getopt) */
1608static char *optptr; /* used by nextopt */
1609
1610/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001611 * XXX - should get rid of. Have all builtins use getopt(3).
1612 * The library getopt must have the BSD extension static variable
1613 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001614 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001615 * Standard option processing (a la getopt) for builtin routines.
1616 * The only argument that is passed to nextopt is the option string;
1617 * the other arguments are unnecessary. It returns the character,
1618 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001619 */
1620static int
1621nextopt(const char *optstring)
1622{
1623 char *p;
1624 const char *q;
1625 char c;
1626
1627 p = optptr;
1628 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001629 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001630 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001631 if (p == NULL)
1632 return '\0';
1633 if (*p != '-')
1634 return '\0';
1635 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001636 return '\0';
1637 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001638 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001639 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001640 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001641 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001642 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001643 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001644 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001645 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001646 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001647 if (*++q == ':')
1648 q++;
1649 }
1650 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001651 if (*p == '\0') {
1652 p = *argptr++;
1653 if (p == NULL)
1654 ash_msg_and_raise_error("no arg for -%c option", c);
1655 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001656 optionarg = p;
1657 p = NULL;
1658 }
1659 optptr = p;
1660 return c;
1661}
1662
1663
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001664/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001665
Denis Vlasenko01631112007-12-16 17:20:38 +00001666/*
1667 * The parsefile structure pointed to by the global variable parsefile
1668 * contains information about the current file being read.
1669 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001670struct shparam {
1671 int nparam; /* # of positional parameters (without $0) */
1672#if ENABLE_ASH_GETOPTS
1673 int optind; /* next parameter to be processed by getopts */
1674 int optoff; /* used by getopts */
1675#endif
1676 unsigned char malloced; /* if parameter list dynamically allocated */
1677 char **p; /* parameter list */
1678};
1679
1680/*
1681 * Free the list of positional parameters.
1682 */
1683static void
1684freeparam(volatile struct shparam *param)
1685{
Denis Vlasenko01631112007-12-16 17:20:38 +00001686 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001687 char **ap, **ap1;
1688 ap = ap1 = param->p;
1689 while (*ap)
1690 free(*ap++);
1691 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001692 }
1693}
1694
1695#if ENABLE_ASH_GETOPTS
1696static void getoptsreset(const char *value);
1697#endif
1698
1699struct var {
1700 struct var *next; /* next entry in hash list */
1701 int flags; /* flags are defined above */
1702 const char *text; /* name=value */
1703 void (*func)(const char *); /* function to be called when */
1704 /* the variable gets set/unset */
1705};
1706
1707struct localvar {
1708 struct localvar *next; /* next local variable in list */
1709 struct var *vp; /* the variable that was made local */
1710 int flags; /* saved flags */
1711 const char *text; /* saved text */
1712};
1713
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001714/* flags */
1715#define VEXPORT 0x01 /* variable is exported */
1716#define VREADONLY 0x02 /* variable cannot be modified */
1717#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1718#define VTEXTFIXED 0x08 /* text is statically allocated */
1719#define VSTACK 0x10 /* text is allocated on the stack */
1720#define VUNSET 0x20 /* the variable is not set */
1721#define VNOFUNC 0x40 /* don't call the callback function */
1722#define VNOSET 0x80 /* do not set variable - just readonly test */
1723#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001724#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001725# define VDYNAMIC 0x200 /* dynamic variable */
1726#else
1727# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728#endif
1729
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001730#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001731static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732#define defifs (defifsvar + 4)
1733#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001734static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#endif
1736
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001737
Denis Vlasenko01631112007-12-16 17:20:38 +00001738/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001739#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001740static void
1741change_lc_all(const char *value)
1742{
1743 if (value && *value != '\0')
1744 setlocale(LC_ALL, value);
1745}
1746static void
1747change_lc_ctype(const char *value)
1748{
1749 if (value && *value != '\0')
1750 setlocale(LC_CTYPE, value);
1751}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001752#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#if ENABLE_ASH_MAIL
1754static void chkmail(void);
1755static void changemail(const char *);
1756#endif
1757static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#if ENABLE_ASH_RANDOM_SUPPORT
1759static void change_random(const char *);
1760#endif
1761
Denis Vlasenko01631112007-12-16 17:20:38 +00001762static const struct {
1763 int flags;
1764 const char *text;
1765 void (*func)(const char *);
1766} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001767#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001768 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001770 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001773 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1774 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001776 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1777 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1778 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1779 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001781 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782#endif
1783#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001784 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785#endif
1786#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001787 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1788 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001789#endif
1790#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001791 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001792#endif
1793};
1794
Denis Vlasenko0b769642008-07-24 07:54:57 +00001795struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001796
1797struct globals_var {
1798 struct shparam shellparam; /* $@ current positional parameters */
1799 struct redirtab *redirlist;
1800 int g_nullredirs;
1801 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1802 struct var *vartab[VTABSIZE];
1803 struct var varinit[ARRAY_SIZE(varinit_data)];
1804};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001805extern struct globals_var *const ash_ptr_to_globals_var;
1806#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001807#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001808//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001809#define g_nullredirs (G_var.g_nullredirs )
1810#define preverrout_fd (G_var.preverrout_fd)
1811#define vartab (G_var.vartab )
1812#define varinit (G_var.varinit )
1813#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001814 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001815 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1816 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001817 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1818 varinit[i].flags = varinit_data[i].flags; \
1819 varinit[i].text = varinit_data[i].text; \
1820 varinit[i].func = varinit_data[i].func; \
1821 } \
1822} while (0)
1823
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001824#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001825#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001826# define vmail (&vifs)[1]
1827# define vmpath (&vmail)[1]
1828# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001829#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001830# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001831#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001832#define vps1 (&vpath)[1]
1833#define vps2 (&vps1)[1]
1834#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001835#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001836# define voptind (&vps4)[1]
1837# if ENABLE_ASH_RANDOM_SUPPORT
1838# define vrandom (&voptind)[1]
1839# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001841# if ENABLE_ASH_RANDOM_SUPPORT
1842# define vrandom (&vps4)[1]
1843# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845
1846/*
1847 * The following macros access the values of the above variables.
1848 * They have to skip over the name. They return the null string
1849 * for unset variables.
1850 */
1851#define ifsval() (vifs.text + 4)
1852#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001853#if ENABLE_ASH_MAIL
1854# define mailval() (vmail.text + 5)
1855# define mpathval() (vmpath.text + 9)
1856# define mpathset() ((vmpath.flags & VUNSET) == 0)
1857#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858#define pathval() (vpath.text + 5)
1859#define ps1val() (vps1.text + 4)
1860#define ps2val() (vps2.text + 4)
1861#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001862#if ENABLE_ASH_GETOPTS
1863# define optindval() (voptind.text + 7)
1864#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001865
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001866
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001867#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1868#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1869
Denis Vlasenko01631112007-12-16 17:20:38 +00001870#if ENABLE_ASH_GETOPTS
1871static void
1872getoptsreset(const char *value)
1873{
1874 shellparam.optind = number(value);
1875 shellparam.optoff = -1;
1876}
1877#endif
1878
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001879/*
1880 * Return of a legal variable name (a letter or underscore followed by zero or
1881 * more letters, underscores, and digits).
1882 */
1883static char *
1884endofname(const char *name)
1885{
1886 char *p;
1887
1888 p = (char *) name;
1889 if (!is_name(*p))
1890 return p;
1891 while (*++p) {
1892 if (!is_in_name(*p))
1893 break;
1894 }
1895 return p;
1896}
1897
1898/*
1899 * Compares two strings up to the first = or '\0'. The first
1900 * string must be terminated by '='; the second may be terminated by
1901 * either '=' or '\0'.
1902 */
1903static int
1904varcmp(const char *p, const char *q)
1905{
1906 int c, d;
1907
1908 while ((c = *p) == (d = *q)) {
1909 if (!c || c == '=')
1910 goto out;
1911 p++;
1912 q++;
1913 }
1914 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001915 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001916 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001917 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918 out:
1919 return c - d;
1920}
1921
1922static int
1923varequal(const char *a, const char *b)
1924{
1925 return !varcmp(a, b);
1926}
1927
1928/*
1929 * Find the appropriate entry in the hash table from the name.
1930 */
1931static struct var **
1932hashvar(const char *p)
1933{
1934 unsigned hashval;
1935
1936 hashval = ((unsigned char) *p) << 4;
1937 while (*p && *p != '=')
1938 hashval += (unsigned char) *p++;
1939 return &vartab[hashval % VTABSIZE];
1940}
1941
1942static int
1943vpcmp(const void *a, const void *b)
1944{
1945 return varcmp(*(const char **)a, *(const char **)b);
1946}
1947
1948/*
1949 * This routine initializes the builtin variables.
1950 */
1951static void
1952initvar(void)
1953{
1954 struct var *vp;
1955 struct var *end;
1956 struct var **vpp;
1957
1958 /*
1959 * PS1 depends on uid
1960 */
1961#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1962 vps1.text = "PS1=\\w \\$ ";
1963#else
1964 if (!geteuid())
1965 vps1.text = "PS1=# ";
1966#endif
1967 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001968 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001969 do {
1970 vpp = hashvar(vp->text);
1971 vp->next = *vpp;
1972 *vpp = vp;
1973 } while (++vp < end);
1974}
1975
1976static struct var **
1977findvar(struct var **vpp, const char *name)
1978{
1979 for (; *vpp; vpp = &(*vpp)->next) {
1980 if (varequal((*vpp)->text, name)) {
1981 break;
1982 }
1983 }
1984 return vpp;
1985}
1986
1987/*
1988 * Find the value of a variable. Returns NULL if not set.
1989 */
1990static char *
1991lookupvar(const char *name)
1992{
1993 struct var *v;
1994
1995 v = *findvar(hashvar(name), name);
1996 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001997#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001998 /*
1999 * Dynamic variables are implemented roughly the same way they are
2000 * in bash. Namely, they're "special" so long as they aren't unset.
2001 * As soon as they're unset, they're no longer dynamic, and dynamic
2002 * lookup will no longer happen at that point. -- PFM.
2003 */
2004 if ((v->flags & VDYNAMIC))
2005 (*v->func)(NULL);
2006#endif
2007 if (!(v->flags & VUNSET))
2008 return strchrnul(v->text, '=') + 1;
2009 }
2010 return NULL;
2011}
2012
2013/*
2014 * Search the environment of a builtin command.
2015 */
2016static char *
2017bltinlookup(const char *name)
2018{
2019 struct strlist *sp;
2020
2021 for (sp = cmdenviron; sp; sp = sp->next) {
2022 if (varequal(sp->text, name))
2023 return strchrnul(sp->text, '=') + 1;
2024 }
2025 return lookupvar(name);
2026}
2027
2028/*
2029 * Same as setvar except that the variable and value are passed in
2030 * the first argument as name=value. Since the first argument will
2031 * be actually stored in the table, it should not be a string that
2032 * will go away.
2033 * Called with interrupts off.
2034 */
2035static void
2036setvareq(char *s, int flags)
2037{
2038 struct var *vp, **vpp;
2039
2040 vpp = hashvar(s);
2041 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2042 vp = *findvar(vpp, s);
2043 if (vp) {
2044 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2045 const char *n;
2046
2047 if (flags & VNOSAVE)
2048 free(s);
2049 n = vp->text;
2050 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2051 }
2052
2053 if (flags & VNOSET)
2054 return;
2055
2056 if (vp->func && (flags & VNOFUNC) == 0)
2057 (*vp->func)(strchrnul(s, '=') + 1);
2058
2059 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2060 free((char*)vp->text);
2061
2062 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2063 } else {
2064 if (flags & VNOSET)
2065 return;
2066 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002067 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002068 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002069 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002070 *vpp = vp;
2071 }
2072 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2073 s = ckstrdup(s);
2074 vp->text = s;
2075 vp->flags = flags;
2076}
2077
2078/*
2079 * Set the value of a variable. The flags argument is ored with the
2080 * flags of the variable. If val is NULL, the variable is unset.
2081 */
2082static void
2083setvar(const char *name, const char *val, int flags)
2084{
2085 char *p, *q;
2086 size_t namelen;
2087 char *nameeq;
2088 size_t vallen;
2089
2090 q = endofname(name);
2091 p = strchrnul(q, '=');
2092 namelen = p - name;
2093 if (!namelen || p != q)
2094 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2095 vallen = 0;
2096 if (val == NULL) {
2097 flags |= VUNSET;
2098 } else {
2099 vallen = strlen(val);
2100 }
2101 INT_OFF;
2102 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002103 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104 if (val) {
2105 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002106 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002107 }
2108 *p = '\0';
2109 setvareq(nameeq, flags | VNOSAVE);
2110 INT_ON;
2111}
2112
2113#if ENABLE_ASH_GETOPTS
2114/*
2115 * Safe version of setvar, returns 1 on success 0 on failure.
2116 */
2117static int
2118setvarsafe(const char *name, const char *val, int flags)
2119{
2120 int err;
2121 volatile int saveint;
2122 struct jmploc *volatile savehandler = exception_handler;
2123 struct jmploc jmploc;
2124
2125 SAVE_INT(saveint);
2126 if (setjmp(jmploc.loc))
2127 err = 1;
2128 else {
2129 exception_handler = &jmploc;
2130 setvar(name, val, flags);
2131 err = 0;
2132 }
2133 exception_handler = savehandler;
2134 RESTORE_INT(saveint);
2135 return err;
2136}
2137#endif
2138
2139/*
2140 * Unset the specified variable.
2141 */
2142static int
2143unsetvar(const char *s)
2144{
2145 struct var **vpp;
2146 struct var *vp;
2147 int retval;
2148
2149 vpp = findvar(hashvar(s), s);
2150 vp = *vpp;
2151 retval = 2;
2152 if (vp) {
2153 int flags = vp->flags;
2154
2155 retval = 1;
2156 if (flags & VREADONLY)
2157 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002158#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002159 vp->flags &= ~VDYNAMIC;
2160#endif
2161 if (flags & VUNSET)
2162 goto ok;
2163 if ((flags & VSTRFIXED) == 0) {
2164 INT_OFF;
2165 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2166 free((char*)vp->text);
2167 *vpp = vp->next;
2168 free(vp);
2169 INT_ON;
2170 } else {
2171 setvar(s, 0, 0);
2172 vp->flags &= ~VEXPORT;
2173 }
2174 ok:
2175 retval = 0;
2176 }
2177 out:
2178 return retval;
2179}
2180
2181/*
2182 * Process a linked list of variable assignments.
2183 */
2184static void
2185listsetvar(struct strlist *list_set_var, int flags)
2186{
2187 struct strlist *lp = list_set_var;
2188
2189 if (!lp)
2190 return;
2191 INT_OFF;
2192 do {
2193 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002194 lp = lp->next;
2195 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002196 INT_ON;
2197}
2198
2199/*
2200 * Generate a list of variables satisfying the given conditions.
2201 */
2202static char **
2203listvars(int on, int off, char ***end)
2204{
2205 struct var **vpp;
2206 struct var *vp;
2207 char **ep;
2208 int mask;
2209
2210 STARTSTACKSTR(ep);
2211 vpp = vartab;
2212 mask = on | off;
2213 do {
2214 for (vp = *vpp; vp; vp = vp->next) {
2215 if ((vp->flags & mask) == on) {
2216 if (ep == stackstrend())
2217 ep = growstackstr();
2218 *ep++ = (char *) vp->text;
2219 }
2220 }
2221 } while (++vpp < vartab + VTABSIZE);
2222 if (ep == stackstrend())
2223 ep = growstackstr();
2224 if (end)
2225 *end = ep;
2226 *ep++ = NULL;
2227 return grabstackstr(ep);
2228}
2229
2230
2231/* ============ Path search helper
2232 *
2233 * The variable path (passed by reference) should be set to the start
2234 * of the path before the first call; padvance will update
2235 * this value as it proceeds. Successive calls to padvance will return
2236 * the possible path expansions in sequence. If an option (indicated by
2237 * a percent sign) appears in the path entry then the global variable
2238 * pathopt will be set to point to it; otherwise pathopt will be set to
2239 * NULL.
2240 */
2241static const char *pathopt; /* set by padvance */
2242
2243static char *
2244padvance(const char **path, const char *name)
2245{
2246 const char *p;
2247 char *q;
2248 const char *start;
2249 size_t len;
2250
2251 if (*path == NULL)
2252 return NULL;
2253 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002254 for (p = start; *p && *p != ':' && *p != '%'; p++)
2255 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2257 while (stackblocksize() < len)
2258 growstackblock();
2259 q = stackblock();
2260 if (p != start) {
2261 memcpy(q, start, p - start);
2262 q += p - start;
2263 *q++ = '/';
2264 }
2265 strcpy(q, name);
2266 pathopt = NULL;
2267 if (*p == '%') {
2268 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002269 while (*p && *p != ':')
2270 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002271 }
2272 if (*p == ':')
2273 *path = p + 1;
2274 else
2275 *path = NULL;
2276 return stalloc(len);
2277}
2278
2279
2280/* ============ Prompt */
2281
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002282static smallint doprompt; /* if set, prompt the user */
2283static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002284
2285#if ENABLE_FEATURE_EDITING
2286static line_input_t *line_input_state;
2287static const char *cmdedit_prompt;
2288static void
2289putprompt(const char *s)
2290{
2291 if (ENABLE_ASH_EXPAND_PRMT) {
2292 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002293 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002294 return;
2295 }
2296 cmdedit_prompt = s;
2297}
2298#else
2299static void
2300putprompt(const char *s)
2301{
2302 out2str(s);
2303}
2304#endif
2305
2306#if ENABLE_ASH_EXPAND_PRMT
2307/* expandstr() needs parsing machinery, so it is far away ahead... */
2308static const char *expandstr(const char *ps);
2309#else
2310#define expandstr(s) s
2311#endif
2312
2313static void
2314setprompt(int whichprompt)
2315{
2316 const char *prompt;
2317#if ENABLE_ASH_EXPAND_PRMT
2318 struct stackmark smark;
2319#endif
2320
2321 needprompt = 0;
2322
2323 switch (whichprompt) {
2324 case 1:
2325 prompt = ps1val();
2326 break;
2327 case 2:
2328 prompt = ps2val();
2329 break;
2330 default: /* 0 */
2331 prompt = nullstr;
2332 }
2333#if ENABLE_ASH_EXPAND_PRMT
2334 setstackmark(&smark);
2335 stalloc(stackblocksize());
2336#endif
2337 putprompt(expandstr(prompt));
2338#if ENABLE_ASH_EXPAND_PRMT
2339 popstackmark(&smark);
2340#endif
2341}
2342
2343
2344/* ============ The cd and pwd commands */
2345
2346#define CD_PHYSICAL 1
2347#define CD_PRINT 2
2348
2349static int docd(const char *, int);
2350
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002351static int
2352cdopt(void)
2353{
2354 int flags = 0;
2355 int i, j;
2356
2357 j = 'L';
2358 while ((i = nextopt("LP"))) {
2359 if (i != j) {
2360 flags ^= CD_PHYSICAL;
2361 j = i;
2362 }
2363 }
2364
2365 return flags;
2366}
2367
2368/*
2369 * Update curdir (the name of the current directory) in response to a
2370 * cd command.
2371 */
2372static const char *
2373updatepwd(const char *dir)
2374{
2375 char *new;
2376 char *p;
2377 char *cdcomppath;
2378 const char *lim;
2379
2380 cdcomppath = ststrdup(dir);
2381 STARTSTACKSTR(new);
2382 if (*dir != '/') {
2383 if (curdir == nullstr)
2384 return 0;
2385 new = stack_putstr(curdir, new);
2386 }
2387 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002388 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002389 if (*dir != '/') {
2390 if (new[-1] != '/')
2391 USTPUTC('/', new);
2392 if (new > lim && *lim == '/')
2393 lim++;
2394 } else {
2395 USTPUTC('/', new);
2396 cdcomppath++;
2397 if (dir[1] == '/' && dir[2] != '/') {
2398 USTPUTC('/', new);
2399 cdcomppath++;
2400 lim++;
2401 }
2402 }
2403 p = strtok(cdcomppath, "/");
2404 while (p) {
2405 switch (*p) {
2406 case '.':
2407 if (p[1] == '.' && p[2] == '\0') {
2408 while (new > lim) {
2409 STUNPUTC(new);
2410 if (new[-1] == '/')
2411 break;
2412 }
2413 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002414 }
2415 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002416 break;
2417 /* fall through */
2418 default:
2419 new = stack_putstr(p, new);
2420 USTPUTC('/', new);
2421 }
2422 p = strtok(0, "/");
2423 }
2424 if (new > lim)
2425 STUNPUTC(new);
2426 *new = 0;
2427 return stackblock();
2428}
2429
2430/*
2431 * Find out what the current directory is. If we already know the current
2432 * directory, this routine returns immediately.
2433 */
2434static char *
2435getpwd(void)
2436{
Denis Vlasenko01631112007-12-16 17:20:38 +00002437 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002438 return dir ? dir : nullstr;
2439}
2440
2441static void
2442setpwd(const char *val, int setold)
2443{
2444 char *oldcur, *dir;
2445
2446 oldcur = dir = curdir;
2447
2448 if (setold) {
2449 setvar("OLDPWD", oldcur, VEXPORT);
2450 }
2451 INT_OFF;
2452 if (physdir != nullstr) {
2453 if (physdir != oldcur)
2454 free(physdir);
2455 physdir = nullstr;
2456 }
2457 if (oldcur == val || !val) {
2458 char *s = getpwd();
2459 physdir = s;
2460 if (!val)
2461 dir = s;
2462 } else
2463 dir = ckstrdup(val);
2464 if (oldcur != dir && oldcur != nullstr) {
2465 free(oldcur);
2466 }
2467 curdir = dir;
2468 INT_ON;
2469 setvar("PWD", dir, VEXPORT);
2470}
2471
2472static void hashcd(void);
2473
2474/*
2475 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2476 * know that the current directory has changed.
2477 */
2478static int
2479docd(const char *dest, int flags)
2480{
2481 const char *dir = 0;
2482 int err;
2483
2484 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2485
2486 INT_OFF;
2487 if (!(flags & CD_PHYSICAL)) {
2488 dir = updatepwd(dest);
2489 if (dir)
2490 dest = dir;
2491 }
2492 err = chdir(dest);
2493 if (err)
2494 goto out;
2495 setpwd(dir, 1);
2496 hashcd();
2497 out:
2498 INT_ON;
2499 return err;
2500}
2501
2502static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002503cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002504{
2505 const char *dest;
2506 const char *path;
2507 const char *p;
2508 char c;
2509 struct stat statb;
2510 int flags;
2511
2512 flags = cdopt();
2513 dest = *argptr;
2514 if (!dest)
2515 dest = bltinlookup(homestr);
2516 else if (LONE_DASH(dest)) {
2517 dest = bltinlookup("OLDPWD");
2518 flags |= CD_PRINT;
2519 }
2520 if (!dest)
2521 dest = nullstr;
2522 if (*dest == '/')
2523 goto step7;
2524 if (*dest == '.') {
2525 c = dest[1];
2526 dotdot:
2527 switch (c) {
2528 case '\0':
2529 case '/':
2530 goto step6;
2531 case '.':
2532 c = dest[2];
2533 if (c != '.')
2534 goto dotdot;
2535 }
2536 }
2537 if (!*dest)
2538 dest = ".";
2539 path = bltinlookup("CDPATH");
2540 if (!path) {
2541 step6:
2542 step7:
2543 p = dest;
2544 goto docd;
2545 }
2546 do {
2547 c = *path;
2548 p = padvance(&path, dest);
2549 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2550 if (c && c != ':')
2551 flags |= CD_PRINT;
2552 docd:
2553 if (!docd(p, flags))
2554 goto out;
2555 break;
2556 }
2557 } while (path);
2558 ash_msg_and_raise_error("can't cd to %s", dest);
2559 /* NOTREACHED */
2560 out:
2561 if (flags & CD_PRINT)
2562 out1fmt(snlfmt, curdir);
2563 return 0;
2564}
2565
2566static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002567pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002568{
2569 int flags;
2570 const char *dir = curdir;
2571
2572 flags = cdopt();
2573 if (flags) {
2574 if (physdir == nullstr)
2575 setpwd(dir, 0);
2576 dir = physdir;
2577 }
2578 out1fmt(snlfmt, dir);
2579 return 0;
2580}
2581
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002582
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002583/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002584
Denis Vlasenko834dee72008-10-07 09:18:30 +00002585
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002586#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002587/* buffer for top level input file */
2588#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002589
Eric Andersenc470f442003-07-28 09:56:35 +00002590/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002591#define CWORD 0 /* character is nothing special */
2592#define CNL 1 /* newline character */
2593#define CBACK 2 /* a backslash character */
2594#define CSQUOTE 3 /* single quote */
2595#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002596#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002597#define CBQUOTE 6 /* backwards single quote */
2598#define CVAR 7 /* a dollar sign */
2599#define CENDVAR 8 /* a '}' character */
2600#define CLP 9 /* a left paren in arithmetic */
2601#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002602#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002603#define CCTL 12 /* like CWORD, except it must be escaped */
2604#define CSPCL 13 /* these terminate a word */
2605#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002606
Denis Vlasenko131ae172007-02-18 13:00:19 +00002607#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002608#define SYNBASE 130
2609#define PEOF -130
2610#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002611#define PEOA_OR_PEOF PEOA
2612#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002613#define SYNBASE 129
2614#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002615#define PEOA_OR_PEOF PEOF
2616#endif
2617
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002618/* number syntax index */
2619#define BASESYNTAX 0 /* not in quotes */
2620#define DQSYNTAX 1 /* in double quotes */
2621#define SQSYNTAX 2 /* in single quotes */
2622#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002623#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002624
Denis Vlasenko131ae172007-02-18 13:00:19 +00002625#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002626#define USE_SIT_FUNCTION
2627#endif
2628
Denis Vlasenko131ae172007-02-18 13:00:19 +00002629#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002630static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002631#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002632 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002633#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002634 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2635 { CNL, CNL, CNL, CNL }, /* 2, \n */
2636 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2637 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2638 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2639 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2640 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2641 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2642 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2643 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2644 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002645#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002646 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2647 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2648 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002649#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002650};
Eric Andersenc470f442003-07-28 09:56:35 +00002651#else
2652static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002653#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002654 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002655#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002656 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2657 { CNL, CNL, CNL }, /* 2, \n */
2658 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2659 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2660 { CVAR, CVAR, CWORD }, /* 5, $ */
2661 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2662 { CSPCL, CWORD, CWORD }, /* 7, ( */
2663 { CSPCL, CWORD, CWORD }, /* 8, ) */
2664 { CBACK, CBACK, CCTL }, /* 9, \ */
2665 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2666 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002667#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002668 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2669 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2670 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002671#endif
2672};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002673#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002674
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675#ifdef USE_SIT_FUNCTION
2676
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002677static int
2678SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002679{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002680 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002681#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002682 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002683 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2684 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2685 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2686 11, 3 /* "}~" */
2687 };
2688#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002689 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002690 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2691 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2692 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2693 10, 2 /* "}~" */
2694 };
2695#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002696 const char *s;
2697 int indx;
2698
Eric Andersenc470f442003-07-28 09:56:35 +00002699 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002700 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002701#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002702 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002703 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002704 else
2705#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002706
2707 if ((unsigned char)c >= (unsigned char)(CTLESC)
2708 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2709 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002710 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002711 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002712 s = strchrnul(spec_symbls, c);
2713 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002714 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002715 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002716 }
2717 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 Vlasenko3af3e5b2007-03-05 00:24:52 +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 * $ _
4682 * TODO: we do not execute "echo hi" as bash does:
4683 * $ bash -c './sleep5intoff; echo hi'
4684 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4685 * $ _
4686 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004687 dowait(DOWAIT_BLOCK, jp);
4688 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004689 INT_ON;
4690
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004691 st = getstatus(jp);
4692#if JOBS
4693 if (jp->jobctl) {
4694 xtcsetpgrp(ttyfd, rootpid);
4695 /*
4696 * This is truly gross.
4697 * If we're doing job control, then we did a TIOCSPGRP which
4698 * caused us (the shell) to no longer be in the controlling
4699 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4700 * intuit from the subprocess exit status whether a SIGINT
4701 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4702 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004703 if (jp->sigint) /* TODO: do the same with all signals */
4704 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004705 }
4706 if (jp->state == JOBDONE)
4707#endif
4708 freejob(jp);
4709 return st;
4710}
4711
4712/*
4713 * return 1 if there are stopped jobs, otherwise 0
4714 */
4715static int
4716stoppedjobs(void)
4717{
4718 struct job *jp;
4719 int retval;
4720
4721 retval = 0;
4722 if (job_warning)
4723 goto out;
4724 jp = curjob;
4725 if (jp && jp->state == JOBSTOPPED) {
4726 out2str("You have stopped jobs.\n");
4727 job_warning = 2;
4728 retval++;
4729 }
4730 out:
4731 return retval;
4732}
4733
4734
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004735/* ============ redir.c
4736 *
4737 * Code for dealing with input/output redirection.
4738 */
4739
4740#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004741#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004742
4743/*
4744 * Open a file in noclobber mode.
4745 * The code was copied from bash.
4746 */
4747static int
4748noclobberopen(const char *fname)
4749{
4750 int r, fd;
4751 struct stat finfo, finfo2;
4752
4753 /*
4754 * If the file exists and is a regular file, return an error
4755 * immediately.
4756 */
4757 r = stat(fname, &finfo);
4758 if (r == 0 && S_ISREG(finfo.st_mode)) {
4759 errno = EEXIST;
4760 return -1;
4761 }
4762
4763 /*
4764 * If the file was not present (r != 0), make sure we open it
4765 * exclusively so that if it is created before we open it, our open
4766 * will fail. Make sure that we do not truncate an existing file.
4767 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4768 * file was not a regular file, we leave O_EXCL off.
4769 */
4770 if (r != 0)
4771 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4772 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4773
4774 /* If the open failed, return the file descriptor right away. */
4775 if (fd < 0)
4776 return fd;
4777
4778 /*
4779 * OK, the open succeeded, but the file may have been changed from a
4780 * non-regular file to a regular file between the stat and the open.
4781 * We are assuming that the O_EXCL open handles the case where FILENAME
4782 * did not exist and is symlinked to an existing file between the stat
4783 * and open.
4784 */
4785
4786 /*
4787 * If we can open it and fstat the file descriptor, and neither check
4788 * revealed that it was a regular file, and the file has not been
4789 * replaced, return the file descriptor.
4790 */
4791 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4792 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4793 return fd;
4794
4795 /* The file has been replaced. badness. */
4796 close(fd);
4797 errno = EEXIST;
4798 return -1;
4799}
4800
4801/*
4802 * Handle here documents. Normally we fork off a process to write the
4803 * data to a pipe. If the document is short, we can stuff the data in
4804 * the pipe without forking.
4805 */
4806/* openhere needs this forward reference */
4807static void expandhere(union node *arg, int fd);
4808static int
4809openhere(union node *redir)
4810{
4811 int pip[2];
4812 size_t len = 0;
4813
4814 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004815 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004816 if (redir->type == NHERE) {
4817 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004818 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004819 full_write(pip[1], redir->nhere.doc->narg.text, len);
4820 goto out;
4821 }
4822 }
4823 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004824 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004825 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004826 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4827 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4828 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4829 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004830 signal(SIGPIPE, SIG_DFL);
4831 if (redir->type == NHERE)
4832 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004833 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004834 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004835 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004836 }
4837 out:
4838 close(pip[1]);
4839 return pip[0];
4840}
4841
4842static int
4843openredirect(union node *redir)
4844{
4845 char *fname;
4846 int f;
4847
4848 switch (redir->nfile.type) {
4849 case NFROM:
4850 fname = redir->nfile.expfname;
4851 f = open(fname, O_RDONLY);
4852 if (f < 0)
4853 goto eopen;
4854 break;
4855 case NFROMTO:
4856 fname = redir->nfile.expfname;
4857 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4858 if (f < 0)
4859 goto ecreate;
4860 break;
4861 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004862#if ENABLE_ASH_BASH_COMPAT
4863 case NTO2:
4864#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004865 /* Take care of noclobber mode. */
4866 if (Cflag) {
4867 fname = redir->nfile.expfname;
4868 f = noclobberopen(fname);
4869 if (f < 0)
4870 goto ecreate;
4871 break;
4872 }
4873 /* FALLTHROUGH */
4874 case NCLOBBER:
4875 fname = redir->nfile.expfname;
4876 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4877 if (f < 0)
4878 goto ecreate;
4879 break;
4880 case NAPPEND:
4881 fname = redir->nfile.expfname;
4882 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4883 if (f < 0)
4884 goto ecreate;
4885 break;
4886 default:
4887#if DEBUG
4888 abort();
4889#endif
4890 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004891/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004892// case NTOFD:
4893// case NFROMFD:
4894// f = -1;
4895// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004896 case NHERE:
4897 case NXHERE:
4898 f = openhere(redir);
4899 break;
4900 }
4901
4902 return f;
4903 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004904 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004905 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004906 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004907}
4908
4909/*
4910 * Copy a file descriptor to be >= to. Returns -1
4911 * if the source file descriptor is closed, EMPTY if there are no unused
4912 * file descriptors left.
4913 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004914/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4915 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004916enum {
4917 COPYFD_EXACT = (int)~(INT_MAX),
4918 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4919};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004920static int
4921copyfd(int from, int to)
4922{
4923 int newfd;
4924
Denis Vlasenko5a867312008-07-24 19:46:38 +00004925 if (to & COPYFD_EXACT) {
4926 to &= ~COPYFD_EXACT;
4927 /*if (from != to)*/
4928 newfd = dup2(from, to);
4929 } else {
4930 newfd = fcntl(from, F_DUPFD, to);
4931 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004932 if (newfd < 0) {
4933 if (errno == EMFILE)
4934 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004935 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936 ash_msg_and_raise_error("%d: %m", from);
4937 }
4938 return newfd;
4939}
4940
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004941/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004942struct two_fd_t {
4943 int orig, copy;
4944};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004945struct redirtab {
4946 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004947 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004948 int pair_count;
4949 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004950};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004951#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004952
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004953static int need_to_remember(struct redirtab *rp, int fd)
4954{
4955 int i;
4956
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004957 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004958 return 0;
4959
4960 for (i = 0; i < rp->pair_count; i++) {
4961 if (rp->two_fd[i].orig == fd) {
4962 /* already remembered */
4963 return 0;
4964 }
4965 }
4966 return 1;
4967}
4968
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004969/* "hidden" fd is a fd used to read scripts, or a copy of such */
4970static int is_hidden_fd(struct redirtab *rp, int fd)
4971{
4972 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004973 struct parsefile *pf;
4974
4975 if (fd == -1)
4976 return 0;
4977 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004978 while (pf) {
4979 if (fd == pf->fd) {
4980 return 1;
4981 }
4982 pf = pf->prev;
4983 }
4984 if (!rp)
4985 return 0;
4986 fd |= COPYFD_RESTORE;
4987 for (i = 0; i < rp->pair_count; i++) {
4988 if (rp->two_fd[i].copy == fd) {
4989 return 1;
4990 }
4991 }
4992 return 0;
4993}
4994
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004995/*
4996 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4997 * old file descriptors are stashed away so that the redirection can be
4998 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4999 * standard output, and the standard error if it becomes a duplicate of
5000 * stdout, is saved in memory.
5001 */
5002/* flags passed to redirect */
5003#define REDIR_PUSH 01 /* save previous values of file descriptors */
5004#define REDIR_SAVEFD2 03 /* set preverrout */
5005static void
5006redirect(union node *redir, int flags)
5007{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005008 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005009 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005010 int i;
5011 int fd;
5012 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005013 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005014
Denis Vlasenko01631112007-12-16 17:20:38 +00005015 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005016 if (!redir) {
5017 return;
5018 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005019
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005020 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005021 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005022 INT_OFF;
5023 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005024 union node *tmp = redir;
5025 do {
5026 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005027#if ENABLE_ASH_BASH_COMPAT
5028 if (redir->nfile.type == NTO2)
5029 sv_pos++;
5030#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005031 tmp = tmp->nfile.next;
5032 } while (tmp);
5033 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005034 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005035 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005036 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005037 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005038 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005039 while (sv_pos > 0) {
5040 sv_pos--;
5041 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5042 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005043 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005044
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005046 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005047 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005048 int right_fd = redir->ndup.dupfd;
5049 /* redirect from/to same file descriptor? */
5050 if (right_fd == fd)
5051 continue;
5052 /* echo >&10 and 10 is a fd opened to the sh script? */
5053 if (is_hidden_fd(sv, right_fd)) {
5054 errno = EBADF; /* as if it is closed */
5055 ash_msg_and_raise_error("%d: %m", right_fd);
5056 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005057 newfd = -1;
5058 } else {
5059 newfd = openredirect(redir); /* always >= 0 */
5060 if (fd == newfd) {
5061 /* Descriptor wasn't open before redirect.
5062 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005063 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005064 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005065 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005066 continue;
5067 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005068 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005069#if ENABLE_ASH_BASH_COMPAT
5070 redirect_more:
5071#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005072 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005073 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005074 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005075/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5076 * are closed in popredir() in the child, preventing them from leaking
5077 * into child. (popredir() also cleans up the mess in case of failures)
5078 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 if (i == -1) {
5080 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005081 if (i != EBADF) {
5082 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005083 if (newfd >= 0)
5084 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005085 errno = i;
5086 ash_msg_and_raise_error("%d: %m", fd);
5087 /* NOTREACHED */
5088 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005089 /* EBADF: it is not open - good, remember to close it */
5090 remember_to_close:
5091 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005092 } else { /* fd is open, save its copy */
5093 /* "exec fd>&-" should not close fds
5094 * which point to script file(s).
5095 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005096 if (is_hidden_fd(sv, fd))
5097 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005098 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005099 if (fd == 2)
5100 copied_fd2 = i;
5101 sv->two_fd[sv_pos].orig = fd;
5102 sv->two_fd[sv_pos].copy = i;
5103 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005104 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005105 if (newfd < 0) {
5106 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005107 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005108 close(fd);
5109 } else {
5110 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005111 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005112 } else if (fd != newfd) { /* move newfd to fd */
5113 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005114#if ENABLE_ASH_BASH_COMPAT
5115 if (!(redir->nfile.type == NTO2 && fd == 2))
5116#endif
5117 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005118 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005119#if ENABLE_ASH_BASH_COMPAT
5120 if (redir->nfile.type == NTO2 && fd == 1) {
5121 /* We already redirected it to fd 1, now copy it to 2 */
5122 newfd = 1;
5123 fd = 2;
5124 goto redirect_more;
5125 }
5126#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005127 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005128
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005129 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005130 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5131 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005132}
5133
5134/*
5135 * Undo the effects of the last redirection.
5136 */
5137static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005138popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005139{
5140 struct redirtab *rp;
5141 int i;
5142
Denis Vlasenko01631112007-12-16 17:20:38 +00005143 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005144 return;
5145 INT_OFF;
5146 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005147 for (i = 0; i < rp->pair_count; i++) {
5148 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005149 int copy = rp->two_fd[i].copy;
5150 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005151 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005152 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005153 continue;
5154 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005155 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005156 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005157 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005158 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005159 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005161 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005162 }
5163 }
5164 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005165 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005166 free(rp);
5167 INT_ON;
5168}
5169
5170/*
5171 * Undo all redirections. Called on error or interrupt.
5172 */
5173
5174/*
5175 * Discard all saved file descriptors.
5176 */
5177static void
5178clearredir(int drop)
5179{
5180 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005181 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005182 if (!redirlist)
5183 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005184 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005185 }
5186}
5187
5188static int
5189redirectsafe(union node *redir, int flags)
5190{
5191 int err;
5192 volatile int saveint;
5193 struct jmploc *volatile savehandler = exception_handler;
5194 struct jmploc jmploc;
5195
5196 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005197 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5198 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005199 if (!err) {
5200 exception_handler = &jmploc;
5201 redirect(redir, flags);
5202 }
5203 exception_handler = savehandler;
5204 if (err && exception != EXERROR)
5205 longjmp(exception_handler->loc, 1);
5206 RESTORE_INT(saveint);
5207 return err;
5208}
5209
5210
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005211/* ============ Routines to expand arguments to commands
5212 *
5213 * We have to deal with backquotes, shell variables, and file metacharacters.
5214 */
5215
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005216#if ENABLE_ASH_MATH_SUPPORT_64
5217typedef int64_t arith_t;
5218#define arith_t_type long long
5219#else
5220typedef long arith_t;
5221#define arith_t_type long
5222#endif
5223
5224#if ENABLE_ASH_MATH_SUPPORT
5225static arith_t dash_arith(const char *);
5226static arith_t arith(const char *expr, int *perrcode);
5227#endif
5228
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005229/*
5230 * expandarg flags
5231 */
5232#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5233#define EXP_TILDE 0x2 /* do normal tilde expansion */
5234#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5235#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5236#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5237#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5238#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5239#define EXP_WORD 0x80 /* expand word in parameter expansion */
5240#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5241/*
5242 * _rmescape() flags
5243 */
5244#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5245#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5246#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5247#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5248#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5249
5250/*
5251 * Structure specifying which parts of the string should be searched
5252 * for IFS characters.
5253 */
5254struct ifsregion {
5255 struct ifsregion *next; /* next region in list */
5256 int begoff; /* offset of start of region */
5257 int endoff; /* offset of end of region */
5258 int nulonly; /* search for nul bytes only */
5259};
5260
5261struct arglist {
5262 struct strlist *list;
5263 struct strlist **lastp;
5264};
5265
5266/* output of current string */
5267static char *expdest;
5268/* list of back quote expressions */
5269static struct nodelist *argbackq;
5270/* first struct in list of ifs regions */
5271static struct ifsregion ifsfirst;
5272/* last struct in list */
5273static struct ifsregion *ifslastp;
5274/* holds expanded arg list */
5275static struct arglist exparg;
5276
5277/*
5278 * Our own itoa().
5279 */
5280static int
5281cvtnum(arith_t num)
5282{
5283 int len;
5284
5285 expdest = makestrspace(32, expdest);
5286#if ENABLE_ASH_MATH_SUPPORT_64
5287 len = fmtstr(expdest, 32, "%lld", (long long) num);
5288#else
5289 len = fmtstr(expdest, 32, "%ld", num);
5290#endif
5291 STADJUST(len, expdest);
5292 return len;
5293}
5294
5295static size_t
5296esclen(const char *start, const char *p)
5297{
5298 size_t esc = 0;
5299
5300 while (p > start && *--p == CTLESC) {
5301 esc++;
5302 }
5303 return esc;
5304}
5305
5306/*
5307 * Remove any CTLESC characters from a string.
5308 */
5309static char *
5310_rmescapes(char *str, int flag)
5311{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005312 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005313
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005314 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005315 unsigned inquotes;
5316 int notescaped;
5317 int globbing;
5318
5319 p = strpbrk(str, qchars);
5320 if (!p) {
5321 return str;
5322 }
5323 q = p;
5324 r = str;
5325 if (flag & RMESCAPE_ALLOC) {
5326 size_t len = p - str;
5327 size_t fulllen = len + strlen(p) + 1;
5328
5329 if (flag & RMESCAPE_GROW) {
5330 r = makestrspace(fulllen, expdest);
5331 } else if (flag & RMESCAPE_HEAP) {
5332 r = ckmalloc(fulllen);
5333 } else {
5334 r = stalloc(fulllen);
5335 }
5336 q = r;
5337 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005338 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005339 }
5340 }
5341 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5342 globbing = flag & RMESCAPE_GLOB;
5343 notescaped = globbing;
5344 while (*p) {
5345 if (*p == CTLQUOTEMARK) {
5346 inquotes = ~inquotes;
5347 p++;
5348 notescaped = globbing;
5349 continue;
5350 }
5351 if (*p == '\\') {
5352 /* naked back slash */
5353 notescaped = 0;
5354 goto copy;
5355 }
5356 if (*p == CTLESC) {
5357 p++;
5358 if (notescaped && inquotes && *p != '/') {
5359 *q++ = '\\';
5360 }
5361 }
5362 notescaped = globbing;
5363 copy:
5364 *q++ = *p++;
5365 }
5366 *q = '\0';
5367 if (flag & RMESCAPE_GROW) {
5368 expdest = r;
5369 STADJUST(q - r + 1, expdest);
5370 }
5371 return r;
5372}
5373#define rmescapes(p) _rmescapes((p), 0)
5374
5375#define pmatch(a, b) !fnmatch((a), (b), 0)
5376
5377/*
5378 * Prepare a pattern for a expmeta (internal glob(3)) call.
5379 *
5380 * Returns an stalloced string.
5381 */
5382static char *
5383preglob(const char *pattern, int quoted, int flag)
5384{
5385 flag |= RMESCAPE_GLOB;
5386 if (quoted) {
5387 flag |= RMESCAPE_QUOTED;
5388 }
5389 return _rmescapes((char *)pattern, flag);
5390}
5391
5392/*
5393 * Put a string on the stack.
5394 */
5395static void
5396memtodest(const char *p, size_t len, int syntax, int quotes)
5397{
5398 char *q = expdest;
5399
5400 q = makestrspace(len * 2, q);
5401
5402 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005403 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005404 if (!c)
5405 continue;
5406 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5407 USTPUTC(CTLESC, q);
5408 USTPUTC(c, q);
5409 }
5410
5411 expdest = q;
5412}
5413
5414static void
5415strtodest(const char *p, int syntax, int quotes)
5416{
5417 memtodest(p, strlen(p), syntax, quotes);
5418}
5419
5420/*
5421 * Record the fact that we have to scan this region of the
5422 * string for IFS characters.
5423 */
5424static void
5425recordregion(int start, int end, int nulonly)
5426{
5427 struct ifsregion *ifsp;
5428
5429 if (ifslastp == NULL) {
5430 ifsp = &ifsfirst;
5431 } else {
5432 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005433 ifsp = ckzalloc(sizeof(*ifsp));
5434 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005435 ifslastp->next = ifsp;
5436 INT_ON;
5437 }
5438 ifslastp = ifsp;
5439 ifslastp->begoff = start;
5440 ifslastp->endoff = end;
5441 ifslastp->nulonly = nulonly;
5442}
5443
5444static void
5445removerecordregions(int endoff)
5446{
5447 if (ifslastp == NULL)
5448 return;
5449
5450 if (ifsfirst.endoff > endoff) {
5451 while (ifsfirst.next != NULL) {
5452 struct ifsregion *ifsp;
5453 INT_OFF;
5454 ifsp = ifsfirst.next->next;
5455 free(ifsfirst.next);
5456 ifsfirst.next = ifsp;
5457 INT_ON;
5458 }
5459 if (ifsfirst.begoff > endoff)
5460 ifslastp = NULL;
5461 else {
5462 ifslastp = &ifsfirst;
5463 ifsfirst.endoff = endoff;
5464 }
5465 return;
5466 }
5467
5468 ifslastp = &ifsfirst;
5469 while (ifslastp->next && ifslastp->next->begoff < endoff)
5470 ifslastp=ifslastp->next;
5471 while (ifslastp->next != NULL) {
5472 struct ifsregion *ifsp;
5473 INT_OFF;
5474 ifsp = ifslastp->next->next;
5475 free(ifslastp->next);
5476 ifslastp->next = ifsp;
5477 INT_ON;
5478 }
5479 if (ifslastp->endoff > endoff)
5480 ifslastp->endoff = endoff;
5481}
5482
5483static char *
5484exptilde(char *startp, char *p, int flag)
5485{
5486 char c;
5487 char *name;
5488 struct passwd *pw;
5489 const char *home;
5490 int quotes = flag & (EXP_FULL | EXP_CASE);
5491 int startloc;
5492
5493 name = p + 1;
5494
5495 while ((c = *++p) != '\0') {
5496 switch (c) {
5497 case CTLESC:
5498 return startp;
5499 case CTLQUOTEMARK:
5500 return startp;
5501 case ':':
5502 if (flag & EXP_VARTILDE)
5503 goto done;
5504 break;
5505 case '/':
5506 case CTLENDVAR:
5507 goto done;
5508 }
5509 }
5510 done:
5511 *p = '\0';
5512 if (*name == '\0') {
5513 home = lookupvar(homestr);
5514 } else {
5515 pw = getpwnam(name);
5516 if (pw == NULL)
5517 goto lose;
5518 home = pw->pw_dir;
5519 }
5520 if (!home || !*home)
5521 goto lose;
5522 *p = c;
5523 startloc = expdest - (char *)stackblock();
5524 strtodest(home, SQSYNTAX, quotes);
5525 recordregion(startloc, expdest - (char *)stackblock(), 0);
5526 return p;
5527 lose:
5528 *p = c;
5529 return startp;
5530}
5531
5532/*
5533 * Execute a command inside back quotes. If it's a builtin command, we
5534 * want to save its output in a block obtained from malloc. Otherwise
5535 * we fork off a subprocess and get the output of the command via a pipe.
5536 * Should be called with interrupts off.
5537 */
5538struct backcmd { /* result of evalbackcmd */
5539 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005541 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542 struct job *jp; /* job structure for command */
5543};
5544
5545/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005546static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005547#define EV_EXIT 01 /* exit after evaluating tree */
5548static void evaltree(union node *, int);
5549
5550static void
5551evalbackcmd(union node *n, struct backcmd *result)
5552{
5553 int saveherefd;
5554
5555 result->fd = -1;
5556 result->buf = NULL;
5557 result->nleft = 0;
5558 result->jp = NULL;
5559 if (n == NULL) {
5560 goto out;
5561 }
5562
5563 saveherefd = herefd;
5564 herefd = -1;
5565
5566 {
5567 int pip[2];
5568 struct job *jp;
5569
5570 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005571 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005572 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005573 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5574 FORCE_INT_ON;
5575 close(pip[0]);
5576 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005577 /*close(1);*/
5578 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005579 close(pip[1]);
5580 }
5581 eflag = 0;
5582 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5583 /* NOTREACHED */
5584 }
5585 close(pip[1]);
5586 result->fd = pip[0];
5587 result->jp = jp;
5588 }
5589 herefd = saveherefd;
5590 out:
5591 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5592 result->fd, result->buf, result->nleft, result->jp));
5593}
5594
5595/*
5596 * Expand stuff in backwards quotes.
5597 */
5598static void
5599expbackq(union node *cmd, int quoted, int quotes)
5600{
5601 struct backcmd in;
5602 int i;
5603 char buf[128];
5604 char *p;
5605 char *dest;
5606 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005607 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005608 struct stackmark smark;
5609
5610 INT_OFF;
5611 setstackmark(&smark);
5612 dest = expdest;
5613 startloc = dest - (char *)stackblock();
5614 grabstackstr(dest);
5615 evalbackcmd(cmd, &in);
5616 popstackmark(&smark);
5617
5618 p = in.buf;
5619 i = in.nleft;
5620 if (i == 0)
5621 goto read;
5622 for (;;) {
5623 memtodest(p, i, syntax, quotes);
5624 read:
5625 if (in.fd < 0)
5626 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005627 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005628 TRACE(("expbackq: read returns %d\n", i));
5629 if (i <= 0)
5630 break;
5631 p = buf;
5632 }
5633
Denis Vlasenko60818682007-09-28 22:07:23 +00005634 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635 if (in.fd >= 0) {
5636 close(in.fd);
5637 back_exitstatus = waitforjob(in.jp);
5638 }
5639 INT_ON;
5640
5641 /* Eat all trailing newlines */
5642 dest = expdest;
5643 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5644 STUNPUTC(dest);
5645 expdest = dest;
5646
5647 if (quoted == 0)
5648 recordregion(startloc, dest - (char *)stackblock(), 0);
5649 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5650 (dest - (char *)stackblock()) - startloc,
5651 (dest - (char *)stackblock()) - startloc,
5652 stackblock() + startloc));
5653}
5654
5655#if ENABLE_ASH_MATH_SUPPORT
5656/*
5657 * Expand arithmetic expression. Backup to start of expression,
5658 * evaluate, place result in (backed up) result, adjust string position.
5659 */
5660static void
5661expari(int quotes)
5662{
5663 char *p, *start;
5664 int begoff;
5665 int flag;
5666 int len;
5667
5668 /* ifsfree(); */
5669
5670 /*
5671 * This routine is slightly over-complicated for
5672 * efficiency. Next we scan backwards looking for the
5673 * start of arithmetic.
5674 */
5675 start = stackblock();
5676 p = expdest - 1;
5677 *p = '\0';
5678 p--;
5679 do {
5680 int esc;
5681
5682 while (*p != CTLARI) {
5683 p--;
5684#if DEBUG
5685 if (p < start) {
5686 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5687 }
5688#endif
5689 }
5690
5691 esc = esclen(start, p);
5692 if (!(esc % 2)) {
5693 break;
5694 }
5695
5696 p -= esc + 1;
5697 } while (1);
5698
5699 begoff = p - start;
5700
5701 removerecordregions(begoff);
5702
5703 flag = p[1];
5704
5705 expdest = p;
5706
5707 if (quotes)
5708 rmescapes(p + 2);
5709
5710 len = cvtnum(dash_arith(p + 2));
5711
5712 if (flag != '"')
5713 recordregion(begoff, begoff + len, 0);
5714}
5715#endif
5716
5717/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005718static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005719
5720/*
5721 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5722 * characters to allow for further processing. Otherwise treat
5723 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005724 *
5725 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5726 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5727 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005728 */
5729static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005730argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005731{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005732 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005733 '=',
5734 ':',
5735 CTLQUOTEMARK,
5736 CTLENDVAR,
5737 CTLESC,
5738 CTLVAR,
5739 CTLBACKQ,
5740 CTLBACKQ | CTLQUOTE,
5741#if ENABLE_ASH_MATH_SUPPORT
5742 CTLENDARI,
5743#endif
5744 0
5745 };
5746 const char *reject = spclchars;
5747 int c;
5748 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5749 int breakall = flag & EXP_WORD;
5750 int inquotes;
5751 size_t length;
5752 int startloc;
5753
5754 if (!(flag & EXP_VARTILDE)) {
5755 reject += 2;
5756 } else if (flag & EXP_VARTILDE2) {
5757 reject++;
5758 }
5759 inquotes = 0;
5760 length = 0;
5761 if (flag & EXP_TILDE) {
5762 char *q;
5763
5764 flag &= ~EXP_TILDE;
5765 tilde:
5766 q = p;
5767 if (*q == CTLESC && (flag & EXP_QWORD))
5768 q++;
5769 if (*q == '~')
5770 p = exptilde(p, q, flag);
5771 }
5772 start:
5773 startloc = expdest - (char *)stackblock();
5774 for (;;) {
5775 length += strcspn(p + length, reject);
5776 c = p[length];
5777 if (c && (!(c & 0x80)
5778#if ENABLE_ASH_MATH_SUPPORT
5779 || c == CTLENDARI
5780#endif
5781 )) {
5782 /* c == '=' || c == ':' || c == CTLENDARI */
5783 length++;
5784 }
5785 if (length > 0) {
5786 int newloc;
5787 expdest = stack_nputstr(p, length, expdest);
5788 newloc = expdest - (char *)stackblock();
5789 if (breakall && !inquotes && newloc > startloc) {
5790 recordregion(startloc, newloc, 0);
5791 }
5792 startloc = newloc;
5793 }
5794 p += length + 1;
5795 length = 0;
5796
5797 switch (c) {
5798 case '\0':
5799 goto breakloop;
5800 case '=':
5801 if (flag & EXP_VARTILDE2) {
5802 p--;
5803 continue;
5804 }
5805 flag |= EXP_VARTILDE2;
5806 reject++;
5807 /* fall through */
5808 case ':':
5809 /*
5810 * sort of a hack - expand tildes in variable
5811 * assignments (after the first '=' and after ':'s).
5812 */
5813 if (*--p == '~') {
5814 goto tilde;
5815 }
5816 continue;
5817 }
5818
5819 switch (c) {
5820 case CTLENDVAR: /* ??? */
5821 goto breakloop;
5822 case CTLQUOTEMARK:
5823 /* "$@" syntax adherence hack */
5824 if (
5825 !inquotes &&
5826 !memcmp(p, dolatstr, 4) &&
5827 (p[4] == CTLQUOTEMARK || (
5828 p[4] == CTLENDVAR &&
5829 p[5] == CTLQUOTEMARK
5830 ))
5831 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005832 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005833 goto start;
5834 }
5835 inquotes = !inquotes;
5836 addquote:
5837 if (quotes) {
5838 p--;
5839 length++;
5840 startloc++;
5841 }
5842 break;
5843 case CTLESC:
5844 startloc++;
5845 length++;
5846 goto addquote;
5847 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005848 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005849 goto start;
5850 case CTLBACKQ:
5851 c = 0;
5852 case CTLBACKQ|CTLQUOTE:
5853 expbackq(argbackq->n, c, quotes);
5854 argbackq = argbackq->next;
5855 goto start;
5856#if ENABLE_ASH_MATH_SUPPORT
5857 case CTLENDARI:
5858 p--;
5859 expari(quotes);
5860 goto start;
5861#endif
5862 }
5863 }
5864 breakloop:
5865 ;
5866}
5867
5868static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005869scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005870 int zero)
5871{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005872// This commented out code was added by James Simmons <jsimmons@infradead.org>
5873// as part of a larger change when he added support for ${var/a/b}.
5874// However, it broke # and % operators:
5875//
5876//var=ababcdcd
5877// ok bad
5878//echo ${var#ab} abcdcd abcdcd
5879//echo ${var##ab} abcdcd abcdcd
5880//echo ${var#a*b} abcdcd ababcdcd (!)
5881//echo ${var##a*b} cdcd cdcd
5882//echo ${var#?} babcdcd ababcdcd (!)
5883//echo ${var##?} babcdcd babcdcd
5884//echo ${var#*} ababcdcd babcdcd (!)
5885//echo ${var##*}
5886//echo ${var%cd} ababcd ababcd
5887//echo ${var%%cd} ababcd abab (!)
5888//echo ${var%c*d} ababcd ababcd
5889//echo ${var%%c*d} abab ababcdcd (!)
5890//echo ${var%?} ababcdc ababcdc
5891//echo ${var%%?} ababcdc ababcdcd (!)
5892//echo ${var%*} ababcdcd ababcdcd
5893//echo ${var%%*}
5894//
5895// Commenting it back out helped. Remove it completely if it really
5896// is not needed.
5897
5898 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005899 char c;
5900
5901 loc = startp;
5902 loc2 = rmesc;
5903 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005904 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005905 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005906
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005907 c = *loc2;
5908 if (zero) {
5909 *loc2 = '\0';
5910 s = rmesc;
5911 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005912 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005913
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005914// // chop off end if its '*'
5915// full = strrchr(str, '*');
5916// if (full && full != str)
5917// match--;
5918//
5919// // If str starts with '*' replace with s.
5920// if ((*str == '*') && strlen(s) >= match) {
5921// full = xstrdup(s);
5922// strncpy(full+strlen(s)-match+1, str+1, match-1);
5923// } else
5924// full = xstrndup(str, match);
5925// match = strncmp(s, full, strlen(full));
5926// free(full);
5927//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005928 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005929 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005930 return loc;
5931 if (quotes && *loc == CTLESC)
5932 loc++;
5933 loc++;
5934 loc2++;
5935 } while (c);
5936 return 0;
5937}
5938
5939static char *
5940scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5941 int zero)
5942{
5943 int esc = 0;
5944 char *loc;
5945 char *loc2;
5946
5947 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5948 int match;
5949 char c = *loc2;
5950 const char *s = loc2;
5951 if (zero) {
5952 *loc2 = '\0';
5953 s = rmesc;
5954 }
5955 match = pmatch(str, s);
5956 *loc2 = c;
5957 if (match)
5958 return loc;
5959 loc--;
5960 if (quotes) {
5961 if (--esc < 0) {
5962 esc = esclen(startp, loc);
5963 }
5964 if (esc % 2) {
5965 esc--;
5966 loc--;
5967 }
5968 }
5969 }
5970 return 0;
5971}
5972
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005973static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005974static void
5975varunset(const char *end, const char *var, const char *umsg, int varflags)
5976{
5977 const char *msg;
5978 const char *tail;
5979
5980 tail = nullstr;
5981 msg = "parameter not set";
5982 if (umsg) {
5983 if (*end == CTLENDVAR) {
5984 if (varflags & VSNUL)
5985 tail = " or null";
5986 } else
5987 msg = umsg;
5988 }
5989 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5990}
5991
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005992#if ENABLE_ASH_BASH_COMPAT
5993static char *
5994parse_sub_pattern(char *arg, int inquotes)
5995{
5996 char *idx, *repl = NULL;
5997 unsigned char c;
5998
Denis Vlasenko2659c632008-06-14 06:04:59 +00005999 idx = arg;
6000 while (1) {
6001 c = *arg;
6002 if (!c)
6003 break;
6004 if (c == '/') {
6005 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006006 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006007 repl = idx + 1;
6008 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006009 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006010 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006011 *idx++ = c;
6012 if (!inquotes && c == '\\' && arg[1] == '\\')
6013 arg++; /* skip both \\, not just first one */
6014 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006015 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006016 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006017
6018 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006019}
6020#endif /* ENABLE_ASH_BASH_COMPAT */
6021
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006022static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006023subevalvar(char *p, char *str, int strloc, int subtype,
6024 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006026 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 char *startp;
6028 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006030 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6031 USE_ASH_BASH_COMPAT(char null = '\0';)
6032 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6033 int saveherefd = herefd;
6034 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006035 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006036 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037
6038 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006039 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6040 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041 STPUTC('\0', expdest);
6042 herefd = saveherefd;
6043 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006044 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045
6046 switch (subtype) {
6047 case VSASSIGN:
6048 setvar(str, startp, 0);
6049 amount = startp - expdest;
6050 STADJUST(amount, expdest);
6051 return startp;
6052
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006053#if ENABLE_ASH_BASH_COMPAT
6054 case VSSUBSTR:
6055 loc = str = stackblock() + strloc;
6056// TODO: number() instead? It does error checking...
6057 pos = atoi(loc);
6058 len = str - startp - 1;
6059
6060 /* *loc != '\0', guaranteed by parser */
6061 if (quotes) {
6062 char *ptr;
6063
6064 /* We must adjust the length by the number of escapes we find. */
6065 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006066 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006067 len--;
6068 ptr++;
6069 }
6070 }
6071 }
6072 orig_len = len;
6073
6074 if (*loc++ == ':') {
6075// TODO: number() instead? It does error checking...
6076 len = atoi(loc);
6077 } else {
6078 len = orig_len;
6079 while (*loc && *loc != ':')
6080 loc++;
6081 if (*loc++ == ':')
6082// TODO: number() instead? It does error checking...
6083 len = atoi(loc);
6084 }
6085 if (pos >= orig_len) {
6086 pos = 0;
6087 len = 0;
6088 }
6089 if (len > (orig_len - pos))
6090 len = orig_len - pos;
6091
6092 for (str = startp; pos; str++, pos--) {
6093 if (quotes && *str == CTLESC)
6094 str++;
6095 }
6096 for (loc = startp; len; len--) {
6097 if (quotes && *str == CTLESC)
6098 *loc++ = *str++;
6099 *loc++ = *str++;
6100 }
6101 *loc = '\0';
6102 amount = loc - expdest;
6103 STADJUST(amount, expdest);
6104 return loc;
6105#endif
6106
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006107 case VSQUESTION:
6108 varunset(p, str, startp, varflags);
6109 /* NOTREACHED */
6110 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006111 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006112
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006113 /* We'll comeback here if we grow the stack while handling
6114 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6115 * stack will need rebasing, and we'll need to remove our work
6116 * areas each time
6117 */
6118 USE_ASH_BASH_COMPAT(restart:)
6119
6120 amount = expdest - ((char *)stackblock() + resetloc);
6121 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006122 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006123
6124 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006125 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 if (quotes) {
6127 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6128 if (rmesc != startp) {
6129 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006130 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006131 }
6132 }
6133 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006134 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006136 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006138#if ENABLE_ASH_BASH_COMPAT
6139 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6140 char *idx, *end, *restart_detect;
6141
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006142 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006143 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6144 if (!repl)
6145 repl = &null;
6146 }
6147
6148 /* If there's no pattern to match, return the expansion unmolested */
6149 if (*str == '\0')
6150 return 0;
6151
6152 len = 0;
6153 idx = startp;
6154 end = str - 1;
6155 while (idx < end) {
6156 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6157 if (!loc) {
6158 /* No match, advance */
6159 restart_detect = stackblock();
6160 STPUTC(*idx, expdest);
6161 if (quotes && *idx == CTLESC) {
6162 idx++;
6163 len++;
6164 STPUTC(*idx, expdest);
6165 }
6166 if (stackblock() != restart_detect)
6167 goto restart;
6168 idx++;
6169 len++;
6170 rmesc++;
6171 continue;
6172 }
6173
6174 if (subtype == VSREPLACEALL) {
6175 while (idx < loc) {
6176 if (quotes && *idx == CTLESC)
6177 idx++;
6178 idx++;
6179 rmesc++;
6180 }
6181 } else
6182 idx = loc;
6183
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
7984 struct jmploc *volatile savehandler = exception_handler;
7985 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007986 int checkexit = 0;
7987 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007988 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007989
Eric Andersenc470f442003-07-28 09:56:35 +00007990 if (n == NULL) {
7991 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007992 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007993 }
Eric Andersenc470f442003-07-28 09:56:35 +00007994 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007995 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007996
7997 exception_handler = &jmploc;
7998 {
7999 int err = setjmp(jmploc.loc);
8000 if (err) {
8001 /* if it was a signal, check for trap handlers */
8002 if (exception == EXSIG)
8003 goto out;
8004 /* continue on the way out */
8005 exception_handler = savehandler;
8006 longjmp(exception_handler->loc, err);
8007 }
8008 }
8009
Eric Andersenc470f442003-07-28 09:56:35 +00008010 switch (n->type) {
8011 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008012#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008013 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008014 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008015 break;
8016#endif
8017 case NNOT:
8018 evaltree(n->nnot.com, EV_TESTED);
8019 status = !exitstatus;
8020 goto setstatus;
8021 case NREDIR:
8022 expredir(n->nredir.redirect);
8023 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8024 if (!status) {
8025 evaltree(n->nredir.n, flags & EV_TESTED);
8026 status = exitstatus;
8027 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008028 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008029 goto setstatus;
8030 case NCMD:
8031 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008032 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008033 if (eflag && !(flags & EV_TESTED))
8034 checkexit = ~0;
8035 goto calleval;
8036 case NFOR:
8037 evalfn = evalfor;
8038 goto calleval;
8039 case NWHILE:
8040 case NUNTIL:
8041 evalfn = evalloop;
8042 goto calleval;
8043 case NSUBSHELL:
8044 case NBACKGND:
8045 evalfn = evalsubshell;
8046 goto calleval;
8047 case NPIPE:
8048 evalfn = evalpipe;
8049 goto checkexit;
8050 case NCASE:
8051 evalfn = evalcase;
8052 goto calleval;
8053 case NAND:
8054 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008055 case NSEMI: {
8056
Eric Andersenc470f442003-07-28 09:56:35 +00008057#if NAND + 1 != NOR
8058#error NAND + 1 != NOR
8059#endif
8060#if NOR + 1 != NSEMI
8061#error NOR + 1 != NSEMI
8062#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008063 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008064 evaltree(
8065 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008066 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008067 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008068 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008069 break;
8070 if (!evalskip) {
8071 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008072 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008073 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008074 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008075 evalfn(n, flags);
8076 break;
8077 }
8078 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008079 }
Eric Andersenc470f442003-07-28 09:56:35 +00008080 case NIF:
8081 evaltree(n->nif.test, EV_TESTED);
8082 if (evalskip)
8083 break;
8084 if (exitstatus == 0) {
8085 n = n->nif.ifpart;
8086 goto evaln;
8087 } else if (n->nif.elsepart) {
8088 n = n->nif.elsepart;
8089 goto evaln;
8090 }
8091 goto success;
8092 case NDEFUN:
8093 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008094 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008095 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008096 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008097 exitstatus = status;
8098 break;
8099 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008100
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008101 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008102 exception_handler = savehandler;
8103 out1:
8104 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008105 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008106 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008107 goto exexit;
8108
8109 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008110 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008111 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008112 }
Eric Andersen62483552001-07-10 06:09:16 +00008113}
8114
Eric Andersenc470f442003-07-28 09:56:35 +00008115#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8116static
8117#endif
8118void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8119
Eric Andersenc470f442003-07-28 09:56:35 +00008120static void
8121evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008122{
8123 int status;
8124
8125 loopnest++;
8126 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008127 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008128 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008129 int i;
8130
Eric Andersencb57d552001-06-28 07:25:16 +00008131 evaltree(n->nbinary.ch1, EV_TESTED);
8132 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008133 skipping:
8134 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008135 evalskip = 0;
8136 continue;
8137 }
8138 if (evalskip == SKIPBREAK && --skipcount <= 0)
8139 evalskip = 0;
8140 break;
8141 }
Eric Andersenc470f442003-07-28 09:56:35 +00008142 i = exitstatus;
8143 if (n->type != NWHILE)
8144 i = !i;
8145 if (i != 0)
8146 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008147 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008148 status = exitstatus;
8149 if (evalskip)
8150 goto skipping;
8151 }
8152 loopnest--;
8153 exitstatus = status;
8154}
8155
Eric Andersenc470f442003-07-28 09:56:35 +00008156static void
8157evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008158{
8159 struct arglist arglist;
8160 union node *argp;
8161 struct strlist *sp;
8162 struct stackmark smark;
8163
8164 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008165 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008166 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008167 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008168 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008169 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008170 if (evalskip)
8171 goto out;
8172 }
8173 *arglist.lastp = NULL;
8174
8175 exitstatus = 0;
8176 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008177 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008178 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008179 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008180 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008181 if (evalskip) {
8182 if (evalskip == SKIPCONT && --skipcount <= 0) {
8183 evalskip = 0;
8184 continue;
8185 }
8186 if (evalskip == SKIPBREAK && --skipcount <= 0)
8187 evalskip = 0;
8188 break;
8189 }
8190 }
8191 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008192 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008193 popstackmark(&smark);
8194}
8195
Eric Andersenc470f442003-07-28 09:56:35 +00008196static void
8197evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008198{
8199 union node *cp;
8200 union node *patp;
8201 struct arglist arglist;
8202 struct stackmark smark;
8203
8204 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008205 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008206 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008207 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008208 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008209 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8210 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008211 if (casematch(patp, arglist.list->text)) {
8212 if (evalskip == 0) {
8213 evaltree(cp->nclist.body, flags);
8214 }
8215 goto out;
8216 }
8217 }
8218 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008219 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008220 popstackmark(&smark);
8221}
8222
Eric Andersenc470f442003-07-28 09:56:35 +00008223/*
8224 * Kick off a subshell to evaluate a tree.
8225 */
Eric Andersenc470f442003-07-28 09:56:35 +00008226static void
8227evalsubshell(union node *n, int flags)
8228{
8229 struct job *jp;
8230 int backgnd = (n->type == NBACKGND);
8231 int status;
8232
8233 expredir(n->nredir.redirect);
8234 if (!backgnd && flags & EV_EXIT && !trap[0])
8235 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008236 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008237 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008238 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008239 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008240 flags |= EV_EXIT;
8241 if (backgnd)
8242 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008243 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008244 redirect(n->nredir.redirect, 0);
8245 evaltreenr(n->nredir.n, flags);
8246 /* never returns */
8247 }
8248 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008249 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008250 status = waitforjob(jp);
8251 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008252 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008253}
8254
Eric Andersenc470f442003-07-28 09:56:35 +00008255/*
8256 * Compute the names of the files in a redirection list.
8257 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008258static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008259static void
8260expredir(union node *n)
8261{
8262 union node *redir;
8263
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008264 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008265 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008266
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008267 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008268 fn.lastp = &fn.list;
8269 switch (redir->type) {
8270 case NFROMTO:
8271 case NFROM:
8272 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008273#if ENABLE_ASH_BASH_COMPAT
8274 case NTO2:
8275#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008276 case NCLOBBER:
8277 case NAPPEND:
8278 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008279#if ENABLE_ASH_BASH_COMPAT
8280 store_expfname:
8281#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008282 redir->nfile.expfname = fn.list->text;
8283 break;
8284 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008285 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008286 if (redir->ndup.vname) {
8287 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008288 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008289 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008290#if ENABLE_ASH_BASH_COMPAT
8291//FIXME: we used expandarg with different args!
8292 if (!isdigit_str9(fn.list->text)) {
8293 /* >&file, not >&fd */
8294 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8295 ash_msg_and_raise_error("redir error");
8296 redir->type = NTO2;
8297 goto store_expfname;
8298 }
8299#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008300 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008301 }
8302 break;
8303 }
8304 }
8305}
8306
Eric Andersencb57d552001-06-28 07:25:16 +00008307/*
Eric Andersencb57d552001-06-28 07:25:16 +00008308 * Evaluate a pipeline. All the processes in the pipeline are children
8309 * of the process creating the pipeline. (This differs from some versions
8310 * of the shell, which make the last process in a pipeline the parent
8311 * of all the rest.)
8312 */
Eric Andersenc470f442003-07-28 09:56:35 +00008313static void
8314evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008315{
8316 struct job *jp;
8317 struct nodelist *lp;
8318 int pipelen;
8319 int prevfd;
8320 int pip[2];
8321
Eric Andersenc470f442003-07-28 09:56:35 +00008322 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008323 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008324 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008325 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008326 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008327 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008328 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008329 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008330 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008331 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008332 pip[1] = -1;
8333 if (lp->next) {
8334 if (pipe(pip) < 0) {
8335 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008336 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008337 }
8338 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008339 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008340 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008341 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008342 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008343 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008344 if (prevfd > 0) {
8345 dup2(prevfd, 0);
8346 close(prevfd);
8347 }
8348 if (pip[1] > 1) {
8349 dup2(pip[1], 1);
8350 close(pip[1]);
8351 }
Eric Andersenc470f442003-07-28 09:56:35 +00008352 evaltreenr(lp->n, flags);
8353 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008354 }
8355 if (prevfd >= 0)
8356 close(prevfd);
8357 prevfd = pip[0];
8358 close(pip[1]);
8359 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008360 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008361 exitstatus = waitforjob(jp);
8362 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008363 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008364 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008365}
8366
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008367/*
8368 * Controls whether the shell is interactive or not.
8369 */
8370static void
8371setinteractive(int on)
8372{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008373 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008374
8375 if (++on == is_interactive)
8376 return;
8377 is_interactive = on;
8378 setsignal(SIGINT);
8379 setsignal(SIGQUIT);
8380 setsignal(SIGTERM);
8381#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8382 if (is_interactive > 1) {
8383 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008384 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008385
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008386 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008387 out1fmt(
8388 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008389 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008390 "Enter 'help' for a list of built-in commands."
8391 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008392 bb_banner);
8393 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008394 }
8395 }
8396#endif
8397}
8398
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008399static void
8400optschanged(void)
8401{
8402#if DEBUG
8403 opentrace();
8404#endif
8405 setinteractive(iflag);
8406 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008407#if ENABLE_FEATURE_EDITING_VI
8408 if (viflag)
8409 line_input_state->flags |= VI_MODE;
8410 else
8411 line_input_state->flags &= ~VI_MODE;
8412#else
8413 viflag = 0; /* forcibly keep the option off */
8414#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008415}
8416
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008417static struct localvar *localvars;
8418
8419/*
8420 * Called after a function returns.
8421 * Interrupts must be off.
8422 */
8423static void
8424poplocalvars(void)
8425{
8426 struct localvar *lvp;
8427 struct var *vp;
8428
8429 while ((lvp = localvars) != NULL) {
8430 localvars = lvp->next;
8431 vp = lvp->vp;
8432 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8433 if (vp == NULL) { /* $- saved */
8434 memcpy(optlist, lvp->text, sizeof(optlist));
8435 free((char*)lvp->text);
8436 optschanged();
8437 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8438 unsetvar(vp->text);
8439 } else {
8440 if (vp->func)
8441 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8442 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8443 free((char*)vp->text);
8444 vp->flags = lvp->flags;
8445 vp->text = lvp->text;
8446 }
8447 free(lvp);
8448 }
8449}
8450
8451static int
8452evalfun(struct funcnode *func, int argc, char **argv, int flags)
8453{
8454 volatile struct shparam saveparam;
8455 struct localvar *volatile savelocalvars;
8456 struct jmploc *volatile savehandler;
8457 struct jmploc jmploc;
8458 int e;
8459
8460 saveparam = shellparam;
8461 savelocalvars = localvars;
8462 e = setjmp(jmploc.loc);
8463 if (e) {
8464 goto funcdone;
8465 }
8466 INT_OFF;
8467 savehandler = exception_handler;
8468 exception_handler = &jmploc;
8469 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008470 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008471 func->count++;
8472 funcnest++;
8473 INT_ON;
8474 shellparam.nparam = argc - 1;
8475 shellparam.p = argv + 1;
8476#if ENABLE_ASH_GETOPTS
8477 shellparam.optind = 1;
8478 shellparam.optoff = -1;
8479#endif
8480 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008481 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008482 INT_OFF;
8483 funcnest--;
8484 freefunc(func);
8485 poplocalvars();
8486 localvars = savelocalvars;
8487 freeparam(&shellparam);
8488 shellparam = saveparam;
8489 exception_handler = savehandler;
8490 INT_ON;
8491 evalskip &= ~SKIPFUNC;
8492 return e;
8493}
8494
Denis Vlasenko131ae172007-02-18 13:00:19 +00008495#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008496static char **
8497parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008498{
8499 char *cp, c;
8500
8501 for (;;) {
8502 cp = *++argv;
8503 if (!cp)
8504 return 0;
8505 if (*cp++ != '-')
8506 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008507 c = *cp++;
8508 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008509 break;
8510 if (c == '-' && !*cp) {
8511 argv++;
8512 break;
8513 }
8514 do {
8515 switch (c) {
8516 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008517 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008518 break;
8519 default:
8520 /* run 'typecmd' for other options */
8521 return 0;
8522 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008523 c = *cp++;
8524 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008525 }
8526 return argv;
8527}
8528#endif
8529
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008530/*
8531 * Make a variable a local variable. When a variable is made local, it's
8532 * value and flags are saved in a localvar structure. The saved values
8533 * will be restored when the shell function returns. We handle the name
8534 * "-" as a special case.
8535 */
8536static void
8537mklocal(char *name)
8538{
8539 struct localvar *lvp;
8540 struct var **vpp;
8541 struct var *vp;
8542
8543 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008544 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008545 if (LONE_DASH(name)) {
8546 char *p;
8547 p = ckmalloc(sizeof(optlist));
8548 lvp->text = memcpy(p, optlist, sizeof(optlist));
8549 vp = NULL;
8550 } else {
8551 char *eq;
8552
8553 vpp = hashvar(name);
8554 vp = *findvar(vpp, name);
8555 eq = strchr(name, '=');
8556 if (vp == NULL) {
8557 if (eq)
8558 setvareq(name, VSTRFIXED);
8559 else
8560 setvar(name, NULL, VSTRFIXED);
8561 vp = *vpp; /* the new variable */
8562 lvp->flags = VUNSET;
8563 } else {
8564 lvp->text = vp->text;
8565 lvp->flags = vp->flags;
8566 vp->flags |= VSTRFIXED|VTEXTFIXED;
8567 if (eq)
8568 setvareq(name, 0);
8569 }
8570 }
8571 lvp->vp = vp;
8572 lvp->next = localvars;
8573 localvars = lvp;
8574 INT_ON;
8575}
8576
8577/*
8578 * The "local" command.
8579 */
8580static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008581localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008582{
8583 char *name;
8584
8585 argv = argptr;
8586 while ((name = *argv++) != NULL) {
8587 mklocal(name);
8588 }
8589 return 0;
8590}
8591
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008592static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008593falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008594{
8595 return 1;
8596}
8597
8598static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008599truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008600{
8601 return 0;
8602}
8603
8604static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008605execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008606{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008607 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008608 iflag = 0; /* exit on error */
8609 mflag = 0;
8610 optschanged();
8611 shellexec(argv + 1, pathval(), 0);
8612 }
8613 return 0;
8614}
8615
8616/*
8617 * The return command.
8618 */
8619static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008620returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008621{
8622 /*
8623 * If called outside a function, do what ksh does;
8624 * skip the rest of the file.
8625 */
8626 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8627 return argv[1] ? number(argv[1]) : exitstatus;
8628}
8629
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008630/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008631static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008632static int dotcmd(int, char **);
8633static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008634static int exitcmd(int, char **);
8635static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008636#if ENABLE_ASH_GETOPTS
8637static int getoptscmd(int, char **);
8638#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008639#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008640static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008641#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008642#if ENABLE_ASH_MATH_SUPPORT
8643static int letcmd(int, char **);
8644#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008645static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008646static int setcmd(int, char **);
8647static int shiftcmd(int, char **);
8648static int timescmd(int, char **);
8649static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008650static int umaskcmd(int, char **);
8651static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008652static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008653
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008654#define BUILTIN_NOSPEC "0"
8655#define BUILTIN_SPECIAL "1"
8656#define BUILTIN_REGULAR "2"
8657#define BUILTIN_SPEC_REG "3"
8658#define BUILTIN_ASSIGN "4"
8659#define BUILTIN_SPEC_ASSG "5"
8660#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008661#define BUILTIN_SPEC_REG_ASSG "7"
8662
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008663/* We do not handle [[ expr ]] bashism bash-compatibly,
8664 * we make it a synonym of [ expr ].
8665 * Basically, word splitting and pathname expansion should NOT be performed
8666 * Examples:
8667 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8668 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8669 * Additional operators:
8670 * || and && should work as -o and -a
8671 * =~ regexp match
8672 * Apart from the above, [[ expr ]] should work as [ expr ]
8673 */
8674
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008675#define echocmd echo_main
8676#define printfcmd printf_main
8677#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008678
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008679/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008680static const struct builtincmd builtintab[] = {
8681 { BUILTIN_SPEC_REG ".", dotcmd },
8682 { BUILTIN_SPEC_REG ":", truecmd },
8683#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008684 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008685#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008686 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008687#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008688#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008689#if ENABLE_ASH_ALIAS
8690 { BUILTIN_REG_ASSG "alias", aliascmd },
8691#endif
8692#if JOBS
8693 { BUILTIN_REGULAR "bg", fg_bgcmd },
8694#endif
8695 { BUILTIN_SPEC_REG "break", breakcmd },
8696 { BUILTIN_REGULAR "cd", cdcmd },
8697 { BUILTIN_NOSPEC "chdir", cdcmd },
8698#if ENABLE_ASH_CMDCMD
8699 { BUILTIN_REGULAR "command", commandcmd },
8700#endif
8701 { BUILTIN_SPEC_REG "continue", breakcmd },
8702#if ENABLE_ASH_BUILTIN_ECHO
8703 { BUILTIN_REGULAR "echo", echocmd },
8704#endif
8705 { BUILTIN_SPEC_REG "eval", evalcmd },
8706 { BUILTIN_SPEC_REG "exec", execcmd },
8707 { BUILTIN_SPEC_REG "exit", exitcmd },
8708 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8709 { BUILTIN_REGULAR "false", falsecmd },
8710#if JOBS
8711 { BUILTIN_REGULAR "fg", fg_bgcmd },
8712#endif
8713#if ENABLE_ASH_GETOPTS
8714 { BUILTIN_REGULAR "getopts", getoptscmd },
8715#endif
8716 { BUILTIN_NOSPEC "hash", hashcmd },
8717#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8718 { BUILTIN_NOSPEC "help", helpcmd },
8719#endif
8720#if JOBS
8721 { BUILTIN_REGULAR "jobs", jobscmd },
8722 { BUILTIN_REGULAR "kill", killcmd },
8723#endif
8724#if ENABLE_ASH_MATH_SUPPORT
8725 { BUILTIN_NOSPEC "let", letcmd },
8726#endif
8727 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008728#if ENABLE_ASH_BUILTIN_PRINTF
8729 { BUILTIN_REGULAR "printf", printfcmd },
8730#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008731 { BUILTIN_NOSPEC "pwd", pwdcmd },
8732 { BUILTIN_REGULAR "read", readcmd },
8733 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8734 { BUILTIN_SPEC_REG "return", returncmd },
8735 { BUILTIN_SPEC_REG "set", setcmd },
8736 { BUILTIN_SPEC_REG "shift", shiftcmd },
8737 { BUILTIN_SPEC_REG "source", dotcmd },
8738#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008739 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008740#endif
8741 { BUILTIN_SPEC_REG "times", timescmd },
8742 { BUILTIN_SPEC_REG "trap", trapcmd },
8743 { BUILTIN_REGULAR "true", truecmd },
8744 { BUILTIN_NOSPEC "type", typecmd },
8745 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8746 { BUILTIN_REGULAR "umask", umaskcmd },
8747#if ENABLE_ASH_ALIAS
8748 { BUILTIN_REGULAR "unalias", unaliascmd },
8749#endif
8750 { BUILTIN_SPEC_REG "unset", unsetcmd },
8751 { BUILTIN_REGULAR "wait", waitcmd },
8752};
8753
Denis Vlasenko80591b02008-03-25 07:49:43 +00008754/* Should match the above table! */
8755#define COMMANDCMD (builtintab + \
8756 2 + \
8757 1 * ENABLE_ASH_BUILTIN_TEST + \
8758 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8759 1 * ENABLE_ASH_ALIAS + \
8760 1 * ENABLE_ASH_JOB_CONTROL + \
8761 3)
8762#define EXECCMD (builtintab + \
8763 2 + \
8764 1 * ENABLE_ASH_BUILTIN_TEST + \
8765 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8766 1 * ENABLE_ASH_ALIAS + \
8767 1 * ENABLE_ASH_JOB_CONTROL + \
8768 3 + \
8769 1 * ENABLE_ASH_CMDCMD + \
8770 1 + \
8771 ENABLE_ASH_BUILTIN_ECHO + \
8772 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008773
8774/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008775 * Search the table of builtin commands.
8776 */
8777static struct builtincmd *
8778find_builtin(const char *name)
8779{
8780 struct builtincmd *bp;
8781
8782 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008783 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008784 pstrcmp
8785 );
8786 return bp;
8787}
8788
8789/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008790 * Execute a simple command.
8791 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008792static int
8793isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008794{
8795 const char *q = endofname(p);
8796 if (p == q)
8797 return 0;
8798 return *q == '=';
8799}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008800static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008801bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008802{
8803 /* Preserve exitstatus of a previous possible redirection
8804 * as POSIX mandates */
8805 return back_exitstatus;
8806}
Eric Andersenc470f442003-07-28 09:56:35 +00008807static void
8808evalcommand(union node *cmd, int flags)
8809{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008810 static const struct builtincmd null_bltin = {
8811 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008812 };
Eric Andersenc470f442003-07-28 09:56:35 +00008813 struct stackmark smark;
8814 union node *argp;
8815 struct arglist arglist;
8816 struct arglist varlist;
8817 char **argv;
8818 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008819 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008820 struct cmdentry cmdentry;
8821 struct job *jp;
8822 char *lastarg;
8823 const char *path;
8824 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008825 int status;
8826 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008827 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008828 smallint cmd_is_exec;
8829 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008830
8831 /* First expand the arguments. */
8832 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8833 setstackmark(&smark);
8834 back_exitstatus = 0;
8835
8836 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008837 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008838 varlist.lastp = &varlist.list;
8839 *varlist.lastp = NULL;
8840 arglist.lastp = &arglist.list;
8841 *arglist.lastp = NULL;
8842
8843 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008844 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008845 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8846 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8847 }
8848
Eric Andersenc470f442003-07-28 09:56:35 +00008849 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8850 struct strlist **spp;
8851
8852 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008853 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008854 expandarg(argp, &arglist, EXP_VARTILDE);
8855 else
8856 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8857
Eric Andersenc470f442003-07-28 09:56:35 +00008858 for (sp = *spp; sp; sp = sp->next)
8859 argc++;
8860 }
8861
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008862 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008863 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008864 TRACE(("evalcommand arg: %s\n", sp->text));
8865 *nargv++ = sp->text;
8866 }
8867 *nargv = NULL;
8868
8869 lastarg = NULL;
8870 if (iflag && funcnest == 0 && argc > 0)
8871 lastarg = nargv[-1];
8872
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008873 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008874 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008875 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008876
8877 path = vpath.text;
8878 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8879 struct strlist **spp;
8880 char *p;
8881
8882 spp = varlist.lastp;
8883 expandarg(argp, &varlist, EXP_VARTILDE);
8884
8885 /*
8886 * Modify the command lookup path, if a PATH= assignment
8887 * is present
8888 */
8889 p = (*spp)->text;
8890 if (varequal(p, path))
8891 path = p;
8892 }
8893
8894 /* Print the command if xflag is set. */
8895 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008896 int n;
8897 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008898
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008899 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008900 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008901
8902 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008903 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008904 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008905 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008906 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008907 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008908 p--;
8909 }
8910 }
8911 sp = arglist.list;
8912 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008913 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008914 }
8915
8916 cmd_is_exec = 0;
8917 spclbltin = -1;
8918
8919 /* Now locate the command. */
8920 if (argc) {
8921 const char *oldpath;
8922 int cmd_flag = DO_ERR;
8923
8924 path += 5;
8925 oldpath = path;
8926 for (;;) {
8927 find_command(argv[0], &cmdentry, cmd_flag, path);
8928 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008929 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008930 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008931 goto bail;
8932 }
8933
8934 /* implement bltin and command here */
8935 if (cmdentry.cmdtype != CMDBUILTIN)
8936 break;
8937 if (spclbltin < 0)
8938 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8939 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008940 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008941#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008942 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008943 path = oldpath;
8944 nargv = parse_command_args(argv, &path);
8945 if (!nargv)
8946 break;
8947 argc -= nargv - argv;
8948 argv = nargv;
8949 cmd_flag |= DO_NOFUNC;
8950 } else
8951#endif
8952 break;
8953 }
8954 }
8955
8956 if (status) {
8957 /* We have a redirection error. */
8958 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008959 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008960 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008961 exitstatus = status;
8962 goto out;
8963 }
8964
8965 /* Execute the command. */
8966 switch (cmdentry.cmdtype) {
8967 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008968
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008969#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008970/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8971 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008972 {
8973 /* find_command() encodes applet_no as (-2 - applet_no) */
8974 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008975 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008976 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008977 /* run <applet>_main() */
8978 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008979 break;
8980 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008981 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008982#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008983 /* Fork off a child process if necessary. */
8984 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008985 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008986 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008987 if (forkshell(jp, cmd, FORK_FG) != 0) {
8988 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008989 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008990 break;
8991 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008992 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008993 }
8994 listsetvar(varlist.list, VEXPORT|VSTACK);
8995 shellexec(argv, path, cmdentry.u.index);
8996 /* NOTREACHED */
8997
8998 case CMDBUILTIN:
8999 cmdenviron = varlist.list;
9000 if (cmdenviron) {
9001 struct strlist *list = cmdenviron;
9002 int i = VNOSET;
9003 if (spclbltin > 0 || argc == 0) {
9004 i = 0;
9005 if (cmd_is_exec && argc > 1)
9006 i = VEXPORT;
9007 }
9008 listsetvar(list, i);
9009 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009010 /* Tight loop with builtins only:
9011 * "while kill -0 $child; do true; done"
9012 * will never exit even if $child died, unless we do this
9013 * to reap the zombie and make kill detect that it's gone: */
9014 dowait(DOWAIT_NONBLOCK, NULL);
9015
Eric Andersenc470f442003-07-28 09:56:35 +00009016 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9017 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009018 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00009019 if (i == EXEXIT)
9020 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009021 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009022 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009023 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009024 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009025 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009026 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009027 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009028 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009029 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009030 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009031 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009032 }
9033 break;
9034
9035 case CMDFUNCTION:
9036 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009037 /* See above for the rationale */
9038 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009039 if (evalfun(cmdentry.u.func, argc, argv, flags))
9040 goto raise;
9041 break;
9042 }
9043
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009044 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009045 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009046 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009047 /* dsl: I think this is intended to be used to support
9048 * '_' in 'vi' command mode during line editing...
9049 * However I implemented that within libedit itself.
9050 */
9051 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009052 }
Eric Andersenc470f442003-07-28 09:56:35 +00009053 popstackmark(&smark);
9054}
9055
9056static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009057evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9058{
Eric Andersenc470f442003-07-28 09:56:35 +00009059 char *volatile savecmdname;
9060 struct jmploc *volatile savehandler;
9061 struct jmploc jmploc;
9062 int i;
9063
9064 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009065 i = setjmp(jmploc.loc);
9066 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009067 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009068 savehandler = exception_handler;
9069 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009070 commandname = argv[0];
9071 argptr = argv + 1;
9072 optptr = NULL; /* initialize nextopt */
9073 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009074 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009075 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009076 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009077 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009078 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009079// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009080 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009081
9082 return i;
9083}
9084
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009085static int
9086goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009087{
9088 return !*endofname(p);
9089}
9090
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009091
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009092/*
9093 * Search for a command. This is called before we fork so that the
9094 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009095 * the child. The check for "goodname" is an overly conservative
9096 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009097 */
Eric Andersenc470f442003-07-28 09:56:35 +00009098static void
9099prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009100{
9101 struct cmdentry entry;
9102
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009103 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9104 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009105}
9106
Eric Andersencb57d552001-06-28 07:25:16 +00009107
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009108/* ============ Builtin commands
9109 *
9110 * Builtin commands whose functions are closely tied to evaluation
9111 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009112 */
9113
9114/*
Eric Andersencb57d552001-06-28 07:25:16 +00009115 * Handle break and continue commands. Break, continue, and return are
9116 * all handled by setting the evalskip flag. The evaluation routines
9117 * above all check this flag, and if it is set they start skipping
9118 * commands rather than executing them. The variable skipcount is
9119 * the number of loops to break/continue, or the number of function
9120 * levels to return. (The latter is always 1.) It should probably
9121 * be an error to break out of more loops than exist, but it isn't
9122 * in the standard shell so we don't make it one here.
9123 */
Eric Andersenc470f442003-07-28 09:56:35 +00009124static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009125breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009126{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009127 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009128
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009129 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009130 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009131 if (n > loopnest)
9132 n = loopnest;
9133 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009134 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009135 skipcount = n;
9136 }
9137 return 0;
9138}
9139
Eric Andersenc470f442003-07-28 09:56:35 +00009140
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009141/* ============ input.c
9142 *
Eric Andersen90898442003-08-06 11:20:52 +00009143 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009144 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009145
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009146enum {
9147 INPUT_PUSH_FILE = 1,
9148 INPUT_NOFILE_OK = 2,
9149};
Eric Andersencb57d552001-06-28 07:25:16 +00009150
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009151static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009152/* values of checkkwd variable */
9153#define CHKALIAS 0x1
9154#define CHKKWD 0x2
9155#define CHKNL 0x4
9156
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009157/*
9158 * Push a string back onto the input at this current parsefile level.
9159 * We handle aliases this way.
9160 */
9161#if !ENABLE_ASH_ALIAS
9162#define pushstring(s, ap) pushstring(s)
9163#endif
9164static void
9165pushstring(char *s, struct alias *ap)
9166{
9167 struct strpush *sp;
9168 int len;
9169
9170 len = strlen(s);
9171 INT_OFF;
9172 if (g_parsefile->strpush) {
9173 sp = ckzalloc(sizeof(*sp));
9174 sp->prev = g_parsefile->strpush;
9175 } else {
9176 sp = &(g_parsefile->basestrpush);
9177 }
9178 g_parsefile->strpush = sp;
9179 sp->prev_string = g_parsefile->next_to_pgetc;
9180 sp->prev_left_in_line = g_parsefile->left_in_line;
9181#if ENABLE_ASH_ALIAS
9182 sp->ap = ap;
9183 if (ap) {
9184 ap->flag |= ALIASINUSE;
9185 sp->string = s;
9186 }
9187#endif
9188 g_parsefile->next_to_pgetc = s;
9189 g_parsefile->left_in_line = len;
9190 INT_ON;
9191}
9192
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009193static void
9194popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009195{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009196 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009197
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009198 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009199#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009200 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009201 if (g_parsefile->next_to_pgetc[-1] == ' '
9202 || g_parsefile->next_to_pgetc[-1] == '\t'
9203 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009204 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009205 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009206 if (sp->string != sp->ap->val) {
9207 free(sp->string);
9208 }
9209 sp->ap->flag &= ~ALIASINUSE;
9210 if (sp->ap->flag & ALIASDEAD) {
9211 unalias(sp->ap->name);
9212 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009213 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009214#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009215 g_parsefile->next_to_pgetc = sp->prev_string;
9216 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009217 g_parsefile->strpush = sp->prev;
9218 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009219 free(sp);
9220 INT_ON;
9221}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009222
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009223//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9224//it peeks whether it is &>, and then pushes back both chars.
9225//This function needs to save last *next_to_pgetc to buf[0]
9226//to make two pungetc() reliable. Currently,
9227// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009228static int
9229preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009230{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009231 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009232 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009233
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009234 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009235#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009236 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009237 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009238 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009239 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009240#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009241 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009242#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009243 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9244 if (nr == 0) {
9245 /* Ctrl+C pressed */
9246 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009247 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009248 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009249 raise(SIGINT);
9250 return 1;
9251 }
Eric Andersenc470f442003-07-28 09:56:35 +00009252 goto retry;
9253 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009254 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009255 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009256 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009257 }
Eric Andersencb57d552001-06-28 07:25:16 +00009258 }
9259#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009260 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009261#endif
9262
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009263#if 0
9264/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009265 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009266 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009267 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009268 if (flags >= 0 && (flags & O_NONBLOCK)) {
9269 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009270 if (fcntl(0, F_SETFL, flags) >= 0) {
9271 out2str("sh: turning off NDELAY mode\n");
9272 goto retry;
9273 }
9274 }
9275 }
9276 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009277#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009278 return nr;
9279}
9280
9281/*
9282 * Refill the input buffer and return the next input character:
9283 *
9284 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009285 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9286 * or we are reading from a string so we can't refill the buffer,
9287 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009288 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9289 * 4) Process input up to the next newline, deleting nul characters.
9290 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009291//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9292#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009293static int
Eric Andersenc470f442003-07-28 09:56:35 +00009294preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009295{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009296 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009297 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009298
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009299 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009300#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009301 if (g_parsefile->left_in_line == -1
9302 && g_parsefile->strpush->ap
9303 && g_parsefile->next_to_pgetc[-1] != ' '
9304 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009305 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009306 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009307 return PEOA;
9308 }
Eric Andersen2870d962001-07-02 17:27:21 +00009309#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009310 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009311 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009312 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9313 g_parsefile->left_in_line,
9314 g_parsefile->next_to_pgetc,
9315 g_parsefile->next_to_pgetc);
9316 if (--g_parsefile->left_in_line >= 0)
9317 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009318 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009319 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009320 * "pgetc" needs refilling.
9321 */
9322
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009323 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009324 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009325 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009326 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009327 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009328 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009329 /* even in failure keep left_in_line and next_to_pgetc
9330 * in lock step, for correct multi-layer pungetc.
9331 * left_in_line was decremented before preadbuffer(),
9332 * must inc next_to_pgetc: */
9333 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009334 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009335 }
Eric Andersencb57d552001-06-28 07:25:16 +00009336
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009337 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009338 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009339 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009340 again:
9341 more = preadfd();
9342 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009343 /* don't try reading again */
9344 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009345 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009346 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009347 return PEOF;
9348 }
9349 }
9350
Denis Vlasenko727752d2008-11-28 03:41:47 +00009351 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009352 * Set g_parsefile->left_in_line
9353 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009354 * NUL chars are deleted.
9355 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009356 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009357 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009358 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009359
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009360 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009361
Denis Vlasenko727752d2008-11-28 03:41:47 +00009362 c = *q;
9363 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009364 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009365 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009366 q++;
9367 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009368 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009369 break;
9370 }
Eric Andersencb57d552001-06-28 07:25:16 +00009371 }
9372
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009373 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009374 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9375 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009376 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009377 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009378 }
9379 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009380 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009381
Eric Andersencb57d552001-06-28 07:25:16 +00009382 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009383 char save = *q;
9384 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009385 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009386 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009387 }
9388
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009389 pgetc_debug("preadbuffer at %d:%p'%s'",
9390 g_parsefile->left_in_line,
9391 g_parsefile->next_to_pgetc,
9392 g_parsefile->next_to_pgetc);
9393 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009394}
9395
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009396#define pgetc_as_macro() \
9397 (--g_parsefile->left_in_line >= 0 \
9398 ? (unsigned char)(*g_parsefile->next_to_pgetc++) \
9399 : preadbuffer() \
9400 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009401
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009402static int
9403pgetc(void)
9404{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009405 pgetc_debug("pgetc_fast at %d:%p'%s'",
9406 g_parsefile->left_in_line,
9407 g_parsefile->next_to_pgetc,
9408 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009409 return pgetc_as_macro();
9410}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009411
9412#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009413#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009414#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009415#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009416#endif
9417
9418/*
9419 * Same as pgetc(), but ignores PEOA.
9420 */
9421#if ENABLE_ASH_ALIAS
9422static int
9423pgetc2(void)
9424{
9425 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009426 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009427 pgetc_debug("pgetc_fast at %d:%p'%s'",
9428 g_parsefile->left_in_line,
9429 g_parsefile->next_to_pgetc,
9430 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009431 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009432 } while (c == PEOA);
9433 return c;
9434}
9435#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009436#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009437#endif
9438
9439/*
9440 * Read a line from the script.
9441 */
9442static char *
9443pfgets(char *line, int len)
9444{
9445 char *p = line;
9446 int nleft = len;
9447 int c;
9448
9449 while (--nleft > 0) {
9450 c = pgetc2();
9451 if (c == PEOF) {
9452 if (p == line)
9453 return NULL;
9454 break;
9455 }
9456 *p++ = c;
9457 if (c == '\n')
9458 break;
9459 }
9460 *p = '\0';
9461 return line;
9462}
9463
Eric Andersenc470f442003-07-28 09:56:35 +00009464/*
9465 * Undo the last call to pgetc. Only one character may be pushed back.
9466 * PEOF may be pushed back.
9467 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009468static void
Eric Andersenc470f442003-07-28 09:56:35 +00009469pungetc(void)
9470{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009471 g_parsefile->left_in_line++;
9472 g_parsefile->next_to_pgetc--;
9473 pgetc_debug("pushed back to %d:%p'%s'",
9474 g_parsefile->left_in_line,
9475 g_parsefile->next_to_pgetc,
9476 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009477}
9478
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009479/*
9480 * To handle the "." command, a stack of input files is used. Pushfile
9481 * adds a new entry to the stack and popfile restores the previous level.
9482 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009483static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009484pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009485{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009486 struct parsefile *pf;
9487
Denis Vlasenko597906c2008-02-20 16:38:54 +00009488 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009489 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009490 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009491 /*pf->strpush = NULL; - ckzalloc did it */
9492 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009493 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009494}
9495
9496static void
9497popfile(void)
9498{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009499 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009500
Denis Vlasenkob012b102007-02-19 22:43:01 +00009501 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009502 if (pf->fd >= 0)
9503 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009504 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009505 while (pf->strpush)
9506 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009507 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009508 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009509 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009510}
9511
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009512/*
9513 * Return to top level.
9514 */
9515static void
9516popallfiles(void)
9517{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009518 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009519 popfile();
9520}
9521
9522/*
9523 * Close the file(s) that the shell is reading commands from. Called
9524 * after a fork is done.
9525 */
9526static void
9527closescript(void)
9528{
9529 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009530 if (g_parsefile->fd > 0) {
9531 close(g_parsefile->fd);
9532 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009533 }
9534}
9535
9536/*
9537 * Like setinputfile, but takes an open file descriptor. Call this with
9538 * interrupts off.
9539 */
9540static void
9541setinputfd(int fd, int push)
9542{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009543 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009544 if (push) {
9545 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009546 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009547 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009548 g_parsefile->fd = fd;
9549 if (g_parsefile->buf == NULL)
9550 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009551 g_parsefile->left_in_buffer = 0;
9552 g_parsefile->left_in_line = 0;
9553 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009554}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009555
Eric Andersenc470f442003-07-28 09:56:35 +00009556/*
9557 * Set the input to take input from a file. If push is set, push the
9558 * old input onto the stack first.
9559 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009560static int
9561setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009562{
9563 int fd;
9564 int fd2;
9565
Denis Vlasenkob012b102007-02-19 22:43:01 +00009566 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009567 fd = open(fname, O_RDONLY);
9568 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009569 if (flags & INPUT_NOFILE_OK)
9570 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009571 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009572 }
Eric Andersenc470f442003-07-28 09:56:35 +00009573 if (fd < 10) {
9574 fd2 = copyfd(fd, 10);
9575 close(fd);
9576 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009577 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009578 fd = fd2;
9579 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009580 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009581 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009582 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009583 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009584}
9585
Eric Andersencb57d552001-06-28 07:25:16 +00009586/*
9587 * Like setinputfile, but takes input from a string.
9588 */
Eric Andersenc470f442003-07-28 09:56:35 +00009589static void
9590setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009591{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009592 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009593 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009594 g_parsefile->next_to_pgetc = string;
9595 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009596 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009597 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009598 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009599}
9600
9601
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009602/* ============ mail.c
9603 *
9604 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009605 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009606
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009607#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009608
Eric Andersencb57d552001-06-28 07:25:16 +00009609#define MAXMBOXES 10
9610
Eric Andersenc470f442003-07-28 09:56:35 +00009611/* times of mailboxes */
9612static time_t mailtime[MAXMBOXES];
9613/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009614static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009615
Eric Andersencb57d552001-06-28 07:25:16 +00009616/*
Eric Andersenc470f442003-07-28 09:56:35 +00009617 * Print appropriate message(s) if mail has arrived.
9618 * If mail_var_path_changed is set,
9619 * then the value of MAIL has mail_var_path_changed,
9620 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009621 */
Eric Andersenc470f442003-07-28 09:56:35 +00009622static void
9623chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009624{
Eric Andersencb57d552001-06-28 07:25:16 +00009625 const char *mpath;
9626 char *p;
9627 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009628 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009629 struct stackmark smark;
9630 struct stat statb;
9631
Eric Andersencb57d552001-06-28 07:25:16 +00009632 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009633 mpath = mpathset() ? mpathval() : mailval();
9634 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009635 p = padvance(&mpath, nullstr);
9636 if (p == NULL)
9637 break;
9638 if (*p == '\0')
9639 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009640 for (q = p; *q; q++)
9641 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009642#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009643 if (q[-1] != '/')
9644 abort();
9645#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009646 q[-1] = '\0'; /* delete trailing '/' */
9647 if (stat(p, &statb) < 0) {
9648 *mtp = 0;
9649 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009650 }
Eric Andersenc470f442003-07-28 09:56:35 +00009651 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9652 fprintf(
9653 stderr, snlfmt,
9654 pathopt ? pathopt : "you have mail"
9655 );
9656 }
9657 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009658 }
Eric Andersenc470f442003-07-28 09:56:35 +00009659 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009660 popstackmark(&smark);
9661}
Eric Andersencb57d552001-06-28 07:25:16 +00009662
Eric Andersenc470f442003-07-28 09:56:35 +00009663static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009664changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009665{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009666 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009667}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009668
Denis Vlasenko131ae172007-02-18 13:00:19 +00009669#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009670
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009671
9672/* ============ ??? */
9673
Eric Andersencb57d552001-06-28 07:25:16 +00009674/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009675 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009676 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009677static void
9678setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009679{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009680 char **newparam;
9681 char **ap;
9682 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009683
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009684 for (nparam = 0; argv[nparam]; nparam++)
9685 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009686 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9687 while (*argv) {
9688 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009689 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009690 *ap = NULL;
9691 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009692 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009693 shellparam.nparam = nparam;
9694 shellparam.p = newparam;
9695#if ENABLE_ASH_GETOPTS
9696 shellparam.optind = 1;
9697 shellparam.optoff = -1;
9698#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009699}
9700
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009701/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009702 * Process shell options. The global variable argptr contains a pointer
9703 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009704 *
9705 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9706 * For a non-interactive shell, an error condition encountered
9707 * by a special built-in ... shall cause the shell to write a diagnostic message
9708 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009709 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009710 * ...
9711 * Utility syntax error (option or operand error) Shall exit
9712 * ...
9713 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9714 * we see that bash does not do that (set "finishes" with error code 1 instead,
9715 * and shell continues), and people rely on this behavior!
9716 * Testcase:
9717 * set -o barfoo 2>/dev/null
9718 * echo $?
9719 *
9720 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009721 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009722static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009723plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009724{
9725 int i;
9726
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009727 if (name) {
9728 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009729 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009730 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009731 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009732 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009733 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009734 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009735 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009736 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009737 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009738 if (val) {
9739 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9740 } else {
9741 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9742 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009743 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009744 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009745}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009746static void
9747setoption(int flag, int val)
9748{
9749 int i;
9750
9751 for (i = 0; i < NOPTS; i++) {
9752 if (optletters(i) == flag) {
9753 optlist[i] = val;
9754 return;
9755 }
9756 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009757 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009758 /* NOTREACHED */
9759}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009760static int
Eric Andersenc470f442003-07-28 09:56:35 +00009761options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009762{
9763 char *p;
9764 int val;
9765 int c;
9766
9767 if (cmdline)
9768 minusc = NULL;
9769 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009770 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009771 if (c != '-' && c != '+')
9772 break;
9773 argptr++;
9774 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009775 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009776 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009777 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009778 if (!cmdline) {
9779 /* "-" means turn off -x and -v */
9780 if (p[0] == '\0')
9781 xflag = vflag = 0;
9782 /* "--" means reset params */
9783 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009784 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009785 }
Eric Andersenc470f442003-07-28 09:56:35 +00009786 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009787 }
Eric Andersencb57d552001-06-28 07:25:16 +00009788 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009789 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009790 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009791 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009792 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009793 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009794 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009795 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009796 /* it already printed err message */
9797 return 1; /* error */
9798 }
Eric Andersencb57d552001-06-28 07:25:16 +00009799 if (*argptr)
9800 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009801 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9802 isloginsh = 1;
9803 /* bash does not accept +-login, we also won't */
9804 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009805 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009806 isloginsh = 1;
9807 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009808 } else {
9809 setoption(c, val);
9810 }
9811 }
9812 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009813 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009814}
9815
Eric Andersencb57d552001-06-28 07:25:16 +00009816/*
Eric Andersencb57d552001-06-28 07:25:16 +00009817 * The shift builtin command.
9818 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009819static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009820shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009821{
9822 int n;
9823 char **ap1, **ap2;
9824
9825 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009826 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009827 n = number(argv[1]);
9828 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009829 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009830 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009831 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009832 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009833 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009834 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009835 }
9836 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009837 while ((*ap2++ = *ap1++) != NULL)
9838 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009839#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009840 shellparam.optind = 1;
9841 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009842#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009843 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009844 return 0;
9845}
9846
Eric Andersencb57d552001-06-28 07:25:16 +00009847/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009848 * POSIX requires that 'set' (but not export or readonly) output the
9849 * variables in lexicographic order - by the locale's collating order (sigh).
9850 * Maybe we could keep them in an ordered balanced binary tree
9851 * instead of hashed lists.
9852 * For now just roll 'em through qsort for printing...
9853 */
9854static int
9855showvars(const char *sep_prefix, int on, int off)
9856{
9857 const char *sep;
9858 char **ep, **epend;
9859
9860 ep = listvars(on, off, &epend);
9861 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9862
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009863 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009864
9865 for (; ep < epend; ep++) {
9866 const char *p;
9867 const char *q;
9868
9869 p = strchrnul(*ep, '=');
9870 q = nullstr;
9871 if (*p)
9872 q = single_quote(++p);
9873 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9874 }
9875 return 0;
9876}
9877
9878/*
Eric Andersencb57d552001-06-28 07:25:16 +00009879 * The set command builtin.
9880 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009881static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009882setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009883{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009884 int retval;
9885
Denis Vlasenko68404f12008-03-17 09:00:54 +00009886 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009887 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009888 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009889 retval = 1;
9890 if (!options(0)) { /* if no parse error... */
9891 retval = 0;
9892 optschanged();
9893 if (*argptr != NULL) {
9894 setparam(argptr);
9895 }
Eric Andersencb57d552001-06-28 07:25:16 +00009896 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009897 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009898 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009899}
9900
Denis Vlasenko131ae172007-02-18 13:00:19 +00009901#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009902static void
9903change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009904{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009905 /* Galois LFSR parameter */
9906 /* Taps at 32 31 29 1: */
9907 enum { MASK = 0x8000000b };
9908 /* Another example - taps at 32 31 30 10: */
9909 /* MASK = 0x00400007 */
9910
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009911 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009912 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009913 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009914
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009915 /* LCG has period of 2^32 and alternating lowest bit */
9916 random_LCG = 1664525 * random_LCG + 1013904223;
9917 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9918 t = (random_galois_LFSR << 1);
9919 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9920 t ^= MASK;
9921 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009922 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009923 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009924 * for $RANDOM range. Combining with subtraction is
9925 * just for fun. + and ^ would work equally well. */
9926 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009927 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009928 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009929 vrandom.flags &= ~VNOFUNC;
9930 } else {
9931 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009932 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009933 }
Eric Andersenef02f822004-03-11 13:34:24 +00009934}
Eric Andersen16767e22004-03-16 05:14:10 +00009935#endif
9936
Denis Vlasenko131ae172007-02-18 13:00:19 +00009937#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009938static int
Eric Andersenc470f442003-07-28 09:56:35 +00009939getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009940{
9941 char *p, *q;
9942 char c = '?';
9943 int done = 0;
9944 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009945 char s[12];
9946 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009947
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009948 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009949 return 1;
9950 optnext = optfirst + *param_optind - 1;
9951
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009952 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009953 p = NULL;
9954 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009955 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (p == NULL || *p == '\0') {
9957 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009958 p = *optnext;
9959 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009960 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009961 p = NULL;
9962 done = 1;
9963 goto out;
9964 }
9965 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009966 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009967 goto atend;
9968 }
9969
9970 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009971 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009972 if (*q == '\0') {
9973 if (optstr[0] == ':') {
9974 s[0] = c;
9975 s[1] = '\0';
9976 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009977 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009978 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009979 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009980 }
9981 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009982 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 }
9984 if (*++q == ':')
9985 q++;
9986 }
9987
9988 if (*++q == ':') {
9989 if (*p == '\0' && (p = *optnext) == NULL) {
9990 if (optstr[0] == ':') {
9991 s[0] = c;
9992 s[1] = '\0';
9993 err |= setvarsafe("OPTARG", s, 0);
9994 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009995 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009996 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009997 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009998 c = '?';
9999 }
Eric Andersenc470f442003-07-28 09:56:35 +000010000 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010001 }
10002
10003 if (p == *optnext)
10004 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010005 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010006 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010007 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010008 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010009 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010010 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010011 *param_optind = optnext - optfirst + 1;
10012 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010013 err |= setvarsafe("OPTIND", s, VNOFUNC);
10014 s[0] = c;
10015 s[1] = '\0';
10016 err |= setvarsafe(optvar, s, 0);
10017 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010018 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010019 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010020 flush_stdout_stderr();
10021 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010022 }
10023 return done;
10024}
Eric Andersenc470f442003-07-28 09:56:35 +000010025
10026/*
10027 * The getopts builtin. Shellparam.optnext points to the next argument
10028 * to be processed. Shellparam.optptr points to the next character to
10029 * be processed in the current argument. If shellparam.optnext is NULL,
10030 * then it's the first time getopts has been called.
10031 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010032static int
Eric Andersenc470f442003-07-28 09:56:35 +000010033getoptscmd(int argc, char **argv)
10034{
10035 char **optbase;
10036
10037 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010038 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010039 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010040 optbase = shellparam.p;
10041 if (shellparam.optind > shellparam.nparam + 1) {
10042 shellparam.optind = 1;
10043 shellparam.optoff = -1;
10044 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010045 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010046 optbase = &argv[3];
10047 if (shellparam.optind > argc - 2) {
10048 shellparam.optind = 1;
10049 shellparam.optoff = -1;
10050 }
10051 }
10052
10053 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010054 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010055}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010056#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010057
Eric Andersencb57d552001-06-28 07:25:16 +000010058
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010059/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010060
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010061struct heredoc {
10062 struct heredoc *next; /* next here document in list */
10063 union node *here; /* redirection node */
10064 char *eofmark; /* string indicating end of input */
10065 smallint striptabs; /* if set, strip leading tabs */
10066};
10067
10068static smallint tokpushback; /* last token pushed back */
10069static smallint parsebackquote; /* nonzero if we are inside backquotes */
10070static smallint quoteflag; /* set if (part of) last token was quoted */
10071static token_id_t lasttoken; /* last token read (integer id Txxx) */
10072static struct heredoc *heredoclist; /* list of here documents to read */
10073static char *wordtext; /* text of last word returned by readtoken */
10074static struct nodelist *backquotelist;
10075static union node *redirnode;
10076static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010077/*
10078 * NEOF is returned by parsecmd when it encounters an end of file. It
10079 * must be distinct from NULL, so we use the address of a variable that
10080 * happens to be handy.
10081 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010082#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010083
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010084static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010085static void
10086raise_error_syntax(const char *msg)
10087{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010088 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010089 /* NOTREACHED */
10090}
10091
10092/*
10093 * Called when an unexpected token is read during the parse. The argument
10094 * is the token that is expected, or -1 if more than one type of token can
10095 * occur at this point.
10096 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010097static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010098static void
10099raise_error_unexpected_syntax(int token)
10100{
10101 char msg[64];
10102 int l;
10103
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010104 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010105 if (token >= 0)
10106 sprintf(msg + l, " (expecting %s)", tokname(token));
10107 raise_error_syntax(msg);
10108 /* NOTREACHED */
10109}
Eric Andersencb57d552001-06-28 07:25:16 +000010110
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010111#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010112
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010113/* parsing is heavily cross-recursive, need these forward decls */
10114static union node *andor(void);
10115static union node *pipeline(void);
10116static union node *parse_command(void);
10117static void parseheredoc(void);
10118static char peektoken(void);
10119static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010120
Eric Andersenc470f442003-07-28 09:56:35 +000010121static union node *
10122list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010123{
10124 union node *n1, *n2, *n3;
10125 int tok;
10126
Eric Andersenc470f442003-07-28 09:56:35 +000010127 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10128 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010129 return NULL;
10130 n1 = NULL;
10131 for (;;) {
10132 n2 = andor();
10133 tok = readtoken();
10134 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010135 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010136 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010137 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010138 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010139 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010140 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010141 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010142 n2 = n3;
10143 }
10144 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010145 }
10146 }
10147 if (n1 == NULL) {
10148 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010149 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010150 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010151 n3->type = NSEMI;
10152 n3->nbinary.ch1 = n1;
10153 n3->nbinary.ch2 = n2;
10154 n1 = n3;
10155 }
10156 switch (tok) {
10157 case TBACKGND:
10158 case TSEMI:
10159 tok = readtoken();
10160 /* fall through */
10161 case TNL:
10162 if (tok == TNL) {
10163 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010164 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010165 return n1;
10166 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010167 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010168 }
Eric Andersenc470f442003-07-28 09:56:35 +000010169 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010170 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010171 return n1;
10172 break;
10173 case TEOF:
10174 if (heredoclist)
10175 parseheredoc();
10176 else
Eric Andersenc470f442003-07-28 09:56:35 +000010177 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010178 return n1;
10179 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010180 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010181 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010182 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010183 return n1;
10184 }
10185 }
10186}
10187
Eric Andersenc470f442003-07-28 09:56:35 +000010188static union node *
10189andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010190{
Eric Andersencb57d552001-06-28 07:25:16 +000010191 union node *n1, *n2, *n3;
10192 int t;
10193
Eric Andersencb57d552001-06-28 07:25:16 +000010194 n1 = pipeline();
10195 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010196 t = readtoken();
10197 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010198 t = NAND;
10199 } else if (t == TOR) {
10200 t = NOR;
10201 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010202 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010203 return n1;
10204 }
Eric Andersenc470f442003-07-28 09:56:35 +000010205 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010206 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010207 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010208 n3->type = t;
10209 n3->nbinary.ch1 = n1;
10210 n3->nbinary.ch2 = n2;
10211 n1 = n3;
10212 }
10213}
10214
Eric Andersenc470f442003-07-28 09:56:35 +000010215static union node *
10216pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010217{
Eric Andersencb57d552001-06-28 07:25:16 +000010218 union node *n1, *n2, *pipenode;
10219 struct nodelist *lp, *prev;
10220 int negate;
10221
10222 negate = 0;
10223 TRACE(("pipeline: entered\n"));
10224 if (readtoken() == TNOT) {
10225 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010226 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010227 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010228 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010229 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010230 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010231 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010232 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010233 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010234 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010235 pipenode->npipe.cmdlist = lp;
10236 lp->n = n1;
10237 do {
10238 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010239 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010240 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010241 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010242 prev->next = lp;
10243 } while (readtoken() == TPIPE);
10244 lp->next = NULL;
10245 n1 = pipenode;
10246 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010247 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010248 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010249 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010250 n2->type = NNOT;
10251 n2->nnot.com = n1;
10252 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010253 }
10254 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010255}
10256
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010257static union node *
10258makename(void)
10259{
10260 union node *n;
10261
Denis Vlasenko597906c2008-02-20 16:38:54 +000010262 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010263 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010264 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010265 n->narg.text = wordtext;
10266 n->narg.backquote = backquotelist;
10267 return n;
10268}
10269
10270static void
10271fixredir(union node *n, const char *text, int err)
10272{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010273 int fd;
10274
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010275 TRACE(("Fix redir %s %d\n", text, err));
10276 if (!err)
10277 n->ndup.vname = NULL;
10278
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010279 fd = bb_strtou(text, NULL, 10);
10280 if (!errno && fd >= 0)
10281 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010282 else if (LONE_DASH(text))
10283 n->ndup.dupfd = -1;
10284 else {
10285 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010286 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010287 n->ndup.vname = makename();
10288 }
10289}
10290
10291/*
10292 * Returns true if the text contains nothing to expand (no dollar signs
10293 * or backquotes).
10294 */
10295static int
10296noexpand(char *text)
10297{
10298 char *p;
10299 char c;
10300
10301 p = text;
10302 while ((c = *p++) != '\0') {
10303 if (c == CTLQUOTEMARK)
10304 continue;
10305 if (c == CTLESC)
10306 p++;
10307 else if (SIT(c, BASESYNTAX) == CCTL)
10308 return 0;
10309 }
10310 return 1;
10311}
10312
10313static void
10314parsefname(void)
10315{
10316 union node *n = redirnode;
10317
10318 if (readtoken() != TWORD)
10319 raise_error_unexpected_syntax(-1);
10320 if (n->type == NHERE) {
10321 struct heredoc *here = heredoc;
10322 struct heredoc *p;
10323 int i;
10324
10325 if (quoteflag == 0)
10326 n->type = NXHERE;
10327 TRACE(("Here document %d\n", n->type));
10328 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010329 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010330 rmescapes(wordtext);
10331 here->eofmark = wordtext;
10332 here->next = NULL;
10333 if (heredoclist == NULL)
10334 heredoclist = here;
10335 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010336 for (p = heredoclist; p->next; p = p->next)
10337 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010338 p->next = here;
10339 }
10340 } else if (n->type == NTOFD || n->type == NFROMFD) {
10341 fixredir(n, wordtext, 0);
10342 } else {
10343 n->nfile.fname = makename();
10344 }
10345}
Eric Andersencb57d552001-06-28 07:25:16 +000010346
Eric Andersenc470f442003-07-28 09:56:35 +000010347static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010348simplecmd(void)
10349{
10350 union node *args, **app;
10351 union node *n = NULL;
10352 union node *vars, **vpp;
10353 union node **rpp, *redir;
10354 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010355#if ENABLE_ASH_BASH_COMPAT
10356 smallint double_brackets_flag = 0;
10357#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010358
10359 args = NULL;
10360 app = &args;
10361 vars = NULL;
10362 vpp = &vars;
10363 redir = NULL;
10364 rpp = &redir;
10365
10366 savecheckkwd = CHKALIAS;
10367 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010368 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010369 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010370 t = readtoken();
10371 switch (t) {
10372#if ENABLE_ASH_BASH_COMPAT
10373 case TAND: /* "&&" */
10374 case TOR: /* "||" */
10375 if (!double_brackets_flag) {
10376 tokpushback = 1;
10377 goto out;
10378 }
10379 wordtext = (char *) (t == TAND ? "-a" : "-o");
10380#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010381 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010382 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010383 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010384 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010385 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010386#if ENABLE_ASH_BASH_COMPAT
10387 if (strcmp("[[", wordtext) == 0)
10388 double_brackets_flag = 1;
10389 else if (strcmp("]]", wordtext) == 0)
10390 double_brackets_flag = 0;
10391#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010392 n->narg.backquote = backquotelist;
10393 if (savecheckkwd && isassignment(wordtext)) {
10394 *vpp = n;
10395 vpp = &n->narg.next;
10396 } else {
10397 *app = n;
10398 app = &n->narg.next;
10399 savecheckkwd = 0;
10400 }
10401 break;
10402 case TREDIR:
10403 *rpp = n = redirnode;
10404 rpp = &n->nfile.next;
10405 parsefname(); /* read name of redirection file */
10406 break;
10407 case TLP:
10408 if (args && app == &args->narg.next
10409 && !vars && !redir
10410 ) {
10411 struct builtincmd *bcmd;
10412 const char *name;
10413
10414 /* We have a function */
10415 if (readtoken() != TRP)
10416 raise_error_unexpected_syntax(TRP);
10417 name = n->narg.text;
10418 if (!goodname(name)
10419 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10420 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010421 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010422 }
10423 n->type = NDEFUN;
10424 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10425 n->narg.next = parse_command();
10426 return n;
10427 }
10428 /* fall through */
10429 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010430 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010431 goto out;
10432 }
10433 }
10434 out:
10435 *app = NULL;
10436 *vpp = NULL;
10437 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010438 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010439 n->type = NCMD;
10440 n->ncmd.args = args;
10441 n->ncmd.assign = vars;
10442 n->ncmd.redirect = redir;
10443 return n;
10444}
10445
10446static union node *
10447parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010448{
Eric Andersencb57d552001-06-28 07:25:16 +000010449 union node *n1, *n2;
10450 union node *ap, **app;
10451 union node *cp, **cpp;
10452 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010453 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010454 int t;
10455
10456 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010457 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010458
Eric Andersencb57d552001-06-28 07:25:16 +000010459 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010460 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010461 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010462 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010463 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010464 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010465 n1->type = NIF;
10466 n1->nif.test = list(0);
10467 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010468 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010469 n1->nif.ifpart = list(0);
10470 n2 = n1;
10471 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010472 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010473 n2 = n2->nif.elsepart;
10474 n2->type = NIF;
10475 n2->nif.test = list(0);
10476 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010477 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010478 n2->nif.ifpart = list(0);
10479 }
10480 if (lasttoken == TELSE)
10481 n2->nif.elsepart = list(0);
10482 else {
10483 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010484 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010485 }
Eric Andersenc470f442003-07-28 09:56:35 +000010486 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010487 break;
10488 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010489 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010490 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010491 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010492 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010493 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010494 got = readtoken();
10495 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010496 TRACE(("expecting DO got %s %s\n", tokname(got),
10497 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010498 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010499 }
10500 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010501 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 break;
10503 }
10504 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010505 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010506 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010507 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010508 n1->type = NFOR;
10509 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010510 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010511 if (readtoken() == TIN) {
10512 app = &ap;
10513 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010514 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010515 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010516 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010517 n2->narg.text = wordtext;
10518 n2->narg.backquote = backquotelist;
10519 *app = n2;
10520 app = &n2->narg.next;
10521 }
10522 *app = NULL;
10523 n1->nfor.args = ap;
10524 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010525 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010526 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010527 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010528 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010529 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010530 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010531 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010532 n1->nfor.args = n2;
10533 /*
10534 * Newline or semicolon here is optional (but note
10535 * that the original Bourne shell only allowed NL).
10536 */
10537 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010538 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010539 }
Eric Andersenc470f442003-07-28 09:56:35 +000010540 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010542 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010543 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010544 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010545 break;
10546 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010547 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010548 n1->type = NCASE;
10549 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010550 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010551 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010552 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010553 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010554 n2->narg.text = wordtext;
10555 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010556 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010557 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010558 } while (readtoken() == TNL);
10559 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010560 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010561 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010562 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010563 checkkwd = CHKNL | CHKKWD;
10564 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010565 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010566 if (lasttoken == TLP)
10567 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010568 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010569 cp->type = NCLIST;
10570 app = &cp->nclist.pattern;
10571 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010572 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010573 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010574 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010575 ap->narg.text = wordtext;
10576 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010577 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010578 break;
10579 app = &ap->narg.next;
10580 readtoken();
10581 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010582 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010583 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010584 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010585 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010586
Eric Andersenc470f442003-07-28 09:56:35 +000010587 cpp = &cp->nclist.next;
10588
10589 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010590 t = readtoken();
10591 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010592 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010593 raise_error_unexpected_syntax(TENDCASE);
10594 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010595 }
Eric Andersenc470f442003-07-28 09:56:35 +000010596 }
Eric Andersencb57d552001-06-28 07:25:16 +000010597 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010598 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010599 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010600 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010601 n1->type = NSUBSHELL;
10602 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010603 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010604 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010605 break;
10606 case TBEGIN:
10607 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010608 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010609 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010610 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010611 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010612 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010613 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010614 }
10615
Eric Andersenc470f442003-07-28 09:56:35 +000010616 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010617 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010618
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010619 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010620 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010621 checkkwd = CHKKWD | CHKALIAS;
10622 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010623 while (readtoken() == TREDIR) {
10624 *rpp = n2 = redirnode;
10625 rpp = &n2->nfile.next;
10626 parsefname();
10627 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010628 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010629 *rpp = NULL;
10630 if (redir) {
10631 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010632 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010633 n2->type = NREDIR;
10634 n2->nredir.n = n1;
10635 n1 = n2;
10636 }
10637 n1->nredir.redirect = redir;
10638 }
Eric Andersencb57d552001-06-28 07:25:16 +000010639 return n1;
10640}
10641
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010642#if ENABLE_ASH_BASH_COMPAT
10643static int decode_dollar_squote(void)
10644{
10645 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10646 int c, cnt;
10647 char *p;
10648 char buf[4];
10649
10650 c = pgetc();
10651 p = strchr(C_escapes, c);
10652 if (p) {
10653 buf[0] = c;
10654 p = buf;
10655 cnt = 3;
10656 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10657 do {
10658 c = pgetc();
10659 *++p = c;
10660 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10661 pungetc();
10662 } else if (c == 'x') { /* \xHH */
10663 do {
10664 c = pgetc();
10665 *++p = c;
10666 } while (isxdigit(c) && --cnt);
10667 pungetc();
10668 if (cnt == 3) { /* \x but next char is "bad" */
10669 c = 'x';
10670 goto unrecognized;
10671 }
10672 } else { /* simple seq like \\ or \t */
10673 p++;
10674 }
10675 *p = '\0';
10676 p = buf;
10677 c = bb_process_escape_sequence((void*)&p);
10678 } else { /* unrecognized "\z": print both chars unless ' or " */
10679 if (c != '\'' && c != '"') {
10680 unrecognized:
10681 c |= 0x100; /* "please encode \, then me" */
10682 }
10683 }
10684 return c;
10685}
10686#endif
10687
Eric Andersencb57d552001-06-28 07:25:16 +000010688/*
10689 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10690 * is not NULL, read a here document. In the latter case, eofmark is the
10691 * word which marks the end of the document and striptabs is true if
10692 * leading tabs should be stripped from the document. The argument firstc
10693 * is the first character of the input token or document.
10694 *
10695 * Because C does not have internal subroutines, I have simulated them
10696 * using goto's to implement the subroutine linkage. The following macros
10697 * will run code that appears at the end of readtoken1.
10698 */
Eric Andersen2870d962001-07-02 17:27:21 +000010699#define CHECKEND() {goto checkend; checkend_return:;}
10700#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10701#define PARSESUB() {goto parsesub; parsesub_return:;}
10702#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10703#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10704#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010705static int
Eric Andersenc470f442003-07-28 09:56:35 +000010706readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010707{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010708 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010709 int c = firstc;
10710 char *out;
10711 int len;
10712 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010713 struct nodelist *bqlist;
10714 smallint quotef;
10715 smallint dblquote;
10716 smallint oldstyle;
10717 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010718#if ENABLE_ASH_EXPAND_PRMT
10719 smallint pssyntax; /* we are expanding a prompt string */
10720#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010721 int varnest; /* levels of variables expansion */
10722 int arinest; /* levels of arithmetic expansion */
10723 int parenlevel; /* levels of parens in arithmetic */
10724 int dqvarnest; /* levels of variables expansion within double quotes */
10725
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010726 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10727
Eric Andersencb57d552001-06-28 07:25:16 +000010728#if __GNUC__
10729 /* Avoid longjmp clobbering */
10730 (void) &out;
10731 (void) &quotef;
10732 (void) &dblquote;
10733 (void) &varnest;
10734 (void) &arinest;
10735 (void) &parenlevel;
10736 (void) &dqvarnest;
10737 (void) &oldstyle;
10738 (void) &prevsyntax;
10739 (void) &syntax;
10740#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010741 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010742 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010743 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010744 oldstyle = 0;
10745 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010746#if ENABLE_ASH_EXPAND_PRMT
10747 pssyntax = (syntax == PSSYNTAX);
10748 if (pssyntax)
10749 syntax = DQSYNTAX;
10750#endif
10751 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010752 varnest = 0;
10753 arinest = 0;
10754 parenlevel = 0;
10755 dqvarnest = 0;
10756
10757 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010758 loop:
10759 /* For each line, until end of word */
10760 {
Eric Andersenc470f442003-07-28 09:56:35 +000010761 CHECKEND(); /* set c to PEOF if at end of here document */
10762 for (;;) { /* until end of line or end of word */
10763 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010764 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010765 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010766 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010767 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010768 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010769 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010770 if (doprompt)
10771 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010772 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010773 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010774 case CWORD:
10775 USTPUTC(c, out);
10776 break;
10777 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010778 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010779 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010780#if ENABLE_ASH_BASH_COMPAT
10781 if (c == '\\' && bash_dollar_squote) {
10782 c = decode_dollar_squote();
10783 if (c & 0x100) {
10784 USTPUTC('\\', out);
10785 c = (unsigned char)c;
10786 }
10787 }
10788#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010789 USTPUTC(c, out);
10790 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010791 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010792 c = pgetc2();
10793 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010794 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010795 USTPUTC('\\', out);
10796 pungetc();
10797 } else if (c == '\n') {
10798 if (doprompt)
10799 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010800 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010801#if ENABLE_ASH_EXPAND_PRMT
10802 if (c == '$' && pssyntax) {
10803 USTPUTC(CTLESC, out);
10804 USTPUTC('\\', out);
10805 }
10806#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010807 if (dblquote && c != '\\'
10808 && c != '`' && c != '$'
10809 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010810 ) {
10811 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010812 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010813 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010814 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010815 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010816 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010817 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010818 }
10819 break;
10820 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010821 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010822 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010823 if (eofmark == NULL) {
10824 USTPUTC(CTLQUOTEMARK, out);
10825 }
Eric Andersencb57d552001-06-28 07:25:16 +000010826 break;
10827 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010828 syntax = DQSYNTAX;
10829 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010830 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010831 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010832 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010833 if (eofmark != NULL && arinest == 0
10834 && varnest == 0
10835 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010836 USTPUTC(c, out);
10837 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010838 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010839 syntax = BASESYNTAX;
10840 dblquote = 0;
10841 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010842 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010843 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010844 }
10845 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010846 case CVAR: /* '$' */
10847 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010848 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010849 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010850 if (varnest > 0) {
10851 varnest--;
10852 if (dqvarnest > 0) {
10853 dqvarnest--;
10854 }
10855 USTPUTC(CTLENDVAR, out);
10856 } else {
10857 USTPUTC(c, out);
10858 }
10859 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010860#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010861 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010862 parenlevel++;
10863 USTPUTC(c, out);
10864 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010865 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010866 if (parenlevel > 0) {
10867 USTPUTC(c, out);
10868 --parenlevel;
10869 } else {
10870 if (pgetc() == ')') {
10871 if (--arinest == 0) {
10872 USTPUTC(CTLENDARI, out);
10873 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010874 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010875 } else
10876 USTPUTC(')', out);
10877 } else {
10878 /*
10879 * unbalanced parens
10880 * (don't 2nd guess - no error)
10881 */
10882 pungetc();
10883 USTPUTC(')', out);
10884 }
10885 }
10886 break;
10887#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010888 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010889 PARSEBACKQOLD();
10890 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010891 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010892 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010893 case CIGN:
10894 break;
10895 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010896 if (varnest == 0) {
10897#if ENABLE_ASH_BASH_COMPAT
10898 if (c == '&') {
10899 if (pgetc() == '>')
10900 c = 0x100 + '>'; /* flag &> */
10901 pungetc();
10902 }
10903#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010904 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010905 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010906#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010907 if (c != PEOA)
10908#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010909 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010910
Eric Andersencb57d552001-06-28 07:25:16 +000010911 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010912 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010913 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010914 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010915 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010916#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010917 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010918 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010919#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010920 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010921 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010922 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010923 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010924 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010925 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010926 }
10927 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010928 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010929 out = stackblock();
10930 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010931 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10932 && quotef == 0
10933 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010934 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010935 PARSEREDIR(); /* passed as params: out, c */
10936 lasttoken = TREDIR;
10937 return lasttoken;
10938 }
10939 /* else: non-number X seen, interpret it
10940 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010941 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010942 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010943 }
10944 quoteflag = quotef;
10945 backquotelist = bqlist;
10946 grabstackblock(len);
10947 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010948 lasttoken = TWORD;
10949 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010950/* end of readtoken routine */
10951
Eric Andersencb57d552001-06-28 07:25:16 +000010952/*
10953 * Check to see whether we are at the end of the here document. When this
10954 * is called, c is set to the first character of the next input line. If
10955 * we are at the end of the here document, this routine sets the c to PEOF.
10956 */
Eric Andersenc470f442003-07-28 09:56:35 +000010957checkend: {
10958 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010959#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010960 if (c == PEOA) {
10961 c = pgetc2();
10962 }
10963#endif
10964 if (striptabs) {
10965 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010966 c = pgetc2();
10967 }
Eric Andersenc470f442003-07-28 09:56:35 +000010968 }
10969 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010970 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010971 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010972
Eric Andersenc470f442003-07-28 09:56:35 +000010973 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010974 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10975 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010976 if (*p == '\n' && *q == '\0') {
10977 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010978 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010979 needprompt = doprompt;
10980 } else {
10981 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010982 }
10983 }
10984 }
10985 }
Eric Andersenc470f442003-07-28 09:56:35 +000010986 goto checkend_return;
10987}
Eric Andersencb57d552001-06-28 07:25:16 +000010988
Eric Andersencb57d552001-06-28 07:25:16 +000010989/*
10990 * Parse a redirection operator. The variable "out" points to a string
10991 * specifying the fd to be redirected. The variable "c" contains the
10992 * first character of the redirection operator.
10993 */
Eric Andersenc470f442003-07-28 09:56:35 +000010994parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010995 /* out is already checked to be a valid number or "" */
10996 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010997 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010998
Denis Vlasenko597906c2008-02-20 16:38:54 +000010999 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011000 if (c == '>') {
11001 np->nfile.fd = 1;
11002 c = pgetc();
11003 if (c == '>')
11004 np->type = NAPPEND;
11005 else if (c == '|')
11006 np->type = NCLOBBER;
11007 else if (c == '&')
11008 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011009 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011010 else {
11011 np->type = NTO;
11012 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011013 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011014 }
11015#if ENABLE_ASH_BASH_COMPAT
11016 else if (c == 0x100 + '>') { /* this flags &> redirection */
11017 np->nfile.fd = 1;
11018 pgetc(); /* this is '>', no need to check */
11019 np->type = NTO2;
11020 }
11021#endif
11022 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011023 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011024 c = pgetc();
11025 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011026 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011027 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011028 np = stzalloc(sizeof(struct nhere));
11029 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011030 }
11031 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011032 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011033 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011034 c = pgetc();
11035 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011036 heredoc->striptabs = 1;
11037 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011038 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011039 pungetc();
11040 }
11041 break;
11042
11043 case '&':
11044 np->type = NFROMFD;
11045 break;
11046
11047 case '>':
11048 np->type = NFROMTO;
11049 break;
11050
11051 default:
11052 np->type = NFROM;
11053 pungetc();
11054 break;
11055 }
Eric Andersencb57d552001-06-28 07:25:16 +000011056 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011057 if (fd >= 0)
11058 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011059 redirnode = np;
11060 goto parseredir_return;
11061}
Eric Andersencb57d552001-06-28 07:25:16 +000011062
Eric Andersencb57d552001-06-28 07:25:16 +000011063/*
11064 * Parse a substitution. At this point, we have read the dollar sign
11065 * and nothing else.
11066 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011067
11068/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11069 * (assuming ascii char codes, as the original implementation did) */
11070#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011071 (((unsigned)(c) - 33 < 32) \
11072 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011073parsesub: {
11074 int subtype;
11075 int typeloc;
11076 int flags;
11077 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011078 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011079
Eric Andersenc470f442003-07-28 09:56:35 +000011080 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011081 if (c <= PEOA_OR_PEOF
11082 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011083 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011084#if ENABLE_ASH_BASH_COMPAT
11085 if (c == '\'')
11086 bash_dollar_squote = 1;
11087 else
11088#endif
11089 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011090 pungetc();
11091 } else if (c == '(') { /* $(command) or $((arith)) */
11092 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011093#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011094 PARSEARITH();
11095#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011096 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011097#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011098 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011099 pungetc();
11100 PARSEBACKQNEW();
11101 }
11102 } else {
11103 USTPUTC(CTLVAR, out);
11104 typeloc = out - (char *)stackblock();
11105 USTPUTC(VSNORMAL, out);
11106 subtype = VSNORMAL;
11107 if (c == '{') {
11108 c = pgetc();
11109 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011110 c = pgetc();
11111 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011112 c = '#';
11113 else
11114 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011115 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011116 subtype = 0;
11117 }
11118 if (c > PEOA_OR_PEOF && is_name(c)) {
11119 do {
11120 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011121 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011122 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011123 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011124 do {
11125 STPUTC(c, out);
11126 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011127 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011128 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011129 USTPUTC(c, out);
11130 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011131 } else {
11132 badsub:
11133 raise_error_syntax("bad substitution");
11134 }
Eric Andersencb57d552001-06-28 07:25:16 +000011135
Eric Andersenc470f442003-07-28 09:56:35 +000011136 STPUTC('=', out);
11137 flags = 0;
11138 if (subtype == 0) {
11139 switch (c) {
11140 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011141 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011142#if ENABLE_ASH_BASH_COMPAT
11143 if (c == ':' || c == '$' || isdigit(c)) {
11144 pungetc();
11145 subtype = VSSUBSTR;
11146 break;
11147 }
11148#endif
11149 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011150 /*FALLTHROUGH*/
11151 default:
11152 p = strchr(types, c);
11153 if (p == NULL)
11154 goto badsub;
11155 subtype = p - types + VSNORMAL;
11156 break;
11157 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011158 case '#': {
11159 int cc = c;
11160 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11161 c = pgetc();
11162 if (c == cc)
11163 subtype++;
11164 else
11165 pungetc();
11166 break;
11167 }
11168#if ENABLE_ASH_BASH_COMPAT
11169 case '/':
11170 subtype = VSREPLACE;
11171 c = pgetc();
11172 if (c == '/')
11173 subtype++; /* VSREPLACEALL */
11174 else
11175 pungetc();
11176 break;
11177#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011178 }
Eric Andersenc470f442003-07-28 09:56:35 +000011179 } else {
11180 pungetc();
11181 }
11182 if (dblquote || arinest)
11183 flags |= VSQUOTE;
11184 *((char *)stackblock() + typeloc) = subtype | flags;
11185 if (subtype != VSNORMAL) {
11186 varnest++;
11187 if (dblquote || arinest) {
11188 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011189 }
11190 }
11191 }
Eric Andersenc470f442003-07-28 09:56:35 +000011192 goto parsesub_return;
11193}
Eric Andersencb57d552001-06-28 07:25:16 +000011194
Eric Andersencb57d552001-06-28 07:25:16 +000011195/*
11196 * Called to parse command substitutions. Newstyle is set if the command
11197 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11198 * list of commands (passed by reference), and savelen is the number of
11199 * characters on the top of the stack which must be preserved.
11200 */
Eric Andersenc470f442003-07-28 09:56:35 +000011201parsebackq: {
11202 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011203 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011204 union node *n;
11205 char *volatile str;
11206 struct jmploc jmploc;
11207 struct jmploc *volatile savehandler;
11208 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011209 smallint saveprompt = 0;
11210
Eric Andersencb57d552001-06-28 07:25:16 +000011211#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011212 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011213#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011214 savepbq = parsebackquote;
11215 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011216 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011217 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011218 exception_handler = savehandler;
11219 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011220 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011221 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011222 str = NULL;
11223 savelen = out - (char *)stackblock();
11224 if (savelen > 0) {
11225 str = ckmalloc(savelen);
11226 memcpy(str, stackblock(), savelen);
11227 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011228 savehandler = exception_handler;
11229 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011230 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011231 if (oldstyle) {
11232 /* We must read until the closing backquote, giving special
11233 treatment to some slashes, and then push the string and
11234 reread it as input, interpreting it normally. */
11235 char *pout;
11236 int pc;
11237 size_t psavelen;
11238 char *pstr;
11239
11240
11241 STARTSTACKSTR(pout);
11242 for (;;) {
11243 if (needprompt) {
11244 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011245 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011246 pc = pgetc();
11247 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011248 case '`':
11249 goto done;
11250
11251 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011252 pc = pgetc();
11253 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011254 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011255 if (doprompt)
11256 setprompt(2);
11257 /*
11258 * If eating a newline, avoid putting
11259 * the newline into the new character
11260 * stream (via the STPUTC after the
11261 * switch).
11262 */
11263 continue;
11264 }
11265 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011266 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011267 STPUTC('\\', pout);
11268 if (pc > PEOA_OR_PEOF) {
11269 break;
11270 }
11271 /* fall through */
11272
11273 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011274#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011275 case PEOA:
11276#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011277 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011278 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011279
11280 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011281 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011282 needprompt = doprompt;
11283 break;
11284
11285 default:
11286 break;
11287 }
11288 STPUTC(pc, pout);
11289 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011290 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011291 STPUTC('\0', pout);
11292 psavelen = pout - (char *)stackblock();
11293 if (psavelen > 0) {
11294 pstr = grabstackstr(pout);
11295 setinputstring(pstr);
11296 }
11297 }
11298 nlpp = &bqlist;
11299 while (*nlpp)
11300 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011301 *nlpp = stzalloc(sizeof(**nlpp));
11302 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011303 parsebackquote = oldstyle;
11304
11305 if (oldstyle) {
11306 saveprompt = doprompt;
11307 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011308 }
11309
Eric Andersenc470f442003-07-28 09:56:35 +000011310 n = list(2);
11311
11312 if (oldstyle)
11313 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011314 else if (readtoken() != TRP)
11315 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011316
11317 (*nlpp)->n = n;
11318 if (oldstyle) {
11319 /*
11320 * Start reading from old file again, ignoring any pushed back
11321 * tokens left from the backquote parsing
11322 */
11323 popfile();
11324 tokpushback = 0;
11325 }
11326 while (stackblocksize() <= savelen)
11327 growstackblock();
11328 STARTSTACKSTR(out);
11329 if (str) {
11330 memcpy(out, str, savelen);
11331 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011332 INT_OFF;
11333 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011334 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011335 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011336 }
11337 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011338 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011339 if (arinest || dblquote)
11340 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11341 else
11342 USTPUTC(CTLBACKQ, out);
11343 if (oldstyle)
11344 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011345 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011346}
11347
Denis Vlasenko131ae172007-02-18 13:00:19 +000011348#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011349/*
11350 * Parse an arithmetic expansion (indicate start of one and set state)
11351 */
Eric Andersenc470f442003-07-28 09:56:35 +000011352parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011353 if (++arinest == 1) {
11354 prevsyntax = syntax;
11355 syntax = ARISYNTAX;
11356 USTPUTC(CTLARI, out);
11357 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011358 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011359 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011360 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011361 } else {
11362 /*
11363 * we collapse embedded arithmetic expansion to
11364 * parenthesis, which should be equivalent
11365 */
11366 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011367 }
Eric Andersenc470f442003-07-28 09:56:35 +000011368 goto parsearith_return;
11369}
11370#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011371
Eric Andersenc470f442003-07-28 09:56:35 +000011372} /* end of readtoken */
11373
Eric Andersencb57d552001-06-28 07:25:16 +000011374/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011375 * Read the next input token.
11376 * If the token is a word, we set backquotelist to the list of cmds in
11377 * backquotes. We set quoteflag to true if any part of the word was
11378 * quoted.
11379 * If the token is TREDIR, then we set redirnode to a structure containing
11380 * the redirection.
11381 * In all cases, the variable startlinno is set to the number of the line
11382 * on which the token starts.
11383 *
11384 * [Change comment: here documents and internal procedures]
11385 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11386 * word parsing code into a separate routine. In this case, readtoken
11387 * doesn't need to have any internal procedures, but parseword does.
11388 * We could also make parseoperator in essence the main routine, and
11389 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011390 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011391#define NEW_xxreadtoken
11392#ifdef NEW_xxreadtoken
11393/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011394static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011395 '\n', '(', ')', /* singles */
11396 '&', '|', ';', /* doubles */
11397 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011398};
Eric Andersencb57d552001-06-28 07:25:16 +000011399
Denis Vlasenko834dee72008-10-07 09:18:30 +000011400#define xxreadtoken_singles 3
11401#define xxreadtoken_doubles 3
11402
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011403static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011404 TNL, TLP, TRP, /* only single occurrence allowed */
11405 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11406 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011407 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011408};
11409
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011410static int
11411xxreadtoken(void)
11412{
11413 int c;
11414
11415 if (tokpushback) {
11416 tokpushback = 0;
11417 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011418 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011419 if (needprompt) {
11420 setprompt(2);
11421 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011422 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011423 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011424 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011425 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11426 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011427
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011428 if (c == '#') {
11429 while ((c = pgetc()) != '\n' && c != PEOF)
11430 continue;
11431 pungetc();
11432 } else if (c == '\\') {
11433 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011434 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011435 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011436 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011437 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011438 if (doprompt)
11439 setprompt(2);
11440 } else {
11441 const char *p;
11442
11443 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11444 if (c != PEOF) {
11445 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011446 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011447 needprompt = doprompt;
11448 }
11449
11450 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011451 if (p == NULL)
11452 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011453
Denis Vlasenko834dee72008-10-07 09:18:30 +000011454 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11455 int cc = pgetc();
11456 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011457 p += xxreadtoken_doubles + 1;
11458 } else {
11459 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011460#if ENABLE_ASH_BASH_COMPAT
11461 if (c == '&' && cc == '>') /* &> */
11462 break; /* return readtoken1(...) */
11463#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011464 }
11465 }
11466 }
11467 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11468 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011469 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011470 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011471
11472 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011473}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011474#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011475#define RETURN(token) return lasttoken = token
11476static int
11477xxreadtoken(void)
11478{
11479 int c;
11480
11481 if (tokpushback) {
11482 tokpushback = 0;
11483 return lasttoken;
11484 }
11485 if (needprompt) {
11486 setprompt(2);
11487 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011488 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011489 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011490 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011491 switch (c) {
11492 case ' ': case '\t':
11493#if ENABLE_ASH_ALIAS
11494 case PEOA:
11495#endif
11496 continue;
11497 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011498 while ((c = pgetc()) != '\n' && c != PEOF)
11499 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011500 pungetc();
11501 continue;
11502 case '\\':
11503 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011504 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011505 if (doprompt)
11506 setprompt(2);
11507 continue;
11508 }
11509 pungetc();
11510 goto breakloop;
11511 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011512 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011513 needprompt = doprompt;
11514 RETURN(TNL);
11515 case PEOF:
11516 RETURN(TEOF);
11517 case '&':
11518 if (pgetc() == '&')
11519 RETURN(TAND);
11520 pungetc();
11521 RETURN(TBACKGND);
11522 case '|':
11523 if (pgetc() == '|')
11524 RETURN(TOR);
11525 pungetc();
11526 RETURN(TPIPE);
11527 case ';':
11528 if (pgetc() == ';')
11529 RETURN(TENDCASE);
11530 pungetc();
11531 RETURN(TSEMI);
11532 case '(':
11533 RETURN(TLP);
11534 case ')':
11535 RETURN(TRP);
11536 default:
11537 goto breakloop;
11538 }
11539 }
11540 breakloop:
11541 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11542#undef RETURN
11543}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011544#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011545
11546static int
11547readtoken(void)
11548{
11549 int t;
11550#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011551 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011552#endif
11553
11554#if ENABLE_ASH_ALIAS
11555 top:
11556#endif
11557
11558 t = xxreadtoken();
11559
11560 /*
11561 * eat newlines
11562 */
11563 if (checkkwd & CHKNL) {
11564 while (t == TNL) {
11565 parseheredoc();
11566 t = xxreadtoken();
11567 }
11568 }
11569
11570 if (t != TWORD || quoteflag) {
11571 goto out;
11572 }
11573
11574 /*
11575 * check for keywords
11576 */
11577 if (checkkwd & CHKKWD) {
11578 const char *const *pp;
11579
11580 pp = findkwd(wordtext);
11581 if (pp) {
11582 lasttoken = t = pp - tokname_array;
11583 TRACE(("keyword %s recognized\n", tokname(t)));
11584 goto out;
11585 }
11586 }
11587
11588 if (checkkwd & CHKALIAS) {
11589#if ENABLE_ASH_ALIAS
11590 struct alias *ap;
11591 ap = lookupalias(wordtext, 1);
11592 if (ap != NULL) {
11593 if (*ap->val) {
11594 pushstring(ap->val, ap);
11595 }
11596 goto top;
11597 }
11598#endif
11599 }
11600 out:
11601 checkkwd = 0;
11602#if DEBUG
11603 if (!alreadyseen)
11604 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11605 else
11606 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11607#endif
11608 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011609}
11610
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011611static char
11612peektoken(void)
11613{
11614 int t;
11615
11616 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011617 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011618 return tokname_array[t][0];
11619}
Eric Andersencb57d552001-06-28 07:25:16 +000011620
11621/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011622 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11623 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011624 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011625static union node *
11626parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011627{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011628 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011629
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011630 tokpushback = 0;
11631 doprompt = interact;
11632 if (doprompt)
11633 setprompt(doprompt);
11634 needprompt = 0;
11635 t = readtoken();
11636 if (t == TEOF)
11637 return NEOF;
11638 if (t == TNL)
11639 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011640 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011641 return list(1);
11642}
11643
11644/*
11645 * Input any here documents.
11646 */
11647static void
11648parseheredoc(void)
11649{
11650 struct heredoc *here;
11651 union node *n;
11652
11653 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011654 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011655
11656 while (here) {
11657 if (needprompt) {
11658 setprompt(2);
11659 }
11660 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11661 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011662 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011663 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011664 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011665 n->narg.text = wordtext;
11666 n->narg.backquote = backquotelist;
11667 here->here->nhere.doc = n;
11668 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011669 }
Eric Andersencb57d552001-06-28 07:25:16 +000011670}
11671
11672
11673/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011674 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011675 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011676#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011677static const char *
11678expandstr(const char *ps)
11679{
11680 union node n;
11681
11682 /* XXX Fix (char *) cast. */
11683 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011684 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011685 popfile();
11686
11687 n.narg.type = NARG;
11688 n.narg.next = NULL;
11689 n.narg.text = wordtext;
11690 n.narg.backquote = backquotelist;
11691
11692 expandarg(&n, NULL, 0);
11693 return stackblock();
11694}
11695#endif
11696
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011697/*
11698 * Execute a command or commands contained in a string.
11699 */
11700static int
11701evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011702{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011703 union node *n;
11704 struct stackmark smark;
11705 int skip;
11706
11707 setinputstring(s);
11708 setstackmark(&smark);
11709
11710 skip = 0;
11711 while ((n = parsecmd(0)) != NEOF) {
11712 evaltree(n, 0);
11713 popstackmark(&smark);
11714 skip = evalskip;
11715 if (skip)
11716 break;
11717 }
11718 popfile();
11719
11720 skip &= mask;
11721 evalskip = skip;
11722 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011723}
11724
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011725/*
11726 * The eval command.
11727 */
11728static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011729evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011730{
11731 char *p;
11732 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011733
Denis Vlasenko68404f12008-03-17 09:00:54 +000011734 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011735 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011736 argv += 2;
11737 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011738 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011739 for (;;) {
11740 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011741 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011742 if (p == NULL)
11743 break;
11744 STPUTC(' ', concat);
11745 }
11746 STPUTC('\0', concat);
11747 p = grabstackstr(concat);
11748 }
11749 evalstring(p, ~SKIPEVAL);
11750
11751 }
11752 return exitstatus;
11753}
11754
11755/*
11756 * Read and execute commands. "Top" is nonzero for the top level command
11757 * loop; it turns on prompting if the shell is interactive.
11758 */
11759static int
11760cmdloop(int top)
11761{
11762 union node *n;
11763 struct stackmark smark;
11764 int inter;
11765 int numeof = 0;
11766
11767 TRACE(("cmdloop(%d) called\n", top));
11768 for (;;) {
11769 int skip;
11770
11771 setstackmark(&smark);
11772#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011773 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011774 showjobs(stderr, SHOW_CHANGED);
11775#endif
11776 inter = 0;
11777 if (iflag && top) {
11778 inter++;
11779#if ENABLE_ASH_MAIL
11780 chkmail();
11781#endif
11782 }
11783 n = parsecmd(inter);
11784 /* showtree(n); DEBUG */
11785 if (n == NEOF) {
11786 if (!top || numeof >= 50)
11787 break;
11788 if (!stoppedjobs()) {
11789 if (!Iflag)
11790 break;
11791 out2str("\nUse \"exit\" to leave shell.\n");
11792 }
11793 numeof++;
11794 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011795 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11796 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011797 numeof = 0;
11798 evaltree(n, 0);
11799 }
11800 popstackmark(&smark);
11801 skip = evalskip;
11802
11803 if (skip) {
11804 evalskip = 0;
11805 return skip & SKIPEVAL;
11806 }
11807 }
11808 return 0;
11809}
11810
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011811/*
11812 * Take commands from a file. To be compatible we should do a path
11813 * search for the file, which is necessary to find sub-commands.
11814 */
11815static char *
11816find_dot_file(char *name)
11817{
11818 char *fullname;
11819 const char *path = pathval();
11820 struct stat statb;
11821
11822 /* don't try this for absolute or relative paths */
11823 if (strchr(name, '/'))
11824 return name;
11825
11826 while ((fullname = padvance(&path, name)) != NULL) {
11827 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11828 /*
11829 * Don't bother freeing here, since it will
11830 * be freed by the caller.
11831 */
11832 return fullname;
11833 }
11834 stunalloc(fullname);
11835 }
11836
11837 /* not found in the PATH */
11838 ash_msg_and_raise_error("%s: not found", name);
11839 /* NOTREACHED */
11840}
11841
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011842static int
11843dotcmd(int argc, char **argv)
11844{
11845 struct strlist *sp;
11846 volatile struct shparam saveparam;
11847 int status = 0;
11848
11849 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011850 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011851
Denis Vlasenko68404f12008-03-17 09:00:54 +000011852 if (argv[1]) { /* That's what SVR2 does */
11853 char *fullname = find_dot_file(argv[1]);
11854 argv += 2;
11855 argc -= 2;
11856 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011857 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011858 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011859 shellparam.nparam = argc;
11860 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011861 };
11862
11863 setinputfile(fullname, INPUT_PUSH_FILE);
11864 commandname = fullname;
11865 cmdloop(0);
11866 popfile();
11867
Denis Vlasenko68404f12008-03-17 09:00:54 +000011868 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011869 freeparam(&shellparam);
11870 shellparam = saveparam;
11871 };
11872 status = exitstatus;
11873 }
11874 return status;
11875}
11876
11877static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011878exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011879{
11880 if (stoppedjobs())
11881 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011882 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011883 exitstatus = number(argv[1]);
11884 raise_exception(EXEXIT);
11885 /* NOTREACHED */
11886}
11887
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011888/*
11889 * Read a file containing shell functions.
11890 */
11891static void
11892readcmdfile(char *name)
11893{
11894 setinputfile(name, INPUT_PUSH_FILE);
11895 cmdloop(0);
11896 popfile();
11897}
11898
11899
Denis Vlasenkocc571512007-02-23 21:10:35 +000011900/* ============ find_command inplementation */
11901
11902/*
11903 * Resolve a command name. If you change this routine, you may have to
11904 * change the shellexec routine as well.
11905 */
11906static void
11907find_command(char *name, struct cmdentry *entry, int act, const char *path)
11908{
11909 struct tblentry *cmdp;
11910 int idx;
11911 int prev;
11912 char *fullname;
11913 struct stat statb;
11914 int e;
11915 int updatetbl;
11916 struct builtincmd *bcmd;
11917
11918 /* If name contains a slash, don't use PATH or hash table */
11919 if (strchr(name, '/') != NULL) {
11920 entry->u.index = -1;
11921 if (act & DO_ABS) {
11922 while (stat(name, &statb) < 0) {
11923#ifdef SYSV
11924 if (errno == EINTR)
11925 continue;
11926#endif
11927 entry->cmdtype = CMDUNKNOWN;
11928 return;
11929 }
11930 }
11931 entry->cmdtype = CMDNORMAL;
11932 return;
11933 }
11934
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011935/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011936
11937 updatetbl = (path == pathval());
11938 if (!updatetbl) {
11939 act |= DO_ALTPATH;
11940 if (strstr(path, "%builtin") != NULL)
11941 act |= DO_ALTBLTIN;
11942 }
11943
11944 /* If name is in the table, check answer will be ok */
11945 cmdp = cmdlookup(name, 0);
11946 if (cmdp != NULL) {
11947 int bit;
11948
11949 switch (cmdp->cmdtype) {
11950 default:
11951#if DEBUG
11952 abort();
11953#endif
11954 case CMDNORMAL:
11955 bit = DO_ALTPATH;
11956 break;
11957 case CMDFUNCTION:
11958 bit = DO_NOFUNC;
11959 break;
11960 case CMDBUILTIN:
11961 bit = DO_ALTBLTIN;
11962 break;
11963 }
11964 if (act & bit) {
11965 updatetbl = 0;
11966 cmdp = NULL;
11967 } else if (cmdp->rehash == 0)
11968 /* if not invalidated by cd, we're done */
11969 goto success;
11970 }
11971
11972 /* If %builtin not in path, check for builtin next */
11973 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011974 if (bcmd) {
11975 if (IS_BUILTIN_REGULAR(bcmd))
11976 goto builtin_success;
11977 if (act & DO_ALTPATH) {
11978 if (!(act & DO_ALTBLTIN))
11979 goto builtin_success;
11980 } else if (builtinloc <= 0) {
11981 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011982 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011983 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011984
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011985#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011986 {
11987 int applet_no = find_applet_by_name(name);
11988 if (applet_no >= 0) {
11989 entry->cmdtype = CMDNORMAL;
11990 entry->u.index = -2 - applet_no;
11991 return;
11992 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011993 }
11994#endif
11995
Denis Vlasenkocc571512007-02-23 21:10:35 +000011996 /* We have to search path. */
11997 prev = -1; /* where to start */
11998 if (cmdp && cmdp->rehash) { /* doing a rehash */
11999 if (cmdp->cmdtype == CMDBUILTIN)
12000 prev = builtinloc;
12001 else
12002 prev = cmdp->param.index;
12003 }
12004
12005 e = ENOENT;
12006 idx = -1;
12007 loop:
12008 while ((fullname = padvance(&path, name)) != NULL) {
12009 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012010 /* NB: code below will still use fullname
12011 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012012 idx++;
12013 if (pathopt) {
12014 if (prefix(pathopt, "builtin")) {
12015 if (bcmd)
12016 goto builtin_success;
12017 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012018 }
12019 if ((act & DO_NOFUNC)
12020 || !prefix(pathopt, "func")
12021 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012022 continue;
12023 }
12024 }
12025 /* if rehash, don't redo absolute path names */
12026 if (fullname[0] == '/' && idx <= prev) {
12027 if (idx < prev)
12028 continue;
12029 TRACE(("searchexec \"%s\": no change\n", name));
12030 goto success;
12031 }
12032 while (stat(fullname, &statb) < 0) {
12033#ifdef SYSV
12034 if (errno == EINTR)
12035 continue;
12036#endif
12037 if (errno != ENOENT && errno != ENOTDIR)
12038 e = errno;
12039 goto loop;
12040 }
12041 e = EACCES; /* if we fail, this will be the error */
12042 if (!S_ISREG(statb.st_mode))
12043 continue;
12044 if (pathopt) { /* this is a %func directory */
12045 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012046 /* NB: stalloc will return space pointed by fullname
12047 * (because we don't have any intervening allocations
12048 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012049 readcmdfile(fullname);
12050 cmdp = cmdlookup(name, 0);
12051 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12052 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12053 stunalloc(fullname);
12054 goto success;
12055 }
12056 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12057 if (!updatetbl) {
12058 entry->cmdtype = CMDNORMAL;
12059 entry->u.index = idx;
12060 return;
12061 }
12062 INT_OFF;
12063 cmdp = cmdlookup(name, 1);
12064 cmdp->cmdtype = CMDNORMAL;
12065 cmdp->param.index = idx;
12066 INT_ON;
12067 goto success;
12068 }
12069
12070 /* We failed. If there was an entry for this command, delete it */
12071 if (cmdp && updatetbl)
12072 delete_cmd_entry();
12073 if (act & DO_ERR)
12074 ash_msg("%s: %s", name, errmsg(e, "not found"));
12075 entry->cmdtype = CMDUNKNOWN;
12076 return;
12077
12078 builtin_success:
12079 if (!updatetbl) {
12080 entry->cmdtype = CMDBUILTIN;
12081 entry->u.cmd = bcmd;
12082 return;
12083 }
12084 INT_OFF;
12085 cmdp = cmdlookup(name, 1);
12086 cmdp->cmdtype = CMDBUILTIN;
12087 cmdp->param.cmd = bcmd;
12088 INT_ON;
12089 success:
12090 cmdp->rehash = 0;
12091 entry->cmdtype = cmdp->cmdtype;
12092 entry->u = cmdp->param;
12093}
12094
12095
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012096/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012097
Eric Andersencb57d552001-06-28 07:25:16 +000012098/*
Eric Andersencb57d552001-06-28 07:25:16 +000012099 * The trap builtin.
12100 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012101static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012102trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012103{
12104 char *action;
12105 char **ap;
12106 int signo;
12107
Eric Andersenc470f442003-07-28 09:56:35 +000012108 nextopt(nullstr);
12109 ap = argptr;
12110 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012111 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012112 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012113 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012114 single_quote(trap[signo]),
12115 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012116 }
12117 }
12118 return 0;
12119 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012120 action = NULL;
12121 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012122 action = *ap++;
12123 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012124 signo = get_signum(*ap);
12125 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012126 ash_msg_and_raise_error("%s: bad trap", *ap);
12127 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012128 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012129 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012130 action = NULL;
12131 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012132 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012133 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012134 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012135 trap[signo] = action;
12136 if (signo != 0)
12137 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012138 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012139 ap++;
12140 }
12141 return 0;
12142}
12143
Eric Andersenc470f442003-07-28 09:56:35 +000012144
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012145/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012146
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012147#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012148/*
12149 * Lists available builtins
12150 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012151static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012152helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012153{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012154 unsigned col;
12155 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012156
12157 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012158 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012159 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012160 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012161 if (col > 60) {
12162 out1fmt("\n");
12163 col = 0;
12164 }
12165 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012166#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012167 {
12168 const char *a = applet_names;
12169 while (*a) {
12170 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12171 if (col > 60) {
12172 out1fmt("\n");
12173 col = 0;
12174 }
12175 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012176 }
12177 }
12178#endif
12179 out1fmt("\n\n");
12180 return EXIT_SUCCESS;
12181}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012182#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012183
Eric Andersencb57d552001-06-28 07:25:16 +000012184/*
Eric Andersencb57d552001-06-28 07:25:16 +000012185 * The export and readonly commands.
12186 */
Eric Andersenc470f442003-07-28 09:56:35 +000012187static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012188exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012189{
12190 struct var *vp;
12191 char *name;
12192 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012193 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012194 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012195
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012196 if (nextopt("p") != 'p') {
12197 aptr = argptr;
12198 name = *aptr;
12199 if (name) {
12200 do {
12201 p = strchr(name, '=');
12202 if (p != NULL) {
12203 p++;
12204 } else {
12205 vp = *findvar(hashvar(name), name);
12206 if (vp) {
12207 vp->flags |= flag;
12208 continue;
12209 }
Eric Andersencb57d552001-06-28 07:25:16 +000012210 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012211 setvar(name, p, flag);
12212 } while ((name = *++aptr) != NULL);
12213 return 0;
12214 }
Eric Andersencb57d552001-06-28 07:25:16 +000012215 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012216 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012217 return 0;
12218}
12219
Eric Andersencb57d552001-06-28 07:25:16 +000012220/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012221 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012222 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012223static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012224unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012225{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012226 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012227
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012228 cmdp = cmdlookup(name, 0);
12229 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12230 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012231}
12232
Eric Andersencb57d552001-06-28 07:25:16 +000012233/*
Eric Andersencb57d552001-06-28 07:25:16 +000012234 * The unset builtin command. We unset the function before we unset the
12235 * variable to allow a function to be unset when there is a readonly variable
12236 * with the same name.
12237 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012238static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012239unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012240{
12241 char **ap;
12242 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012243 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012244 int ret = 0;
12245
12246 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012247 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012248 }
Eric Andersencb57d552001-06-28 07:25:16 +000012249
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012250 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012251 if (flag != 'f') {
12252 i = unsetvar(*ap);
12253 ret |= i;
12254 if (!(i & 2))
12255 continue;
12256 }
12257 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012258 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012259 }
Eric Andersenc470f442003-07-28 09:56:35 +000012260 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012261}
12262
12263
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012264/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012265
Eric Andersenc470f442003-07-28 09:56:35 +000012266#include <sys/times.h>
12267
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012268static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012269 ' ', offsetof(struct tms, tms_utime),
12270 '\n', offsetof(struct tms, tms_stime),
12271 ' ', offsetof(struct tms, tms_cutime),
12272 '\n', offsetof(struct tms, tms_cstime),
12273 0
12274};
Eric Andersencb57d552001-06-28 07:25:16 +000012275
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012276static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012277timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012278{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012279 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012280 const unsigned char *p;
12281 struct tms buf;
12282
12283 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012284 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012285
12286 p = timescmd_str;
12287 do {
12288 t = *(clock_t *)(((char *) &buf) + p[1]);
12289 s = t / clk_tck;
12290 out1fmt("%ldm%ld.%.3lds%c",
12291 s/60, s%60,
12292 ((t - s * clk_tck) * 1000) / clk_tck,
12293 p[0]);
12294 } while (*(p += 2));
12295
Eric Andersencb57d552001-06-28 07:25:16 +000012296 return 0;
12297}
12298
Denis Vlasenko131ae172007-02-18 13:00:19 +000012299#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012300static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012301dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012302{
Eric Andersened9ecf72004-06-22 08:29:45 +000012303 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012304 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012305
Denis Vlasenkob012b102007-02-19 22:43:01 +000012306 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012307 result = arith(s, &errcode);
12308 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012309 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012310 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012311 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012312 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012313 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012314 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012315 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012316 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012317 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012318
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012319 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012320}
Eric Andersenc470f442003-07-28 09:56:35 +000012321
Eric Andersenc470f442003-07-28 09:56:35 +000012322/*
Eric Andersen90898442003-08-06 11:20:52 +000012323 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12324 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12325 *
12326 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012327 */
12328static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012329letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012330{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012331 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012332
Denis Vlasenko68404f12008-03-17 09:00:54 +000012333 argv++;
12334 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012335 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012336 do {
12337 i = dash_arith(*argv);
12338 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012339
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012340 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012341}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012342#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012343
Eric Andersenc470f442003-07-28 09:56:35 +000012344
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012345/* ============ miscbltin.c
12346 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012347 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012348 */
12349
12350#undef rflag
12351
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012352#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012353typedef enum __rlimit_resource rlim_t;
12354#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012355
Eric Andersenc470f442003-07-28 09:56:35 +000012356/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012357 * The read builtin. Options:
12358 * -r Do not interpret '\' specially
12359 * -s Turn off echo (tty only)
12360 * -n NCHARS Read NCHARS max
12361 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12362 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12363 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012364 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012365 * TODO: bash also has:
12366 * -a ARRAY Read into array[0],[1],etc
12367 * -d DELIM End on DELIM char, not newline
12368 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012369 */
Eric Andersenc470f442003-07-28 09:56:35 +000012370static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012371readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012372{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012373 static const char *const arg_REPLY[] = { "REPLY", NULL };
12374
Eric Andersenc470f442003-07-28 09:56:35 +000012375 char **ap;
12376 int backslash;
12377 char c;
12378 int rflag;
12379 char *prompt;
12380 const char *ifs;
12381 char *p;
12382 int startword;
12383 int status;
12384 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012385 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012386#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012387 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012388 int silent = 0;
12389 struct termios tty, old_tty;
12390#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012391#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012392 unsigned end_ms = 0;
12393 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012394#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012395
12396 rflag = 0;
12397 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012398 while ((i = nextopt("p:u:r"
12399 USE_ASH_READ_TIMEOUT("t:")
12400 USE_ASH_READ_NCHARS("n:s")
12401 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012402 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012403 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012404 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012405 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012406#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012407 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012408 nchars = bb_strtou(optionarg, NULL, 10);
12409 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012410 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012411 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012412 break;
12413 case 's':
12414 silent = 1;
12415 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012416#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012417#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012418 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012419 timeout = bb_strtou(optionarg, NULL, 10);
12420 if (errno || timeout > UINT_MAX / 2048)
12421 ash_msg_and_raise_error("invalid timeout");
12422 timeout *= 1000;
12423#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012424 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012425 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012426 /* EINVAL means number is ok, but not terminated by NUL */
12427 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012428 char *p2;
12429 if (*++p) {
12430 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012431 ts.tv_usec = bb_strtou(p, &p2, 10);
12432 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012433 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012434 scale = p2 - p;
12435 /* normalize to usec */
12436 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012437 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012438 while (scale++ < 6)
12439 ts.tv_usec *= 10;
12440 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012441 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012442 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012443 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012444 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012445 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012446 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012447#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012448 break;
12449#endif
12450 case 'r':
12451 rflag = 1;
12452 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012453 case 'u':
12454 fd = bb_strtou(optionarg, NULL, 10);
12455 if (fd < 0 || errno)
12456 ash_msg_and_raise_error("invalid file descriptor");
12457 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012458 default:
12459 break;
12460 }
Eric Andersenc470f442003-07-28 09:56:35 +000012461 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012462 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012463 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012464 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012465 ap = argptr;
12466 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012467 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012468 ifs = bltinlookup("IFS");
12469 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012470 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012471#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012472 tcgetattr(fd, &tty);
12473 old_tty = tty;
12474 if (nchars || silent) {
12475 if (nchars) {
12476 tty.c_lflag &= ~ICANON;
12477 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012478 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012479 if (silent) {
12480 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12481 }
12482 /* if tcgetattr failed, tcsetattr will fail too.
12483 * Ignoring, it's harmless. */
12484 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012485 }
12486#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012487
Eric Andersenc470f442003-07-28 09:56:35 +000012488 status = 0;
12489 startword = 1;
12490 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012491#if ENABLE_ASH_READ_TIMEOUT
12492 if (timeout) /* NB: ensuring end_ms is nonzero */
12493 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12494#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012495 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012496 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012497#if ENABLE_ASH_READ_TIMEOUT
12498 if (end_ms) {
12499 struct pollfd pfd[1];
12500 pfd[0].fd = fd;
12501 pfd[0].events = POLLIN;
12502 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12503 if ((int)timeout <= 0 /* already late? */
12504 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12505 ) { /* timed out! */
12506#if ENABLE_ASH_READ_NCHARS
12507 tcsetattr(fd, TCSANOW, &old_tty);
12508#endif
12509 return 1;
12510 }
12511 }
12512#endif
12513 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012514 status = 1;
12515 break;
12516 }
12517 if (c == '\0')
12518 continue;
12519 if (backslash) {
12520 backslash = 0;
12521 if (c != '\n')
12522 goto put;
12523 continue;
12524 }
12525 if (!rflag && c == '\\') {
12526 backslash++;
12527 continue;
12528 }
12529 if (c == '\n')
12530 break;
12531 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12532 continue;
12533 }
12534 startword = 0;
12535 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12536 STACKSTRNUL(p);
12537 setvar(*ap, stackblock(), 0);
12538 ap++;
12539 startword = 1;
12540 STARTSTACKSTR(p);
12541 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012542 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012543 STPUTC(c, p);
12544 }
12545 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012546/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012547#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012548 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012549#else
12550 while (1);
12551#endif
12552
12553#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012554 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012555#endif
12556
Eric Andersenc470f442003-07-28 09:56:35 +000012557 STACKSTRNUL(p);
12558 /* Remove trailing blanks */
12559 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12560 *p = '\0';
12561 setvar(*ap, stackblock(), 0);
12562 while (*++ap != NULL)
12563 setvar(*ap, nullstr, 0);
12564 return status;
12565}
12566
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012567static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012568umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012569{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012570 static const char permuser[3] ALIGN1 = "ugo";
12571 static const char permmode[3] ALIGN1 = "rwx";
12572 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012573 S_IRUSR, S_IWUSR, S_IXUSR,
12574 S_IRGRP, S_IWGRP, S_IXGRP,
12575 S_IROTH, S_IWOTH, S_IXOTH
12576 };
12577
12578 char *ap;
12579 mode_t mask;
12580 int i;
12581 int symbolic_mode = 0;
12582
12583 while (nextopt("S") != '\0') {
12584 symbolic_mode = 1;
12585 }
12586
Denis Vlasenkob012b102007-02-19 22:43:01 +000012587 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012588 mask = umask(0);
12589 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012590 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012591
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012592 ap = *argptr;
12593 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012594 if (symbolic_mode) {
12595 char buf[18];
12596 char *p = buf;
12597
12598 for (i = 0; i < 3; i++) {
12599 int j;
12600
12601 *p++ = permuser[i];
12602 *p++ = '=';
12603 for (j = 0; j < 3; j++) {
12604 if ((mask & permmask[3 * i + j]) == 0) {
12605 *p++ = permmode[j];
12606 }
12607 }
12608 *p++ = ',';
12609 }
12610 *--p = 0;
12611 puts(buf);
12612 } else {
12613 out1fmt("%.4o\n", mask);
12614 }
12615 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012616 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012617 mask = 0;
12618 do {
12619 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012620 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012621 mask = (mask << 3) + (*ap - '0');
12622 } while (*++ap != '\0');
12623 umask(mask);
12624 } else {
12625 mask = ~mask & 0777;
12626 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012627 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012628 }
12629 umask(~mask & 0777);
12630 }
12631 }
12632 return 0;
12633}
12634
12635/*
12636 * ulimit builtin
12637 *
12638 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12639 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12640 * ash by J.T. Conklin.
12641 *
12642 * Public domain.
12643 */
12644
12645struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012646 uint8_t cmd; /* RLIMIT_xxx fit into it */
12647 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012648 char option;
12649};
12650
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012651static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012652#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012653 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012654#endif
12655#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012656 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012657#endif
12658#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012659 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012660#endif
12661#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012662 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012663#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012664#ifdef RLIMIT_CORE
12665 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012666#endif
12667#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012668 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012669#endif
12670#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012671 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012672#endif
12673#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012674 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012675#endif
12676#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012677 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012678#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012679#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012680 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012681#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012682#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012683 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012684#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012685};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012686static const char limits_name[] =
12687#ifdef RLIMIT_CPU
12688 "time(seconds)" "\0"
12689#endif
12690#ifdef RLIMIT_FSIZE
12691 "file(blocks)" "\0"
12692#endif
12693#ifdef RLIMIT_DATA
12694 "data(kb)" "\0"
12695#endif
12696#ifdef RLIMIT_STACK
12697 "stack(kb)" "\0"
12698#endif
12699#ifdef RLIMIT_CORE
12700 "coredump(blocks)" "\0"
12701#endif
12702#ifdef RLIMIT_RSS
12703 "memory(kb)" "\0"
12704#endif
12705#ifdef RLIMIT_MEMLOCK
12706 "locked memory(kb)" "\0"
12707#endif
12708#ifdef RLIMIT_NPROC
12709 "process" "\0"
12710#endif
12711#ifdef RLIMIT_NOFILE
12712 "nofiles" "\0"
12713#endif
12714#ifdef RLIMIT_AS
12715 "vmemory(kb)" "\0"
12716#endif
12717#ifdef RLIMIT_LOCKS
12718 "locks" "\0"
12719#endif
12720;
Eric Andersenc470f442003-07-28 09:56:35 +000012721
Glenn L McGrath76620622004-01-13 10:19:37 +000012722enum limtype { SOFT = 0x1, HARD = 0x2 };
12723
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012724static void
12725printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012726 const struct limits *l)
12727{
12728 rlim_t val;
12729
12730 val = limit->rlim_max;
12731 if (how & SOFT)
12732 val = limit->rlim_cur;
12733
12734 if (val == RLIM_INFINITY)
12735 out1fmt("unlimited\n");
12736 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012737 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012738 out1fmt("%lld\n", (long long) val);
12739 }
12740}
12741
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012742static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012743ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012744{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012745 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012746 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012747 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012748 const struct limits *l;
12749 int set, all = 0;
12750 int optc, what;
12751 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012752
12753 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012754 while ((optc = nextopt("HSa"
12755#ifdef RLIMIT_CPU
12756 "t"
12757#endif
12758#ifdef RLIMIT_FSIZE
12759 "f"
12760#endif
12761#ifdef RLIMIT_DATA
12762 "d"
12763#endif
12764#ifdef RLIMIT_STACK
12765 "s"
12766#endif
12767#ifdef RLIMIT_CORE
12768 "c"
12769#endif
12770#ifdef RLIMIT_RSS
12771 "m"
12772#endif
12773#ifdef RLIMIT_MEMLOCK
12774 "l"
12775#endif
12776#ifdef RLIMIT_NPROC
12777 "p"
12778#endif
12779#ifdef RLIMIT_NOFILE
12780 "n"
12781#endif
12782#ifdef RLIMIT_AS
12783 "v"
12784#endif
12785#ifdef RLIMIT_LOCKS
12786 "w"
12787#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012788 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012789 switch (optc) {
12790 case 'H':
12791 how = HARD;
12792 break;
12793 case 'S':
12794 how = SOFT;
12795 break;
12796 case 'a':
12797 all = 1;
12798 break;
12799 default:
12800 what = optc;
12801 }
12802
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012803 for (l = limits_tbl; l->option != what; l++)
12804 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012805
12806 set = *argptr ? 1 : 0;
12807 if (set) {
12808 char *p = *argptr;
12809
12810 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012811 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012812 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012813 val = RLIM_INFINITY;
12814 else {
12815 val = (rlim_t) 0;
12816
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012817 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012818 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012819 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012820 if (val < (rlim_t) 0)
12821 break;
12822 }
12823 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012824 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012825 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012826 }
12827 }
12828 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012829 const char *lname = limits_name;
12830 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012831 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012832 out1fmt("%-20s ", lname);
12833 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012834 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012835 }
12836 return 0;
12837 }
12838
12839 getrlimit(l->cmd, &limit);
12840 if (set) {
12841 if (how & HARD)
12842 limit.rlim_max = val;
12843 if (how & SOFT)
12844 limit.rlim_cur = val;
12845 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012846 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012847 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012848 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012849 }
12850 return 0;
12851}
12852
Eric Andersen90898442003-08-06 11:20:52 +000012853
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012854/* ============ Math support */
12855
Denis Vlasenko131ae172007-02-18 13:00:19 +000012856#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012857
12858/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12859
12860 Permission is hereby granted, free of charge, to any person obtaining
12861 a copy of this software and associated documentation files (the
12862 "Software"), to deal in the Software without restriction, including
12863 without limitation the rights to use, copy, modify, merge, publish,
12864 distribute, sublicense, and/or sell copies of the Software, and to
12865 permit persons to whom the Software is furnished to do so, subject to
12866 the following conditions:
12867
12868 The above copyright notice and this permission notice shall be
12869 included in all copies or substantial portions of the Software.
12870
12871 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12872 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12873 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12874 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12875 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12876 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12877 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12878*/
12879
12880/* This is my infix parser/evaluator. It is optimized for size, intended
12881 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012882 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012883 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012884 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012885 * be that which POSIX specifies for shells. */
12886
12887/* The code uses a simple two-stack algorithm. See
12888 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012889 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012890 * this is based (this code differs in that it applies operators immediately
12891 * to the stack instead of adding them to a queue to end up with an
12892 * expression). */
12893
12894/* To use the routine, call it with an expression string and error return
12895 * pointer */
12896
12897/*
12898 * Aug 24, 2001 Manuel Novoa III
12899 *
12900 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12901 *
12902 * 1) In arith_apply():
12903 * a) Cached values of *numptr and &(numptr[-1]).
12904 * b) Removed redundant test for zero denominator.
12905 *
12906 * 2) In arith():
12907 * a) Eliminated redundant code for processing operator tokens by moving
12908 * to a table-based implementation. Also folded handling of parens
12909 * into the table.
12910 * b) Combined all 3 loops which called arith_apply to reduce generated
12911 * code size at the cost of speed.
12912 *
12913 * 3) The following expressions were treated as valid by the original code:
12914 * 1() , 0! , 1 ( *3 ) .
12915 * These bugs have been fixed by internally enclosing the expression in
12916 * parens and then checking that all binary ops and right parens are
12917 * preceded by a valid expression (NUM_TOKEN).
12918 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012919 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012920 * ctype's isspace() if it is used by another busybox applet or if additional
12921 * whitespace chars should be considered. Look below the "#include"s for a
12922 * precompiler test.
12923 */
12924
12925/*
12926 * Aug 26, 2001 Manuel Novoa III
12927 *
12928 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12929 *
12930 * Merge in Aaron's comments previously posted to the busybox list,
12931 * modified slightly to take account of my changes to the code.
12932 *
12933 */
12934
12935/*
12936 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12937 *
12938 * - allow access to variable,
12939 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12940 * - realize assign syntax (VAR=expr, +=, *= etc)
12941 * - realize exponentiation (** operator)
12942 * - realize comma separated - expr, expr
12943 * - realise ++expr --expr expr++ expr--
12944 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012945 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012946 * - was restored loses XOR operator
12947 * - remove one goto label, added three ;-)
12948 * - protect $((num num)) as true zero expr (Manuel`s error)
12949 * - always use special isspace(), see comment from bash ;-)
12950 */
12951
Eric Andersen90898442003-08-06 11:20:52 +000012952#define arith_isspace(arithval) \
12953 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12954
Eric Andersen90898442003-08-06 11:20:52 +000012955typedef unsigned char operator;
12956
12957/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012958 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012959 * precedence. The ID portion is so that multiple operators can have the
12960 * same precedence, ensuring that the leftmost one is evaluated first.
12961 * Consider * and /. */
12962
12963#define tok_decl(prec,id) (((id)<<5)|(prec))
12964#define PREC(op) ((op) & 0x1F)
12965
12966#define TOK_LPAREN tok_decl(0,0)
12967
12968#define TOK_COMMA tok_decl(1,0)
12969
12970#define TOK_ASSIGN tok_decl(2,0)
12971#define TOK_AND_ASSIGN tok_decl(2,1)
12972#define TOK_OR_ASSIGN tok_decl(2,2)
12973#define TOK_XOR_ASSIGN tok_decl(2,3)
12974#define TOK_PLUS_ASSIGN tok_decl(2,4)
12975#define TOK_MINUS_ASSIGN tok_decl(2,5)
12976#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12977#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12978
12979#define TOK_MUL_ASSIGN tok_decl(3,0)
12980#define TOK_DIV_ASSIGN tok_decl(3,1)
12981#define TOK_REM_ASSIGN tok_decl(3,2)
12982
12983/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012984#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012985
12986/* conditional is right associativity too */
12987#define TOK_CONDITIONAL tok_decl(4,0)
12988#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12989
12990#define TOK_OR tok_decl(5,0)
12991
12992#define TOK_AND tok_decl(6,0)
12993
12994#define TOK_BOR tok_decl(7,0)
12995
12996#define TOK_BXOR tok_decl(8,0)
12997
12998#define TOK_BAND tok_decl(9,0)
12999
13000#define TOK_EQ tok_decl(10,0)
13001#define TOK_NE tok_decl(10,1)
13002
13003#define TOK_LT tok_decl(11,0)
13004#define TOK_GT tok_decl(11,1)
13005#define TOK_GE tok_decl(11,2)
13006#define TOK_LE tok_decl(11,3)
13007
13008#define TOK_LSHIFT tok_decl(12,0)
13009#define TOK_RSHIFT tok_decl(12,1)
13010
13011#define TOK_ADD tok_decl(13,0)
13012#define TOK_SUB tok_decl(13,1)
13013
13014#define TOK_MUL tok_decl(14,0)
13015#define TOK_DIV tok_decl(14,1)
13016#define TOK_REM tok_decl(14,2)
13017
13018/* exponent is right associativity */
13019#define TOK_EXPONENT tok_decl(15,1)
13020
13021/* For now unary operators. */
13022#define UNARYPREC 16
13023#define TOK_BNOT tok_decl(UNARYPREC,0)
13024#define TOK_NOT tok_decl(UNARYPREC,1)
13025
13026#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13027#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13028
13029#define PREC_PRE (UNARYPREC+2)
13030
13031#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13032#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13033
13034#define PREC_POST (UNARYPREC+3)
13035
13036#define TOK_POST_INC tok_decl(PREC_POST, 0)
13037#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13038
13039#define SPEC_PREC (UNARYPREC+4)
13040
13041#define TOK_NUM tok_decl(SPEC_PREC, 0)
13042#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13043
13044#define NUMPTR (*numstackptr)
13045
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013046static int
13047tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013048{
13049 operator prec = PREC(op);
13050
13051 convert_prec_is_assing(prec);
13052 return (prec == PREC(TOK_ASSIGN) ||
13053 prec == PREC_PRE || prec == PREC_POST);
13054}
13055
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013056static int
13057is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013058{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013059 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13060 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013061}
13062
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013063typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013064 arith_t val;
13065 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013066 char contidional_second_val_initialized;
13067 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013068 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013069} v_n_t;
13070
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013071typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013072 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013073 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013074} chk_var_recursive_looped_t;
13075
13076static chk_var_recursive_looped_t *prev_chk_var_recursive;
13077
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013078static int
13079arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013080{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 if (t->var) {
13082 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013083
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013084 if (p) {
13085 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013086
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013087 /* recursive try as expression */
13088 chk_var_recursive_looped_t *cur;
13089 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013090
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013091 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13092 if (strcmp(cur->var, t->var) == 0) {
13093 /* expression recursion loop detected */
13094 return -5;
13095 }
13096 }
13097 /* save current lookuped var name */
13098 cur = prev_chk_var_recursive;
13099 cur_save.var = t->var;
13100 cur_save.next = cur;
13101 prev_chk_var_recursive = &cur_save;
13102
13103 t->val = arith (p, &errcode);
13104 /* restore previous ptr after recursiving */
13105 prev_chk_var_recursive = cur;
13106 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013107 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013108 /* allow undefined var as 0 */
13109 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013110 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013112}
13113
13114/* "applying" a token means performing it on the top elements on the integer
13115 * stack. For a unary operator it will only change the top element, but a
13116 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013117static int
13118arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013119{
Eric Andersen90898442003-08-06 11:20:52 +000013120 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013121 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013122 int ret_arith_lookup_val;
13123
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013124 /* There is no operator that can work without arguments */
13125 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013126 numptr_m1 = NUMPTR - 1;
13127
13128 /* check operand is var with noninteger value */
13129 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013130 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013131 return ret_arith_lookup_val;
13132
13133 rez = numptr_m1->val;
13134 if (op == TOK_UMINUS)
13135 rez *= -1;
13136 else if (op == TOK_NOT)
13137 rez = !rez;
13138 else if (op == TOK_BNOT)
13139 rez = ~rez;
13140 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13141 rez++;
13142 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13143 rez--;
13144 else if (op != TOK_UPLUS) {
13145 /* Binary operators */
13146
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013147 /* check and binary operators need two arguments */
13148 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013149
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013150 /* ... and they pop one */
13151 --NUMPTR;
13152 numptr_val = rez;
13153 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013154 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013155 /* protect $((expr1 ? expr2)) without ": expr" */
13156 goto err;
13157 }
13158 rez = numptr_m1->contidional_second_val;
13159 } else if (numptr_m1->contidional_second_val_initialized) {
13160 /* protect $((expr1 : expr2)) without "expr ? " */
13161 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013162 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013163 numptr_m1 = NUMPTR - 1;
13164 if (op != TOK_ASSIGN) {
13165 /* check operand is var with noninteger value for not '=' */
13166 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13167 if (ret_arith_lookup_val)
13168 return ret_arith_lookup_val;
13169 }
13170 if (op == TOK_CONDITIONAL) {
13171 numptr_m1->contidional_second_val = rez;
13172 }
13173 rez = numptr_m1->val;
13174 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013175 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013176 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013177 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013178 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013179 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013180 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013181 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013182 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013183 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013184 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013185 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013186 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013187 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013188 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013189 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013190 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013191 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013192 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013193 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013194 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013195 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013196 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013197 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013198 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013199 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013200 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013201 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013202 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013203 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013204 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013205 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013206 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013207 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013208 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013209 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013210 /* protect $((expr : expr)) without "expr ? " */
13211 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013212 }
13213 numptr_m1->contidional_second_val_initialized = op;
13214 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013215 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013216 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013217 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013218 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013219 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013220 return -3; /* exponent less than 0 */
13221 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013222 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013223
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013224 if (numptr_val)
13225 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013226 c *= rez;
13227 rez = c;
13228 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013229 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013230 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013231 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013232 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013233 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013234 rez %= numptr_val;
13235 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013236 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013237 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013238
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013239 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013240 /* Hmm, 1=2 ? */
13241 goto err;
13242 }
13243 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013244#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013245 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013246#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013247 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013248#endif
Eric Andersen90898442003-08-06 11:20:52 +000013249 setvar(numptr_m1->var, buf, 0);
13250 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013251 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013252 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013253 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013254 rez++;
13255 }
13256 numptr_m1->val = rez;
13257 /* protect geting var value, is number now */
13258 numptr_m1->var = NULL;
13259 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013260 err:
13261 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013262}
13263
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013264/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013265static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013266 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13267 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13268 '<','<', 0, TOK_LSHIFT,
13269 '>','>', 0, TOK_RSHIFT,
13270 '|','|', 0, TOK_OR,
13271 '&','&', 0, TOK_AND,
13272 '!','=', 0, TOK_NE,
13273 '<','=', 0, TOK_LE,
13274 '>','=', 0, TOK_GE,
13275 '=','=', 0, TOK_EQ,
13276 '|','=', 0, TOK_OR_ASSIGN,
13277 '&','=', 0, TOK_AND_ASSIGN,
13278 '*','=', 0, TOK_MUL_ASSIGN,
13279 '/','=', 0, TOK_DIV_ASSIGN,
13280 '%','=', 0, TOK_REM_ASSIGN,
13281 '+','=', 0, TOK_PLUS_ASSIGN,
13282 '-','=', 0, TOK_MINUS_ASSIGN,
13283 '-','-', 0, TOK_POST_DEC,
13284 '^','=', 0, TOK_XOR_ASSIGN,
13285 '+','+', 0, TOK_POST_INC,
13286 '*','*', 0, TOK_EXPONENT,
13287 '!', 0, TOK_NOT,
13288 '<', 0, TOK_LT,
13289 '>', 0, TOK_GT,
13290 '=', 0, TOK_ASSIGN,
13291 '|', 0, TOK_BOR,
13292 '&', 0, TOK_BAND,
13293 '*', 0, TOK_MUL,
13294 '/', 0, TOK_DIV,
13295 '%', 0, TOK_REM,
13296 '+', 0, TOK_ADD,
13297 '-', 0, TOK_SUB,
13298 '^', 0, TOK_BXOR,
13299 /* uniq */
13300 '~', 0, TOK_BNOT,
13301 ',', 0, TOK_COMMA,
13302 '?', 0, TOK_CONDITIONAL,
13303 ':', 0, TOK_CONDITIONAL_SEP,
13304 ')', 0, TOK_RPAREN,
13305 '(', 0, TOK_LPAREN,
13306 0
13307};
13308/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013309#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013310
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013311static arith_t
13312arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013313{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013314 char arithval; /* Current character under analysis */
13315 operator lasttok, op;
13316 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013317 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013318 const char *p = endexpression;
13319 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013320 v_n_t *numstack, *numstackptr;
13321 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013322
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013323 /* Stack of integers */
13324 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13325 * in any given correct or incorrect expression is left as an exercise to
13326 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013327 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013328 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013329 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013330
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013331 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13332 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013333
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013334 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013335 arithval = *expr;
13336 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013337 if (p == endexpression) {
13338 /* Null expression. */
13339 return 0;
13340 }
13341
13342 /* This is only reached after all tokens have been extracted from the
13343 * input stream. If there are still tokens on the operator stack, they
13344 * are to be applied in order. At the end, there should be a final
13345 * result on the integer stack */
13346
13347 if (expr != endexpression + 1) {
13348 /* If we haven't done so already, */
13349 /* append a closing right paren */
13350 expr = endexpression;
13351 /* and let the loop process it. */
13352 continue;
13353 }
13354 /* At this point, we're done with the expression. */
13355 if (numstackptr != numstack+1) {
13356 /* ... but if there isn't, it's bad */
13357 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013358 *perrcode = -1;
13359 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013360 }
13361 if (numstack->var) {
13362 /* expression is $((var)) only, lookup now */
13363 errcode = arith_lookup_val(numstack);
13364 }
13365 ret:
13366 *perrcode = errcode;
13367 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013368 }
13369
Eric Andersen90898442003-08-06 11:20:52 +000013370 /* Continue processing the expression. */
13371 if (arith_isspace(arithval)) {
13372 /* Skip whitespace */
13373 goto prologue;
13374 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013375 p = endofname(expr);
13376 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013377 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013378
13379 numstackptr->var = alloca(var_name_size);
13380 safe_strncpy(numstackptr->var, expr, var_name_size);
13381 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013382 num:
Eric Andersen90898442003-08-06 11:20:52 +000013383 numstackptr->contidional_second_val_initialized = 0;
13384 numstackptr++;
13385 lasttok = TOK_NUM;
13386 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013387 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013388 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013389 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013390#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013391 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013392#else
13393 numstackptr->val = strtol(expr, (char **) &expr, 0);
13394#endif
Eric Andersen90898442003-08-06 11:20:52 +000013395 goto num;
13396 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013397 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013398 const char *o;
13399
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013400 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013401 /* strange operator not found */
13402 goto err;
13403 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013404 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013405 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013406 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013407 /* found */
13408 expr = o - 1;
13409 break;
13410 }
13411 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013412 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013413 p++;
13414 /* skip zero delim */
13415 p++;
13416 }
13417 op = p[1];
13418
13419 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013420 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13421 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013422
13423 /* Plus and minus are binary (not unary) _only_ if the last
13424 * token was as number, or a right paren (which pretends to be
13425 * a number, since it evaluates to one). Think about it.
13426 * It makes sense. */
13427 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013428 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013429 case TOK_ADD:
13430 op = TOK_UPLUS;
13431 break;
13432 case TOK_SUB:
13433 op = TOK_UMINUS;
13434 break;
13435 case TOK_POST_INC:
13436 op = TOK_PRE_INC;
13437 break;
13438 case TOK_POST_DEC:
13439 op = TOK_PRE_DEC;
13440 break;
Eric Andersen90898442003-08-06 11:20:52 +000013441 }
13442 }
13443 /* We don't want a unary operator to cause recursive descent on the
13444 * stack, because there can be many in a row and it could cause an
13445 * operator to be evaluated before its argument is pushed onto the
13446 * integer stack. */
13447 /* But for binary operators, "apply" everything on the operator
13448 * stack until we find an operator with a lesser priority than the
13449 * one we have just extracted. */
13450 /* Left paren is given the lowest priority so it will never be
13451 * "applied" in this way.
13452 * if associativity is right and priority eq, applied also skip
13453 */
13454 prec = PREC(op);
13455 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13456 /* not left paren or unary */
13457 if (lasttok != TOK_NUM) {
13458 /* binary op must be preceded by a num */
13459 goto err;
13460 }
13461 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013462 if (op == TOK_RPAREN) {
13463 /* The algorithm employed here is simple: while we don't
13464 * hit an open paren nor the bottom of the stack, pop
13465 * tokens and apply them */
13466 if (stackptr[-1] == TOK_LPAREN) {
13467 --stackptr;
13468 /* Any operator directly after a */
13469 lasttok = TOK_NUM;
13470 /* close paren should consider itself binary */
13471 goto prologue;
13472 }
13473 } else {
13474 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013475
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013476 convert_prec_is_assing(prec);
13477 convert_prec_is_assing(prev_prec);
13478 if (prev_prec < prec)
13479 break;
13480 /* check right assoc */
13481 if (prev_prec == prec && is_right_associativity(prec))
13482 break;
13483 }
13484 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13485 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013486 }
13487 if (op == TOK_RPAREN) {
13488 goto err;
13489 }
13490 }
13491
13492 /* Push this operator to the stack and remember it. */
13493 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013494 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013495 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013496 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013497}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013498#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013499
13500
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013501/* ============ main() and helpers */
13502
13503/*
13504 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013505 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013506static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013507static void
13508exitshell(void)
13509{
13510 struct jmploc loc;
13511 char *p;
13512 int status;
13513
13514 status = exitstatus;
13515 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13516 if (setjmp(loc.loc)) {
13517 if (exception == EXEXIT)
13518/* dash bug: it just does _exit(exitstatus) here
13519 * but we have to do setjobctl(0) first!
13520 * (bug is still not fixed in dash-0.5.3 - if you run dash
13521 * under Midnight Commander, on exit from dash MC is backgrounded) */
13522 status = exitstatus;
13523 goto out;
13524 }
13525 exception_handler = &loc;
13526 p = trap[0];
13527 if (p) {
13528 trap[0] = NULL;
13529 evalstring(p, 0);
13530 }
13531 flush_stdout_stderr();
13532 out:
13533 setjobctl(0);
13534 _exit(status);
13535 /* NOTREACHED */
13536}
13537
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013538static void
13539init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013540{
13541 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013542 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013543
13544 /* from trap.c: */
13545 signal(SIGCHLD, SIG_DFL);
13546
13547 /* from var.c: */
13548 {
13549 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013550 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013551 const char *p;
13552 struct stat st1, st2;
13553
13554 initvar();
13555 for (envp = environ; envp && *envp; envp++) {
13556 if (strchr(*envp, '=')) {
13557 setvareq(*envp, VEXPORT|VTEXTFIXED);
13558 }
13559 }
13560
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013561 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013562 setvar("PPID", ppid, 0);
13563
13564 p = lookupvar("PWD");
13565 if (p)
13566 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13567 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13568 p = '\0';
13569 setpwd(p, 0);
13570 }
13571}
13572
13573/*
13574 * Process the shell command line arguments.
13575 */
13576static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013577procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013578{
13579 int i;
13580 const char *xminusc;
13581 char **xargv;
13582
13583 xargv = argv;
13584 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013585 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013586 xargv++;
13587 for (i = 0; i < NOPTS; i++)
13588 optlist[i] = 2;
13589 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013590 if (options(1)) {
13591 /* it already printed err message */
13592 raise_exception(EXERROR);
13593 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013594 xargv = argptr;
13595 xminusc = minusc;
13596 if (*xargv == NULL) {
13597 if (xminusc)
13598 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13599 sflag = 1;
13600 }
13601 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13602 iflag = 1;
13603 if (mflag == 2)
13604 mflag = iflag;
13605 for (i = 0; i < NOPTS; i++)
13606 if (optlist[i] == 2)
13607 optlist[i] = 0;
13608#if DEBUG == 2
13609 debug = 1;
13610#endif
13611 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13612 if (xminusc) {
13613 minusc = *xargv++;
13614 if (*xargv)
13615 goto setarg0;
13616 } else if (!sflag) {
13617 setinputfile(*xargv, 0);
13618 setarg0:
13619 arg0 = *xargv++;
13620 commandname = arg0;
13621 }
13622
13623 shellparam.p = xargv;
13624#if ENABLE_ASH_GETOPTS
13625 shellparam.optind = 1;
13626 shellparam.optoff = -1;
13627#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013628 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013629 while (*xargv) {
13630 shellparam.nparam++;
13631 xargv++;
13632 }
13633 optschanged();
13634}
13635
13636/*
13637 * Read /etc/profile or .profile.
13638 */
13639static void
13640read_profile(const char *name)
13641{
13642 int skip;
13643
13644 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13645 return;
13646 skip = cmdloop(0);
13647 popfile();
13648 if (skip)
13649 exitshell();
13650}
13651
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013652/*
13653 * This routine is called when an error or an interrupt occurs in an
13654 * interactive shell and control is returned to the main command loop.
13655 */
13656static void
13657reset(void)
13658{
13659 /* from eval.c: */
13660 evalskip = 0;
13661 loopnest = 0;
13662 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013663 g_parsefile->left_in_buffer = 0;
13664 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013665 popallfiles();
13666 /* from parser.c: */
13667 tokpushback = 0;
13668 checkkwd = 0;
13669 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013670 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013671}
13672
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013673#if PROFILE
13674static short profile_buf[16384];
13675extern int etext();
13676#endif
13677
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013678/*
13679 * Main routine. We initialize things, parse the arguments, execute
13680 * profiles if we're a login shell, and then call cmdloop to execute
13681 * commands. The setjmp call sets up the location to jump to when an
13682 * exception occurs. When an exception occurs the variable "state"
13683 * is used to figure out how far we had gotten.
13684 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013685int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013686int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013687{
13688 char *shinit;
13689 volatile int state;
13690 struct jmploc jmploc;
13691 struct stackmark smark;
13692
Denis Vlasenko01631112007-12-16 17:20:38 +000013693 /* Initialize global data */
13694 INIT_G_misc();
13695 INIT_G_memstack();
13696 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013697#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013698 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013699#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013700 INIT_G_cmdtable();
13701
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013702#if PROFILE
13703 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13704#endif
13705
13706#if ENABLE_FEATURE_EDITING
13707 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13708#endif
13709 state = 0;
13710 if (setjmp(jmploc.loc)) {
13711 int e;
13712 int s;
13713
13714 reset();
13715
13716 e = exception;
13717 if (e == EXERROR)
13718 exitstatus = 2;
13719 s = state;
13720 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13721 exitshell();
13722
13723 if (e == EXINT) {
13724 outcslow('\n', stderr);
13725 }
13726 popstackmark(&smark);
13727 FORCE_INT_ON; /* enable interrupts */
13728 if (s == 1)
13729 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013730 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013731 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013732 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013733 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013734 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013735 }
13736 exception_handler = &jmploc;
13737#if DEBUG
13738 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013739 trace_puts("Shell args: ");
13740 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013741#endif
13742 rootpid = getpid();
13743
13744#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013745 /* Can use monotonic_ns() for better randomness but for now it is
13746 * not used anywhere else in busybox... so avoid bloat */
13747 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013748#endif
13749 init();
13750 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013751 procargs(argv);
13752
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013753#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13754 if (iflag) {
13755 const char *hp = lookupvar("HISTFILE");
13756
13757 if (hp == NULL) {
13758 hp = lookupvar("HOME");
13759 if (hp != NULL) {
13760 char *defhp = concat_path_file(hp, ".ash_history");
13761 setvar("HISTFILE", defhp, 0);
13762 free(defhp);
13763 }
13764 }
13765 }
13766#endif
13767 if (argv[0] && argv[0][0] == '-')
13768 isloginsh = 1;
13769 if (isloginsh) {
13770 state = 1;
13771 read_profile("/etc/profile");
13772 state1:
13773 state = 2;
13774 read_profile(".profile");
13775 }
13776 state2:
13777 state = 3;
13778 if (
13779#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013780 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013781#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013782 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013783 ) {
13784 shinit = lookupvar("ENV");
13785 if (shinit != NULL && *shinit != '\0') {
13786 read_profile(shinit);
13787 }
13788 }
13789 state3:
13790 state = 4;
13791 if (minusc)
13792 evalstring(minusc, 0);
13793
13794 if (sflag || minusc == NULL) {
13795#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013796 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013797 const char *hp = lookupvar("HISTFILE");
13798
13799 if (hp != NULL)
13800 line_input_state->hist_file = hp;
13801 }
13802#endif
13803 state4: /* XXX ??? - why isn't this before the "if" statement */
13804 cmdloop(1);
13805 }
13806#if PROFILE
13807 monitor(0);
13808#endif
13809#ifdef GPROF
13810 {
13811 extern void _mcleanup(void);
13812 _mcleanup();
13813 }
13814#endif
13815 exitshell();
13816 /* NOTREACHED */
13817}
13818
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013819#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013820const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013821int main(int argc, char **argv)
13822{
13823 return ash_main(argc, argv);
13824}
13825#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013826
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013827
Eric Andersendf82f612001-06-28 07:46:40 +000013828/*-
13829 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013830 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013831 *
13832 * This code is derived from software contributed to Berkeley by
13833 * Kenneth Almquist.
13834 *
13835 * Redistribution and use in source and binary forms, with or without
13836 * modification, are permitted provided that the following conditions
13837 * are met:
13838 * 1. Redistributions of source code must retain the above copyright
13839 * notice, this list of conditions and the following disclaimer.
13840 * 2. Redistributions in binary form must reproduce the above copyright
13841 * notice, this list of conditions and the following disclaimer in the
13842 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013843 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013844 * may be used to endorse or promote products derived from this software
13845 * without specific prior written permission.
13846 *
13847 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13848 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13849 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13850 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13851 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13852 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13853 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13854 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13855 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13856 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13857 * SUCH DAMAGE.
13858 */