blob: 492ccd79fc08f33f7b95dce73d9760e6c2d21f8f [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
Eric Andersencb57d552001-06-28 07:25:16 +000011 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000014 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000015 *
Eric Andersen81fe1232003-07-29 06:38:40 +000016 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Eric Andersen90898442003-08-06 11:20:52 +000020 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
Eric Andersenef02f822004-03-11 13:34:24 +000023 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000025 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000026 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000027 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
Eric Andersen90898442003-08-06 11:20:52 +000030 */
31
Eric Andersen90898442003-08-06 11:20:52 +000032/*
Eric Andersenc470f442003-07-28 09:56:35 +000033 * The follow should be set to reflect the type of system you have:
34 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V.
36 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
37 * define DEBUG=2 to compile in and turn on debugging.
38 *
39 * When debugging is on, debugging info will be written to ./trace and
40 * a quit signal will generate a core dump.
41 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000042#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000043#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000044
45#define IFS_BROKEN
46
47#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048
Denis Vlasenkob012b102007-02-19 22:43:01 +000049#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000050#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#define _GNU_SOURCE
52#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000053#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000054
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000055#include "busybox.h" /* for applet_names */
Denis Vlasenko61befda2008-11-25 01:36:03 +000056//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
57//#include "applet_tables.h" doesn't work
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko61befda2008-11-25 01:36:03 +000061
62#if defined SINGLE_APPLET_MAIN
63/* STANDALONE does not make sense, and won't compile */
64#undef CONFIG_FEATURE_SH_STANDALONE
65#undef ENABLE_FEATURE_SH_STANDALONE
66#undef USE_FEATURE_SH_STANDALONE
67#undef SKIP_FEATURE_SH_STANDALONE(...)
68#define ENABLE_FEATURE_SH_STANDALONE 0
69#define USE_FEATURE_SH_STANDALONE(...)
70#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000071#endif
72
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000073#ifndef PIPE_BUF
74#define PIPE_BUF 4096 /* amount of buffering in a pipe */
75#endif
76
Denis Vlasenkob012b102007-02-19 22:43:01 +000077#if defined(__uClinux__)
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +000078#error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000079#endif
80
Denis Vlasenkob012b102007-02-19 22:43:01 +000081
Denis Vlasenko01631112007-12-16 17:20:38 +000082/* ============ Hash table sizes. Configurable. */
83
84#define VTABSIZE 39
85#define ATABSIZE 39
86#define CMDTABLESIZE 31 /* should be prime */
87
88
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000089/* ============ Misc helpers */
90
91#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
92
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +000093/* C99 says: "char" declaration may be signed or unsigned by default */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000094#define signed_char2int(sc) ((int)((signed char)sc))
95
96
Denis Vlasenkob012b102007-02-19 22:43:01 +000097/* ============ Shell options */
98
99static const char *const optletters_optnames[] = {
100 "e" "errexit",
101 "f" "noglob",
102 "I" "ignoreeof",
103 "i" "interactive",
104 "m" "monitor",
105 "n" "noexec",
106 "s" "stdin",
107 "x" "xtrace",
108 "v" "verbose",
109 "C" "noclobber",
110 "a" "allexport",
111 "b" "notify",
112 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000115 ,"\0" "nolog"
116 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000117#endif
118};
119
120#define optletters(n) optletters_optnames[(n)][0]
121#define optnames(n) (&optletters_optnames[(n)][1])
122
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000123enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000124
Eric Andersenc470f442003-07-28 09:56:35 +0000125
Denis Vlasenkob012b102007-02-19 22:43:01 +0000126/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000127
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000128static const char homestr[] ALIGN1 = "HOME";
129static const char snlfmt[] ALIGN1 = "%s\n";
130static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000131
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000132/*
Eric Andersenc470f442003-07-28 09:56:35 +0000133 * We enclose jmp_buf in a structure so that we can declare pointers to
134 * jump locations. The global variable handler contains the location to
135 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000136 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000137 * exception handlers, the user should save the value of handler on entry
138 * to an inner scope, set handler to point to a jmploc structure for the
139 * inner scope, and restore handler on exit from the scope.
140 */
Eric Andersenc470f442003-07-28 09:56:35 +0000141struct jmploc {
142 jmp_buf loc;
143};
Denis Vlasenko01631112007-12-16 17:20:38 +0000144
145struct globals_misc {
146 /* pid of main shell */
147 int rootpid;
148 /* shell level: 0 for the main shell, 1 for its children, and so on */
149 int shlvl;
150#define rootshell (!shlvl)
151 char *minusc; /* argument to -c option */
152
153 char *curdir; // = nullstr; /* current working directory */
154 char *physdir; // = nullstr; /* physical working directory */
155
156 char *arg0; /* value of $0 */
157
158 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000159
160// disabled by vda: cannot understand how it was supposed to work -
161// cannot fix bugs. That's why you have to explain your non-trivial designs!
162// /* do we generate EXSIG events */
163// int exsig; /* counter */
164 volatile int suppressint; /* counter */
165 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
166 /* last pending signal */
167 volatile /*sig_atomic_t*/ smallint pendingsig;
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;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002711 }
Denis Vlasenko68819d12008-12-15 11:26:36 +00002712 s = strchrnul(spec_symbls, c);
2713 if (*s == '\0')
2714 return CWORD;
2715 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002716 return S_I_T[indx][syntax];
2717}
2718
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002719#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002720
Denis Vlasenko131ae172007-02-18 13:00:19 +00002721#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002722#define CSPCL_CIGN_CIGN_CIGN 0
2723#define CSPCL_CWORD_CWORD_CWORD 1
2724#define CNL_CNL_CNL_CNL 2
2725#define CWORD_CCTL_CCTL_CWORD 3
2726#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2727#define CVAR_CVAR_CWORD_CVAR 5
2728#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2729#define CSPCL_CWORD_CWORD_CLP 7
2730#define CSPCL_CWORD_CWORD_CRP 8
2731#define CBACK_CBACK_CCTL_CBACK 9
2732#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2733#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2734#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2735#define CWORD_CWORD_CWORD_CWORD 13
2736#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002737#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002738#define CSPCL_CWORD_CWORD_CWORD 0
2739#define CNL_CNL_CNL_CNL 1
2740#define CWORD_CCTL_CCTL_CWORD 2
2741#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2742#define CVAR_CVAR_CWORD_CVAR 4
2743#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2744#define CSPCL_CWORD_CWORD_CLP 6
2745#define CSPCL_CWORD_CWORD_CRP 7
2746#define CBACK_CBACK_CCTL_CBACK 8
2747#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2748#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2749#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2750#define CWORD_CWORD_CWORD_CWORD 12
2751#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002752#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002753
2754static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002755 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002756 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002757#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002758 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2759#endif
2760 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2762 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2763 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2764 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2765 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2766 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2767 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2768 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002769 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2899 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2913 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2914 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2915 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2916 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2917 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2918 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2919 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2920 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2921 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002922 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002923 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2925 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002927 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002928 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2929 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2930 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2931 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2933 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2934 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2935 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2947 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2948 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2949 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2950 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2951 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2980 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2981 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2982 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2985 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3005 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3006 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3007 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3008 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3009 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3010 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3011 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3012 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3013 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3014 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3015 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003016};
3017
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003018#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3019
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003020#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003021
Eric Andersen2870d962001-07-02 17:27:21 +00003022
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003023/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003024
Denis Vlasenko131ae172007-02-18 13:00:19 +00003025#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003026
3027#define ALIASINUSE 1
3028#define ALIASDEAD 2
3029
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003030struct alias {
3031 struct alias *next;
3032 char *name;
3033 char *val;
3034 int flag;
3035};
3036
Denis Vlasenko01631112007-12-16 17:20:38 +00003037
3038static struct alias **atab; // [ATABSIZE];
3039#define INIT_G_alias() do { \
3040 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3041} while (0)
3042
Eric Andersen2870d962001-07-02 17:27:21 +00003043
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003044static struct alias **
3045__lookupalias(const char *name) {
3046 unsigned int hashval;
3047 struct alias **app;
3048 const char *p;
3049 unsigned int ch;
3050
3051 p = name;
3052
3053 ch = (unsigned char)*p;
3054 hashval = ch << 4;
3055 while (ch) {
3056 hashval += ch;
3057 ch = (unsigned char)*++p;
3058 }
3059 app = &atab[hashval % ATABSIZE];
3060
3061 for (; *app; app = &(*app)->next) {
3062 if (strcmp(name, (*app)->name) == 0) {
3063 break;
3064 }
3065 }
3066
3067 return app;
3068}
3069
3070static struct alias *
3071lookupalias(const char *name, int check)
3072{
3073 struct alias *ap = *__lookupalias(name);
3074
3075 if (check && ap && (ap->flag & ALIASINUSE))
3076 return NULL;
3077 return ap;
3078}
3079
3080static struct alias *
3081freealias(struct alias *ap)
3082{
3083 struct alias *next;
3084
3085 if (ap->flag & ALIASINUSE) {
3086 ap->flag |= ALIASDEAD;
3087 return ap;
3088 }
3089
3090 next = ap->next;
3091 free(ap->name);
3092 free(ap->val);
3093 free(ap);
3094 return next;
3095}
Eric Andersencb57d552001-06-28 07:25:16 +00003096
Eric Andersenc470f442003-07-28 09:56:35 +00003097static void
3098setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003099{
3100 struct alias *ap, **app;
3101
3102 app = __lookupalias(name);
3103 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003104 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003105 if (ap) {
3106 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003107 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003108 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003109 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003110 ap->flag &= ~ALIASDEAD;
3111 } else {
3112 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003113 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003114 ap->name = ckstrdup(name);
3115 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003116 /*ap->flag = 0; - ckzalloc did it */
3117 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003118 *app = ap;
3119 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003120 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003121}
3122
Eric Andersenc470f442003-07-28 09:56:35 +00003123static int
3124unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003125{
Eric Andersencb57d552001-06-28 07:25:16 +00003126 struct alias **app;
3127
3128 app = __lookupalias(name);
3129
3130 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003131 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003132 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003133 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003134 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003135 }
3136
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003137 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003138}
3139
Eric Andersenc470f442003-07-28 09:56:35 +00003140static void
3141rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003142{
Eric Andersencb57d552001-06-28 07:25:16 +00003143 struct alias *ap, **app;
3144 int i;
3145
Denis Vlasenkob012b102007-02-19 22:43:01 +00003146 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003147 for (i = 0; i < ATABSIZE; i++) {
3148 app = &atab[i];
3149 for (ap = *app; ap; ap = *app) {
3150 *app = freealias(*app);
3151 if (ap == *app) {
3152 app = &ap->next;
3153 }
3154 }
3155 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003156 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003157}
3158
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003159static void
3160printalias(const struct alias *ap)
3161{
3162 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3163}
3164
Eric Andersencb57d552001-06-28 07:25:16 +00003165/*
3166 * TODO - sort output
3167 */
Eric Andersenc470f442003-07-28 09:56:35 +00003168static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003169aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003170{
3171 char *n, *v;
3172 int ret = 0;
3173 struct alias *ap;
3174
Denis Vlasenko68404f12008-03-17 09:00:54 +00003175 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003176 int i;
3177
Denis Vlasenko68404f12008-03-17 09:00:54 +00003178 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003179 for (ap = atab[i]; ap; ap = ap->next) {
3180 printalias(ap);
3181 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003182 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003183 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003184 }
3185 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003186 v = strchr(n+1, '=');
3187 if (v == NULL) { /* n+1: funny ksh stuff */
3188 ap = *__lookupalias(n);
3189 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003190 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003191 ret = 1;
3192 } else
3193 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003194 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003195 *v++ = '\0';
3196 setalias(n, v);
3197 }
3198 }
3199
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003200 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003201}
3202
Eric Andersenc470f442003-07-28 09:56:35 +00003203static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003204unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003205{
3206 int i;
3207
3208 while ((i = nextopt("a")) != '\0') {
3209 if (i == 'a') {
3210 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003211 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003212 }
3213 }
3214 for (i = 0; *argptr; argptr++) {
3215 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003216 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003217 i = 1;
3218 }
3219 }
3220
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003221 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003222}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003223
Denis Vlasenko131ae172007-02-18 13:00:19 +00003224#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003225
Eric Andersenc470f442003-07-28 09:56:35 +00003226
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003227/* ============ jobs.c */
3228
3229/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3230#define FORK_FG 0
3231#define FORK_BG 1
3232#define FORK_NOJOB 2
3233
3234/* mode flags for showjob(s) */
3235#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3236#define SHOW_PID 0x04 /* include process pid */
3237#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3238
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003239/*
3240 * A job structure contains information about a job. A job is either a
3241 * single process or a set of processes contained in a pipeline. In the
3242 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3243 * array of pids.
3244 */
3245
3246struct procstat {
3247 pid_t pid; /* process id */
3248 int status; /* last process status from wait() */
3249 char *cmd; /* text of command being run */
3250};
3251
3252struct job {
3253 struct procstat ps0; /* status of process */
3254 struct procstat *ps; /* status or processes when more than one */
3255#if JOBS
3256 int stopstatus; /* status of a stopped job */
3257#endif
3258 uint32_t
3259 nprocs: 16, /* number of processes */
3260 state: 8,
3261#define JOBRUNNING 0 /* at least one proc running */
3262#define JOBSTOPPED 1 /* all procs are stopped */
3263#define JOBDONE 2 /* all procs are completed */
3264#if JOBS
3265 sigint: 1, /* job was killed by SIGINT */
3266 jobctl: 1, /* job running under job control */
3267#endif
3268 waited: 1, /* true if this entry has been waited for */
3269 used: 1, /* true if this entry is in used */
3270 changed: 1; /* true if status has changed */
3271 struct job *prev_job; /* previous job */
3272};
3273
Denis Vlasenko68404f12008-03-17 09:00:54 +00003274static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003275#if !JOBS
3276#define forkshell(job, node, mode) forkshell(job, mode)
3277#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003278static int forkshell(struct job *, union node *, int);
3279static int waitforjob(struct job *);
3280
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003281#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003282enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003283#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003284#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003285static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003286static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003287#endif
3288
3289/*
3290 * Set the signal handler for the specified signal. The routine figures
3291 * out what it should be set to.
3292 */
3293static void
3294setsignal(int signo)
3295{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003296 char *t;
3297 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003298 struct sigaction act;
3299
3300 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003301 new_act = S_DFL;
3302 if (t != NULL) { /* trap for this sig is set */
3303 new_act = S_CATCH;
3304 if (t[0] == '\0') /* trap is "": ignore this sig */
3305 new_act = S_IGN;
3306 }
3307
3308 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309 switch (signo) {
3310 case SIGINT:
3311 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003312 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003313 break;
3314 case SIGQUIT:
3315#if DEBUG
3316 if (debug)
3317 break;
3318#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003319 /* man bash:
3320 * "In all cases, bash ignores SIGQUIT. Non-builtin
3321 * commands run by bash have signal handlers
3322 * set to the values inherited by the shell
3323 * from its parent". */
3324 new_act = S_IGN;
3325 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326 case SIGTERM:
3327 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003328 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 break;
3330#if JOBS
3331 case SIGTSTP:
3332 case SIGTTOU:
3333 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003334 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003335 break;
3336#endif
3337 }
3338 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003339//TODO: if !rootshell, we reset SIGQUIT to DFL,
3340//whereas we have to restore it to what shell got on entry
3341//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342
3343 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003344 cur_act = *t;
3345 if (cur_act == 0) {
3346 /* current setting is not yet known */
3347 if (sigaction(signo, NULL, &act)) {
3348 /* pretend it worked; maybe we should give a warning,
3349 * but other shells don't. We don't alter sigmode,
3350 * so we retry every time.
3351 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003352 return;
3353 }
3354 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003355 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356 if (mflag
3357 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3358 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003359 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003360 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361 }
3362 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003363 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003365
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003366 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003367 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003368 case S_CATCH:
3369 act.sa_handler = onsig;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003370 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3371 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003372 break;
3373 case S_IGN:
3374 act.sa_handler = SIG_IGN;
3375 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003376 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003377 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003378
3379 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380}
3381
3382/* mode flags for set_curjob */
3383#define CUR_DELETE 2
3384#define CUR_RUNNING 1
3385#define CUR_STOPPED 0
3386
3387/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003388#define DOWAIT_NONBLOCK WNOHANG
3389#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003390
3391#if JOBS
3392/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003393static int initialpgrp; //references:2
3394static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003395#endif
3396/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003397static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003398/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003399static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003400/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003401static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003402/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003403static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003404
3405static void
3406set_curjob(struct job *jp, unsigned mode)
3407{
3408 struct job *jp1;
3409 struct job **jpp, **curp;
3410
3411 /* first remove from list */
3412 jpp = curp = &curjob;
3413 do {
3414 jp1 = *jpp;
3415 if (jp1 == jp)
3416 break;
3417 jpp = &jp1->prev_job;
3418 } while (1);
3419 *jpp = jp1->prev_job;
3420
3421 /* Then re-insert in correct position */
3422 jpp = curp;
3423 switch (mode) {
3424 default:
3425#if DEBUG
3426 abort();
3427#endif
3428 case CUR_DELETE:
3429 /* job being deleted */
3430 break;
3431 case CUR_RUNNING:
3432 /* newly created job or backgrounded job,
3433 put after all stopped jobs. */
3434 do {
3435 jp1 = *jpp;
3436#if JOBS
3437 if (!jp1 || jp1->state != JOBSTOPPED)
3438#endif
3439 break;
3440 jpp = &jp1->prev_job;
3441 } while (1);
3442 /* FALLTHROUGH */
3443#if JOBS
3444 case CUR_STOPPED:
3445#endif
3446 /* newly stopped job - becomes curjob */
3447 jp->prev_job = *jpp;
3448 *jpp = jp;
3449 break;
3450 }
3451}
3452
3453#if JOBS || DEBUG
3454static int
3455jobno(const struct job *jp)
3456{
3457 return jp - jobtab + 1;
3458}
3459#endif
3460
3461/*
3462 * Convert a job name to a job structure.
3463 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003464#if !JOBS
3465#define getjob(name, getctl) getjob(name)
3466#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003467static struct job *
3468getjob(const char *name, int getctl)
3469{
3470 struct job *jp;
3471 struct job *found;
3472 const char *err_msg = "No such job: %s";
3473 unsigned num;
3474 int c;
3475 const char *p;
3476 char *(*match)(const char *, const char *);
3477
3478 jp = curjob;
3479 p = name;
3480 if (!p)
3481 goto currentjob;
3482
3483 if (*p != '%')
3484 goto err;
3485
3486 c = *++p;
3487 if (!c)
3488 goto currentjob;
3489
3490 if (!p[1]) {
3491 if (c == '+' || c == '%') {
3492 currentjob:
3493 err_msg = "No current job";
3494 goto check;
3495 }
3496 if (c == '-') {
3497 if (jp)
3498 jp = jp->prev_job;
3499 err_msg = "No previous job";
3500 check:
3501 if (!jp)
3502 goto err;
3503 goto gotit;
3504 }
3505 }
3506
3507 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003508// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509 num = atoi(p);
3510 if (num < njobs) {
3511 jp = jobtab + num - 1;
3512 if (jp->used)
3513 goto gotit;
3514 goto err;
3515 }
3516 }
3517
3518 match = prefix;
3519 if (*p == '?') {
3520 match = strstr;
3521 p++;
3522 }
3523
3524 found = 0;
3525 while (1) {
3526 if (!jp)
3527 goto err;
3528 if (match(jp->ps[0].cmd, p)) {
3529 if (found)
3530 goto err;
3531 found = jp;
3532 err_msg = "%s: ambiguous";
3533 }
3534 jp = jp->prev_job;
3535 }
3536
3537 gotit:
3538#if JOBS
3539 err_msg = "job %s not created under job control";
3540 if (getctl && jp->jobctl == 0)
3541 goto err;
3542#endif
3543 return jp;
3544 err:
3545 ash_msg_and_raise_error(err_msg, name);
3546}
3547
3548/*
3549 * Mark a job structure as unused.
3550 */
3551static void
3552freejob(struct job *jp)
3553{
3554 struct procstat *ps;
3555 int i;
3556
3557 INT_OFF;
3558 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3559 if (ps->cmd != nullstr)
3560 free(ps->cmd);
3561 }
3562 if (jp->ps != &jp->ps0)
3563 free(jp->ps);
3564 jp->used = 0;
3565 set_curjob(jp, CUR_DELETE);
3566 INT_ON;
3567}
3568
3569#if JOBS
3570static void
3571xtcsetpgrp(int fd, pid_t pgrp)
3572{
3573 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003574 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575}
3576
3577/*
3578 * Turn job control on and off.
3579 *
3580 * Note: This code assumes that the third arg to ioctl is a character
3581 * pointer, which is true on Berkeley systems but not System V. Since
3582 * System V doesn't have job control yet, this isn't a problem now.
3583 *
3584 * Called with interrupts off.
3585 */
3586static void
3587setjobctl(int on)
3588{
3589 int fd;
3590 int pgrp;
3591
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003592 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003593 return;
3594 if (on) {
3595 int ofd;
3596 ofd = fd = open(_PATH_TTY, O_RDWR);
3597 if (fd < 0) {
3598 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3599 * That sometimes helps to acquire controlling tty.
3600 * Obviously, a workaround for bugs when someone
3601 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003602 fd = 2;
3603 while (!isatty(fd))
3604 if (--fd < 0)
3605 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003606 }
3607 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003608 if (ofd >= 0)
3609 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003610 if (fd < 0)
3611 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003612 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003613 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614 do { /* while we are in the background */
3615 pgrp = tcgetpgrp(fd);
3616 if (pgrp < 0) {
3617 out:
3618 ash_msg("can't access tty; job control turned off");
3619 mflag = on = 0;
3620 goto close;
3621 }
3622 if (pgrp == getpgrp())
3623 break;
3624 killpg(0, SIGTTIN);
3625 } while (1);
3626 initialpgrp = pgrp;
3627
3628 setsignal(SIGTSTP);
3629 setsignal(SIGTTOU);
3630 setsignal(SIGTTIN);
3631 pgrp = rootpid;
3632 setpgid(0, pgrp);
3633 xtcsetpgrp(fd, pgrp);
3634 } else {
3635 /* turning job control off */
3636 fd = ttyfd;
3637 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003638 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003639 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003640 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003641 setpgid(0, pgrp);
3642 setsignal(SIGTSTP);
3643 setsignal(SIGTTOU);
3644 setsignal(SIGTTIN);
3645 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003646 if (fd >= 0)
3647 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003648 fd = -1;
3649 }
3650 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003651 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003652}
3653
3654static int
3655killcmd(int argc, char **argv)
3656{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003657 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003658 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003659 do {
3660 if (argv[i][0] == '%') {
3661 struct job *jp = getjob(argv[i], 0);
3662 unsigned pid = jp->ps[0].pid;
3663 /* Enough space for ' -NNN<nul>' */
3664 argv[i] = alloca(sizeof(int)*3 + 3);
3665 /* kill_main has matching code to expect
3666 * leading space. Needed to not confuse
3667 * negative pids with "kill -SIGNAL_NO" syntax */
3668 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003669 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003670 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003671 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003672 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003673}
3674
3675static void
3676showpipe(struct job *jp, FILE *out)
3677{
3678 struct procstat *sp;
3679 struct procstat *spend;
3680
3681 spend = jp->ps + jp->nprocs;
3682 for (sp = jp->ps + 1; sp < spend; sp++)
3683 fprintf(out, " | %s", sp->cmd);
3684 outcslow('\n', out);
3685 flush_stdout_stderr();
3686}
3687
3688
3689static int
3690restartjob(struct job *jp, int mode)
3691{
3692 struct procstat *ps;
3693 int i;
3694 int status;
3695 pid_t pgid;
3696
3697 INT_OFF;
3698 if (jp->state == JOBDONE)
3699 goto out;
3700 jp->state = JOBRUNNING;
3701 pgid = jp->ps->pid;
3702 if (mode == FORK_FG)
3703 xtcsetpgrp(ttyfd, pgid);
3704 killpg(pgid, SIGCONT);
3705 ps = jp->ps;
3706 i = jp->nprocs;
3707 do {
3708 if (WIFSTOPPED(ps->status)) {
3709 ps->status = -1;
3710 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003711 ps++;
3712 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003713 out:
3714 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3715 INT_ON;
3716 return status;
3717}
3718
3719static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003720fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003721{
3722 struct job *jp;
3723 FILE *out;
3724 int mode;
3725 int retval;
3726
3727 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3728 nextopt(nullstr);
3729 argv = argptr;
3730 out = stdout;
3731 do {
3732 jp = getjob(*argv, 1);
3733 if (mode == FORK_BG) {
3734 set_curjob(jp, CUR_RUNNING);
3735 fprintf(out, "[%d] ", jobno(jp));
3736 }
3737 outstr(jp->ps->cmd, out);
3738 showpipe(jp, out);
3739 retval = restartjob(jp, mode);
3740 } while (*argv && *++argv);
3741 return retval;
3742}
3743#endif
3744
3745static int
3746sprint_status(char *s, int status, int sigonly)
3747{
3748 int col;
3749 int st;
3750
3751 col = 0;
3752 if (!WIFEXITED(status)) {
3753#if JOBS
3754 if (WIFSTOPPED(status))
3755 st = WSTOPSIG(status);
3756 else
3757#endif
3758 st = WTERMSIG(status);
3759 if (sigonly) {
3760 if (st == SIGINT || st == SIGPIPE)
3761 goto out;
3762#if JOBS
3763 if (WIFSTOPPED(status))
3764 goto out;
3765#endif
3766 }
3767 st &= 0x7f;
3768 col = fmtstr(s, 32, strsignal(st));
3769 if (WCOREDUMP(status)) {
3770 col += fmtstr(s + col, 16, " (core dumped)");
3771 }
3772 } else if (!sigonly) {
3773 st = WEXITSTATUS(status);
3774 if (st)
3775 col = fmtstr(s, 16, "Done(%d)", st);
3776 else
3777 col = fmtstr(s, 16, "Done");
3778 }
3779 out:
3780 return col;
3781}
3782
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003783static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003784dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785{
3786 int pid;
3787 int status;
3788 struct job *jp;
3789 struct job *thisjob;
3790 int state;
3791
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003792 TRACE(("dowait(0x%x) called\n", wait_flags));
3793
3794 /* Do a wait system call. If job control is compiled in, we accept
3795 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3796 * NB: _not_ safe_waitpid, we need to detect EINTR */
3797 pid = waitpid(-1, &status,
3798 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3799 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003800 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003801 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003802
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003803 INT_OFF;
3804 thisjob = NULL;
3805 for (jp = curjob; jp; jp = jp->prev_job) {
3806 struct procstat *sp;
3807 struct procstat *spend;
3808 if (jp->state == JOBDONE)
3809 continue;
3810 state = JOBDONE;
3811 spend = jp->ps + jp->nprocs;
3812 sp = jp->ps;
3813 do {
3814 if (sp->pid == pid) {
3815 TRACE(("Job %d: changing status of proc %d "
3816 "from 0x%x to 0x%x\n",
3817 jobno(jp), pid, sp->status, status));
3818 sp->status = status;
3819 thisjob = jp;
3820 }
3821 if (sp->status == -1)
3822 state = JOBRUNNING;
3823#if JOBS
3824 if (state == JOBRUNNING)
3825 continue;
3826 if (WIFSTOPPED(sp->status)) {
3827 jp->stopstatus = sp->status;
3828 state = JOBSTOPPED;
3829 }
3830#endif
3831 } while (++sp < spend);
3832 if (thisjob)
3833 goto gotjob;
3834 }
3835#if JOBS
3836 if (!WIFSTOPPED(status))
3837#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003838 jobless--;
3839 goto out;
3840
3841 gotjob:
3842 if (state != JOBRUNNING) {
3843 thisjob->changed = 1;
3844
3845 if (thisjob->state != state) {
3846 TRACE(("Job %d: changing state from %d to %d\n",
3847 jobno(thisjob), thisjob->state, state));
3848 thisjob->state = state;
3849#if JOBS
3850 if (state == JOBSTOPPED) {
3851 set_curjob(thisjob, CUR_STOPPED);
3852 }
3853#endif
3854 }
3855 }
3856
3857 out:
3858 INT_ON;
3859
3860 if (thisjob && thisjob == job) {
3861 char s[48 + 1];
3862 int len;
3863
3864 len = sprint_status(s, status, 1);
3865 if (len) {
3866 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003867 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003868 out2str(s);
3869 }
3870 }
3871 return pid;
3872}
3873
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003874static int
3875blocking_wait_with_raise_on_sig(struct job *job)
3876{
3877 pid_t pid = dowait(DOWAIT_BLOCK, job);
3878 if (pid <= 0 && pendingsig)
3879 raise_exception(EXSIG);
3880 return pid;
3881}
3882
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883#if JOBS
3884static void
3885showjob(FILE *out, struct job *jp, int mode)
3886{
3887 struct procstat *ps;
3888 struct procstat *psend;
3889 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003890 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891 char s[80];
3892
3893 ps = jp->ps;
3894
3895 if (mode & SHOW_PGID) {
3896 /* just output process (group) id of pipeline */
3897 fprintf(out, "%d\n", ps->pid);
3898 return;
3899 }
3900
3901 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003902 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903
3904 if (jp == curjob)
3905 s[col - 2] = '+';
3906 else if (curjob && jp == curjob->prev_job)
3907 s[col - 2] = '-';
3908
3909 if (mode & SHOW_PID)
3910 col += fmtstr(s + col, 16, "%d ", ps->pid);
3911
3912 psend = ps + jp->nprocs;
3913
3914 if (jp->state == JOBRUNNING) {
3915 strcpy(s + col, "Running");
3916 col += sizeof("Running") - 1;
3917 } else {
3918 int status = psend[-1].status;
3919 if (jp->state == JOBSTOPPED)
3920 status = jp->stopstatus;
3921 col += sprint_status(s + col, status, 0);
3922 }
3923
3924 goto start;
3925
3926 do {
3927 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003928 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003929 start:
3930 fprintf(out, "%s%*c%s",
3931 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3932 );
3933 if (!(mode & SHOW_PID)) {
3934 showpipe(jp, out);
3935 break;
3936 }
3937 if (++ps == psend) {
3938 outcslow('\n', out);
3939 break;
3940 }
3941 } while (1);
3942
3943 jp->changed = 0;
3944
3945 if (jp->state == JOBDONE) {
3946 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3947 freejob(jp);
3948 }
3949}
3950
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003951/*
3952 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3953 * statuses have changed since the last call to showjobs.
3954 */
3955static void
3956showjobs(FILE *out, int mode)
3957{
3958 struct job *jp;
3959
3960 TRACE(("showjobs(%x) called\n", mode));
3961
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003962 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003963 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 continue;
3965
3966 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003967 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003969 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 }
3971}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003972
3973static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003974jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003975{
3976 int mode, m;
3977
3978 mode = 0;
3979 while ((m = nextopt("lp"))) {
3980 if (m == 'l')
3981 mode = SHOW_PID;
3982 else
3983 mode = SHOW_PGID;
3984 }
3985
3986 argv = argptr;
3987 if (*argv) {
3988 do
3989 showjob(stdout, getjob(*argv,0), mode);
3990 while (*++argv);
3991 } else
3992 showjobs(stdout, mode);
3993
3994 return 0;
3995}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003996#endif /* JOBS */
3997
3998static int
3999getstatus(struct job *job)
4000{
4001 int status;
4002 int retval;
4003
4004 status = job->ps[job->nprocs - 1].status;
4005 retval = WEXITSTATUS(status);
4006 if (!WIFEXITED(status)) {
4007#if JOBS
4008 retval = WSTOPSIG(status);
4009 if (!WIFSTOPPED(status))
4010#endif
4011 {
4012 /* XXX: limits number of signals */
4013 retval = WTERMSIG(status);
4014#if JOBS
4015 if (retval == SIGINT)
4016 job->sigint = 1;
4017#endif
4018 }
4019 retval += 128;
4020 }
4021 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4022 jobno(job), job->nprocs, status, retval));
4023 return retval;
4024}
4025
4026static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004027waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004028{
4029 struct job *job;
4030 int retval;
4031 struct job *jp;
4032
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004033// exsig++;
4034// xbarrier();
4035 if (pendingsig)
4036 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004037
4038 nextopt(nullstr);
4039 retval = 0;
4040
4041 argv = argptr;
4042 if (!*argv) {
4043 /* wait for all jobs */
4044 for (;;) {
4045 jp = curjob;
4046 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004047 if (!jp) /* no running procs */
4048 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004049 if (jp->state == JOBRUNNING)
4050 break;
4051 jp->waited = 1;
4052 jp = jp->prev_job;
4053 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004054 /* man bash:
4055 * "When bash is waiting for an asynchronous command via
4056 * the wait builtin, the reception of a signal for which a trap
4057 * has been set will cause the wait builtin to return immediately
4058 * with an exit status greater than 128, immediately after which
4059 * the trap is executed."
4060 * Do we do it that way? */
4061 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 }
4063 }
4064
4065 retval = 127;
4066 do {
4067 if (**argv != '%') {
4068 pid_t pid = number(*argv);
4069 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004070 while (1) {
4071 if (!job)
4072 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004073 if (job->ps[job->nprocs - 1].pid == pid)
4074 break;
4075 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004076 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004077 } else
4078 job = getjob(*argv, 0);
4079 /* loop until process terminated or stopped */
4080 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004081 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082 job->waited = 1;
4083 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004084 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004085 } while (*++argv);
4086
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004087 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 return retval;
4089}
4090
4091static struct job *
4092growjobtab(void)
4093{
4094 size_t len;
4095 ptrdiff_t offset;
4096 struct job *jp, *jq;
4097
4098 len = njobs * sizeof(*jp);
4099 jq = jobtab;
4100 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4101
4102 offset = (char *)jp - (char *)jq;
4103 if (offset) {
4104 /* Relocate pointers */
4105 size_t l = len;
4106
4107 jq = (struct job *)((char *)jq + l);
4108 while (l) {
4109 l -= sizeof(*jp);
4110 jq--;
4111#define joff(p) ((struct job *)((char *)(p) + l))
4112#define jmove(p) (p) = (void *)((char *)(p) + offset)
4113 if (joff(jp)->ps == &jq->ps0)
4114 jmove(joff(jp)->ps);
4115 if (joff(jp)->prev_job)
4116 jmove(joff(jp)->prev_job);
4117 }
4118 if (curjob)
4119 jmove(curjob);
4120#undef joff
4121#undef jmove
4122 }
4123
4124 njobs += 4;
4125 jobtab = jp;
4126 jp = (struct job *)((char *)jp + len);
4127 jq = jp + 3;
4128 do {
4129 jq->used = 0;
4130 } while (--jq >= jp);
4131 return jp;
4132}
4133
4134/*
4135 * Return a new job structure.
4136 * Called with interrupts off.
4137 */
4138static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004139makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140{
4141 int i;
4142 struct job *jp;
4143
4144 for (i = njobs, jp = jobtab; ; jp++) {
4145 if (--i < 0) {
4146 jp = growjobtab();
4147 break;
4148 }
4149 if (jp->used == 0)
4150 break;
4151 if (jp->state != JOBDONE || !jp->waited)
4152 continue;
4153#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004154 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155 continue;
4156#endif
4157 freejob(jp);
4158 break;
4159 }
4160 memset(jp, 0, sizeof(*jp));
4161#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004162 /* jp->jobctl is a bitfield.
4163 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004164 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004165 jp->jobctl = 1;
4166#endif
4167 jp->prev_job = curjob;
4168 curjob = jp;
4169 jp->used = 1;
4170 jp->ps = &jp->ps0;
4171 if (nprocs > 1) {
4172 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4173 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004174 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 jobno(jp)));
4176 return jp;
4177}
4178
4179#if JOBS
4180/*
4181 * Return a string identifying a command (to be printed by the
4182 * jobs command).
4183 */
4184static char *cmdnextc;
4185
4186static void
4187cmdputs(const char *s)
4188{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004189 static const char vstype[VSTYPE + 1][3] = {
4190 "", "}", "-", "+", "?", "=",
4191 "%", "%%", "#", "##"
4192 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4193 };
4194
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 const char *p, *str;
4196 char c, cc[2] = " ";
4197 char *nextc;
4198 int subtype = 0;
4199 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004200
4201 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4202 p = s;
4203 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004204 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004205 switch (c) {
4206 case CTLESC:
4207 c = *p++;
4208 break;
4209 case CTLVAR:
4210 subtype = *p++;
4211 if ((subtype & VSTYPE) == VSLENGTH)
4212 str = "${#";
4213 else
4214 str = "${";
4215 if (!(subtype & VSQUOTE) == !(quoted & 1))
4216 goto dostr;
4217 quoted ^= 1;
4218 c = '"';
4219 break;
4220 case CTLENDVAR:
4221 str = "\"}" + !(quoted & 1);
4222 quoted >>= 1;
4223 subtype = 0;
4224 goto dostr;
4225 case CTLBACKQ:
4226 str = "$(...)";
4227 goto dostr;
4228 case CTLBACKQ+CTLQUOTE:
4229 str = "\"$(...)\"";
4230 goto dostr;
4231#if ENABLE_ASH_MATH_SUPPORT
4232 case CTLARI:
4233 str = "$((";
4234 goto dostr;
4235 case CTLENDARI:
4236 str = "))";
4237 goto dostr;
4238#endif
4239 case CTLQUOTEMARK:
4240 quoted ^= 1;
4241 c = '"';
4242 break;
4243 case '=':
4244 if (subtype == 0)
4245 break;
4246 if ((subtype & VSTYPE) != VSNORMAL)
4247 quoted <<= 1;
4248 str = vstype[subtype & VSTYPE];
4249 if (subtype & VSNUL)
4250 c = ':';
4251 else
4252 goto checkstr;
4253 break;
4254 case '\'':
4255 case '\\':
4256 case '"':
4257 case '$':
4258 /* These can only happen inside quotes */
4259 cc[0] = c;
4260 str = cc;
4261 c = '\\';
4262 break;
4263 default:
4264 break;
4265 }
4266 USTPUTC(c, nextc);
4267 checkstr:
4268 if (!str)
4269 continue;
4270 dostr:
4271 while ((c = *str++)) {
4272 USTPUTC(c, nextc);
4273 }
4274 }
4275 if (quoted & 1) {
4276 USTPUTC('"', nextc);
4277 }
4278 *nextc = 0;
4279 cmdnextc = nextc;
4280}
4281
4282/* cmdtxt() and cmdlist() call each other */
4283static void cmdtxt(union node *n);
4284
4285static void
4286cmdlist(union node *np, int sep)
4287{
4288 for (; np; np = np->narg.next) {
4289 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004290 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004291 cmdtxt(np);
4292 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004293 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004294 }
4295}
4296
4297static void
4298cmdtxt(union node *n)
4299{
4300 union node *np;
4301 struct nodelist *lp;
4302 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004303
4304 if (!n)
4305 return;
4306 switch (n->type) {
4307 default:
4308#if DEBUG
4309 abort();
4310#endif
4311 case NPIPE:
4312 lp = n->npipe.cmdlist;
4313 for (;;) {
4314 cmdtxt(lp->n);
4315 lp = lp->next;
4316 if (!lp)
4317 break;
4318 cmdputs(" | ");
4319 }
4320 break;
4321 case NSEMI:
4322 p = "; ";
4323 goto binop;
4324 case NAND:
4325 p = " && ";
4326 goto binop;
4327 case NOR:
4328 p = " || ";
4329 binop:
4330 cmdtxt(n->nbinary.ch1);
4331 cmdputs(p);
4332 n = n->nbinary.ch2;
4333 goto donode;
4334 case NREDIR:
4335 case NBACKGND:
4336 n = n->nredir.n;
4337 goto donode;
4338 case NNOT:
4339 cmdputs("!");
4340 n = n->nnot.com;
4341 donode:
4342 cmdtxt(n);
4343 break;
4344 case NIF:
4345 cmdputs("if ");
4346 cmdtxt(n->nif.test);
4347 cmdputs("; then ");
4348 n = n->nif.ifpart;
4349 if (n->nif.elsepart) {
4350 cmdtxt(n);
4351 cmdputs("; else ");
4352 n = n->nif.elsepart;
4353 }
4354 p = "; fi";
4355 goto dotail;
4356 case NSUBSHELL:
4357 cmdputs("(");
4358 n = n->nredir.n;
4359 p = ")";
4360 goto dotail;
4361 case NWHILE:
4362 p = "while ";
4363 goto until;
4364 case NUNTIL:
4365 p = "until ";
4366 until:
4367 cmdputs(p);
4368 cmdtxt(n->nbinary.ch1);
4369 n = n->nbinary.ch2;
4370 p = "; done";
4371 dodo:
4372 cmdputs("; do ");
4373 dotail:
4374 cmdtxt(n);
4375 goto dotail2;
4376 case NFOR:
4377 cmdputs("for ");
4378 cmdputs(n->nfor.var);
4379 cmdputs(" in ");
4380 cmdlist(n->nfor.args, 1);
4381 n = n->nfor.body;
4382 p = "; done";
4383 goto dodo;
4384 case NDEFUN:
4385 cmdputs(n->narg.text);
4386 p = "() { ... }";
4387 goto dotail2;
4388 case NCMD:
4389 cmdlist(n->ncmd.args, 1);
4390 cmdlist(n->ncmd.redirect, 0);
4391 break;
4392 case NARG:
4393 p = n->narg.text;
4394 dotail2:
4395 cmdputs(p);
4396 break;
4397 case NHERE:
4398 case NXHERE:
4399 p = "<<...";
4400 goto dotail2;
4401 case NCASE:
4402 cmdputs("case ");
4403 cmdputs(n->ncase.expr->narg.text);
4404 cmdputs(" in ");
4405 for (np = n->ncase.cases; np; np = np->nclist.next) {
4406 cmdtxt(np->nclist.pattern);
4407 cmdputs(") ");
4408 cmdtxt(np->nclist.body);
4409 cmdputs(";; ");
4410 }
4411 p = "esac";
4412 goto dotail2;
4413 case NTO:
4414 p = ">";
4415 goto redir;
4416 case NCLOBBER:
4417 p = ">|";
4418 goto redir;
4419 case NAPPEND:
4420 p = ">>";
4421 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004422#if ENABLE_ASH_BASH_COMPAT
4423 case NTO2:
4424#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425 case NTOFD:
4426 p = ">&";
4427 goto redir;
4428 case NFROM:
4429 p = "<";
4430 goto redir;
4431 case NFROMFD:
4432 p = "<&";
4433 goto redir;
4434 case NFROMTO:
4435 p = "<>";
4436 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004437 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004438 cmdputs(p);
4439 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004440 cmdputs(utoa(n->ndup.dupfd));
4441 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004442 }
4443 n = n->nfile.fname;
4444 goto donode;
4445 }
4446}
4447
4448static char *
4449commandtext(union node *n)
4450{
4451 char *name;
4452
4453 STARTSTACKSTR(cmdnextc);
4454 cmdtxt(n);
4455 name = stackblock();
4456 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4457 name, cmdnextc, cmdnextc));
4458 return ckstrdup(name);
4459}
4460#endif /* JOBS */
4461
4462/*
4463 * Fork off a subshell. If we are doing job control, give the subshell its
4464 * own process group. Jp is a job structure that the job is to be added to.
4465 * N is the command that will be evaluated by the child. Both jp and n may
4466 * be NULL. The mode parameter can be one of the following:
4467 * FORK_FG - Fork off a foreground process.
4468 * FORK_BG - Fork off a background process.
4469 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4470 * process group even if job control is on.
4471 *
4472 * When job control is turned off, background processes have their standard
4473 * input redirected to /dev/null (except for the second and later processes
4474 * in a pipeline).
4475 *
4476 * Called with interrupts off.
4477 */
4478/*
4479 * Clear traps on a fork.
4480 */
4481static void
4482clear_traps(void)
4483{
4484 char **tp;
4485
4486 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004487 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488 INT_OFF;
4489 free(*tp);
4490 *tp = NULL;
4491 if (tp != &trap[0])
4492 setsignal(tp - trap);
4493 INT_ON;
4494 }
4495 }
4496}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004497
4498/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004499static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004500
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004501/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004502static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004503forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004504{
4505 int oldlvl;
4506
4507 TRACE(("Child shell %d\n", getpid()));
4508 oldlvl = shlvl;
4509 shlvl++;
4510
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004511 /* man bash: "Non-builtin commands run by bash have signal handlers
4512 * set to the values inherited by the shell from its parent".
4513 * Do we do it correctly? */
4514
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004515 closescript();
4516 clear_traps();
4517#if JOBS
4518 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004519 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004520 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4521 pid_t pgrp;
4522
4523 if (jp->nprocs == 0)
4524 pgrp = getpid();
4525 else
4526 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004527 /* this can fail because we are doing it in the parent also */
4528 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004529 if (mode == FORK_FG)
4530 xtcsetpgrp(ttyfd, pgrp);
4531 setsignal(SIGTSTP);
4532 setsignal(SIGTTOU);
4533 } else
4534#endif
4535 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004536 /* man bash: "When job control is not in effect,
4537 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538 ignoresig(SIGINT);
4539 ignoresig(SIGQUIT);
4540 if (jp->nprocs == 0) {
4541 close(0);
4542 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004543 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 }
4545 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004546 if (!oldlvl) {
4547 if (iflag) { /* why if iflag only? */
4548 setsignal(SIGINT);
4549 setsignal(SIGTERM);
4550 }
4551 /* man bash:
4552 * "In all cases, bash ignores SIGQUIT. Non-builtin
4553 * commands run by bash have signal handlers
4554 * set to the values inherited by the shell
4555 * from its parent".
4556 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004557 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004558 }
4559 for (jp = curjob; jp; jp = jp->prev_job)
4560 freejob(jp);
4561 jobless = 0;
4562}
4563
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004564/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004565#if !JOBS
4566#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4567#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568static void
4569forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4570{
4571 TRACE(("In parent shell: child = %d\n", pid));
4572 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004573 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4574 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004575 jobless++;
4576 return;
4577 }
4578#if JOBS
4579 if (mode != FORK_NOJOB && jp->jobctl) {
4580 int pgrp;
4581
4582 if (jp->nprocs == 0)
4583 pgrp = pid;
4584 else
4585 pgrp = jp->ps[0].pid;
4586 /* This can fail because we are doing it in the child also */
4587 setpgid(pid, pgrp);
4588 }
4589#endif
4590 if (mode == FORK_BG) {
4591 backgndpid = pid; /* set $! */
4592 set_curjob(jp, CUR_RUNNING);
4593 }
4594 if (jp) {
4595 struct procstat *ps = &jp->ps[jp->nprocs++];
4596 ps->pid = pid;
4597 ps->status = -1;
4598 ps->cmd = nullstr;
4599#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004600 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004601 ps->cmd = commandtext(n);
4602#endif
4603 }
4604}
4605
4606static int
4607forkshell(struct job *jp, union node *n, int mode)
4608{
4609 int pid;
4610
4611 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4612 pid = fork();
4613 if (pid < 0) {
4614 TRACE(("Fork failed, errno=%d", errno));
4615 if (jp)
4616 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004617 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004618 }
4619 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004620 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004621 else
4622 forkparent(jp, n, mode, pid);
4623 return pid;
4624}
4625
4626/*
4627 * Wait for job to finish.
4628 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004629 * Under job control we have the problem that while a child process
4630 * is running interrupts generated by the user are sent to the child
4631 * but not to the shell. This means that an infinite loop started by
4632 * an interactive user may be hard to kill. With job control turned off,
4633 * an interactive user may place an interactive program inside a loop.
4634 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004635 * these interrupts to also abort the loop. The approach we take here
4636 * is to have the shell ignore interrupt signals while waiting for a
4637 * foreground process to terminate, and then send itself an interrupt
4638 * signal if the child process was terminated by an interrupt signal.
4639 * Unfortunately, some programs want to do a bit of cleanup and then
4640 * exit on interrupt; unless these processes terminate themselves by
4641 * sending a signal to themselves (instead of calling exit) they will
4642 * confuse this approach.
4643 *
4644 * Called with interrupts off.
4645 */
4646static int
4647waitforjob(struct job *jp)
4648{
4649 int st;
4650
4651 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004652
4653 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004654 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004655 /* In non-interactive shells, we _can_ get
4656 * a keyboard signal here and be EINTRed,
4657 * but we just loop back, waiting for command to complete.
4658 *
4659 * man bash:
4660 * "If bash is waiting for a command to complete and receives
4661 * a signal for which a trap has been set, the trap
4662 * will not be executed until the command completes."
4663 *
4664 * Reality is that even if trap is not set, bash
4665 * will not act on the signal until command completes.
4666 * Try this. sleep5intoff.c:
4667 * #include <signal.h>
4668 * #include <unistd.h>
4669 * int main() {
4670 * sigset_t set;
4671 * sigemptyset(&set);
4672 * sigaddset(&set, SIGINT);
4673 * sigaddset(&set, SIGQUIT);
4674 * sigprocmask(SIG_BLOCK, &set, NULL);
4675 * sleep(5);
4676 * return 0;
4677 * }
4678 * $ bash -c './sleep5intoff; echo hi'
4679 * ^C^C^C^C <--- pressing ^C once a second
4680 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004681 * $ bash -c './sleep5intoff; echo hi'
4682 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4683 * $ _
4684 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004685 dowait(DOWAIT_BLOCK, jp);
4686 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004687 INT_ON;
4688
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004689 st = getstatus(jp);
4690#if JOBS
4691 if (jp->jobctl) {
4692 xtcsetpgrp(ttyfd, rootpid);
4693 /*
4694 * This is truly gross.
4695 * If we're doing job control, then we did a TIOCSPGRP which
4696 * caused us (the shell) to no longer be in the controlling
4697 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4698 * intuit from the subprocess exit status whether a SIGINT
4699 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4700 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004701 if (jp->sigint) /* TODO: do the same with all signals */
4702 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004703 }
4704 if (jp->state == JOBDONE)
4705#endif
4706 freejob(jp);
4707 return st;
4708}
4709
4710/*
4711 * return 1 if there are stopped jobs, otherwise 0
4712 */
4713static int
4714stoppedjobs(void)
4715{
4716 struct job *jp;
4717 int retval;
4718
4719 retval = 0;
4720 if (job_warning)
4721 goto out;
4722 jp = curjob;
4723 if (jp && jp->state == JOBSTOPPED) {
4724 out2str("You have stopped jobs.\n");
4725 job_warning = 2;
4726 retval++;
4727 }
4728 out:
4729 return retval;
4730}
4731
4732
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004733/* ============ redir.c
4734 *
4735 * Code for dealing with input/output redirection.
4736 */
4737
4738#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004739#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004740
4741/*
4742 * Open a file in noclobber mode.
4743 * The code was copied from bash.
4744 */
4745static int
4746noclobberopen(const char *fname)
4747{
4748 int r, fd;
4749 struct stat finfo, finfo2;
4750
4751 /*
4752 * If the file exists and is a regular file, return an error
4753 * immediately.
4754 */
4755 r = stat(fname, &finfo);
4756 if (r == 0 && S_ISREG(finfo.st_mode)) {
4757 errno = EEXIST;
4758 return -1;
4759 }
4760
4761 /*
4762 * If the file was not present (r != 0), make sure we open it
4763 * exclusively so that if it is created before we open it, our open
4764 * will fail. Make sure that we do not truncate an existing file.
4765 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4766 * file was not a regular file, we leave O_EXCL off.
4767 */
4768 if (r != 0)
4769 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4770 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4771
4772 /* If the open failed, return the file descriptor right away. */
4773 if (fd < 0)
4774 return fd;
4775
4776 /*
4777 * OK, the open succeeded, but the file may have been changed from a
4778 * non-regular file to a regular file between the stat and the open.
4779 * We are assuming that the O_EXCL open handles the case where FILENAME
4780 * did not exist and is symlinked to an existing file between the stat
4781 * and open.
4782 */
4783
4784 /*
4785 * If we can open it and fstat the file descriptor, and neither check
4786 * revealed that it was a regular file, and the file has not been
4787 * replaced, return the file descriptor.
4788 */
4789 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4790 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4791 return fd;
4792
4793 /* The file has been replaced. badness. */
4794 close(fd);
4795 errno = EEXIST;
4796 return -1;
4797}
4798
4799/*
4800 * Handle here documents. Normally we fork off a process to write the
4801 * data to a pipe. If the document is short, we can stuff the data in
4802 * the pipe without forking.
4803 */
4804/* openhere needs this forward reference */
4805static void expandhere(union node *arg, int fd);
4806static int
4807openhere(union node *redir)
4808{
4809 int pip[2];
4810 size_t len = 0;
4811
4812 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004813 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004814 if (redir->type == NHERE) {
4815 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004816 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004817 full_write(pip[1], redir->nhere.doc->narg.text, len);
4818 goto out;
4819 }
4820 }
4821 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004822 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004823 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004824 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4825 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4826 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4827 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004828 signal(SIGPIPE, SIG_DFL);
4829 if (redir->type == NHERE)
4830 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004831 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004832 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004833 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004834 }
4835 out:
4836 close(pip[1]);
4837 return pip[0];
4838}
4839
4840static int
4841openredirect(union node *redir)
4842{
4843 char *fname;
4844 int f;
4845
4846 switch (redir->nfile.type) {
4847 case NFROM:
4848 fname = redir->nfile.expfname;
4849 f = open(fname, O_RDONLY);
4850 if (f < 0)
4851 goto eopen;
4852 break;
4853 case NFROMTO:
4854 fname = redir->nfile.expfname;
4855 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4856 if (f < 0)
4857 goto ecreate;
4858 break;
4859 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004860#if ENABLE_ASH_BASH_COMPAT
4861 case NTO2:
4862#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004863 /* Take care of noclobber mode. */
4864 if (Cflag) {
4865 fname = redir->nfile.expfname;
4866 f = noclobberopen(fname);
4867 if (f < 0)
4868 goto ecreate;
4869 break;
4870 }
4871 /* FALLTHROUGH */
4872 case NCLOBBER:
4873 fname = redir->nfile.expfname;
4874 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4875 if (f < 0)
4876 goto ecreate;
4877 break;
4878 case NAPPEND:
4879 fname = redir->nfile.expfname;
4880 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4881 if (f < 0)
4882 goto ecreate;
4883 break;
4884 default:
4885#if DEBUG
4886 abort();
4887#endif
4888 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004889/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004890// case NTOFD:
4891// case NFROMFD:
4892// f = -1;
4893// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004894 case NHERE:
4895 case NXHERE:
4896 f = openhere(redir);
4897 break;
4898 }
4899
4900 return f;
4901 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004902 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004903 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004904 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004905}
4906
4907/*
4908 * Copy a file descriptor to be >= to. Returns -1
4909 * if the source file descriptor is closed, EMPTY if there are no unused
4910 * file descriptors left.
4911 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004912/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4913 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004914enum {
4915 COPYFD_EXACT = (int)~(INT_MAX),
4916 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4917};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004918static int
4919copyfd(int from, int to)
4920{
4921 int newfd;
4922
Denis Vlasenko5a867312008-07-24 19:46:38 +00004923 if (to & COPYFD_EXACT) {
4924 to &= ~COPYFD_EXACT;
4925 /*if (from != to)*/
4926 newfd = dup2(from, to);
4927 } else {
4928 newfd = fcntl(from, F_DUPFD, to);
4929 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004930 if (newfd < 0) {
4931 if (errno == EMFILE)
4932 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004933 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004934 ash_msg_and_raise_error("%d: %m", from);
4935 }
4936 return newfd;
4937}
4938
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004939/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004940struct two_fd_t {
4941 int orig, copy;
4942};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004943struct redirtab {
4944 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004945 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004946 int pair_count;
4947 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004948};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004949#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004950
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004951static int need_to_remember(struct redirtab *rp, int fd)
4952{
4953 int i;
4954
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004955 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004956 return 0;
4957
4958 for (i = 0; i < rp->pair_count; i++) {
4959 if (rp->two_fd[i].orig == fd) {
4960 /* already remembered */
4961 return 0;
4962 }
4963 }
4964 return 1;
4965}
4966
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004967/* "hidden" fd is a fd used to read scripts, or a copy of such */
4968static int is_hidden_fd(struct redirtab *rp, int fd)
4969{
4970 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004971 struct parsefile *pf;
4972
4973 if (fd == -1)
4974 return 0;
4975 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004976 while (pf) {
4977 if (fd == pf->fd) {
4978 return 1;
4979 }
4980 pf = pf->prev;
4981 }
4982 if (!rp)
4983 return 0;
4984 fd |= COPYFD_RESTORE;
4985 for (i = 0; i < rp->pair_count; i++) {
4986 if (rp->two_fd[i].copy == fd) {
4987 return 1;
4988 }
4989 }
4990 return 0;
4991}
4992
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004993/*
4994 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4995 * old file descriptors are stashed away so that the redirection can be
4996 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4997 * standard output, and the standard error if it becomes a duplicate of
4998 * stdout, is saved in memory.
4999 */
5000/* flags passed to redirect */
5001#define REDIR_PUSH 01 /* save previous values of file descriptors */
5002#define REDIR_SAVEFD2 03 /* set preverrout */
5003static void
5004redirect(union node *redir, int flags)
5005{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005006 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005007 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005008 int i;
5009 int fd;
5010 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005011 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005012
Denis Vlasenko01631112007-12-16 17:20:38 +00005013 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005014 if (!redir) {
5015 return;
5016 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005017
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005018 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005019 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005020 INT_OFF;
5021 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005022 union node *tmp = redir;
5023 do {
5024 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005025#if ENABLE_ASH_BASH_COMPAT
5026 if (redir->nfile.type == NTO2)
5027 sv_pos++;
5028#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005029 tmp = tmp->nfile.next;
5030 } while (tmp);
5031 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005032 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005033 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005034 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005035 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005036 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005037 while (sv_pos > 0) {
5038 sv_pos--;
5039 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5040 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005041 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005042
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005043 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005044 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005045 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005046 int right_fd = redir->ndup.dupfd;
5047 /* redirect from/to same file descriptor? */
5048 if (right_fd == fd)
5049 continue;
5050 /* echo >&10 and 10 is a fd opened to the sh script? */
5051 if (is_hidden_fd(sv, right_fd)) {
5052 errno = EBADF; /* as if it is closed */
5053 ash_msg_and_raise_error("%d: %m", right_fd);
5054 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005055 newfd = -1;
5056 } else {
5057 newfd = openredirect(redir); /* always >= 0 */
5058 if (fd == newfd) {
5059 /* Descriptor wasn't open before redirect.
5060 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005061 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005062 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005063 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005064 continue;
5065 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005066 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005067#if ENABLE_ASH_BASH_COMPAT
5068 redirect_more:
5069#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005070 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005071 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005072 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005073/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5074 * are closed in popredir() in the child, preventing them from leaking
5075 * into child. (popredir() also cleans up the mess in case of failures)
5076 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005077 if (i == -1) {
5078 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005079 if (i != EBADF) {
5080 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005081 if (newfd >= 0)
5082 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 errno = i;
5084 ash_msg_and_raise_error("%d: %m", fd);
5085 /* NOTREACHED */
5086 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005087 /* EBADF: it is not open - good, remember to close it */
5088 remember_to_close:
5089 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005090 } else { /* fd is open, save its copy */
5091 /* "exec fd>&-" should not close fds
5092 * which point to script file(s).
5093 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005094 if (is_hidden_fd(sv, fd))
5095 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005096 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005097 if (fd == 2)
5098 copied_fd2 = i;
5099 sv->two_fd[sv_pos].orig = fd;
5100 sv->two_fd[sv_pos].copy = i;
5101 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005102 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005103 if (newfd < 0) {
5104 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005105 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005106 close(fd);
5107 } else {
5108 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005109 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005110 } else if (fd != newfd) { /* move newfd to fd */
5111 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005112#if ENABLE_ASH_BASH_COMPAT
5113 if (!(redir->nfile.type == NTO2 && fd == 2))
5114#endif
5115 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005116 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005117#if ENABLE_ASH_BASH_COMPAT
5118 if (redir->nfile.type == NTO2 && fd == 1) {
5119 /* We already redirected it to fd 1, now copy it to 2 */
5120 newfd = 1;
5121 fd = 2;
5122 goto redirect_more;
5123 }
5124#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005125 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005126
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005127 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005128 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5129 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005130}
5131
5132/*
5133 * Undo the effects of the last redirection.
5134 */
5135static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005136popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005137{
5138 struct redirtab *rp;
5139 int i;
5140
Denis Vlasenko01631112007-12-16 17:20:38 +00005141 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005142 return;
5143 INT_OFF;
5144 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005145 for (i = 0; i < rp->pair_count; i++) {
5146 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005147 int copy = rp->two_fd[i].copy;
5148 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005149 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005150 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005151 continue;
5152 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005153 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005154 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005155 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005156 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005157 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005158 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005159 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 }
5161 }
5162 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005163 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005164 free(rp);
5165 INT_ON;
5166}
5167
5168/*
5169 * Undo all redirections. Called on error or interrupt.
5170 */
5171
5172/*
5173 * Discard all saved file descriptors.
5174 */
5175static void
5176clearredir(int drop)
5177{
5178 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005179 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005180 if (!redirlist)
5181 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005182 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005183 }
5184}
5185
5186static int
5187redirectsafe(union node *redir, int flags)
5188{
5189 int err;
5190 volatile int saveint;
5191 struct jmploc *volatile savehandler = exception_handler;
5192 struct jmploc jmploc;
5193
5194 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005195 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5196 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005197 if (!err) {
5198 exception_handler = &jmploc;
5199 redirect(redir, flags);
5200 }
5201 exception_handler = savehandler;
5202 if (err && exception != EXERROR)
5203 longjmp(exception_handler->loc, 1);
5204 RESTORE_INT(saveint);
5205 return err;
5206}
5207
5208
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005209/* ============ Routines to expand arguments to commands
5210 *
5211 * We have to deal with backquotes, shell variables, and file metacharacters.
5212 */
5213
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005214#if ENABLE_ASH_MATH_SUPPORT_64
5215typedef int64_t arith_t;
5216#define arith_t_type long long
5217#else
5218typedef long arith_t;
5219#define arith_t_type long
5220#endif
5221
5222#if ENABLE_ASH_MATH_SUPPORT
5223static arith_t dash_arith(const char *);
5224static arith_t arith(const char *expr, int *perrcode);
5225#endif
5226
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005227/*
5228 * expandarg flags
5229 */
5230#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5231#define EXP_TILDE 0x2 /* do normal tilde expansion */
5232#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5233#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5234#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5235#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5236#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5237#define EXP_WORD 0x80 /* expand word in parameter expansion */
5238#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5239/*
5240 * _rmescape() flags
5241 */
5242#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5243#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5244#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5245#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5246#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5247
5248/*
5249 * Structure specifying which parts of the string should be searched
5250 * for IFS characters.
5251 */
5252struct ifsregion {
5253 struct ifsregion *next; /* next region in list */
5254 int begoff; /* offset of start of region */
5255 int endoff; /* offset of end of region */
5256 int nulonly; /* search for nul bytes only */
5257};
5258
5259struct arglist {
5260 struct strlist *list;
5261 struct strlist **lastp;
5262};
5263
5264/* output of current string */
5265static char *expdest;
5266/* list of back quote expressions */
5267static struct nodelist *argbackq;
5268/* first struct in list of ifs regions */
5269static struct ifsregion ifsfirst;
5270/* last struct in list */
5271static struct ifsregion *ifslastp;
5272/* holds expanded arg list */
5273static struct arglist exparg;
5274
5275/*
5276 * Our own itoa().
5277 */
5278static int
5279cvtnum(arith_t num)
5280{
5281 int len;
5282
5283 expdest = makestrspace(32, expdest);
5284#if ENABLE_ASH_MATH_SUPPORT_64
5285 len = fmtstr(expdest, 32, "%lld", (long long) num);
5286#else
5287 len = fmtstr(expdest, 32, "%ld", num);
5288#endif
5289 STADJUST(len, expdest);
5290 return len;
5291}
5292
5293static size_t
5294esclen(const char *start, const char *p)
5295{
5296 size_t esc = 0;
5297
5298 while (p > start && *--p == CTLESC) {
5299 esc++;
5300 }
5301 return esc;
5302}
5303
5304/*
5305 * Remove any CTLESC characters from a string.
5306 */
5307static char *
5308_rmescapes(char *str, int flag)
5309{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005310 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005311
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005312 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005313 unsigned inquotes;
5314 int notescaped;
5315 int globbing;
5316
5317 p = strpbrk(str, qchars);
5318 if (!p) {
5319 return str;
5320 }
5321 q = p;
5322 r = str;
5323 if (flag & RMESCAPE_ALLOC) {
5324 size_t len = p - str;
5325 size_t fulllen = len + strlen(p) + 1;
5326
5327 if (flag & RMESCAPE_GROW) {
5328 r = makestrspace(fulllen, expdest);
5329 } else if (flag & RMESCAPE_HEAP) {
5330 r = ckmalloc(fulllen);
5331 } else {
5332 r = stalloc(fulllen);
5333 }
5334 q = r;
5335 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005336 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005337 }
5338 }
5339 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5340 globbing = flag & RMESCAPE_GLOB;
5341 notescaped = globbing;
5342 while (*p) {
5343 if (*p == CTLQUOTEMARK) {
5344 inquotes = ~inquotes;
5345 p++;
5346 notescaped = globbing;
5347 continue;
5348 }
5349 if (*p == '\\') {
5350 /* naked back slash */
5351 notescaped = 0;
5352 goto copy;
5353 }
5354 if (*p == CTLESC) {
5355 p++;
5356 if (notescaped && inquotes && *p != '/') {
5357 *q++ = '\\';
5358 }
5359 }
5360 notescaped = globbing;
5361 copy:
5362 *q++ = *p++;
5363 }
5364 *q = '\0';
5365 if (flag & RMESCAPE_GROW) {
5366 expdest = r;
5367 STADJUST(q - r + 1, expdest);
5368 }
5369 return r;
5370}
5371#define rmescapes(p) _rmescapes((p), 0)
5372
5373#define pmatch(a, b) !fnmatch((a), (b), 0)
5374
5375/*
5376 * Prepare a pattern for a expmeta (internal glob(3)) call.
5377 *
5378 * Returns an stalloced string.
5379 */
5380static char *
5381preglob(const char *pattern, int quoted, int flag)
5382{
5383 flag |= RMESCAPE_GLOB;
5384 if (quoted) {
5385 flag |= RMESCAPE_QUOTED;
5386 }
5387 return _rmescapes((char *)pattern, flag);
5388}
5389
5390/*
5391 * Put a string on the stack.
5392 */
5393static void
5394memtodest(const char *p, size_t len, int syntax, int quotes)
5395{
5396 char *q = expdest;
5397
5398 q = makestrspace(len * 2, q);
5399
5400 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005401 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005402 if (!c)
5403 continue;
5404 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5405 USTPUTC(CTLESC, q);
5406 USTPUTC(c, q);
5407 }
5408
5409 expdest = q;
5410}
5411
5412static void
5413strtodest(const char *p, int syntax, int quotes)
5414{
5415 memtodest(p, strlen(p), syntax, quotes);
5416}
5417
5418/*
5419 * Record the fact that we have to scan this region of the
5420 * string for IFS characters.
5421 */
5422static void
5423recordregion(int start, int end, int nulonly)
5424{
5425 struct ifsregion *ifsp;
5426
5427 if (ifslastp == NULL) {
5428 ifsp = &ifsfirst;
5429 } else {
5430 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005431 ifsp = ckzalloc(sizeof(*ifsp));
5432 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005433 ifslastp->next = ifsp;
5434 INT_ON;
5435 }
5436 ifslastp = ifsp;
5437 ifslastp->begoff = start;
5438 ifslastp->endoff = end;
5439 ifslastp->nulonly = nulonly;
5440}
5441
5442static void
5443removerecordregions(int endoff)
5444{
5445 if (ifslastp == NULL)
5446 return;
5447
5448 if (ifsfirst.endoff > endoff) {
5449 while (ifsfirst.next != NULL) {
5450 struct ifsregion *ifsp;
5451 INT_OFF;
5452 ifsp = ifsfirst.next->next;
5453 free(ifsfirst.next);
5454 ifsfirst.next = ifsp;
5455 INT_ON;
5456 }
5457 if (ifsfirst.begoff > endoff)
5458 ifslastp = NULL;
5459 else {
5460 ifslastp = &ifsfirst;
5461 ifsfirst.endoff = endoff;
5462 }
5463 return;
5464 }
5465
5466 ifslastp = &ifsfirst;
5467 while (ifslastp->next && ifslastp->next->begoff < endoff)
5468 ifslastp=ifslastp->next;
5469 while (ifslastp->next != NULL) {
5470 struct ifsregion *ifsp;
5471 INT_OFF;
5472 ifsp = ifslastp->next->next;
5473 free(ifslastp->next);
5474 ifslastp->next = ifsp;
5475 INT_ON;
5476 }
5477 if (ifslastp->endoff > endoff)
5478 ifslastp->endoff = endoff;
5479}
5480
5481static char *
5482exptilde(char *startp, char *p, int flag)
5483{
5484 char c;
5485 char *name;
5486 struct passwd *pw;
5487 const char *home;
5488 int quotes = flag & (EXP_FULL | EXP_CASE);
5489 int startloc;
5490
5491 name = p + 1;
5492
5493 while ((c = *++p) != '\0') {
5494 switch (c) {
5495 case CTLESC:
5496 return startp;
5497 case CTLQUOTEMARK:
5498 return startp;
5499 case ':':
5500 if (flag & EXP_VARTILDE)
5501 goto done;
5502 break;
5503 case '/':
5504 case CTLENDVAR:
5505 goto done;
5506 }
5507 }
5508 done:
5509 *p = '\0';
5510 if (*name == '\0') {
5511 home = lookupvar(homestr);
5512 } else {
5513 pw = getpwnam(name);
5514 if (pw == NULL)
5515 goto lose;
5516 home = pw->pw_dir;
5517 }
5518 if (!home || !*home)
5519 goto lose;
5520 *p = c;
5521 startloc = expdest - (char *)stackblock();
5522 strtodest(home, SQSYNTAX, quotes);
5523 recordregion(startloc, expdest - (char *)stackblock(), 0);
5524 return p;
5525 lose:
5526 *p = c;
5527 return startp;
5528}
5529
5530/*
5531 * Execute a command inside back quotes. If it's a builtin command, we
5532 * want to save its output in a block obtained from malloc. Otherwise
5533 * we fork off a subprocess and get the output of the command via a pipe.
5534 * Should be called with interrupts off.
5535 */
5536struct backcmd { /* result of evalbackcmd */
5537 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005538 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005539 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540 struct job *jp; /* job structure for command */
5541};
5542
5543/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005544static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005545#define EV_EXIT 01 /* exit after evaluating tree */
5546static void evaltree(union node *, int);
5547
5548static void
5549evalbackcmd(union node *n, struct backcmd *result)
5550{
5551 int saveherefd;
5552
5553 result->fd = -1;
5554 result->buf = NULL;
5555 result->nleft = 0;
5556 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005557 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005558 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005559
5560 saveherefd = herefd;
5561 herefd = -1;
5562
5563 {
5564 int pip[2];
5565 struct job *jp;
5566
5567 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005568 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005569 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005570 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5571 FORCE_INT_ON;
5572 close(pip[0]);
5573 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005574 /*close(1);*/
5575 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 close(pip[1]);
5577 }
5578 eflag = 0;
5579 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5580 /* NOTREACHED */
5581 }
5582 close(pip[1]);
5583 result->fd = pip[0];
5584 result->jp = jp;
5585 }
5586 herefd = saveherefd;
5587 out:
5588 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5589 result->fd, result->buf, result->nleft, result->jp));
5590}
5591
5592/*
5593 * Expand stuff in backwards quotes.
5594 */
5595static void
5596expbackq(union node *cmd, int quoted, int quotes)
5597{
5598 struct backcmd in;
5599 int i;
5600 char buf[128];
5601 char *p;
5602 char *dest;
5603 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005604 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005605 struct stackmark smark;
5606
5607 INT_OFF;
5608 setstackmark(&smark);
5609 dest = expdest;
5610 startloc = dest - (char *)stackblock();
5611 grabstackstr(dest);
5612 evalbackcmd(cmd, &in);
5613 popstackmark(&smark);
5614
5615 p = in.buf;
5616 i = in.nleft;
5617 if (i == 0)
5618 goto read;
5619 for (;;) {
5620 memtodest(p, i, syntax, quotes);
5621 read:
5622 if (in.fd < 0)
5623 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005624 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005625 TRACE(("expbackq: read returns %d\n", i));
5626 if (i <= 0)
5627 break;
5628 p = buf;
5629 }
5630
Denis Vlasenko60818682007-09-28 22:07:23 +00005631 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005632 if (in.fd >= 0) {
5633 close(in.fd);
5634 back_exitstatus = waitforjob(in.jp);
5635 }
5636 INT_ON;
5637
5638 /* Eat all trailing newlines */
5639 dest = expdest;
5640 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5641 STUNPUTC(dest);
5642 expdest = dest;
5643
5644 if (quoted == 0)
5645 recordregion(startloc, dest - (char *)stackblock(), 0);
5646 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5647 (dest - (char *)stackblock()) - startloc,
5648 (dest - (char *)stackblock()) - startloc,
5649 stackblock() + startloc));
5650}
5651
5652#if ENABLE_ASH_MATH_SUPPORT
5653/*
5654 * Expand arithmetic expression. Backup to start of expression,
5655 * evaluate, place result in (backed up) result, adjust string position.
5656 */
5657static void
5658expari(int quotes)
5659{
5660 char *p, *start;
5661 int begoff;
5662 int flag;
5663 int len;
5664
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005665 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005666
5667 /*
5668 * This routine is slightly over-complicated for
5669 * efficiency. Next we scan backwards looking for the
5670 * start of arithmetic.
5671 */
5672 start = stackblock();
5673 p = expdest - 1;
5674 *p = '\0';
5675 p--;
5676 do {
5677 int esc;
5678
5679 while (*p != CTLARI) {
5680 p--;
5681#if DEBUG
5682 if (p < start) {
5683 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5684 }
5685#endif
5686 }
5687
5688 esc = esclen(start, p);
5689 if (!(esc % 2)) {
5690 break;
5691 }
5692
5693 p -= esc + 1;
5694 } while (1);
5695
5696 begoff = p - start;
5697
5698 removerecordregions(begoff);
5699
5700 flag = p[1];
5701
5702 expdest = p;
5703
5704 if (quotes)
5705 rmescapes(p + 2);
5706
5707 len = cvtnum(dash_arith(p + 2));
5708
5709 if (flag != '"')
5710 recordregion(begoff, begoff + len, 0);
5711}
5712#endif
5713
5714/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005715static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005716
5717/*
5718 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5719 * characters to allow for further processing. Otherwise treat
5720 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005721 *
5722 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5723 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5724 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005725 */
5726static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005727argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005728{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005729 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 '=',
5731 ':',
5732 CTLQUOTEMARK,
5733 CTLENDVAR,
5734 CTLESC,
5735 CTLVAR,
5736 CTLBACKQ,
5737 CTLBACKQ | CTLQUOTE,
5738#if ENABLE_ASH_MATH_SUPPORT
5739 CTLENDARI,
5740#endif
5741 0
5742 };
5743 const char *reject = spclchars;
5744 int c;
5745 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5746 int breakall = flag & EXP_WORD;
5747 int inquotes;
5748 size_t length;
5749 int startloc;
5750
5751 if (!(flag & EXP_VARTILDE)) {
5752 reject += 2;
5753 } else if (flag & EXP_VARTILDE2) {
5754 reject++;
5755 }
5756 inquotes = 0;
5757 length = 0;
5758 if (flag & EXP_TILDE) {
5759 char *q;
5760
5761 flag &= ~EXP_TILDE;
5762 tilde:
5763 q = p;
5764 if (*q == CTLESC && (flag & EXP_QWORD))
5765 q++;
5766 if (*q == '~')
5767 p = exptilde(p, q, flag);
5768 }
5769 start:
5770 startloc = expdest - (char *)stackblock();
5771 for (;;) {
5772 length += strcspn(p + length, reject);
5773 c = p[length];
5774 if (c && (!(c & 0x80)
5775#if ENABLE_ASH_MATH_SUPPORT
5776 || c == CTLENDARI
5777#endif
5778 )) {
5779 /* c == '=' || c == ':' || c == CTLENDARI */
5780 length++;
5781 }
5782 if (length > 0) {
5783 int newloc;
5784 expdest = stack_nputstr(p, length, expdest);
5785 newloc = expdest - (char *)stackblock();
5786 if (breakall && !inquotes && newloc > startloc) {
5787 recordregion(startloc, newloc, 0);
5788 }
5789 startloc = newloc;
5790 }
5791 p += length + 1;
5792 length = 0;
5793
5794 switch (c) {
5795 case '\0':
5796 goto breakloop;
5797 case '=':
5798 if (flag & EXP_VARTILDE2) {
5799 p--;
5800 continue;
5801 }
5802 flag |= EXP_VARTILDE2;
5803 reject++;
5804 /* fall through */
5805 case ':':
5806 /*
5807 * sort of a hack - expand tildes in variable
5808 * assignments (after the first '=' and after ':'s).
5809 */
5810 if (*--p == '~') {
5811 goto tilde;
5812 }
5813 continue;
5814 }
5815
5816 switch (c) {
5817 case CTLENDVAR: /* ??? */
5818 goto breakloop;
5819 case CTLQUOTEMARK:
5820 /* "$@" syntax adherence hack */
5821 if (
5822 !inquotes &&
5823 !memcmp(p, dolatstr, 4) &&
5824 (p[4] == CTLQUOTEMARK || (
5825 p[4] == CTLENDVAR &&
5826 p[5] == CTLQUOTEMARK
5827 ))
5828 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005829 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830 goto start;
5831 }
5832 inquotes = !inquotes;
5833 addquote:
5834 if (quotes) {
5835 p--;
5836 length++;
5837 startloc++;
5838 }
5839 break;
5840 case CTLESC:
5841 startloc++;
5842 length++;
5843 goto addquote;
5844 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005845 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005846 goto start;
5847 case CTLBACKQ:
5848 c = 0;
5849 case CTLBACKQ|CTLQUOTE:
5850 expbackq(argbackq->n, c, quotes);
5851 argbackq = argbackq->next;
5852 goto start;
5853#if ENABLE_ASH_MATH_SUPPORT
5854 case CTLENDARI:
5855 p--;
5856 expari(quotes);
5857 goto start;
5858#endif
5859 }
5860 }
5861 breakloop:
5862 ;
5863}
5864
5865static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005866scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867 int zero)
5868{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005869// This commented out code was added by James Simmons <jsimmons@infradead.org>
5870// as part of a larger change when he added support for ${var/a/b}.
5871// However, it broke # and % operators:
5872//
5873//var=ababcdcd
5874// ok bad
5875//echo ${var#ab} abcdcd abcdcd
5876//echo ${var##ab} abcdcd abcdcd
5877//echo ${var#a*b} abcdcd ababcdcd (!)
5878//echo ${var##a*b} cdcd cdcd
5879//echo ${var#?} babcdcd ababcdcd (!)
5880//echo ${var##?} babcdcd babcdcd
5881//echo ${var#*} ababcdcd babcdcd (!)
5882//echo ${var##*}
5883//echo ${var%cd} ababcd ababcd
5884//echo ${var%%cd} ababcd abab (!)
5885//echo ${var%c*d} ababcd ababcd
5886//echo ${var%%c*d} abab ababcdcd (!)
5887//echo ${var%?} ababcdc ababcdc
5888//echo ${var%%?} ababcdc ababcdcd (!)
5889//echo ${var%*} ababcdcd ababcdcd
5890//echo ${var%%*}
5891//
5892// Commenting it back out helped. Remove it completely if it really
5893// is not needed.
5894
5895 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005896 char c;
5897
5898 loc = startp;
5899 loc2 = rmesc;
5900 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005901 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005902 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005903
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005904 c = *loc2;
5905 if (zero) {
5906 *loc2 = '\0';
5907 s = rmesc;
5908 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005909 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005910
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005911// // chop off end if its '*'
5912// full = strrchr(str, '*');
5913// if (full && full != str)
5914// match--;
5915//
5916// // If str starts with '*' replace with s.
5917// if ((*str == '*') && strlen(s) >= match) {
5918// full = xstrdup(s);
5919// strncpy(full+strlen(s)-match+1, str+1, match-1);
5920// } else
5921// full = xstrndup(str, match);
5922// match = strncmp(s, full, strlen(full));
5923// free(full);
5924//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005925 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005926 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005927 return loc;
5928 if (quotes && *loc == CTLESC)
5929 loc++;
5930 loc++;
5931 loc2++;
5932 } while (c);
5933 return 0;
5934}
5935
5936static char *
5937scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5938 int zero)
5939{
5940 int esc = 0;
5941 char *loc;
5942 char *loc2;
5943
5944 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5945 int match;
5946 char c = *loc2;
5947 const char *s = loc2;
5948 if (zero) {
5949 *loc2 = '\0';
5950 s = rmesc;
5951 }
5952 match = pmatch(str, s);
5953 *loc2 = c;
5954 if (match)
5955 return loc;
5956 loc--;
5957 if (quotes) {
5958 if (--esc < 0) {
5959 esc = esclen(startp, loc);
5960 }
5961 if (esc % 2) {
5962 esc--;
5963 loc--;
5964 }
5965 }
5966 }
5967 return 0;
5968}
5969
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005970static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971static void
5972varunset(const char *end, const char *var, const char *umsg, int varflags)
5973{
5974 const char *msg;
5975 const char *tail;
5976
5977 tail = nullstr;
5978 msg = "parameter not set";
5979 if (umsg) {
5980 if (*end == CTLENDVAR) {
5981 if (varflags & VSNUL)
5982 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005983 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005984 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005985 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986 }
5987 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5988}
5989
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005990#if ENABLE_ASH_BASH_COMPAT
5991static char *
5992parse_sub_pattern(char *arg, int inquotes)
5993{
5994 char *idx, *repl = NULL;
5995 unsigned char c;
5996
Denis Vlasenko2659c632008-06-14 06:04:59 +00005997 idx = arg;
5998 while (1) {
5999 c = *arg;
6000 if (!c)
6001 break;
6002 if (c == '/') {
6003 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006004 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006005 repl = idx + 1;
6006 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006007 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006008 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006009 *idx++ = c;
6010 if (!inquotes && c == '\\' && arg[1] == '\\')
6011 arg++; /* skip both \\, not just first one */
6012 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006013 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006014 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006015
6016 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006017}
6018#endif /* ENABLE_ASH_BASH_COMPAT */
6019
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006021subevalvar(char *p, char *str, int strloc, int subtype,
6022 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006024 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025 char *startp;
6026 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006028 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6029 USE_ASH_BASH_COMPAT(char null = '\0';)
6030 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6031 int saveherefd = herefd;
6032 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006033 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006034 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006035
6036 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006037 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6038 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 STPUTC('\0', expdest);
6040 herefd = saveherefd;
6041 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006042 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043
6044 switch (subtype) {
6045 case VSASSIGN:
6046 setvar(str, startp, 0);
6047 amount = startp - expdest;
6048 STADJUST(amount, expdest);
6049 return startp;
6050
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006051#if ENABLE_ASH_BASH_COMPAT
6052 case VSSUBSTR:
6053 loc = str = stackblock() + strloc;
6054// TODO: number() instead? It does error checking...
6055 pos = atoi(loc);
6056 len = str - startp - 1;
6057
6058 /* *loc != '\0', guaranteed by parser */
6059 if (quotes) {
6060 char *ptr;
6061
6062 /* We must adjust the length by the number of escapes we find. */
6063 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006064 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006065 len--;
6066 ptr++;
6067 }
6068 }
6069 }
6070 orig_len = len;
6071
6072 if (*loc++ == ':') {
6073// TODO: number() instead? It does error checking...
6074 len = atoi(loc);
6075 } else {
6076 len = orig_len;
6077 while (*loc && *loc != ':')
6078 loc++;
6079 if (*loc++ == ':')
6080// TODO: number() instead? It does error checking...
6081 len = atoi(loc);
6082 }
6083 if (pos >= orig_len) {
6084 pos = 0;
6085 len = 0;
6086 }
6087 if (len > (orig_len - pos))
6088 len = orig_len - pos;
6089
6090 for (str = startp; pos; str++, pos--) {
6091 if (quotes && *str == CTLESC)
6092 str++;
6093 }
6094 for (loc = startp; len; len--) {
6095 if (quotes && *str == CTLESC)
6096 *loc++ = *str++;
6097 *loc++ = *str++;
6098 }
6099 *loc = '\0';
6100 amount = loc - expdest;
6101 STADJUST(amount, expdest);
6102 return loc;
6103#endif
6104
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006105 case VSQUESTION:
6106 varunset(p, str, startp, varflags);
6107 /* NOTREACHED */
6108 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006109 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006110
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006111 /* We'll comeback here if we grow the stack while handling
6112 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6113 * stack will need rebasing, and we'll need to remove our work
6114 * areas each time
6115 */
6116 USE_ASH_BASH_COMPAT(restart:)
6117
6118 amount = expdest - ((char *)stackblock() + resetloc);
6119 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006120 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006121
6122 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006123 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124 if (quotes) {
6125 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6126 if (rmesc != startp) {
6127 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006128 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006129 }
6130 }
6131 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006132 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006133 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006134 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006136#if ENABLE_ASH_BASH_COMPAT
6137 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6138 char *idx, *end, *restart_detect;
6139
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006140 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006141 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6142 if (!repl)
6143 repl = &null;
6144 }
6145
6146 /* If there's no pattern to match, return the expansion unmolested */
6147 if (*str == '\0')
6148 return 0;
6149
6150 len = 0;
6151 idx = startp;
6152 end = str - 1;
6153 while (idx < end) {
6154 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6155 if (!loc) {
6156 /* No match, advance */
6157 restart_detect = stackblock();
6158 STPUTC(*idx, expdest);
6159 if (quotes && *idx == CTLESC) {
6160 idx++;
6161 len++;
6162 STPUTC(*idx, expdest);
6163 }
6164 if (stackblock() != restart_detect)
6165 goto restart;
6166 idx++;
6167 len++;
6168 rmesc++;
6169 continue;
6170 }
6171
6172 if (subtype == VSREPLACEALL) {
6173 while (idx < loc) {
6174 if (quotes && *idx == CTLESC)
6175 idx++;
6176 idx++;
6177 rmesc++;
6178 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006179 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006180 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006181 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006182
6183 for (loc = repl; *loc; loc++) {
6184 restart_detect = stackblock();
6185 STPUTC(*loc, expdest);
6186 if (stackblock() != restart_detect)
6187 goto restart;
6188 len++;
6189 }
6190
6191 if (subtype == VSREPLACE) {
6192 while (*idx) {
6193 restart_detect = stackblock();
6194 STPUTC(*idx, expdest);
6195 if (stackblock() != restart_detect)
6196 goto restart;
6197 len++;
6198 idx++;
6199 }
6200 break;
6201 }
6202 }
6203
6204 /* We've put the replaced text into a buffer at workloc, now
6205 * move it to the right place and adjust the stack.
6206 */
6207 startp = stackblock() + startloc;
6208 STPUTC('\0', expdest);
6209 memmove(startp, stackblock() + workloc, len);
6210 startp[len++] = '\0';
6211 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6212 STADJUST(-amount, expdest);
6213 return startp;
6214 }
6215#endif /* ENABLE_ASH_BASH_COMPAT */
6216
6217 subtype -= VSTRIMRIGHT;
6218#if DEBUG
6219 if (subtype < 0 || subtype > 7)
6220 abort();
6221#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6223 zero = subtype >> 1;
6224 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6225 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6226
6227 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6228 if (loc) {
6229 if (zero) {
6230 memmove(startp, loc, str - loc);
6231 loc = startp + (str - loc) - 1;
6232 }
6233 *loc = '\0';
6234 amount = loc - expdest;
6235 STADJUST(amount, expdest);
6236 }
6237 return loc;
6238}
6239
6240/*
6241 * Add the value of a specialized variable to the stack string.
6242 */
6243static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006244varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006245{
6246 int num;
6247 char *p;
6248 int i;
6249 int sep = 0;
6250 int sepq = 0;
6251 ssize_t len = 0;
6252 char **ap;
6253 int syntax;
6254 int quoted = varflags & VSQUOTE;
6255 int subtype = varflags & VSTYPE;
6256 int quotes = flags & (EXP_FULL | EXP_CASE);
6257
6258 if (quoted && (flags & EXP_FULL))
6259 sep = 1 << CHAR_BIT;
6260
6261 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6262 switch (*name) {
6263 case '$':
6264 num = rootpid;
6265 goto numvar;
6266 case '?':
6267 num = exitstatus;
6268 goto numvar;
6269 case '#':
6270 num = shellparam.nparam;
6271 goto numvar;
6272 case '!':
6273 num = backgndpid;
6274 if (num == 0)
6275 return -1;
6276 numvar:
6277 len = cvtnum(num);
6278 break;
6279 case '-':
6280 p = makestrspace(NOPTS, expdest);
6281 for (i = NOPTS - 1; i >= 0; i--) {
6282 if (optlist[i]) {
6283 USTPUTC(optletters(i), p);
6284 len++;
6285 }
6286 }
6287 expdest = p;
6288 break;
6289 case '@':
6290 if (sep)
6291 goto param;
6292 /* fall through */
6293 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006294 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006295 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6296 sepq = 1;
6297 param:
6298 ap = shellparam.p;
6299 if (!ap)
6300 return -1;
6301 while ((p = *ap++)) {
6302 size_t partlen;
6303
6304 partlen = strlen(p);
6305 len += partlen;
6306
6307 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6308 memtodest(p, partlen, syntax, quotes);
6309
6310 if (*ap && sep) {
6311 char *q;
6312
6313 len++;
6314 if (subtype == VSPLUS || subtype == VSLENGTH) {
6315 continue;
6316 }
6317 q = expdest;
6318 if (sepq)
6319 STPUTC(CTLESC, q);
6320 STPUTC(sep, q);
6321 expdest = q;
6322 }
6323 }
6324 return len;
6325 case '0':
6326 case '1':
6327 case '2':
6328 case '3':
6329 case '4':
6330 case '5':
6331 case '6':
6332 case '7':
6333 case '8':
6334 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006335// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 num = atoi(name);
6337 if (num < 0 || num > shellparam.nparam)
6338 return -1;
6339 p = num ? shellparam.p[num - 1] : arg0;
6340 goto value;
6341 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006342 /* NB: name has form "VAR=..." */
6343
6344 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6345 * which should be considered before we check variables. */
6346 if (var_str_list) {
6347 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6348 p = NULL;
6349 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006350 char *str, *eq;
6351 str = var_str_list->text;
6352 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006353 if (!eq) /* stop at first non-assignment */
6354 break;
6355 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006356 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006357 && strncmp(str, name, name_len) == 0) {
6358 p = eq;
6359 /* goto value; - WRONG! */
6360 /* think "A=1 A=2 B=$A" */
6361 }
6362 var_str_list = var_str_list->next;
6363 } while (var_str_list);
6364 if (p)
6365 goto value;
6366 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006367 p = lookupvar(name);
6368 value:
6369 if (!p)
6370 return -1;
6371
6372 len = strlen(p);
6373 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6374 memtodest(p, len, syntax, quotes);
6375 return len;
6376 }
6377
6378 if (subtype == VSPLUS || subtype == VSLENGTH)
6379 STADJUST(-len, expdest);
6380 return len;
6381}
6382
6383/*
6384 * Expand a variable, and return a pointer to the next character in the
6385 * input string.
6386 */
6387static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006388evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006389{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006390 char varflags;
6391 char subtype;
6392 char quoted;
6393 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006394 char *var;
6395 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006396 int startloc;
6397 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006398
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399 varflags = *p++;
6400 subtype = varflags & VSTYPE;
6401 quoted = varflags & VSQUOTE;
6402 var = p;
6403 easy = (!quoted || (*var == '@' && shellparam.nparam));
6404 startloc = expdest - (char *)stackblock();
6405 p = strchr(p, '=') + 1;
6406
6407 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006408 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006409 if (varflags & VSNUL)
6410 varlen--;
6411
6412 if (subtype == VSPLUS) {
6413 varlen = -1 - varlen;
6414 goto vsplus;
6415 }
6416
6417 if (subtype == VSMINUS) {
6418 vsplus:
6419 if (varlen < 0) {
6420 argstr(
6421 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006422 (quoted ? EXP_QWORD : EXP_WORD),
6423 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424 );
6425 goto end;
6426 }
6427 if (easy)
6428 goto record;
6429 goto end;
6430 }
6431
6432 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6433 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006434 if (subevalvar(p, var, /* strloc: */ 0,
6435 subtype, startloc, varflags,
6436 /* quotes: */ 0,
6437 var_str_list)
6438 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006439 varflags &= ~VSNUL;
6440 /*
6441 * Remove any recorded regions beyond
6442 * start of variable
6443 */
6444 removerecordregions(startloc);
6445 goto again;
6446 }
6447 goto end;
6448 }
6449 if (easy)
6450 goto record;
6451 goto end;
6452 }
6453
6454 if (varlen < 0 && uflag)
6455 varunset(p, var, 0, 0);
6456
6457 if (subtype == VSLENGTH) {
6458 cvtnum(varlen > 0 ? varlen : 0);
6459 goto record;
6460 }
6461
6462 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006463 if (easy)
6464 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006465 goto end;
6466 }
6467
6468#if DEBUG
6469 switch (subtype) {
6470 case VSTRIMLEFT:
6471 case VSTRIMLEFTMAX:
6472 case VSTRIMRIGHT:
6473 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006474#if ENABLE_ASH_BASH_COMPAT
6475 case VSSUBSTR:
6476 case VSREPLACE:
6477 case VSREPLACEALL:
6478#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006479 break;
6480 default:
6481 abort();
6482 }
6483#endif
6484
6485 if (varlen >= 0) {
6486 /*
6487 * Terminate the string and start recording the pattern
6488 * right after it
6489 */
6490 STPUTC('\0', expdest);
6491 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006492 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6493 startloc, varflags,
6494 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6495 var_str_list)
6496 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006497 int amount = expdest - (
6498 (char *)stackblock() + patloc - 1
6499 );
6500 STADJUST(-amount, expdest);
6501 }
6502 /* Remove any recorded regions beyond start of variable */
6503 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006504 record:
6505 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006506 }
6507
6508 end:
6509 if (subtype != VSNORMAL) { /* skip to end of alternative */
6510 int nesting = 1;
6511 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006512 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006513 if (c == CTLESC)
6514 p++;
6515 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6516 if (varlen >= 0)
6517 argbackq = argbackq->next;
6518 } else if (c == CTLVAR) {
6519 if ((*p++ & VSTYPE) != VSNORMAL)
6520 nesting++;
6521 } else if (c == CTLENDVAR) {
6522 if (--nesting == 0)
6523 break;
6524 }
6525 }
6526 }
6527 return p;
6528}
6529
6530/*
6531 * Break the argument string into pieces based upon IFS and add the
6532 * strings to the argument list. The regions of the string to be
6533 * searched for IFS characters have been stored by recordregion.
6534 */
6535static void
6536ifsbreakup(char *string, struct arglist *arglist)
6537{
6538 struct ifsregion *ifsp;
6539 struct strlist *sp;
6540 char *start;
6541 char *p;
6542 char *q;
6543 const char *ifs, *realifs;
6544 int ifsspc;
6545 int nulonly;
6546
6547 start = string;
6548 if (ifslastp != NULL) {
6549 ifsspc = 0;
6550 nulonly = 0;
6551 realifs = ifsset() ? ifsval() : defifs;
6552 ifsp = &ifsfirst;
6553 do {
6554 p = string + ifsp->begoff;
6555 nulonly = ifsp->nulonly;
6556 ifs = nulonly ? nullstr : realifs;
6557 ifsspc = 0;
6558 while (p < string + ifsp->endoff) {
6559 q = p;
6560 if (*p == CTLESC)
6561 p++;
6562 if (!strchr(ifs, *p)) {
6563 p++;
6564 continue;
6565 }
6566 if (!nulonly)
6567 ifsspc = (strchr(defifs, *p) != NULL);
6568 /* Ignore IFS whitespace at start */
6569 if (q == start && ifsspc) {
6570 p++;
6571 start = p;
6572 continue;
6573 }
6574 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006575 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006576 sp->text = start;
6577 *arglist->lastp = sp;
6578 arglist->lastp = &sp->next;
6579 p++;
6580 if (!nulonly) {
6581 for (;;) {
6582 if (p >= string + ifsp->endoff) {
6583 break;
6584 }
6585 q = p;
6586 if (*p == CTLESC)
6587 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006588 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589 p = q;
6590 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006591 }
6592 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006593 if (ifsspc) {
6594 p++;
6595 ifsspc = 0;
6596 } else {
6597 p = q;
6598 break;
6599 }
6600 } else
6601 p++;
6602 }
6603 }
6604 start = p;
6605 } /* while */
6606 ifsp = ifsp->next;
6607 } while (ifsp != NULL);
6608 if (nulonly)
6609 goto add;
6610 }
6611
6612 if (!*start)
6613 return;
6614
6615 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006616 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006617 sp->text = start;
6618 *arglist->lastp = sp;
6619 arglist->lastp = &sp->next;
6620}
6621
6622static void
6623ifsfree(void)
6624{
6625 struct ifsregion *p;
6626
6627 INT_OFF;
6628 p = ifsfirst.next;
6629 do {
6630 struct ifsregion *ifsp;
6631 ifsp = p->next;
6632 free(p);
6633 p = ifsp;
6634 } while (p);
6635 ifslastp = NULL;
6636 ifsfirst.next = NULL;
6637 INT_ON;
6638}
6639
6640/*
6641 * Add a file name to the list.
6642 */
6643static void
6644addfname(const char *name)
6645{
6646 struct strlist *sp;
6647
Denis Vlasenko597906c2008-02-20 16:38:54 +00006648 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006649 sp->text = ststrdup(name);
6650 *exparg.lastp = sp;
6651 exparg.lastp = &sp->next;
6652}
6653
6654static char *expdir;
6655
6656/*
6657 * Do metacharacter (i.e. *, ?, [...]) expansion.
6658 */
6659static void
6660expmeta(char *enddir, char *name)
6661{
6662 char *p;
6663 const char *cp;
6664 char *start;
6665 char *endname;
6666 int metaflag;
6667 struct stat statb;
6668 DIR *dirp;
6669 struct dirent *dp;
6670 int atend;
6671 int matchdot;
6672
6673 metaflag = 0;
6674 start = name;
6675 for (p = name; *p; p++) {
6676 if (*p == '*' || *p == '?')
6677 metaflag = 1;
6678 else if (*p == '[') {
6679 char *q = p + 1;
6680 if (*q == '!')
6681 q++;
6682 for (;;) {
6683 if (*q == '\\')
6684 q++;
6685 if (*q == '/' || *q == '\0')
6686 break;
6687 if (*++q == ']') {
6688 metaflag = 1;
6689 break;
6690 }
6691 }
6692 } else if (*p == '\\')
6693 p++;
6694 else if (*p == '/') {
6695 if (metaflag)
6696 goto out;
6697 start = p + 1;
6698 }
6699 }
6700 out:
6701 if (metaflag == 0) { /* we've reached the end of the file name */
6702 if (enddir != expdir)
6703 metaflag++;
6704 p = name;
6705 do {
6706 if (*p == '\\')
6707 p++;
6708 *enddir++ = *p;
6709 } while (*p++);
6710 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6711 addfname(expdir);
6712 return;
6713 }
6714 endname = p;
6715 if (name < start) {
6716 p = name;
6717 do {
6718 if (*p == '\\')
6719 p++;
6720 *enddir++ = *p++;
6721 } while (p < start);
6722 }
6723 if (enddir == expdir) {
6724 cp = ".";
6725 } else if (enddir == expdir + 1 && *expdir == '/') {
6726 cp = "/";
6727 } else {
6728 cp = expdir;
6729 enddir[-1] = '\0';
6730 }
6731 dirp = opendir(cp);
6732 if (dirp == NULL)
6733 return;
6734 if (enddir != expdir)
6735 enddir[-1] = '/';
6736 if (*endname == 0) {
6737 atend = 1;
6738 } else {
6739 atend = 0;
6740 *endname++ = '\0';
6741 }
6742 matchdot = 0;
6743 p = start;
6744 if (*p == '\\')
6745 p++;
6746 if (*p == '.')
6747 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006748 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006749 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006750 continue;
6751 if (pmatch(start, dp->d_name)) {
6752 if (atend) {
6753 strcpy(enddir, dp->d_name);
6754 addfname(expdir);
6755 } else {
6756 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6757 continue;
6758 p[-1] = '/';
6759 expmeta(p, endname);
6760 }
6761 }
6762 }
6763 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006764 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006765 endname[-1] = '/';
6766}
6767
6768static struct strlist *
6769msort(struct strlist *list, int len)
6770{
6771 struct strlist *p, *q = NULL;
6772 struct strlist **lpp;
6773 int half;
6774 int n;
6775
6776 if (len <= 1)
6777 return list;
6778 half = len >> 1;
6779 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006780 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006781 q = p;
6782 p = p->next;
6783 }
6784 q->next = NULL; /* terminate first half of list */
6785 q = msort(list, half); /* sort first half of list */
6786 p = msort(p, len - half); /* sort second half */
6787 lpp = &list;
6788 for (;;) {
6789#if ENABLE_LOCALE_SUPPORT
6790 if (strcoll(p->text, q->text) < 0)
6791#else
6792 if (strcmp(p->text, q->text) < 0)
6793#endif
6794 {
6795 *lpp = p;
6796 lpp = &p->next;
6797 p = *lpp;
6798 if (p == NULL) {
6799 *lpp = q;
6800 break;
6801 }
6802 } else {
6803 *lpp = q;
6804 lpp = &q->next;
6805 q = *lpp;
6806 if (q == NULL) {
6807 *lpp = p;
6808 break;
6809 }
6810 }
6811 }
6812 return list;
6813}
6814
6815/*
6816 * Sort the results of file name expansion. It calculates the number of
6817 * strings to sort and then calls msort (short for merge sort) to do the
6818 * work.
6819 */
6820static struct strlist *
6821expsort(struct strlist *str)
6822{
6823 int len;
6824 struct strlist *sp;
6825
6826 len = 0;
6827 for (sp = str; sp; sp = sp->next)
6828 len++;
6829 return msort(str, len);
6830}
6831
6832static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006833expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006834{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006835 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006836 '*', '?', '[', 0
6837 };
6838 /* TODO - EXP_REDIR */
6839
6840 while (str) {
6841 struct strlist **savelastp;
6842 struct strlist *sp;
6843 char *p;
6844
6845 if (fflag)
6846 goto nometa;
6847 if (!strpbrk(str->text, metachars))
6848 goto nometa;
6849 savelastp = exparg.lastp;
6850
6851 INT_OFF;
6852 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6853 {
6854 int i = strlen(str->text);
6855 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6856 }
6857
6858 expmeta(expdir, p);
6859 free(expdir);
6860 if (p != str->text)
6861 free(p);
6862 INT_ON;
6863 if (exparg.lastp == savelastp) {
6864 /*
6865 * no matches
6866 */
6867 nometa:
6868 *exparg.lastp = str;
6869 rmescapes(str->text);
6870 exparg.lastp = &str->next;
6871 } else {
6872 *exparg.lastp = NULL;
6873 *savelastp = sp = expsort(*savelastp);
6874 while (sp->next != NULL)
6875 sp = sp->next;
6876 exparg.lastp = &sp->next;
6877 }
6878 str = str->next;
6879 }
6880}
6881
6882/*
6883 * Perform variable substitution and command substitution on an argument,
6884 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6885 * perform splitting and file name expansion. When arglist is NULL, perform
6886 * here document expansion.
6887 */
6888static void
6889expandarg(union node *arg, struct arglist *arglist, int flag)
6890{
6891 struct strlist *sp;
6892 char *p;
6893
6894 argbackq = arg->narg.backquote;
6895 STARTSTACKSTR(expdest);
6896 ifsfirst.next = NULL;
6897 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006898 argstr(arg->narg.text, flag,
6899 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006900 p = _STPUTC('\0', expdest);
6901 expdest = p - 1;
6902 if (arglist == NULL) {
6903 return; /* here document expanded */
6904 }
6905 p = grabstackstr(p);
6906 exparg.lastp = &exparg.list;
6907 /*
6908 * TODO - EXP_REDIR
6909 */
6910 if (flag & EXP_FULL) {
6911 ifsbreakup(p, &exparg);
6912 *exparg.lastp = NULL;
6913 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006914 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006915 } else {
6916 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6917 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006918 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006919 sp->text = p;
6920 *exparg.lastp = sp;
6921 exparg.lastp = &sp->next;
6922 }
6923 if (ifsfirst.next)
6924 ifsfree();
6925 *exparg.lastp = NULL;
6926 if (exparg.list) {
6927 *arglist->lastp = exparg.list;
6928 arglist->lastp = exparg.lastp;
6929 }
6930}
6931
6932/*
6933 * Expand shell variables and backquotes inside a here document.
6934 */
6935static void
6936expandhere(union node *arg, int fd)
6937{
6938 herefd = fd;
6939 expandarg(arg, (struct arglist *)NULL, 0);
6940 full_write(fd, stackblock(), expdest - (char *)stackblock());
6941}
6942
6943/*
6944 * Returns true if the pattern matches the string.
6945 */
6946static int
6947patmatch(char *pattern, const char *string)
6948{
6949 return pmatch(preglob(pattern, 0, 0), string);
6950}
6951
6952/*
6953 * See if a pattern matches in a case statement.
6954 */
6955static int
6956casematch(union node *pattern, char *val)
6957{
6958 struct stackmark smark;
6959 int result;
6960
6961 setstackmark(&smark);
6962 argbackq = pattern->narg.backquote;
6963 STARTSTACKSTR(expdest);
6964 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006965 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6966 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006967 STACKSTRNUL(expdest);
6968 result = patmatch(stackblock(), val);
6969 popstackmark(&smark);
6970 return result;
6971}
6972
6973
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006974/* ============ find_command */
6975
6976struct builtincmd {
6977 const char *name;
6978 int (*builtin)(int, char **);
6979 /* unsigned flags; */
6980};
6981#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006982/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006983 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006984#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006985#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006986
6987struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006988 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006989 union param {
6990 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006991 /* index >= 0 for commands without path (slashes) */
6992 /* (TODO: what exactly does the value mean? PATH position?) */
6993 /* index == -1 for commands with slashes */
6994 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006995 const struct builtincmd *cmd;
6996 struct funcnode *func;
6997 } u;
6998};
6999/* values of cmdtype */
7000#define CMDUNKNOWN -1 /* no entry in table for command */
7001#define CMDNORMAL 0 /* command is an executable program */
7002#define CMDFUNCTION 1 /* command is a shell function */
7003#define CMDBUILTIN 2 /* command is a shell builtin */
7004
7005/* action to find_command() */
7006#define DO_ERR 0x01 /* prints errors */
7007#define DO_ABS 0x02 /* checks absolute paths */
7008#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7009#define DO_ALTPATH 0x08 /* using alternate path */
7010#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7011
7012static void find_command(char *, struct cmdentry *, int, const char *);
7013
7014
7015/* ============ Hashing commands */
7016
7017/*
7018 * When commands are first encountered, they are entered in a hash table.
7019 * This ensures that a full path search will not have to be done for them
7020 * on each invocation.
7021 *
7022 * We should investigate converting to a linear search, even though that
7023 * would make the command name "hash" a misnomer.
7024 */
7025
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007026struct tblentry {
7027 struct tblentry *next; /* next entry in hash chain */
7028 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007029 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007030 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007031 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007032};
7033
Denis Vlasenko01631112007-12-16 17:20:38 +00007034static struct tblentry **cmdtable;
7035#define INIT_G_cmdtable() do { \
7036 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7037} while (0)
7038
7039static int builtinloc = -1; /* index in path of %builtin, or -1 */
7040
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007041
7042static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007043tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007044{
7045 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007046
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007047#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007048 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007049 if (APPLET_IS_NOEXEC(applet_no)) {
7050 while (*envp)
7051 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007052 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007053 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007054 /* re-exec ourselves with the new arguments */
7055 execve(bb_busybox_exec_path, argv, envp);
7056 /* If they called chroot or otherwise made the binary no longer
7057 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007058 }
7059#endif
7060
7061 repeat:
7062#ifdef SYSV
7063 do {
7064 execve(cmd, argv, envp);
7065 } while (errno == EINTR);
7066#else
7067 execve(cmd, argv, envp);
7068#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007069 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007070 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007071 return;
7072 }
7073 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007074 char **ap;
7075 char **new;
7076
7077 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007078 continue;
7079 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007080 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007081 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007082 ap += 2;
7083 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007084 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007085 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007086 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007087 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007088 goto repeat;
7089 }
7090}
7091
7092/*
7093 * Exec a program. Never returns. If you change this routine, you may
7094 * have to change the find_command routine as well.
7095 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007096static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007097static void
7098shellexec(char **argv, const char *path, int idx)
7099{
7100 char *cmdname;
7101 int e;
7102 char **envp;
7103 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007104#if ENABLE_FEATURE_SH_STANDALONE
7105 int applet_no = -1;
7106#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007107
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007108 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007109 envp = listvars(VEXPORT, VUNSET, 0);
7110 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007111#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007112 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007113#endif
7114 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007115 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007116 e = errno;
7117 } else {
7118 e = ENOENT;
7119 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7120 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007121 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007122 if (errno != ENOENT && errno != ENOTDIR)
7123 e = errno;
7124 }
7125 stunalloc(cmdname);
7126 }
7127 }
7128
7129 /* Map to POSIX errors */
7130 switch (e) {
7131 case EACCES:
7132 exerrno = 126;
7133 break;
7134 case ENOENT:
7135 exerrno = 127;
7136 break;
7137 default:
7138 exerrno = 2;
7139 break;
7140 }
7141 exitstatus = exerrno;
7142 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007143 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007144 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7145 /* NOTREACHED */
7146}
7147
7148static void
7149printentry(struct tblentry *cmdp)
7150{
7151 int idx;
7152 const char *path;
7153 char *name;
7154
7155 idx = cmdp->param.index;
7156 path = pathval();
7157 do {
7158 name = padvance(&path, cmdp->cmdname);
7159 stunalloc(name);
7160 } while (--idx >= 0);
7161 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7162}
7163
7164/*
7165 * Clear out command entries. The argument specifies the first entry in
7166 * PATH which has changed.
7167 */
7168static void
7169clearcmdentry(int firstchange)
7170{
7171 struct tblentry **tblp;
7172 struct tblentry **pp;
7173 struct tblentry *cmdp;
7174
7175 INT_OFF;
7176 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7177 pp = tblp;
7178 while ((cmdp = *pp) != NULL) {
7179 if ((cmdp->cmdtype == CMDNORMAL &&
7180 cmdp->param.index >= firstchange)
7181 || (cmdp->cmdtype == CMDBUILTIN &&
7182 builtinloc >= firstchange)
7183 ) {
7184 *pp = cmdp->next;
7185 free(cmdp);
7186 } else {
7187 pp = &cmdp->next;
7188 }
7189 }
7190 }
7191 INT_ON;
7192}
7193
7194/*
7195 * Locate a command in the command hash table. If "add" is nonzero,
7196 * add the command to the table if it is not already present. The
7197 * variable "lastcmdentry" is set to point to the address of the link
7198 * pointing to the entry, so that delete_cmd_entry can delete the
7199 * entry.
7200 *
7201 * Interrupts must be off if called with add != 0.
7202 */
7203static struct tblentry **lastcmdentry;
7204
7205static struct tblentry *
7206cmdlookup(const char *name, int add)
7207{
7208 unsigned int hashval;
7209 const char *p;
7210 struct tblentry *cmdp;
7211 struct tblentry **pp;
7212
7213 p = name;
7214 hashval = (unsigned char)*p << 4;
7215 while (*p)
7216 hashval += (unsigned char)*p++;
7217 hashval &= 0x7FFF;
7218 pp = &cmdtable[hashval % CMDTABLESIZE];
7219 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7220 if (strcmp(cmdp->cmdname, name) == 0)
7221 break;
7222 pp = &cmdp->next;
7223 }
7224 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007225 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7226 + strlen(name)
7227 /* + 1 - already done because
7228 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007229 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007230 cmdp->cmdtype = CMDUNKNOWN;
7231 strcpy(cmdp->cmdname, name);
7232 }
7233 lastcmdentry = pp;
7234 return cmdp;
7235}
7236
7237/*
7238 * Delete the command entry returned on the last lookup.
7239 */
7240static void
7241delete_cmd_entry(void)
7242{
7243 struct tblentry *cmdp;
7244
7245 INT_OFF;
7246 cmdp = *lastcmdentry;
7247 *lastcmdentry = cmdp->next;
7248 if (cmdp->cmdtype == CMDFUNCTION)
7249 freefunc(cmdp->param.func);
7250 free(cmdp);
7251 INT_ON;
7252}
7253
7254/*
7255 * Add a new command entry, replacing any existing command entry for
7256 * the same name - except special builtins.
7257 */
7258static void
7259addcmdentry(char *name, struct cmdentry *entry)
7260{
7261 struct tblentry *cmdp;
7262
7263 cmdp = cmdlookup(name, 1);
7264 if (cmdp->cmdtype == CMDFUNCTION) {
7265 freefunc(cmdp->param.func);
7266 }
7267 cmdp->cmdtype = entry->cmdtype;
7268 cmdp->param = entry->u;
7269 cmdp->rehash = 0;
7270}
7271
7272static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007273hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007274{
7275 struct tblentry **pp;
7276 struct tblentry *cmdp;
7277 int c;
7278 struct cmdentry entry;
7279 char *name;
7280
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007281 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007282 clearcmdentry(0);
7283 return 0;
7284 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007285
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007286 if (*argptr == NULL) {
7287 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7288 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7289 if (cmdp->cmdtype == CMDNORMAL)
7290 printentry(cmdp);
7291 }
7292 }
7293 return 0;
7294 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007295
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007296 c = 0;
7297 while ((name = *argptr) != NULL) {
7298 cmdp = cmdlookup(name, 0);
7299 if (cmdp != NULL
7300 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007301 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7302 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007303 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007304 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305 find_command(name, &entry, DO_ERR, pathval());
7306 if (entry.cmdtype == CMDUNKNOWN)
7307 c = 1;
7308 argptr++;
7309 }
7310 return c;
7311}
7312
7313/*
7314 * Called when a cd is done. Marks all commands so the next time they
7315 * are executed they will be rehashed.
7316 */
7317static void
7318hashcd(void)
7319{
7320 struct tblentry **pp;
7321 struct tblentry *cmdp;
7322
7323 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7324 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007325 if (cmdp->cmdtype == CMDNORMAL
7326 || (cmdp->cmdtype == CMDBUILTIN
7327 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7328 && builtinloc > 0)
7329 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007330 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007331 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007332 }
7333 }
7334}
7335
7336/*
7337 * Fix command hash table when PATH changed.
7338 * Called before PATH is changed. The argument is the new value of PATH;
7339 * pathval() still returns the old value at this point.
7340 * Called with interrupts off.
7341 */
7342static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007343changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007344{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007345 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007346 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007347 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007348 int idx_bltin;
7349
7350 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007351 firstchange = 9999; /* assume no change */
7352 idx = 0;
7353 idx_bltin = -1;
7354 for (;;) {
7355 if (*old != *new) {
7356 firstchange = idx;
7357 if ((*old == '\0' && *new == ':')
7358 || (*old == ':' && *new == '\0'))
7359 firstchange++;
7360 old = new; /* ignore subsequent differences */
7361 }
7362 if (*new == '\0')
7363 break;
7364 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7365 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007366 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007367 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007368 new++, old++;
7369 }
7370 if (builtinloc < 0 && idx_bltin >= 0)
7371 builtinloc = idx_bltin; /* zap builtins */
7372 if (builtinloc >= 0 && idx_bltin < 0)
7373 firstchange = 0;
7374 clearcmdentry(firstchange);
7375 builtinloc = idx_bltin;
7376}
7377
7378#define TEOF 0
7379#define TNL 1
7380#define TREDIR 2
7381#define TWORD 3
7382#define TSEMI 4
7383#define TBACKGND 5
7384#define TAND 6
7385#define TOR 7
7386#define TPIPE 8
7387#define TLP 9
7388#define TRP 10
7389#define TENDCASE 11
7390#define TENDBQUOTE 12
7391#define TNOT 13
7392#define TCASE 14
7393#define TDO 15
7394#define TDONE 16
7395#define TELIF 17
7396#define TELSE 18
7397#define TESAC 19
7398#define TFI 20
7399#define TFOR 21
7400#define TIF 22
7401#define TIN 23
7402#define TTHEN 24
7403#define TUNTIL 25
7404#define TWHILE 26
7405#define TBEGIN 27
7406#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007407typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408
7409/* first char is indicating which tokens mark the end of a list */
7410static const char *const tokname_array[] = {
7411 "\1end of file",
7412 "\0newline",
7413 "\0redirection",
7414 "\0word",
7415 "\0;",
7416 "\0&",
7417 "\0&&",
7418 "\0||",
7419 "\0|",
7420 "\0(",
7421 "\1)",
7422 "\1;;",
7423 "\1`",
7424#define KWDOFFSET 13
7425 /* the following are keywords */
7426 "\0!",
7427 "\0case",
7428 "\1do",
7429 "\1done",
7430 "\1elif",
7431 "\1else",
7432 "\1esac",
7433 "\1fi",
7434 "\0for",
7435 "\0if",
7436 "\0in",
7437 "\1then",
7438 "\0until",
7439 "\0while",
7440 "\0{",
7441 "\1}",
7442};
7443
7444static const char *
7445tokname(int tok)
7446{
7447 static char buf[16];
7448
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007449//try this:
7450//if (tok < TSEMI) return tokname_array[tok] + 1;
7451//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7452//return buf;
7453
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454 if (tok >= TSEMI)
7455 buf[0] = '"';
7456 sprintf(buf + (tok >= TSEMI), "%s%c",
7457 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7458 return buf;
7459}
7460
7461/* Wrapper around strcmp for qsort/bsearch/... */
7462static int
7463pstrcmp(const void *a, const void *b)
7464{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007465 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007466}
7467
7468static const char *const *
7469findkwd(const char *s)
7470{
7471 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007472 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7473 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007474}
7475
7476/*
7477 * Locate and print what a word is...
7478 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007479static int
7480describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007481{
7482 struct cmdentry entry;
7483 struct tblentry *cmdp;
7484#if ENABLE_ASH_ALIAS
7485 const struct alias *ap;
7486#endif
7487 const char *path = pathval();
7488
7489 if (describe_command_verbose) {
7490 out1str(command);
7491 }
7492
7493 /* First look at the keywords */
7494 if (findkwd(command)) {
7495 out1str(describe_command_verbose ? " is a shell keyword" : command);
7496 goto out;
7497 }
7498
7499#if ENABLE_ASH_ALIAS
7500 /* Then look at the aliases */
7501 ap = lookupalias(command, 0);
7502 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007503 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007504 out1str("alias ");
7505 printalias(ap);
7506 return 0;
7507 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007508 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007509 goto out;
7510 }
7511#endif
7512 /* Then check if it is a tracked alias */
7513 cmdp = cmdlookup(command, 0);
7514 if (cmdp != NULL) {
7515 entry.cmdtype = cmdp->cmdtype;
7516 entry.u = cmdp->param;
7517 } else {
7518 /* Finally use brute force */
7519 find_command(command, &entry, DO_ABS, path);
7520 }
7521
7522 switch (entry.cmdtype) {
7523 case CMDNORMAL: {
7524 int j = entry.u.index;
7525 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007526 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007527 p = command;
7528 } else {
7529 do {
7530 p = padvance(&path, command);
7531 stunalloc(p);
7532 } while (--j >= 0);
7533 }
7534 if (describe_command_verbose) {
7535 out1fmt(" is%s %s",
7536 (cmdp ? " a tracked alias for" : nullstr), p
7537 );
7538 } else {
7539 out1str(p);
7540 }
7541 break;
7542 }
7543
7544 case CMDFUNCTION:
7545 if (describe_command_verbose) {
7546 out1str(" is a shell function");
7547 } else {
7548 out1str(command);
7549 }
7550 break;
7551
7552 case CMDBUILTIN:
7553 if (describe_command_verbose) {
7554 out1fmt(" is a %sshell builtin",
7555 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7556 "special " : nullstr
7557 );
7558 } else {
7559 out1str(command);
7560 }
7561 break;
7562
7563 default:
7564 if (describe_command_verbose) {
7565 out1str(": not found\n");
7566 }
7567 return 127;
7568 }
7569 out:
7570 outstr("\n", stdout);
7571 return 0;
7572}
7573
7574static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007575typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007576{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007577 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007578 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007579 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007580
Denis Vlasenko46846e22007-05-20 13:08:31 +00007581 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007582 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007583 i++;
7584 verbose = 0;
7585 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007586 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007587 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007588 }
7589 return err;
7590}
7591
7592#if ENABLE_ASH_CMDCMD
7593static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007594commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007595{
7596 int c;
7597 enum {
7598 VERIFY_BRIEF = 1,
7599 VERIFY_VERBOSE = 2,
7600 } verify = 0;
7601
7602 while ((c = nextopt("pvV")) != '\0')
7603 if (c == 'V')
7604 verify |= VERIFY_VERBOSE;
7605 else if (c == 'v')
7606 verify |= VERIFY_BRIEF;
7607#if DEBUG
7608 else if (c != 'p')
7609 abort();
7610#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007611 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7612 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007613 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007614 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007615
7616 return 0;
7617}
7618#endif
7619
7620
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007621/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007622
Denis Vlasenko340299a2008-11-21 10:36:36 +00007623static int funcblocksize; /* size of structures in function */
7624static int funcstringsize; /* size of strings in node */
7625static void *funcblock; /* block to allocate function from */
7626static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007627
Eric Andersencb57d552001-06-28 07:25:16 +00007628/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007629#define EV_EXIT 01 /* exit after evaluating tree */
7630#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007631#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007632
Denis Vlasenko340299a2008-11-21 10:36:36 +00007633static const short nodesize[N_NUMBER] = {
7634 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7635 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7636 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7637 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7638 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7639 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7640 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7641 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7642 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7643 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7644 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7645 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7646 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7647 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7648 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7649 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7650 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007651#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007652 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007653#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007654 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7655 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7656 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7657 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7658 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7659 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7660 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7661 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7662 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007663};
7664
7665static void calcsize(union node *n);
7666
7667static void
7668sizenodelist(struct nodelist *lp)
7669{
7670 while (lp) {
7671 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7672 calcsize(lp->n);
7673 lp = lp->next;
7674 }
7675}
7676
7677static void
7678calcsize(union node *n)
7679{
7680 if (n == NULL)
7681 return;
7682 funcblocksize += nodesize[n->type];
7683 switch (n->type) {
7684 case NCMD:
7685 calcsize(n->ncmd.redirect);
7686 calcsize(n->ncmd.args);
7687 calcsize(n->ncmd.assign);
7688 break;
7689 case NPIPE:
7690 sizenodelist(n->npipe.cmdlist);
7691 break;
7692 case NREDIR:
7693 case NBACKGND:
7694 case NSUBSHELL:
7695 calcsize(n->nredir.redirect);
7696 calcsize(n->nredir.n);
7697 break;
7698 case NAND:
7699 case NOR:
7700 case NSEMI:
7701 case NWHILE:
7702 case NUNTIL:
7703 calcsize(n->nbinary.ch2);
7704 calcsize(n->nbinary.ch1);
7705 break;
7706 case NIF:
7707 calcsize(n->nif.elsepart);
7708 calcsize(n->nif.ifpart);
7709 calcsize(n->nif.test);
7710 break;
7711 case NFOR:
7712 funcstringsize += strlen(n->nfor.var) + 1;
7713 calcsize(n->nfor.body);
7714 calcsize(n->nfor.args);
7715 break;
7716 case NCASE:
7717 calcsize(n->ncase.cases);
7718 calcsize(n->ncase.expr);
7719 break;
7720 case NCLIST:
7721 calcsize(n->nclist.body);
7722 calcsize(n->nclist.pattern);
7723 calcsize(n->nclist.next);
7724 break;
7725 case NDEFUN:
7726 case NARG:
7727 sizenodelist(n->narg.backquote);
7728 funcstringsize += strlen(n->narg.text) + 1;
7729 calcsize(n->narg.next);
7730 break;
7731 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007732#if ENABLE_ASH_BASH_COMPAT
7733 case NTO2:
7734#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007735 case NCLOBBER:
7736 case NFROM:
7737 case NFROMTO:
7738 case NAPPEND:
7739 calcsize(n->nfile.fname);
7740 calcsize(n->nfile.next);
7741 break;
7742 case NTOFD:
7743 case NFROMFD:
7744 calcsize(n->ndup.vname);
7745 calcsize(n->ndup.next);
7746 break;
7747 case NHERE:
7748 case NXHERE:
7749 calcsize(n->nhere.doc);
7750 calcsize(n->nhere.next);
7751 break;
7752 case NNOT:
7753 calcsize(n->nnot.com);
7754 break;
7755 };
7756}
7757
7758static char *
7759nodeckstrdup(char *s)
7760{
7761 char *rtn = funcstring;
7762
7763 strcpy(funcstring, s);
7764 funcstring += strlen(s) + 1;
7765 return rtn;
7766}
7767
7768static union node *copynode(union node *);
7769
7770static struct nodelist *
7771copynodelist(struct nodelist *lp)
7772{
7773 struct nodelist *start;
7774 struct nodelist **lpp;
7775
7776 lpp = &start;
7777 while (lp) {
7778 *lpp = funcblock;
7779 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7780 (*lpp)->n = copynode(lp->n);
7781 lp = lp->next;
7782 lpp = &(*lpp)->next;
7783 }
7784 *lpp = NULL;
7785 return start;
7786}
7787
7788static union node *
7789copynode(union node *n)
7790{
7791 union node *new;
7792
7793 if (n == NULL)
7794 return NULL;
7795 new = funcblock;
7796 funcblock = (char *) funcblock + nodesize[n->type];
7797
7798 switch (n->type) {
7799 case NCMD:
7800 new->ncmd.redirect = copynode(n->ncmd.redirect);
7801 new->ncmd.args = copynode(n->ncmd.args);
7802 new->ncmd.assign = copynode(n->ncmd.assign);
7803 break;
7804 case NPIPE:
7805 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007806 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007807 break;
7808 case NREDIR:
7809 case NBACKGND:
7810 case NSUBSHELL:
7811 new->nredir.redirect = copynode(n->nredir.redirect);
7812 new->nredir.n = copynode(n->nredir.n);
7813 break;
7814 case NAND:
7815 case NOR:
7816 case NSEMI:
7817 case NWHILE:
7818 case NUNTIL:
7819 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7820 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7821 break;
7822 case NIF:
7823 new->nif.elsepart = copynode(n->nif.elsepart);
7824 new->nif.ifpart = copynode(n->nif.ifpart);
7825 new->nif.test = copynode(n->nif.test);
7826 break;
7827 case NFOR:
7828 new->nfor.var = nodeckstrdup(n->nfor.var);
7829 new->nfor.body = copynode(n->nfor.body);
7830 new->nfor.args = copynode(n->nfor.args);
7831 break;
7832 case NCASE:
7833 new->ncase.cases = copynode(n->ncase.cases);
7834 new->ncase.expr = copynode(n->ncase.expr);
7835 break;
7836 case NCLIST:
7837 new->nclist.body = copynode(n->nclist.body);
7838 new->nclist.pattern = copynode(n->nclist.pattern);
7839 new->nclist.next = copynode(n->nclist.next);
7840 break;
7841 case NDEFUN:
7842 case NARG:
7843 new->narg.backquote = copynodelist(n->narg.backquote);
7844 new->narg.text = nodeckstrdup(n->narg.text);
7845 new->narg.next = copynode(n->narg.next);
7846 break;
7847 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007848#if ENABLE_ASH_BASH_COMPAT
7849 case NTO2:
7850#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007851 case NCLOBBER:
7852 case NFROM:
7853 case NFROMTO:
7854 case NAPPEND:
7855 new->nfile.fname = copynode(n->nfile.fname);
7856 new->nfile.fd = n->nfile.fd;
7857 new->nfile.next = copynode(n->nfile.next);
7858 break;
7859 case NTOFD:
7860 case NFROMFD:
7861 new->ndup.vname = copynode(n->ndup.vname);
7862 new->ndup.dupfd = n->ndup.dupfd;
7863 new->ndup.fd = n->ndup.fd;
7864 new->ndup.next = copynode(n->ndup.next);
7865 break;
7866 case NHERE:
7867 case NXHERE:
7868 new->nhere.doc = copynode(n->nhere.doc);
7869 new->nhere.fd = n->nhere.fd;
7870 new->nhere.next = copynode(n->nhere.next);
7871 break;
7872 case NNOT:
7873 new->nnot.com = copynode(n->nnot.com);
7874 break;
7875 };
7876 new->type = n->type;
7877 return new;
7878}
7879
7880/*
7881 * Make a copy of a parse tree.
7882 */
7883static struct funcnode *
7884copyfunc(union node *n)
7885{
7886 struct funcnode *f;
7887 size_t blocksize;
7888
7889 funcblocksize = offsetof(struct funcnode, n);
7890 funcstringsize = 0;
7891 calcsize(n);
7892 blocksize = funcblocksize;
7893 f = ckmalloc(blocksize + funcstringsize);
7894 funcblock = (char *) f + offsetof(struct funcnode, n);
7895 funcstring = (char *) f + blocksize;
7896 copynode(n);
7897 f->count = 0;
7898 return f;
7899}
7900
7901/*
7902 * Define a shell function.
7903 */
7904static void
7905defun(char *name, union node *func)
7906{
7907 struct cmdentry entry;
7908
7909 INT_OFF;
7910 entry.cmdtype = CMDFUNCTION;
7911 entry.u.func = copyfunc(func);
7912 addcmdentry(name, &entry);
7913 INT_ON;
7914}
7915
7916static int evalskip; /* set if we are skipping commands */
7917/* reasons for skipping commands (see comment on breakcmd routine) */
7918#define SKIPBREAK (1 << 0)
7919#define SKIPCONT (1 << 1)
7920#define SKIPFUNC (1 << 2)
7921#define SKIPFILE (1 << 3)
7922#define SKIPEVAL (1 << 4)
7923static int skipcount; /* number of levels to skip */
7924static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007925static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007926
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007927/* forward decl way out to parsing code - dotrap needs it */
7928static int evalstring(char *s, int mask);
7929
7930/*
7931 * Called to execute a trap. Perhaps we should avoid entering new trap
7932 * handlers while we are executing a trap handler.
7933 */
7934static int
7935dotrap(void)
7936{
7937 char *p;
7938 char *q;
7939 int i;
7940 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007941 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007942
7943 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007944 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007945 xbarrier();
7946
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007947 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007948 if (!*q)
7949 continue;
7950 *q = '\0';
7951
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007952 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007953 if (!p)
7954 continue;
7955 skip = evalstring(p, SKIPEVAL);
7956 exitstatus = savestatus;
7957 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007958 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007959 }
7960
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007961 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007962}
7963
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007964/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007965static void evalloop(union node *, int);
7966static void evalfor(union node *, int);
7967static void evalcase(union node *, int);
7968static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007969static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007970static void evalpipe(union node *, int);
7971static void evalcommand(union node *, int);
7972static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007973static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007974
Eric Andersen62483552001-07-10 06:09:16 +00007975/*
Eric Andersenc470f442003-07-28 09:56:35 +00007976 * Evaluate a parse tree. The value is left in the global variable
7977 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007978 */
Eric Andersenc470f442003-07-28 09:56:35 +00007979static void
7980evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007981{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007982
7983 struct jmploc *volatile savehandler = exception_handler;
7984 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007985 int checkexit = 0;
7986 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007988
Eric Andersenc470f442003-07-28 09:56:35 +00007989 if (n == NULL) {
7990 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007991 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007992 }
Eric Andersenc470f442003-07-28 09:56:35 +00007993 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007994 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007995
7996 exception_handler = &jmploc;
7997 {
7998 int err = setjmp(jmploc.loc);
7999 if (err) {
8000 /* if it was a signal, check for trap handlers */
8001 if (exception == EXSIG)
8002 goto out;
8003 /* continue on the way out */
8004 exception_handler = savehandler;
8005 longjmp(exception_handler->loc, err);
8006 }
8007 }
8008
Eric Andersenc470f442003-07-28 09:56:35 +00008009 switch (n->type) {
8010 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008011#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008012 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008013 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008014 break;
8015#endif
8016 case NNOT:
8017 evaltree(n->nnot.com, EV_TESTED);
8018 status = !exitstatus;
8019 goto setstatus;
8020 case NREDIR:
8021 expredir(n->nredir.redirect);
8022 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8023 if (!status) {
8024 evaltree(n->nredir.n, flags & EV_TESTED);
8025 status = exitstatus;
8026 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008027 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008028 goto setstatus;
8029 case NCMD:
8030 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008031 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008032 if (eflag && !(flags & EV_TESTED))
8033 checkexit = ~0;
8034 goto calleval;
8035 case NFOR:
8036 evalfn = evalfor;
8037 goto calleval;
8038 case NWHILE:
8039 case NUNTIL:
8040 evalfn = evalloop;
8041 goto calleval;
8042 case NSUBSHELL:
8043 case NBACKGND:
8044 evalfn = evalsubshell;
8045 goto calleval;
8046 case NPIPE:
8047 evalfn = evalpipe;
8048 goto checkexit;
8049 case NCASE:
8050 evalfn = evalcase;
8051 goto calleval;
8052 case NAND:
8053 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008054 case NSEMI: {
8055
Eric Andersenc470f442003-07-28 09:56:35 +00008056#if NAND + 1 != NOR
8057#error NAND + 1 != NOR
8058#endif
8059#if NOR + 1 != NSEMI
8060#error NOR + 1 != NSEMI
8061#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008062 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008063 evaltree(
8064 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008065 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008066 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008067 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008068 break;
8069 if (!evalskip) {
8070 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008071 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008072 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008073 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008074 evalfn(n, flags);
8075 break;
8076 }
8077 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008078 }
Eric Andersenc470f442003-07-28 09:56:35 +00008079 case NIF:
8080 evaltree(n->nif.test, EV_TESTED);
8081 if (evalskip)
8082 break;
8083 if (exitstatus == 0) {
8084 n = n->nif.ifpart;
8085 goto evaln;
8086 } else if (n->nif.elsepart) {
8087 n = n->nif.elsepart;
8088 goto evaln;
8089 }
8090 goto success;
8091 case NDEFUN:
8092 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008093 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008094 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008095 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008096 exitstatus = status;
8097 break;
8098 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008099
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008100 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008101 exception_handler = savehandler;
8102 out1:
8103 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008104 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008105 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008106 goto exexit;
8107
8108 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008109 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008110 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008111 }
Eric Andersen62483552001-07-10 06:09:16 +00008112}
8113
Eric Andersenc470f442003-07-28 09:56:35 +00008114#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8115static
8116#endif
8117void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8118
Eric Andersenc470f442003-07-28 09:56:35 +00008119static void
8120evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008121{
8122 int status;
8123
8124 loopnest++;
8125 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008126 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008127 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008128 int i;
8129
Eric Andersencb57d552001-06-28 07:25:16 +00008130 evaltree(n->nbinary.ch1, EV_TESTED);
8131 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008132 skipping:
8133 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008134 evalskip = 0;
8135 continue;
8136 }
8137 if (evalskip == SKIPBREAK && --skipcount <= 0)
8138 evalskip = 0;
8139 break;
8140 }
Eric Andersenc470f442003-07-28 09:56:35 +00008141 i = exitstatus;
8142 if (n->type != NWHILE)
8143 i = !i;
8144 if (i != 0)
8145 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008146 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008147 status = exitstatus;
8148 if (evalskip)
8149 goto skipping;
8150 }
8151 loopnest--;
8152 exitstatus = status;
8153}
8154
Eric Andersenc470f442003-07-28 09:56:35 +00008155static void
8156evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008157{
8158 struct arglist arglist;
8159 union node *argp;
8160 struct strlist *sp;
8161 struct stackmark smark;
8162
8163 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008164 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008165 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008166 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008167 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008168 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008169 if (evalskip)
8170 goto out;
8171 }
8172 *arglist.lastp = NULL;
8173
8174 exitstatus = 0;
8175 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008176 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008177 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008178 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008179 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008180 if (evalskip) {
8181 if (evalskip == SKIPCONT && --skipcount <= 0) {
8182 evalskip = 0;
8183 continue;
8184 }
8185 if (evalskip == SKIPBREAK && --skipcount <= 0)
8186 evalskip = 0;
8187 break;
8188 }
8189 }
8190 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008191 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008192 popstackmark(&smark);
8193}
8194
Eric Andersenc470f442003-07-28 09:56:35 +00008195static void
8196evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008197{
8198 union node *cp;
8199 union node *patp;
8200 struct arglist arglist;
8201 struct stackmark smark;
8202
8203 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008204 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008205 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008206 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008207 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008208 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8209 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008210 if (casematch(patp, arglist.list->text)) {
8211 if (evalskip == 0) {
8212 evaltree(cp->nclist.body, flags);
8213 }
8214 goto out;
8215 }
8216 }
8217 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008218 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008219 popstackmark(&smark);
8220}
8221
Eric Andersenc470f442003-07-28 09:56:35 +00008222/*
8223 * Kick off a subshell to evaluate a tree.
8224 */
Eric Andersenc470f442003-07-28 09:56:35 +00008225static void
8226evalsubshell(union node *n, int flags)
8227{
8228 struct job *jp;
8229 int backgnd = (n->type == NBACKGND);
8230 int status;
8231
8232 expredir(n->nredir.redirect);
8233 if (!backgnd && flags & EV_EXIT && !trap[0])
8234 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008235 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008236 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008237 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008238 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008239 flags |= EV_EXIT;
8240 if (backgnd)
8241 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008242 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008243 redirect(n->nredir.redirect, 0);
8244 evaltreenr(n->nredir.n, flags);
8245 /* never returns */
8246 }
8247 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008248 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008249 status = waitforjob(jp);
8250 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008251 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008252}
8253
Eric Andersenc470f442003-07-28 09:56:35 +00008254/*
8255 * Compute the names of the files in a redirection list.
8256 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008257static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008258static void
8259expredir(union node *n)
8260{
8261 union node *redir;
8262
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008263 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008264 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008265
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008266 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008267 fn.lastp = &fn.list;
8268 switch (redir->type) {
8269 case NFROMTO:
8270 case NFROM:
8271 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008272#if ENABLE_ASH_BASH_COMPAT
8273 case NTO2:
8274#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008275 case NCLOBBER:
8276 case NAPPEND:
8277 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008278#if ENABLE_ASH_BASH_COMPAT
8279 store_expfname:
8280#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008281 redir->nfile.expfname = fn.list->text;
8282 break;
8283 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008284 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008285 if (redir->ndup.vname) {
8286 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008287 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008288 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008289#if ENABLE_ASH_BASH_COMPAT
8290//FIXME: we used expandarg with different args!
8291 if (!isdigit_str9(fn.list->text)) {
8292 /* >&file, not >&fd */
8293 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8294 ash_msg_and_raise_error("redir error");
8295 redir->type = NTO2;
8296 goto store_expfname;
8297 }
8298#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008299 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008300 }
8301 break;
8302 }
8303 }
8304}
8305
Eric Andersencb57d552001-06-28 07:25:16 +00008306/*
Eric Andersencb57d552001-06-28 07:25:16 +00008307 * Evaluate a pipeline. All the processes in the pipeline are children
8308 * of the process creating the pipeline. (This differs from some versions
8309 * of the shell, which make the last process in a pipeline the parent
8310 * of all the rest.)
8311 */
Eric Andersenc470f442003-07-28 09:56:35 +00008312static void
8313evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008314{
8315 struct job *jp;
8316 struct nodelist *lp;
8317 int pipelen;
8318 int prevfd;
8319 int pip[2];
8320
Eric Andersenc470f442003-07-28 09:56:35 +00008321 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008322 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008323 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008324 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008325 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008326 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008327 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008328 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008329 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008330 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008331 pip[1] = -1;
8332 if (lp->next) {
8333 if (pipe(pip) < 0) {
8334 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008335 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008336 }
8337 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008338 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008339 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008340 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008341 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008342 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008343 if (prevfd > 0) {
8344 dup2(prevfd, 0);
8345 close(prevfd);
8346 }
8347 if (pip[1] > 1) {
8348 dup2(pip[1], 1);
8349 close(pip[1]);
8350 }
Eric Andersenc470f442003-07-28 09:56:35 +00008351 evaltreenr(lp->n, flags);
8352 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008353 }
8354 if (prevfd >= 0)
8355 close(prevfd);
8356 prevfd = pip[0];
8357 close(pip[1]);
8358 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008359 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008360 exitstatus = waitforjob(jp);
8361 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008362 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008363 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008364}
8365
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008366/*
8367 * Controls whether the shell is interactive or not.
8368 */
8369static void
8370setinteractive(int on)
8371{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008372 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008373
8374 if (++on == is_interactive)
8375 return;
8376 is_interactive = on;
8377 setsignal(SIGINT);
8378 setsignal(SIGQUIT);
8379 setsignal(SIGTERM);
8380#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8381 if (is_interactive > 1) {
8382 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008383 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008384
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008385 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008386 out1fmt(
8387 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008388 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008389 "Enter 'help' for a list of built-in commands."
8390 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008391 bb_banner);
8392 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008393 }
8394 }
8395#endif
8396}
8397
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008398static void
8399optschanged(void)
8400{
8401#if DEBUG
8402 opentrace();
8403#endif
8404 setinteractive(iflag);
8405 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008406#if ENABLE_FEATURE_EDITING_VI
8407 if (viflag)
8408 line_input_state->flags |= VI_MODE;
8409 else
8410 line_input_state->flags &= ~VI_MODE;
8411#else
8412 viflag = 0; /* forcibly keep the option off */
8413#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008414}
8415
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008416static struct localvar *localvars;
8417
8418/*
8419 * Called after a function returns.
8420 * Interrupts must be off.
8421 */
8422static void
8423poplocalvars(void)
8424{
8425 struct localvar *lvp;
8426 struct var *vp;
8427
8428 while ((lvp = localvars) != NULL) {
8429 localvars = lvp->next;
8430 vp = lvp->vp;
8431 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8432 if (vp == NULL) { /* $- saved */
8433 memcpy(optlist, lvp->text, sizeof(optlist));
8434 free((char*)lvp->text);
8435 optschanged();
8436 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8437 unsetvar(vp->text);
8438 } else {
8439 if (vp->func)
8440 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8441 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8442 free((char*)vp->text);
8443 vp->flags = lvp->flags;
8444 vp->text = lvp->text;
8445 }
8446 free(lvp);
8447 }
8448}
8449
8450static int
8451evalfun(struct funcnode *func, int argc, char **argv, int flags)
8452{
8453 volatile struct shparam saveparam;
8454 struct localvar *volatile savelocalvars;
8455 struct jmploc *volatile savehandler;
8456 struct jmploc jmploc;
8457 int e;
8458
8459 saveparam = shellparam;
8460 savelocalvars = localvars;
8461 e = setjmp(jmploc.loc);
8462 if (e) {
8463 goto funcdone;
8464 }
8465 INT_OFF;
8466 savehandler = exception_handler;
8467 exception_handler = &jmploc;
8468 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008469 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008470 func->count++;
8471 funcnest++;
8472 INT_ON;
8473 shellparam.nparam = argc - 1;
8474 shellparam.p = argv + 1;
8475#if ENABLE_ASH_GETOPTS
8476 shellparam.optind = 1;
8477 shellparam.optoff = -1;
8478#endif
8479 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008480 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008481 INT_OFF;
8482 funcnest--;
8483 freefunc(func);
8484 poplocalvars();
8485 localvars = savelocalvars;
8486 freeparam(&shellparam);
8487 shellparam = saveparam;
8488 exception_handler = savehandler;
8489 INT_ON;
8490 evalskip &= ~SKIPFUNC;
8491 return e;
8492}
8493
Denis Vlasenko131ae172007-02-18 13:00:19 +00008494#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008495static char **
8496parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008497{
8498 char *cp, c;
8499
8500 for (;;) {
8501 cp = *++argv;
8502 if (!cp)
8503 return 0;
8504 if (*cp++ != '-')
8505 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008506 c = *cp++;
8507 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008508 break;
8509 if (c == '-' && !*cp) {
8510 argv++;
8511 break;
8512 }
8513 do {
8514 switch (c) {
8515 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008516 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008517 break;
8518 default:
8519 /* run 'typecmd' for other options */
8520 return 0;
8521 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008522 c = *cp++;
8523 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008524 }
8525 return argv;
8526}
8527#endif
8528
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008529/*
8530 * Make a variable a local variable. When a variable is made local, it's
8531 * value and flags are saved in a localvar structure. The saved values
8532 * will be restored when the shell function returns. We handle the name
8533 * "-" as a special case.
8534 */
8535static void
8536mklocal(char *name)
8537{
8538 struct localvar *lvp;
8539 struct var **vpp;
8540 struct var *vp;
8541
8542 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008543 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008544 if (LONE_DASH(name)) {
8545 char *p;
8546 p = ckmalloc(sizeof(optlist));
8547 lvp->text = memcpy(p, optlist, sizeof(optlist));
8548 vp = NULL;
8549 } else {
8550 char *eq;
8551
8552 vpp = hashvar(name);
8553 vp = *findvar(vpp, name);
8554 eq = strchr(name, '=');
8555 if (vp == NULL) {
8556 if (eq)
8557 setvareq(name, VSTRFIXED);
8558 else
8559 setvar(name, NULL, VSTRFIXED);
8560 vp = *vpp; /* the new variable */
8561 lvp->flags = VUNSET;
8562 } else {
8563 lvp->text = vp->text;
8564 lvp->flags = vp->flags;
8565 vp->flags |= VSTRFIXED|VTEXTFIXED;
8566 if (eq)
8567 setvareq(name, 0);
8568 }
8569 }
8570 lvp->vp = vp;
8571 lvp->next = localvars;
8572 localvars = lvp;
8573 INT_ON;
8574}
8575
8576/*
8577 * The "local" command.
8578 */
8579static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008580localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008581{
8582 char *name;
8583
8584 argv = argptr;
8585 while ((name = *argv++) != NULL) {
8586 mklocal(name);
8587 }
8588 return 0;
8589}
8590
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008591static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008592falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008593{
8594 return 1;
8595}
8596
8597static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008598truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008599{
8600 return 0;
8601}
8602
8603static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008604execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008605{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008606 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008607 iflag = 0; /* exit on error */
8608 mflag = 0;
8609 optschanged();
8610 shellexec(argv + 1, pathval(), 0);
8611 }
8612 return 0;
8613}
8614
8615/*
8616 * The return command.
8617 */
8618static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008619returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008620{
8621 /*
8622 * If called outside a function, do what ksh does;
8623 * skip the rest of the file.
8624 */
8625 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8626 return argv[1] ? number(argv[1]) : exitstatus;
8627}
8628
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008629/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008630static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008631static int dotcmd(int, char **);
8632static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008633static int exitcmd(int, char **);
8634static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008635#if ENABLE_ASH_GETOPTS
8636static int getoptscmd(int, char **);
8637#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008638#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008639static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008640#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008641#if ENABLE_ASH_MATH_SUPPORT
8642static int letcmd(int, char **);
8643#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008644static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008645static int setcmd(int, char **);
8646static int shiftcmd(int, char **);
8647static int timescmd(int, char **);
8648static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008649static int umaskcmd(int, char **);
8650static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008651static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008652
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008653#define BUILTIN_NOSPEC "0"
8654#define BUILTIN_SPECIAL "1"
8655#define BUILTIN_REGULAR "2"
8656#define BUILTIN_SPEC_REG "3"
8657#define BUILTIN_ASSIGN "4"
8658#define BUILTIN_SPEC_ASSG "5"
8659#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008660#define BUILTIN_SPEC_REG_ASSG "7"
8661
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008662/* We do not handle [[ expr ]] bashism bash-compatibly,
8663 * we make it a synonym of [ expr ].
8664 * Basically, word splitting and pathname expansion should NOT be performed
8665 * Examples:
8666 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8667 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8668 * Additional operators:
8669 * || and && should work as -o and -a
8670 * =~ regexp match
8671 * Apart from the above, [[ expr ]] should work as [ expr ]
8672 */
8673
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008674#define echocmd echo_main
8675#define printfcmd printf_main
8676#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008677
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008678/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008679static const struct builtincmd builtintab[] = {
8680 { BUILTIN_SPEC_REG ".", dotcmd },
8681 { BUILTIN_SPEC_REG ":", truecmd },
8682#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008683 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008684#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008685 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008686#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008687#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008688#if ENABLE_ASH_ALIAS
8689 { BUILTIN_REG_ASSG "alias", aliascmd },
8690#endif
8691#if JOBS
8692 { BUILTIN_REGULAR "bg", fg_bgcmd },
8693#endif
8694 { BUILTIN_SPEC_REG "break", breakcmd },
8695 { BUILTIN_REGULAR "cd", cdcmd },
8696 { BUILTIN_NOSPEC "chdir", cdcmd },
8697#if ENABLE_ASH_CMDCMD
8698 { BUILTIN_REGULAR "command", commandcmd },
8699#endif
8700 { BUILTIN_SPEC_REG "continue", breakcmd },
8701#if ENABLE_ASH_BUILTIN_ECHO
8702 { BUILTIN_REGULAR "echo", echocmd },
8703#endif
8704 { BUILTIN_SPEC_REG "eval", evalcmd },
8705 { BUILTIN_SPEC_REG "exec", execcmd },
8706 { BUILTIN_SPEC_REG "exit", exitcmd },
8707 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8708 { BUILTIN_REGULAR "false", falsecmd },
8709#if JOBS
8710 { BUILTIN_REGULAR "fg", fg_bgcmd },
8711#endif
8712#if ENABLE_ASH_GETOPTS
8713 { BUILTIN_REGULAR "getopts", getoptscmd },
8714#endif
8715 { BUILTIN_NOSPEC "hash", hashcmd },
8716#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8717 { BUILTIN_NOSPEC "help", helpcmd },
8718#endif
8719#if JOBS
8720 { BUILTIN_REGULAR "jobs", jobscmd },
8721 { BUILTIN_REGULAR "kill", killcmd },
8722#endif
8723#if ENABLE_ASH_MATH_SUPPORT
8724 { BUILTIN_NOSPEC "let", letcmd },
8725#endif
8726 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008727#if ENABLE_ASH_BUILTIN_PRINTF
8728 { BUILTIN_REGULAR "printf", printfcmd },
8729#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008730 { BUILTIN_NOSPEC "pwd", pwdcmd },
8731 { BUILTIN_REGULAR "read", readcmd },
8732 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8733 { BUILTIN_SPEC_REG "return", returncmd },
8734 { BUILTIN_SPEC_REG "set", setcmd },
8735 { BUILTIN_SPEC_REG "shift", shiftcmd },
8736 { BUILTIN_SPEC_REG "source", dotcmd },
8737#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008738 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008739#endif
8740 { BUILTIN_SPEC_REG "times", timescmd },
8741 { BUILTIN_SPEC_REG "trap", trapcmd },
8742 { BUILTIN_REGULAR "true", truecmd },
8743 { BUILTIN_NOSPEC "type", typecmd },
8744 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8745 { BUILTIN_REGULAR "umask", umaskcmd },
8746#if ENABLE_ASH_ALIAS
8747 { BUILTIN_REGULAR "unalias", unaliascmd },
8748#endif
8749 { BUILTIN_SPEC_REG "unset", unsetcmd },
8750 { BUILTIN_REGULAR "wait", waitcmd },
8751};
8752
Denis Vlasenko80591b02008-03-25 07:49:43 +00008753/* Should match the above table! */
8754#define COMMANDCMD (builtintab + \
8755 2 + \
8756 1 * ENABLE_ASH_BUILTIN_TEST + \
8757 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8758 1 * ENABLE_ASH_ALIAS + \
8759 1 * ENABLE_ASH_JOB_CONTROL + \
8760 3)
8761#define EXECCMD (builtintab + \
8762 2 + \
8763 1 * ENABLE_ASH_BUILTIN_TEST + \
8764 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8765 1 * ENABLE_ASH_ALIAS + \
8766 1 * ENABLE_ASH_JOB_CONTROL + \
8767 3 + \
8768 1 * ENABLE_ASH_CMDCMD + \
8769 1 + \
8770 ENABLE_ASH_BUILTIN_ECHO + \
8771 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008772
8773/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008774 * Search the table of builtin commands.
8775 */
8776static struct builtincmd *
8777find_builtin(const char *name)
8778{
8779 struct builtincmd *bp;
8780
8781 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008782 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008783 pstrcmp
8784 );
8785 return bp;
8786}
8787
8788/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008789 * Execute a simple command.
8790 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008791static int
8792isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008793{
8794 const char *q = endofname(p);
8795 if (p == q)
8796 return 0;
8797 return *q == '=';
8798}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008799static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008800bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008801{
8802 /* Preserve exitstatus of a previous possible redirection
8803 * as POSIX mandates */
8804 return back_exitstatus;
8805}
Eric Andersenc470f442003-07-28 09:56:35 +00008806static void
8807evalcommand(union node *cmd, int flags)
8808{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008809 static const struct builtincmd null_bltin = {
8810 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008811 };
Eric Andersenc470f442003-07-28 09:56:35 +00008812 struct stackmark smark;
8813 union node *argp;
8814 struct arglist arglist;
8815 struct arglist varlist;
8816 char **argv;
8817 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008818 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008819 struct cmdentry cmdentry;
8820 struct job *jp;
8821 char *lastarg;
8822 const char *path;
8823 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008824 int status;
8825 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008826 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008827 smallint cmd_is_exec;
8828 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008829
8830 /* First expand the arguments. */
8831 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8832 setstackmark(&smark);
8833 back_exitstatus = 0;
8834
8835 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008836 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008837 varlist.lastp = &varlist.list;
8838 *varlist.lastp = NULL;
8839 arglist.lastp = &arglist.list;
8840 *arglist.lastp = NULL;
8841
8842 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008843 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008844 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8845 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8846 }
8847
Eric Andersenc470f442003-07-28 09:56:35 +00008848 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8849 struct strlist **spp;
8850
8851 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008852 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008853 expandarg(argp, &arglist, EXP_VARTILDE);
8854 else
8855 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8856
Eric Andersenc470f442003-07-28 09:56:35 +00008857 for (sp = *spp; sp; sp = sp->next)
8858 argc++;
8859 }
8860
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008861 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008862 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008863 TRACE(("evalcommand arg: %s\n", sp->text));
8864 *nargv++ = sp->text;
8865 }
8866 *nargv = NULL;
8867
8868 lastarg = NULL;
8869 if (iflag && funcnest == 0 && argc > 0)
8870 lastarg = nargv[-1];
8871
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008872 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008873 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008874 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008875
8876 path = vpath.text;
8877 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8878 struct strlist **spp;
8879 char *p;
8880
8881 spp = varlist.lastp;
8882 expandarg(argp, &varlist, EXP_VARTILDE);
8883
8884 /*
8885 * Modify the command lookup path, if a PATH= assignment
8886 * is present
8887 */
8888 p = (*spp)->text;
8889 if (varequal(p, path))
8890 path = p;
8891 }
8892
8893 /* Print the command if xflag is set. */
8894 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008895 int n;
8896 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008897
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008898 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008899 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008900
8901 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008902 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008903 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008904 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008905 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008906 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008907 p--;
8908 }
8909 }
8910 sp = arglist.list;
8911 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008912 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008913 }
8914
8915 cmd_is_exec = 0;
8916 spclbltin = -1;
8917
8918 /* Now locate the command. */
8919 if (argc) {
8920 const char *oldpath;
8921 int cmd_flag = DO_ERR;
8922
8923 path += 5;
8924 oldpath = path;
8925 for (;;) {
8926 find_command(argv[0], &cmdentry, cmd_flag, path);
8927 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008928 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008929 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008930 goto bail;
8931 }
8932
8933 /* implement bltin and command here */
8934 if (cmdentry.cmdtype != CMDBUILTIN)
8935 break;
8936 if (spclbltin < 0)
8937 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8938 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008939 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008940#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008941 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008942 path = oldpath;
8943 nargv = parse_command_args(argv, &path);
8944 if (!nargv)
8945 break;
8946 argc -= nargv - argv;
8947 argv = nargv;
8948 cmd_flag |= DO_NOFUNC;
8949 } else
8950#endif
8951 break;
8952 }
8953 }
8954
8955 if (status) {
8956 /* We have a redirection error. */
8957 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008958 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008959 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008960 exitstatus = status;
8961 goto out;
8962 }
8963
8964 /* Execute the command. */
8965 switch (cmdentry.cmdtype) {
8966 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008967
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008968#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008969/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8970 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008971 {
8972 /* find_command() encodes applet_no as (-2 - applet_no) */
8973 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008974 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008975 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008976 /* run <applet>_main() */
8977 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008978 break;
8979 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008980 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008981#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008982 /* Fork off a child process if necessary. */
8983 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008984 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008985 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008986 if (forkshell(jp, cmd, FORK_FG) != 0) {
8987 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008988 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008989 break;
8990 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008991 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008992 }
8993 listsetvar(varlist.list, VEXPORT|VSTACK);
8994 shellexec(argv, path, cmdentry.u.index);
8995 /* NOTREACHED */
8996
8997 case CMDBUILTIN:
8998 cmdenviron = varlist.list;
8999 if (cmdenviron) {
9000 struct strlist *list = cmdenviron;
9001 int i = VNOSET;
9002 if (spclbltin > 0 || argc == 0) {
9003 i = 0;
9004 if (cmd_is_exec && argc > 1)
9005 i = VEXPORT;
9006 }
9007 listsetvar(list, i);
9008 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009009 /* Tight loop with builtins only:
9010 * "while kill -0 $child; do true; done"
9011 * will never exit even if $child died, unless we do this
9012 * to reap the zombie and make kill detect that it's gone: */
9013 dowait(DOWAIT_NONBLOCK, NULL);
9014
Eric Andersenc470f442003-07-28 09:56:35 +00009015 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9016 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009017 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00009018 if (i == EXEXIT)
9019 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009020 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009021 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009022 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009023 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009024 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009025 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009026 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009027 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009028 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009029 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009030 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009031 }
9032 break;
9033
9034 case CMDFUNCTION:
9035 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009036 /* See above for the rationale */
9037 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009038 if (evalfun(cmdentry.u.func, argc, argv, flags))
9039 goto raise;
9040 break;
9041 }
9042
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009043 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009044 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009045 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009046 /* dsl: I think this is intended to be used to support
9047 * '_' in 'vi' command mode during line editing...
9048 * However I implemented that within libedit itself.
9049 */
9050 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009051 }
Eric Andersenc470f442003-07-28 09:56:35 +00009052 popstackmark(&smark);
9053}
9054
9055static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009056evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9057{
Eric Andersenc470f442003-07-28 09:56:35 +00009058 char *volatile savecmdname;
9059 struct jmploc *volatile savehandler;
9060 struct jmploc jmploc;
9061 int i;
9062
9063 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009064 i = setjmp(jmploc.loc);
9065 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009066 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009067 savehandler = exception_handler;
9068 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009069 commandname = argv[0];
9070 argptr = argv + 1;
9071 optptr = NULL; /* initialize nextopt */
9072 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009073 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009074 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009075 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009076 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009077 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009078// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009079 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009080
9081 return i;
9082}
9083
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009084static int
9085goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009086{
9087 return !*endofname(p);
9088}
9089
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009090
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009091/*
9092 * Search for a command. This is called before we fork so that the
9093 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009094 * the child. The check for "goodname" is an overly conservative
9095 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009096 */
Eric Andersenc470f442003-07-28 09:56:35 +00009097static void
9098prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009099{
9100 struct cmdentry entry;
9101
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009102 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9103 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009104}
9105
Eric Andersencb57d552001-06-28 07:25:16 +00009106
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009107/* ============ Builtin commands
9108 *
9109 * Builtin commands whose functions are closely tied to evaluation
9110 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009111 */
9112
9113/*
Eric Andersencb57d552001-06-28 07:25:16 +00009114 * Handle break and continue commands. Break, continue, and return are
9115 * all handled by setting the evalskip flag. The evaluation routines
9116 * above all check this flag, and if it is set they start skipping
9117 * commands rather than executing them. The variable skipcount is
9118 * the number of loops to break/continue, or the number of function
9119 * levels to return. (The latter is always 1.) It should probably
9120 * be an error to break out of more loops than exist, but it isn't
9121 * in the standard shell so we don't make it one here.
9122 */
Eric Andersenc470f442003-07-28 09:56:35 +00009123static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009124breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009125{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009126 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009127
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009128 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009129 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009130 if (n > loopnest)
9131 n = loopnest;
9132 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009133 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009134 skipcount = n;
9135 }
9136 return 0;
9137}
9138
Eric Andersenc470f442003-07-28 09:56:35 +00009139
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009140/* ============ input.c
9141 *
Eric Andersen90898442003-08-06 11:20:52 +00009142 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009143 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009144
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009145enum {
9146 INPUT_PUSH_FILE = 1,
9147 INPUT_NOFILE_OK = 2,
9148};
Eric Andersencb57d552001-06-28 07:25:16 +00009149
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009150static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009151/* values of checkkwd variable */
9152#define CHKALIAS 0x1
9153#define CHKKWD 0x2
9154#define CHKNL 0x4
9155
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009156/*
9157 * Push a string back onto the input at this current parsefile level.
9158 * We handle aliases this way.
9159 */
9160#if !ENABLE_ASH_ALIAS
9161#define pushstring(s, ap) pushstring(s)
9162#endif
9163static void
9164pushstring(char *s, struct alias *ap)
9165{
9166 struct strpush *sp;
9167 int len;
9168
9169 len = strlen(s);
9170 INT_OFF;
9171 if (g_parsefile->strpush) {
9172 sp = ckzalloc(sizeof(*sp));
9173 sp->prev = g_parsefile->strpush;
9174 } else {
9175 sp = &(g_parsefile->basestrpush);
9176 }
9177 g_parsefile->strpush = sp;
9178 sp->prev_string = g_parsefile->next_to_pgetc;
9179 sp->prev_left_in_line = g_parsefile->left_in_line;
9180#if ENABLE_ASH_ALIAS
9181 sp->ap = ap;
9182 if (ap) {
9183 ap->flag |= ALIASINUSE;
9184 sp->string = s;
9185 }
9186#endif
9187 g_parsefile->next_to_pgetc = s;
9188 g_parsefile->left_in_line = len;
9189 INT_ON;
9190}
9191
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009192static void
9193popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009194{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009195 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009196
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009197 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009198#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009199 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009200 if (g_parsefile->next_to_pgetc[-1] == ' '
9201 || g_parsefile->next_to_pgetc[-1] == '\t'
9202 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009204 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205 if (sp->string != sp->ap->val) {
9206 free(sp->string);
9207 }
9208 sp->ap->flag &= ~ALIASINUSE;
9209 if (sp->ap->flag & ALIASDEAD) {
9210 unalias(sp->ap->name);
9211 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009212 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009213#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009214 g_parsefile->next_to_pgetc = sp->prev_string;
9215 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009216 g_parsefile->strpush = sp->prev;
9217 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218 free(sp);
9219 INT_ON;
9220}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009221
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009222//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9223//it peeks whether it is &>, and then pushes back both chars.
9224//This function needs to save last *next_to_pgetc to buf[0]
9225//to make two pungetc() reliable. Currently,
9226// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009227static int
9228preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009229{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009230 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009231 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009232
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009233 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009234#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009235 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009236 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009237 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009238 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009239#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009240 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009241#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009242 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9243 if (nr == 0) {
9244 /* Ctrl+C pressed */
9245 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009246 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009247 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009248 raise(SIGINT);
9249 return 1;
9250 }
Eric Andersenc470f442003-07-28 09:56:35 +00009251 goto retry;
9252 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009253 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009254 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009255 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009256 }
Eric Andersencb57d552001-06-28 07:25:16 +00009257 }
9258#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009259 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009260#endif
9261
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009262#if 0
9263/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009264 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009265 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009266 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009267 if (flags >= 0 && (flags & O_NONBLOCK)) {
9268 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009269 if (fcntl(0, F_SETFL, flags) >= 0) {
9270 out2str("sh: turning off NDELAY mode\n");
9271 goto retry;
9272 }
9273 }
9274 }
9275 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009276#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009277 return nr;
9278}
9279
9280/*
9281 * Refill the input buffer and return the next input character:
9282 *
9283 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009284 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9285 * or we are reading from a string so we can't refill the buffer,
9286 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009287 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9288 * 4) Process input up to the next newline, deleting nul characters.
9289 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009290//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9291#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009292/*
9293 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9294 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9295 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9296 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9297 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009298static int
Eric Andersenc470f442003-07-28 09:56:35 +00009299preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009300{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009301 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009302 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009303
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009304 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009305#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009306 if (g_parsefile->left_in_line == -1
9307 && g_parsefile->strpush->ap
9308 && g_parsefile->next_to_pgetc[-1] != ' '
9309 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009310 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009311 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009312 return PEOA;
9313 }
Eric Andersen2870d962001-07-02 17:27:21 +00009314#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009315 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009316 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009317 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9318 g_parsefile->left_in_line,
9319 g_parsefile->next_to_pgetc,
9320 g_parsefile->next_to_pgetc);
9321 if (--g_parsefile->left_in_line >= 0)
9322 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009323 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009324 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009325 * "pgetc" needs refilling.
9326 */
9327
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009328 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009329 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009330 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009331 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009332 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009333 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009334 /* even in failure keep left_in_line and next_to_pgetc
9335 * in lock step, for correct multi-layer pungetc.
9336 * left_in_line was decremented before preadbuffer(),
9337 * must inc next_to_pgetc: */
9338 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009339 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009340 }
Eric Andersencb57d552001-06-28 07:25:16 +00009341
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009342 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009343 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009344 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009345 again:
9346 more = preadfd();
9347 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009348 /* don't try reading again */
9349 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009350 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009351 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009352 return PEOF;
9353 }
9354 }
9355
Denis Vlasenko727752d2008-11-28 03:41:47 +00009356 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009357 * Set g_parsefile->left_in_line
9358 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009359 * NUL chars are deleted.
9360 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009361 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009362 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009363 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009364
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009365 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009366
Denis Vlasenko727752d2008-11-28 03:41:47 +00009367 c = *q;
9368 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009369 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009370 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009371 q++;
9372 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009373 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009374 break;
9375 }
Eric Andersencb57d552001-06-28 07:25:16 +00009376 }
9377
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009378 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009379 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9380 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009381 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009382 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009383 }
9384 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009385 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009386
Eric Andersencb57d552001-06-28 07:25:16 +00009387 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009388 char save = *q;
9389 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009390 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009391 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009392 }
9393
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009394 pgetc_debug("preadbuffer at %d:%p'%s'",
9395 g_parsefile->left_in_line,
9396 g_parsefile->next_to_pgetc,
9397 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009398 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009399}
9400
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009401#define pgetc_as_macro() \
9402 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009403 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009404 : preadbuffer() \
9405 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009406
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009407static int
9408pgetc(void)
9409{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009410 pgetc_debug("pgetc_fast at %d:%p'%s'",
9411 g_parsefile->left_in_line,
9412 g_parsefile->next_to_pgetc,
9413 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009414 return pgetc_as_macro();
9415}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009416
9417#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009418#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009419#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009420#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009421#endif
9422
9423/*
9424 * Same as pgetc(), but ignores PEOA.
9425 */
9426#if ENABLE_ASH_ALIAS
9427static int
9428pgetc2(void)
9429{
9430 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009431 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009432 pgetc_debug("pgetc_fast at %d:%p'%s'",
9433 g_parsefile->left_in_line,
9434 g_parsefile->next_to_pgetc,
9435 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009436 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009437 } while (c == PEOA);
9438 return c;
9439}
9440#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009441#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009442#endif
9443
9444/*
9445 * Read a line from the script.
9446 */
9447static char *
9448pfgets(char *line, int len)
9449{
9450 char *p = line;
9451 int nleft = len;
9452 int c;
9453
9454 while (--nleft > 0) {
9455 c = pgetc2();
9456 if (c == PEOF) {
9457 if (p == line)
9458 return NULL;
9459 break;
9460 }
9461 *p++ = c;
9462 if (c == '\n')
9463 break;
9464 }
9465 *p = '\0';
9466 return line;
9467}
9468
Eric Andersenc470f442003-07-28 09:56:35 +00009469/*
9470 * Undo the last call to pgetc. Only one character may be pushed back.
9471 * PEOF may be pushed back.
9472 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009473static void
Eric Andersenc470f442003-07-28 09:56:35 +00009474pungetc(void)
9475{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009476 g_parsefile->left_in_line++;
9477 g_parsefile->next_to_pgetc--;
9478 pgetc_debug("pushed back to %d:%p'%s'",
9479 g_parsefile->left_in_line,
9480 g_parsefile->next_to_pgetc,
9481 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009482}
9483
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009484/*
9485 * To handle the "." command, a stack of input files is used. Pushfile
9486 * adds a new entry to the stack and popfile restores the previous level.
9487 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009489pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009490{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009491 struct parsefile *pf;
9492
Denis Vlasenko597906c2008-02-20 16:38:54 +00009493 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009494 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009495 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009496 /*pf->strpush = NULL; - ckzalloc did it */
9497 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009498 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009499}
9500
9501static void
9502popfile(void)
9503{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009504 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009505
Denis Vlasenkob012b102007-02-19 22:43:01 +00009506 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009507 if (pf->fd >= 0)
9508 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009509 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009510 while (pf->strpush)
9511 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009512 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009513 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009514 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009515}
9516
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009517/*
9518 * Return to top level.
9519 */
9520static void
9521popallfiles(void)
9522{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009523 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009524 popfile();
9525}
9526
9527/*
9528 * Close the file(s) that the shell is reading commands from. Called
9529 * after a fork is done.
9530 */
9531static void
9532closescript(void)
9533{
9534 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009535 if (g_parsefile->fd > 0) {
9536 close(g_parsefile->fd);
9537 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009538 }
9539}
9540
9541/*
9542 * Like setinputfile, but takes an open file descriptor. Call this with
9543 * interrupts off.
9544 */
9545static void
9546setinputfd(int fd, int push)
9547{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009548 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009549 if (push) {
9550 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009551 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009552 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009553 g_parsefile->fd = fd;
9554 if (g_parsefile->buf == NULL)
9555 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009556 g_parsefile->left_in_buffer = 0;
9557 g_parsefile->left_in_line = 0;
9558 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009559}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009560
Eric Andersenc470f442003-07-28 09:56:35 +00009561/*
9562 * Set the input to take input from a file. If push is set, push the
9563 * old input onto the stack first.
9564 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009565static int
9566setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009567{
9568 int fd;
9569 int fd2;
9570
Denis Vlasenkob012b102007-02-19 22:43:01 +00009571 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009572 fd = open(fname, O_RDONLY);
9573 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009574 if (flags & INPUT_NOFILE_OK)
9575 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009576 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009577 }
Eric Andersenc470f442003-07-28 09:56:35 +00009578 if (fd < 10) {
9579 fd2 = copyfd(fd, 10);
9580 close(fd);
9581 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009582 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009583 fd = fd2;
9584 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009585 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009586 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009587 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009588 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009589}
9590
Eric Andersencb57d552001-06-28 07:25:16 +00009591/*
9592 * Like setinputfile, but takes input from a string.
9593 */
Eric Andersenc470f442003-07-28 09:56:35 +00009594static void
9595setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009596{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009597 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009598 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009599 g_parsefile->next_to_pgetc = string;
9600 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009601 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009602 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009603 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009604}
9605
9606
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009607/* ============ mail.c
9608 *
9609 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009610 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009611
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009612#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009613
Eric Andersencb57d552001-06-28 07:25:16 +00009614#define MAXMBOXES 10
9615
Eric Andersenc470f442003-07-28 09:56:35 +00009616/* times of mailboxes */
9617static time_t mailtime[MAXMBOXES];
9618/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009619static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009620
Eric Andersencb57d552001-06-28 07:25:16 +00009621/*
Eric Andersenc470f442003-07-28 09:56:35 +00009622 * Print appropriate message(s) if mail has arrived.
9623 * If mail_var_path_changed is set,
9624 * then the value of MAIL has mail_var_path_changed,
9625 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009626 */
Eric Andersenc470f442003-07-28 09:56:35 +00009627static void
9628chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009629{
Eric Andersencb57d552001-06-28 07:25:16 +00009630 const char *mpath;
9631 char *p;
9632 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009633 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009634 struct stackmark smark;
9635 struct stat statb;
9636
Eric Andersencb57d552001-06-28 07:25:16 +00009637 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009638 mpath = mpathset() ? mpathval() : mailval();
9639 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009640 p = padvance(&mpath, nullstr);
9641 if (p == NULL)
9642 break;
9643 if (*p == '\0')
9644 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009645 for (q = p; *q; q++)
9646 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009647#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009648 if (q[-1] != '/')
9649 abort();
9650#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009651 q[-1] = '\0'; /* delete trailing '/' */
9652 if (stat(p, &statb) < 0) {
9653 *mtp = 0;
9654 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009655 }
Eric Andersenc470f442003-07-28 09:56:35 +00009656 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9657 fprintf(
9658 stderr, snlfmt,
9659 pathopt ? pathopt : "you have mail"
9660 );
9661 }
9662 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009663 }
Eric Andersenc470f442003-07-28 09:56:35 +00009664 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009665 popstackmark(&smark);
9666}
Eric Andersencb57d552001-06-28 07:25:16 +00009667
Eric Andersenc470f442003-07-28 09:56:35 +00009668static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009669changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009670{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009671 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009672}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009673
Denis Vlasenko131ae172007-02-18 13:00:19 +00009674#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009675
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009676
9677/* ============ ??? */
9678
Eric Andersencb57d552001-06-28 07:25:16 +00009679/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009680 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009681 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009682static void
9683setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009684{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009685 char **newparam;
9686 char **ap;
9687 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009688
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009689 for (nparam = 0; argv[nparam]; nparam++)
9690 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009691 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9692 while (*argv) {
9693 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009694 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009695 *ap = NULL;
9696 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009697 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009698 shellparam.nparam = nparam;
9699 shellparam.p = newparam;
9700#if ENABLE_ASH_GETOPTS
9701 shellparam.optind = 1;
9702 shellparam.optoff = -1;
9703#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009704}
9705
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009706/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009707 * Process shell options. The global variable argptr contains a pointer
9708 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009709 *
9710 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9711 * For a non-interactive shell, an error condition encountered
9712 * by a special built-in ... shall cause the shell to write a diagnostic message
9713 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009714 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009715 * ...
9716 * Utility syntax error (option or operand error) Shall exit
9717 * ...
9718 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9719 * we see that bash does not do that (set "finishes" with error code 1 instead,
9720 * and shell continues), and people rely on this behavior!
9721 * Testcase:
9722 * set -o barfoo 2>/dev/null
9723 * echo $?
9724 *
9725 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009726 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009727static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009728plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009729{
9730 int i;
9731
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009732 if (name) {
9733 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009734 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009735 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009736 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009737 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009738 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009739 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009740 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009741 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009742 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009743 if (val) {
9744 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9745 } else {
9746 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9747 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009748 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009749 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009750}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009751static void
9752setoption(int flag, int val)
9753{
9754 int i;
9755
9756 for (i = 0; i < NOPTS; i++) {
9757 if (optletters(i) == flag) {
9758 optlist[i] = val;
9759 return;
9760 }
9761 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009762 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009763 /* NOTREACHED */
9764}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009765static int
Eric Andersenc470f442003-07-28 09:56:35 +00009766options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009767{
9768 char *p;
9769 int val;
9770 int c;
9771
9772 if (cmdline)
9773 minusc = NULL;
9774 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009775 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009776 if (c != '-' && c != '+')
9777 break;
9778 argptr++;
9779 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009780 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009781 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009782 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009783 if (!cmdline) {
9784 /* "-" means turn off -x and -v */
9785 if (p[0] == '\0')
9786 xflag = vflag = 0;
9787 /* "--" means reset params */
9788 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009789 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009790 }
Eric Andersenc470f442003-07-28 09:56:35 +00009791 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009792 }
Eric Andersencb57d552001-06-28 07:25:16 +00009793 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009794 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009795 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009796 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009797 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009798 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009799 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009800 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009801 /* it already printed err message */
9802 return 1; /* error */
9803 }
Eric Andersencb57d552001-06-28 07:25:16 +00009804 if (*argptr)
9805 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009806 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9807 isloginsh = 1;
9808 /* bash does not accept +-login, we also won't */
9809 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009810 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009811 isloginsh = 1;
9812 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 } else {
9814 setoption(c, val);
9815 }
9816 }
9817 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009818 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009819}
9820
Eric Andersencb57d552001-06-28 07:25:16 +00009821/*
Eric Andersencb57d552001-06-28 07:25:16 +00009822 * The shift builtin command.
9823 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009824static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009825shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009826{
9827 int n;
9828 char **ap1, **ap2;
9829
9830 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009831 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009832 n = number(argv[1]);
9833 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009834 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009835 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009836 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009837 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009838 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009839 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009840 }
9841 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009842 while ((*ap2++ = *ap1++) != NULL)
9843 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009844#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009845 shellparam.optind = 1;
9846 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009847#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009848 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009849 return 0;
9850}
9851
Eric Andersencb57d552001-06-28 07:25:16 +00009852/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009853 * POSIX requires that 'set' (but not export or readonly) output the
9854 * variables in lexicographic order - by the locale's collating order (sigh).
9855 * Maybe we could keep them in an ordered balanced binary tree
9856 * instead of hashed lists.
9857 * For now just roll 'em through qsort for printing...
9858 */
9859static int
9860showvars(const char *sep_prefix, int on, int off)
9861{
9862 const char *sep;
9863 char **ep, **epend;
9864
9865 ep = listvars(on, off, &epend);
9866 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9867
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009868 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009869
9870 for (; ep < epend; ep++) {
9871 const char *p;
9872 const char *q;
9873
9874 p = strchrnul(*ep, '=');
9875 q = nullstr;
9876 if (*p)
9877 q = single_quote(++p);
9878 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9879 }
9880 return 0;
9881}
9882
9883/*
Eric Andersencb57d552001-06-28 07:25:16 +00009884 * The set command builtin.
9885 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009886static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009887setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009888{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009889 int retval;
9890
Denis Vlasenko68404f12008-03-17 09:00:54 +00009891 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009892 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009893 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009894 retval = 1;
9895 if (!options(0)) { /* if no parse error... */
9896 retval = 0;
9897 optschanged();
9898 if (*argptr != NULL) {
9899 setparam(argptr);
9900 }
Eric Andersencb57d552001-06-28 07:25:16 +00009901 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009902 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009903 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009904}
9905
Denis Vlasenko131ae172007-02-18 13:00:19 +00009906#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009907static void
9908change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009909{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009910 /* Galois LFSR parameter */
9911 /* Taps at 32 31 29 1: */
9912 enum { MASK = 0x8000000b };
9913 /* Another example - taps at 32 31 30 10: */
9914 /* MASK = 0x00400007 */
9915
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009916 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009917 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009918 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009919
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009920 /* LCG has period of 2^32 and alternating lowest bit */
9921 random_LCG = 1664525 * random_LCG + 1013904223;
9922 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9923 t = (random_galois_LFSR << 1);
9924 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9925 t ^= MASK;
9926 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009927 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009928 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009929 * for $RANDOM range. Combining with subtraction is
9930 * just for fun. + and ^ would work equally well. */
9931 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009932 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009933 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009934 vrandom.flags &= ~VNOFUNC;
9935 } else {
9936 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009937 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009938 }
Eric Andersenef02f822004-03-11 13:34:24 +00009939}
Eric Andersen16767e22004-03-16 05:14:10 +00009940#endif
9941
Denis Vlasenko131ae172007-02-18 13:00:19 +00009942#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009943static int
Eric Andersenc470f442003-07-28 09:56:35 +00009944getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009945{
9946 char *p, *q;
9947 char c = '?';
9948 int done = 0;
9949 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009950 char s[12];
9951 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009952
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009953 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009954 return 1;
9955 optnext = optfirst + *param_optind - 1;
9956
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009957 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009958 p = NULL;
9959 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009960 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009961 if (p == NULL || *p == '\0') {
9962 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009963 p = *optnext;
9964 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009965 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009966 p = NULL;
9967 done = 1;
9968 goto out;
9969 }
9970 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009971 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009972 goto atend;
9973 }
9974
9975 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009976 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009977 if (*q == '\0') {
9978 if (optstr[0] == ':') {
9979 s[0] = c;
9980 s[1] = '\0';
9981 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009982 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009983 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009984 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009985 }
9986 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009987 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009988 }
9989 if (*++q == ':')
9990 q++;
9991 }
9992
9993 if (*++q == ':') {
9994 if (*p == '\0' && (p = *optnext) == NULL) {
9995 if (optstr[0] == ':') {
9996 s[0] = c;
9997 s[1] = '\0';
9998 err |= setvarsafe("OPTARG", s, 0);
9999 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010000 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010001 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010002 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010003 c = '?';
10004 }
Eric Andersenc470f442003-07-28 09:56:35 +000010005 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010006 }
10007
10008 if (p == *optnext)
10009 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010010 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010011 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010012 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010013 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010014 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010015 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010016 *param_optind = optnext - optfirst + 1;
10017 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010018 err |= setvarsafe("OPTIND", s, VNOFUNC);
10019 s[0] = c;
10020 s[1] = '\0';
10021 err |= setvarsafe(optvar, s, 0);
10022 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010023 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010024 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010025 flush_stdout_stderr();
10026 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010027 }
10028 return done;
10029}
Eric Andersenc470f442003-07-28 09:56:35 +000010030
10031/*
10032 * The getopts builtin. Shellparam.optnext points to the next argument
10033 * to be processed. Shellparam.optptr points to the next character to
10034 * be processed in the current argument. If shellparam.optnext is NULL,
10035 * then it's the first time getopts has been called.
10036 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010037static int
Eric Andersenc470f442003-07-28 09:56:35 +000010038getoptscmd(int argc, char **argv)
10039{
10040 char **optbase;
10041
10042 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010043 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010044 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010045 optbase = shellparam.p;
10046 if (shellparam.optind > shellparam.nparam + 1) {
10047 shellparam.optind = 1;
10048 shellparam.optoff = -1;
10049 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010050 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010051 optbase = &argv[3];
10052 if (shellparam.optind > argc - 2) {
10053 shellparam.optind = 1;
10054 shellparam.optoff = -1;
10055 }
10056 }
10057
10058 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010059 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010060}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010061#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010062
Eric Andersencb57d552001-06-28 07:25:16 +000010063
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010064/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010065
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010066struct heredoc {
10067 struct heredoc *next; /* next here document in list */
10068 union node *here; /* redirection node */
10069 char *eofmark; /* string indicating end of input */
10070 smallint striptabs; /* if set, strip leading tabs */
10071};
10072
10073static smallint tokpushback; /* last token pushed back */
10074static smallint parsebackquote; /* nonzero if we are inside backquotes */
10075static smallint quoteflag; /* set if (part of) last token was quoted */
10076static token_id_t lasttoken; /* last token read (integer id Txxx) */
10077static struct heredoc *heredoclist; /* list of here documents to read */
10078static char *wordtext; /* text of last word returned by readtoken */
10079static struct nodelist *backquotelist;
10080static union node *redirnode;
10081static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010082/*
10083 * NEOF is returned by parsecmd when it encounters an end of file. It
10084 * must be distinct from NULL, so we use the address of a variable that
10085 * happens to be handy.
10086 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010087#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010088
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010089static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010090static void
10091raise_error_syntax(const char *msg)
10092{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010093 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010094 /* NOTREACHED */
10095}
10096
10097/*
10098 * Called when an unexpected token is read during the parse. The argument
10099 * is the token that is expected, or -1 if more than one type of token can
10100 * occur at this point.
10101 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010102static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010103static void
10104raise_error_unexpected_syntax(int token)
10105{
10106 char msg[64];
10107 int l;
10108
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010109 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010110 if (token >= 0)
10111 sprintf(msg + l, " (expecting %s)", tokname(token));
10112 raise_error_syntax(msg);
10113 /* NOTREACHED */
10114}
Eric Andersencb57d552001-06-28 07:25:16 +000010115
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010116#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010117
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010118/* parsing is heavily cross-recursive, need these forward decls */
10119static union node *andor(void);
10120static union node *pipeline(void);
10121static union node *parse_command(void);
10122static void parseheredoc(void);
10123static char peektoken(void);
10124static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010125
Eric Andersenc470f442003-07-28 09:56:35 +000010126static union node *
10127list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010128{
10129 union node *n1, *n2, *n3;
10130 int tok;
10131
Eric Andersenc470f442003-07-28 09:56:35 +000010132 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10133 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010134 return NULL;
10135 n1 = NULL;
10136 for (;;) {
10137 n2 = andor();
10138 tok = readtoken();
10139 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010140 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010141 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010142 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010143 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010144 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010145 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010146 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010147 n2 = n3;
10148 }
10149 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010150 }
10151 }
10152 if (n1 == NULL) {
10153 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010154 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010155 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010156 n3->type = NSEMI;
10157 n3->nbinary.ch1 = n1;
10158 n3->nbinary.ch2 = n2;
10159 n1 = n3;
10160 }
10161 switch (tok) {
10162 case TBACKGND:
10163 case TSEMI:
10164 tok = readtoken();
10165 /* fall through */
10166 case TNL:
10167 if (tok == TNL) {
10168 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010169 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010170 return n1;
10171 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010172 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010173 }
Eric Andersenc470f442003-07-28 09:56:35 +000010174 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010175 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010176 return n1;
10177 break;
10178 case TEOF:
10179 if (heredoclist)
10180 parseheredoc();
10181 else
Eric Andersenc470f442003-07-28 09:56:35 +000010182 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010183 return n1;
10184 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010185 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010186 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010187 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010188 return n1;
10189 }
10190 }
10191}
10192
Eric Andersenc470f442003-07-28 09:56:35 +000010193static union node *
10194andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010195{
Eric Andersencb57d552001-06-28 07:25:16 +000010196 union node *n1, *n2, *n3;
10197 int t;
10198
Eric Andersencb57d552001-06-28 07:25:16 +000010199 n1 = pipeline();
10200 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010201 t = readtoken();
10202 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010203 t = NAND;
10204 } else if (t == TOR) {
10205 t = NOR;
10206 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010207 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010208 return n1;
10209 }
Eric Andersenc470f442003-07-28 09:56:35 +000010210 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010211 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010212 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010213 n3->type = t;
10214 n3->nbinary.ch1 = n1;
10215 n3->nbinary.ch2 = n2;
10216 n1 = n3;
10217 }
10218}
10219
Eric Andersenc470f442003-07-28 09:56:35 +000010220static union node *
10221pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010222{
Eric Andersencb57d552001-06-28 07:25:16 +000010223 union node *n1, *n2, *pipenode;
10224 struct nodelist *lp, *prev;
10225 int negate;
10226
10227 negate = 0;
10228 TRACE(("pipeline: entered\n"));
10229 if (readtoken() == TNOT) {
10230 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010231 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010232 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010233 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010234 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010235 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010236 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010237 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010238 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010239 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010240 pipenode->npipe.cmdlist = lp;
10241 lp->n = n1;
10242 do {
10243 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010244 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010245 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010247 prev->next = lp;
10248 } while (readtoken() == TPIPE);
10249 lp->next = NULL;
10250 n1 = pipenode;
10251 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010252 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010253 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010254 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010255 n2->type = NNOT;
10256 n2->nnot.com = n1;
10257 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010258 }
10259 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010260}
10261
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010262static union node *
10263makename(void)
10264{
10265 union node *n;
10266
Denis Vlasenko597906c2008-02-20 16:38:54 +000010267 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010268 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010269 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010270 n->narg.text = wordtext;
10271 n->narg.backquote = backquotelist;
10272 return n;
10273}
10274
10275static void
10276fixredir(union node *n, const char *text, int err)
10277{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010278 int fd;
10279
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010280 TRACE(("Fix redir %s %d\n", text, err));
10281 if (!err)
10282 n->ndup.vname = NULL;
10283
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010284 fd = bb_strtou(text, NULL, 10);
10285 if (!errno && fd >= 0)
10286 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010287 else if (LONE_DASH(text))
10288 n->ndup.dupfd = -1;
10289 else {
10290 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010291 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010292 n->ndup.vname = makename();
10293 }
10294}
10295
10296/*
10297 * Returns true if the text contains nothing to expand (no dollar signs
10298 * or backquotes).
10299 */
10300static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010301noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010302{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010303 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010304 char c;
10305
10306 p = text;
10307 while ((c = *p++) != '\0') {
10308 if (c == CTLQUOTEMARK)
10309 continue;
10310 if (c == CTLESC)
10311 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010312 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010313 return 0;
10314 }
10315 return 1;
10316}
10317
10318static void
10319parsefname(void)
10320{
10321 union node *n = redirnode;
10322
10323 if (readtoken() != TWORD)
10324 raise_error_unexpected_syntax(-1);
10325 if (n->type == NHERE) {
10326 struct heredoc *here = heredoc;
10327 struct heredoc *p;
10328 int i;
10329
10330 if (quoteflag == 0)
10331 n->type = NXHERE;
10332 TRACE(("Here document %d\n", n->type));
10333 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010334 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010335 rmescapes(wordtext);
10336 here->eofmark = wordtext;
10337 here->next = NULL;
10338 if (heredoclist == NULL)
10339 heredoclist = here;
10340 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010341 for (p = heredoclist; p->next; p = p->next)
10342 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010343 p->next = here;
10344 }
10345 } else if (n->type == NTOFD || n->type == NFROMFD) {
10346 fixredir(n, wordtext, 0);
10347 } else {
10348 n->nfile.fname = makename();
10349 }
10350}
Eric Andersencb57d552001-06-28 07:25:16 +000010351
Eric Andersenc470f442003-07-28 09:56:35 +000010352static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353simplecmd(void)
10354{
10355 union node *args, **app;
10356 union node *n = NULL;
10357 union node *vars, **vpp;
10358 union node **rpp, *redir;
10359 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010360#if ENABLE_ASH_BASH_COMPAT
10361 smallint double_brackets_flag = 0;
10362#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010363
10364 args = NULL;
10365 app = &args;
10366 vars = NULL;
10367 vpp = &vars;
10368 redir = NULL;
10369 rpp = &redir;
10370
10371 savecheckkwd = CHKALIAS;
10372 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010373 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010374 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010375 t = readtoken();
10376 switch (t) {
10377#if ENABLE_ASH_BASH_COMPAT
10378 case TAND: /* "&&" */
10379 case TOR: /* "||" */
10380 if (!double_brackets_flag) {
10381 tokpushback = 1;
10382 goto out;
10383 }
10384 wordtext = (char *) (t == TAND ? "-a" : "-o");
10385#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010386 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010387 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010388 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010389 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010390 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010391#if ENABLE_ASH_BASH_COMPAT
10392 if (strcmp("[[", wordtext) == 0)
10393 double_brackets_flag = 1;
10394 else if (strcmp("]]", wordtext) == 0)
10395 double_brackets_flag = 0;
10396#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010397 n->narg.backquote = backquotelist;
10398 if (savecheckkwd && isassignment(wordtext)) {
10399 *vpp = n;
10400 vpp = &n->narg.next;
10401 } else {
10402 *app = n;
10403 app = &n->narg.next;
10404 savecheckkwd = 0;
10405 }
10406 break;
10407 case TREDIR:
10408 *rpp = n = redirnode;
10409 rpp = &n->nfile.next;
10410 parsefname(); /* read name of redirection file */
10411 break;
10412 case TLP:
10413 if (args && app == &args->narg.next
10414 && !vars && !redir
10415 ) {
10416 struct builtincmd *bcmd;
10417 const char *name;
10418
10419 /* We have a function */
10420 if (readtoken() != TRP)
10421 raise_error_unexpected_syntax(TRP);
10422 name = n->narg.text;
10423 if (!goodname(name)
10424 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10425 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010426 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010427 }
10428 n->type = NDEFUN;
10429 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10430 n->narg.next = parse_command();
10431 return n;
10432 }
10433 /* fall through */
10434 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010435 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010436 goto out;
10437 }
10438 }
10439 out:
10440 *app = NULL;
10441 *vpp = NULL;
10442 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010443 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010444 n->type = NCMD;
10445 n->ncmd.args = args;
10446 n->ncmd.assign = vars;
10447 n->ncmd.redirect = redir;
10448 return n;
10449}
10450
10451static union node *
10452parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010453{
Eric Andersencb57d552001-06-28 07:25:16 +000010454 union node *n1, *n2;
10455 union node *ap, **app;
10456 union node *cp, **cpp;
10457 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010458 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010459 int t;
10460
10461 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010462 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010463
Eric Andersencb57d552001-06-28 07:25:16 +000010464 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010465 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010466 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010467 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010468 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010469 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010470 n1->type = NIF;
10471 n1->nif.test = list(0);
10472 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010474 n1->nif.ifpart = list(0);
10475 n2 = n1;
10476 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010477 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010478 n2 = n2->nif.elsepart;
10479 n2->type = NIF;
10480 n2->nif.test = list(0);
10481 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010482 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010483 n2->nif.ifpart = list(0);
10484 }
10485 if (lasttoken == TELSE)
10486 n2->nif.elsepart = list(0);
10487 else {
10488 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010489 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010490 }
Eric Andersenc470f442003-07-28 09:56:35 +000010491 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010492 break;
10493 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010494 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010495 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010496 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010497 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010498 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010499 got = readtoken();
10500 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010501 TRACE(("expecting DO got %s %s\n", tokname(got),
10502 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010503 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010504 }
10505 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010506 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 break;
10508 }
10509 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010510 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010511 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010512 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010513 n1->type = NFOR;
10514 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010515 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 if (readtoken() == TIN) {
10517 app = &ap;
10518 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010519 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010520 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010521 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010522 n2->narg.text = wordtext;
10523 n2->narg.backquote = backquotelist;
10524 *app = n2;
10525 app = &n2->narg.next;
10526 }
10527 *app = NULL;
10528 n1->nfor.args = ap;
10529 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010530 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010531 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010532 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010533 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010534 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010535 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010536 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010537 n1->nfor.args = n2;
10538 /*
10539 * Newline or semicolon here is optional (but note
10540 * that the original Bourne shell only allowed NL).
10541 */
10542 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010543 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010544 }
Eric Andersenc470f442003-07-28 09:56:35 +000010545 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010546 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010547 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010548 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010549 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010550 break;
10551 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010552 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010553 n1->type = NCASE;
10554 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010555 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010556 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010557 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010558 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010559 n2->narg.text = wordtext;
10560 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010561 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010562 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010563 } while (readtoken() == TNL);
10564 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010565 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010566 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010567 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010568 checkkwd = CHKNL | CHKKWD;
10569 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010570 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010571 if (lasttoken == TLP)
10572 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010573 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010574 cp->type = NCLIST;
10575 app = &cp->nclist.pattern;
10576 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010577 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010578 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010579 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010580 ap->narg.text = wordtext;
10581 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010582 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010583 break;
10584 app = &ap->narg.next;
10585 readtoken();
10586 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010587 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010588 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010589 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010590 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010591
Eric Andersenc470f442003-07-28 09:56:35 +000010592 cpp = &cp->nclist.next;
10593
10594 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010595 t = readtoken();
10596 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010597 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010598 raise_error_unexpected_syntax(TENDCASE);
10599 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010600 }
Eric Andersenc470f442003-07-28 09:56:35 +000010601 }
Eric Andersencb57d552001-06-28 07:25:16 +000010602 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010603 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010604 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010605 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010606 n1->type = NSUBSHELL;
10607 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010608 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010609 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010610 break;
10611 case TBEGIN:
10612 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010613 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010614 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010615 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010616 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010617 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010618 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010619 }
10620
Eric Andersenc470f442003-07-28 09:56:35 +000010621 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010622 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010623
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010624 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010625 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010626 checkkwd = CHKKWD | CHKALIAS;
10627 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010628 while (readtoken() == TREDIR) {
10629 *rpp = n2 = redirnode;
10630 rpp = &n2->nfile.next;
10631 parsefname();
10632 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010633 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010634 *rpp = NULL;
10635 if (redir) {
10636 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010637 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010638 n2->type = NREDIR;
10639 n2->nredir.n = n1;
10640 n1 = n2;
10641 }
10642 n1->nredir.redirect = redir;
10643 }
Eric Andersencb57d552001-06-28 07:25:16 +000010644 return n1;
10645}
10646
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010647#if ENABLE_ASH_BASH_COMPAT
10648static int decode_dollar_squote(void)
10649{
10650 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10651 int c, cnt;
10652 char *p;
10653 char buf[4];
10654
10655 c = pgetc();
10656 p = strchr(C_escapes, c);
10657 if (p) {
10658 buf[0] = c;
10659 p = buf;
10660 cnt = 3;
10661 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10662 do {
10663 c = pgetc();
10664 *++p = c;
10665 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10666 pungetc();
10667 } else if (c == 'x') { /* \xHH */
10668 do {
10669 c = pgetc();
10670 *++p = c;
10671 } while (isxdigit(c) && --cnt);
10672 pungetc();
10673 if (cnt == 3) { /* \x but next char is "bad" */
10674 c = 'x';
10675 goto unrecognized;
10676 }
10677 } else { /* simple seq like \\ or \t */
10678 p++;
10679 }
10680 *p = '\0';
10681 p = buf;
10682 c = bb_process_escape_sequence((void*)&p);
10683 } else { /* unrecognized "\z": print both chars unless ' or " */
10684 if (c != '\'' && c != '"') {
10685 unrecognized:
10686 c |= 0x100; /* "please encode \, then me" */
10687 }
10688 }
10689 return c;
10690}
10691#endif
10692
Eric Andersencb57d552001-06-28 07:25:16 +000010693/*
10694 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10695 * is not NULL, read a here document. In the latter case, eofmark is the
10696 * word which marks the end of the document and striptabs is true if
10697 * leading tabs should be stripped from the document. The argument firstc
10698 * is the first character of the input token or document.
10699 *
10700 * Because C does not have internal subroutines, I have simulated them
10701 * using goto's to implement the subroutine linkage. The following macros
10702 * will run code that appears at the end of readtoken1.
10703 */
Eric Andersen2870d962001-07-02 17:27:21 +000010704#define CHECKEND() {goto checkend; checkend_return:;}
10705#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10706#define PARSESUB() {goto parsesub; parsesub_return:;}
10707#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10708#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10709#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010710static int
Eric Andersenc470f442003-07-28 09:56:35 +000010711readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010712{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010713 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010714 int c = firstc;
10715 char *out;
10716 int len;
10717 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010718 struct nodelist *bqlist;
10719 smallint quotef;
10720 smallint dblquote;
10721 smallint oldstyle;
10722 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010723#if ENABLE_ASH_EXPAND_PRMT
10724 smallint pssyntax; /* we are expanding a prompt string */
10725#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010726 int varnest; /* levels of variables expansion */
10727 int arinest; /* levels of arithmetic expansion */
10728 int parenlevel; /* levels of parens in arithmetic */
10729 int dqvarnest; /* levels of variables expansion within double quotes */
10730
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010731 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10732
Eric Andersencb57d552001-06-28 07:25:16 +000010733#if __GNUC__
10734 /* Avoid longjmp clobbering */
10735 (void) &out;
10736 (void) &quotef;
10737 (void) &dblquote;
10738 (void) &varnest;
10739 (void) &arinest;
10740 (void) &parenlevel;
10741 (void) &dqvarnest;
10742 (void) &oldstyle;
10743 (void) &prevsyntax;
10744 (void) &syntax;
10745#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010746 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010747 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010748 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010749 oldstyle = 0;
10750 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010751#if ENABLE_ASH_EXPAND_PRMT
10752 pssyntax = (syntax == PSSYNTAX);
10753 if (pssyntax)
10754 syntax = DQSYNTAX;
10755#endif
10756 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010757 varnest = 0;
10758 arinest = 0;
10759 parenlevel = 0;
10760 dqvarnest = 0;
10761
10762 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010763 loop:
10764 /* For each line, until end of word */
10765 {
Eric Andersenc470f442003-07-28 09:56:35 +000010766 CHECKEND(); /* set c to PEOF if at end of here document */
10767 for (;;) { /* until end of line or end of word */
10768 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010769 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010770 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010771 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010772 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010773 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010774 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010775 if (doprompt)
10776 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010777 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010778 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010779 case CWORD:
10780 USTPUTC(c, out);
10781 break;
10782 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010783 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010784 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010785#if ENABLE_ASH_BASH_COMPAT
10786 if (c == '\\' && bash_dollar_squote) {
10787 c = decode_dollar_squote();
10788 if (c & 0x100) {
10789 USTPUTC('\\', out);
10790 c = (unsigned char)c;
10791 }
10792 }
10793#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010794 USTPUTC(c, out);
10795 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010796 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010797 c = pgetc2();
10798 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010799 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010800 USTPUTC('\\', out);
10801 pungetc();
10802 } else if (c == '\n') {
10803 if (doprompt)
10804 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010805 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010806#if ENABLE_ASH_EXPAND_PRMT
10807 if (c == '$' && pssyntax) {
10808 USTPUTC(CTLESC, out);
10809 USTPUTC('\\', out);
10810 }
10811#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010812 if (dblquote && c != '\\'
10813 && c != '`' && c != '$'
10814 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010815 ) {
10816 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010818 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010819 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010820 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010821 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010822 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010823 }
10824 break;
10825 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010826 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010827 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010828 if (eofmark == NULL) {
10829 USTPUTC(CTLQUOTEMARK, out);
10830 }
Eric Andersencb57d552001-06-28 07:25:16 +000010831 break;
10832 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010833 syntax = DQSYNTAX;
10834 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010835 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010836 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010837 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010838 if (eofmark != NULL && arinest == 0
10839 && varnest == 0
10840 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010841 USTPUTC(c, out);
10842 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010843 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010844 syntax = BASESYNTAX;
10845 dblquote = 0;
10846 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010847 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010848 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010849 }
10850 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010851 case CVAR: /* '$' */
10852 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010853 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010854 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010855 if (varnest > 0) {
10856 varnest--;
10857 if (dqvarnest > 0) {
10858 dqvarnest--;
10859 }
10860 USTPUTC(CTLENDVAR, out);
10861 } else {
10862 USTPUTC(c, out);
10863 }
10864 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010865#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010866 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010867 parenlevel++;
10868 USTPUTC(c, out);
10869 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010870 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010871 if (parenlevel > 0) {
10872 USTPUTC(c, out);
10873 --parenlevel;
10874 } else {
10875 if (pgetc() == ')') {
10876 if (--arinest == 0) {
10877 USTPUTC(CTLENDARI, out);
10878 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010879 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010880 } else
10881 USTPUTC(')', out);
10882 } else {
10883 /*
10884 * unbalanced parens
10885 * (don't 2nd guess - no error)
10886 */
10887 pungetc();
10888 USTPUTC(')', out);
10889 }
10890 }
10891 break;
10892#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010893 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010894 PARSEBACKQOLD();
10895 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010896 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010897 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010898 case CIGN:
10899 break;
10900 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010901 if (varnest == 0) {
10902#if ENABLE_ASH_BASH_COMPAT
10903 if (c == '&') {
10904 if (pgetc() == '>')
10905 c = 0x100 + '>'; /* flag &> */
10906 pungetc();
10907 }
10908#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010909 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010910 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010911#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010912 if (c != PEOA)
10913#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010914 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010915
Eric Andersencb57d552001-06-28 07:25:16 +000010916 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010917 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010918 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010919 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010920 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010921#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010922 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010923 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010924#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010925 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010926 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010927 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010928 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010929 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010930 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010931 }
10932 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010933 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010934 out = stackblock();
10935 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010936 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10937 && quotef == 0
10938 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010939 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010940 PARSEREDIR(); /* passed as params: out, c */
10941 lasttoken = TREDIR;
10942 return lasttoken;
10943 }
10944 /* else: non-number X seen, interpret it
10945 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010946 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010947 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010948 }
10949 quoteflag = quotef;
10950 backquotelist = bqlist;
10951 grabstackblock(len);
10952 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010953 lasttoken = TWORD;
10954 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010955/* end of readtoken routine */
10956
Eric Andersencb57d552001-06-28 07:25:16 +000010957/*
10958 * Check to see whether we are at the end of the here document. When this
10959 * is called, c is set to the first character of the next input line. If
10960 * we are at the end of the here document, this routine sets the c to PEOF.
10961 */
Eric Andersenc470f442003-07-28 09:56:35 +000010962checkend: {
10963 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010964#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010965 if (c == PEOA) {
10966 c = pgetc2();
10967 }
10968#endif
10969 if (striptabs) {
10970 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010971 c = pgetc2();
10972 }
Eric Andersenc470f442003-07-28 09:56:35 +000010973 }
10974 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010975 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010976 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010977
Eric Andersenc470f442003-07-28 09:56:35 +000010978 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010979 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10980 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010981 if (*p == '\n' && *q == '\0') {
10982 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010983 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010984 needprompt = doprompt;
10985 } else {
10986 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010987 }
10988 }
10989 }
10990 }
Eric Andersenc470f442003-07-28 09:56:35 +000010991 goto checkend_return;
10992}
Eric Andersencb57d552001-06-28 07:25:16 +000010993
Eric Andersencb57d552001-06-28 07:25:16 +000010994/*
10995 * Parse a redirection operator. The variable "out" points to a string
10996 * specifying the fd to be redirected. The variable "c" contains the
10997 * first character of the redirection operator.
10998 */
Eric Andersenc470f442003-07-28 09:56:35 +000010999parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011000 /* out is already checked to be a valid number or "" */
11001 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011002 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011003
Denis Vlasenko597906c2008-02-20 16:38:54 +000011004 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011005 if (c == '>') {
11006 np->nfile.fd = 1;
11007 c = pgetc();
11008 if (c == '>')
11009 np->type = NAPPEND;
11010 else if (c == '|')
11011 np->type = NCLOBBER;
11012 else if (c == '&')
11013 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011014 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011015 else {
11016 np->type = NTO;
11017 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011018 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011019 }
11020#if ENABLE_ASH_BASH_COMPAT
11021 else if (c == 0x100 + '>') { /* this flags &> redirection */
11022 np->nfile.fd = 1;
11023 pgetc(); /* this is '>', no need to check */
11024 np->type = NTO2;
11025 }
11026#endif
11027 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011028 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011029 c = pgetc();
11030 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011031 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011032 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011033 np = stzalloc(sizeof(struct nhere));
11034 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011035 }
11036 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011037 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011038 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011039 c = pgetc();
11040 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011041 heredoc->striptabs = 1;
11042 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011043 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011044 pungetc();
11045 }
11046 break;
11047
11048 case '&':
11049 np->type = NFROMFD;
11050 break;
11051
11052 case '>':
11053 np->type = NFROMTO;
11054 break;
11055
11056 default:
11057 np->type = NFROM;
11058 pungetc();
11059 break;
11060 }
Eric Andersencb57d552001-06-28 07:25:16 +000011061 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011062 if (fd >= 0)
11063 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011064 redirnode = np;
11065 goto parseredir_return;
11066}
Eric Andersencb57d552001-06-28 07:25:16 +000011067
Eric Andersencb57d552001-06-28 07:25:16 +000011068/*
11069 * Parse a substitution. At this point, we have read the dollar sign
11070 * and nothing else.
11071 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011072
11073/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11074 * (assuming ascii char codes, as the original implementation did) */
11075#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011076 (((unsigned)(c) - 33 < 32) \
11077 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011078parsesub: {
11079 int subtype;
11080 int typeloc;
11081 int flags;
11082 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011083 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011084
Eric Andersenc470f442003-07-28 09:56:35 +000011085 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011086 if (c <= PEOA_OR_PEOF
11087 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011088 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011089#if ENABLE_ASH_BASH_COMPAT
11090 if (c == '\'')
11091 bash_dollar_squote = 1;
11092 else
11093#endif
11094 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011095 pungetc();
11096 } else if (c == '(') { /* $(command) or $((arith)) */
11097 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011098#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011099 PARSEARITH();
11100#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011101 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011102#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011103 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011104 pungetc();
11105 PARSEBACKQNEW();
11106 }
11107 } else {
11108 USTPUTC(CTLVAR, out);
11109 typeloc = out - (char *)stackblock();
11110 USTPUTC(VSNORMAL, out);
11111 subtype = VSNORMAL;
11112 if (c == '{') {
11113 c = pgetc();
11114 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011115 c = pgetc();
11116 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011117 c = '#';
11118 else
11119 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011120 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011121 subtype = 0;
11122 }
11123 if (c > PEOA_OR_PEOF && is_name(c)) {
11124 do {
11125 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011126 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011127 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011128 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011129 do {
11130 STPUTC(c, out);
11131 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011132 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011133 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011134 USTPUTC(c, out);
11135 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011136 } else {
11137 badsub:
11138 raise_error_syntax("bad substitution");
11139 }
Eric Andersencb57d552001-06-28 07:25:16 +000011140
Eric Andersenc470f442003-07-28 09:56:35 +000011141 STPUTC('=', out);
11142 flags = 0;
11143 if (subtype == 0) {
11144 switch (c) {
11145 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011146 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011147#if ENABLE_ASH_BASH_COMPAT
11148 if (c == ':' || c == '$' || isdigit(c)) {
11149 pungetc();
11150 subtype = VSSUBSTR;
11151 break;
11152 }
11153#endif
11154 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011155 /*FALLTHROUGH*/
11156 default:
11157 p = strchr(types, c);
11158 if (p == NULL)
11159 goto badsub;
11160 subtype = p - types + VSNORMAL;
11161 break;
11162 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011163 case '#': {
11164 int cc = c;
11165 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11166 c = pgetc();
11167 if (c == cc)
11168 subtype++;
11169 else
11170 pungetc();
11171 break;
11172 }
11173#if ENABLE_ASH_BASH_COMPAT
11174 case '/':
11175 subtype = VSREPLACE;
11176 c = pgetc();
11177 if (c == '/')
11178 subtype++; /* VSREPLACEALL */
11179 else
11180 pungetc();
11181 break;
11182#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011183 }
Eric Andersenc470f442003-07-28 09:56:35 +000011184 } else {
11185 pungetc();
11186 }
11187 if (dblquote || arinest)
11188 flags |= VSQUOTE;
11189 *((char *)stackblock() + typeloc) = subtype | flags;
11190 if (subtype != VSNORMAL) {
11191 varnest++;
11192 if (dblquote || arinest) {
11193 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011194 }
11195 }
11196 }
Eric Andersenc470f442003-07-28 09:56:35 +000011197 goto parsesub_return;
11198}
Eric Andersencb57d552001-06-28 07:25:16 +000011199
Eric Andersencb57d552001-06-28 07:25:16 +000011200/*
11201 * Called to parse command substitutions. Newstyle is set if the command
11202 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11203 * list of commands (passed by reference), and savelen is the number of
11204 * characters on the top of the stack which must be preserved.
11205 */
Eric Andersenc470f442003-07-28 09:56:35 +000011206parsebackq: {
11207 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011208 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011209 union node *n;
11210 char *volatile str;
11211 struct jmploc jmploc;
11212 struct jmploc *volatile savehandler;
11213 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011214 smallint saveprompt = 0;
11215
Eric Andersencb57d552001-06-28 07:25:16 +000011216#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011217 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011218#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011219 savepbq = parsebackquote;
11220 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011221 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011222 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011223 exception_handler = savehandler;
11224 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011225 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011226 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011227 str = NULL;
11228 savelen = out - (char *)stackblock();
11229 if (savelen > 0) {
11230 str = ckmalloc(savelen);
11231 memcpy(str, stackblock(), savelen);
11232 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011233 savehandler = exception_handler;
11234 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011235 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011236 if (oldstyle) {
11237 /* We must read until the closing backquote, giving special
11238 treatment to some slashes, and then push the string and
11239 reread it as input, interpreting it normally. */
11240 char *pout;
11241 int pc;
11242 size_t psavelen;
11243 char *pstr;
11244
11245
11246 STARTSTACKSTR(pout);
11247 for (;;) {
11248 if (needprompt) {
11249 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011250 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011251 pc = pgetc();
11252 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011253 case '`':
11254 goto done;
11255
11256 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011257 pc = pgetc();
11258 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011259 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011260 if (doprompt)
11261 setprompt(2);
11262 /*
11263 * If eating a newline, avoid putting
11264 * the newline into the new character
11265 * stream (via the STPUTC after the
11266 * switch).
11267 */
11268 continue;
11269 }
11270 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011271 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011272 STPUTC('\\', pout);
11273 if (pc > PEOA_OR_PEOF) {
11274 break;
11275 }
11276 /* fall through */
11277
11278 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011279#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011280 case PEOA:
11281#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011282 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011283 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011284
11285 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011286 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011287 needprompt = doprompt;
11288 break;
11289
11290 default:
11291 break;
11292 }
11293 STPUTC(pc, pout);
11294 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011295 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011296 STPUTC('\0', pout);
11297 psavelen = pout - (char *)stackblock();
11298 if (psavelen > 0) {
11299 pstr = grabstackstr(pout);
11300 setinputstring(pstr);
11301 }
11302 }
11303 nlpp = &bqlist;
11304 while (*nlpp)
11305 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011306 *nlpp = stzalloc(sizeof(**nlpp));
11307 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011308 parsebackquote = oldstyle;
11309
11310 if (oldstyle) {
11311 saveprompt = doprompt;
11312 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011313 }
11314
Eric Andersenc470f442003-07-28 09:56:35 +000011315 n = list(2);
11316
11317 if (oldstyle)
11318 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011319 else if (readtoken() != TRP)
11320 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011321
11322 (*nlpp)->n = n;
11323 if (oldstyle) {
11324 /*
11325 * Start reading from old file again, ignoring any pushed back
11326 * tokens left from the backquote parsing
11327 */
11328 popfile();
11329 tokpushback = 0;
11330 }
11331 while (stackblocksize() <= savelen)
11332 growstackblock();
11333 STARTSTACKSTR(out);
11334 if (str) {
11335 memcpy(out, str, savelen);
11336 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011337 INT_OFF;
11338 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011339 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011340 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011341 }
11342 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011343 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011344 if (arinest || dblquote)
11345 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11346 else
11347 USTPUTC(CTLBACKQ, out);
11348 if (oldstyle)
11349 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011350 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011351}
11352
Denis Vlasenko131ae172007-02-18 13:00:19 +000011353#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011354/*
11355 * Parse an arithmetic expansion (indicate start of one and set state)
11356 */
Eric Andersenc470f442003-07-28 09:56:35 +000011357parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011358 if (++arinest == 1) {
11359 prevsyntax = syntax;
11360 syntax = ARISYNTAX;
11361 USTPUTC(CTLARI, out);
11362 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011363 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011364 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011365 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011366 } else {
11367 /*
11368 * we collapse embedded arithmetic expansion to
11369 * parenthesis, which should be equivalent
11370 */
11371 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011372 }
Eric Andersenc470f442003-07-28 09:56:35 +000011373 goto parsearith_return;
11374}
11375#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011376
Eric Andersenc470f442003-07-28 09:56:35 +000011377} /* end of readtoken */
11378
Eric Andersencb57d552001-06-28 07:25:16 +000011379/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011380 * Read the next input token.
11381 * If the token is a word, we set backquotelist to the list of cmds in
11382 * backquotes. We set quoteflag to true if any part of the word was
11383 * quoted.
11384 * If the token is TREDIR, then we set redirnode to a structure containing
11385 * the redirection.
11386 * In all cases, the variable startlinno is set to the number of the line
11387 * on which the token starts.
11388 *
11389 * [Change comment: here documents and internal procedures]
11390 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11391 * word parsing code into a separate routine. In this case, readtoken
11392 * doesn't need to have any internal procedures, but parseword does.
11393 * We could also make parseoperator in essence the main routine, and
11394 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011395 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011396#define NEW_xxreadtoken
11397#ifdef NEW_xxreadtoken
11398/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011399static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011400 '\n', '(', ')', /* singles */
11401 '&', '|', ';', /* doubles */
11402 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011403};
Eric Andersencb57d552001-06-28 07:25:16 +000011404
Denis Vlasenko834dee72008-10-07 09:18:30 +000011405#define xxreadtoken_singles 3
11406#define xxreadtoken_doubles 3
11407
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011408static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011409 TNL, TLP, TRP, /* only single occurrence allowed */
11410 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11411 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011412 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011413};
11414
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011415static int
11416xxreadtoken(void)
11417{
11418 int c;
11419
11420 if (tokpushback) {
11421 tokpushback = 0;
11422 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011424 if (needprompt) {
11425 setprompt(2);
11426 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011427 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011428 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011429 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011430 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11431 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011432
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011433 if (c == '#') {
11434 while ((c = pgetc()) != '\n' && c != PEOF)
11435 continue;
11436 pungetc();
11437 } else if (c == '\\') {
11438 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011439 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011440 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011442 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011443 if (doprompt)
11444 setprompt(2);
11445 } else {
11446 const char *p;
11447
11448 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11449 if (c != PEOF) {
11450 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011451 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011452 needprompt = doprompt;
11453 }
11454
11455 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011456 if (p == NULL)
11457 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011458
Denis Vlasenko834dee72008-10-07 09:18:30 +000011459 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11460 int cc = pgetc();
11461 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011462 p += xxreadtoken_doubles + 1;
11463 } else {
11464 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011465#if ENABLE_ASH_BASH_COMPAT
11466 if (c == '&' && cc == '>') /* &> */
11467 break; /* return readtoken1(...) */
11468#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011469 }
11470 }
11471 }
11472 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11473 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011474 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011475 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011476
11477 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011478}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011479#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011480#define RETURN(token) return lasttoken = token
11481static int
11482xxreadtoken(void)
11483{
11484 int c;
11485
11486 if (tokpushback) {
11487 tokpushback = 0;
11488 return lasttoken;
11489 }
11490 if (needprompt) {
11491 setprompt(2);
11492 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011493 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011494 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011495 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011496 switch (c) {
11497 case ' ': case '\t':
11498#if ENABLE_ASH_ALIAS
11499 case PEOA:
11500#endif
11501 continue;
11502 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011503 while ((c = pgetc()) != '\n' && c != PEOF)
11504 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011505 pungetc();
11506 continue;
11507 case '\\':
11508 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011509 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011510 if (doprompt)
11511 setprompt(2);
11512 continue;
11513 }
11514 pungetc();
11515 goto breakloop;
11516 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011517 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011518 needprompt = doprompt;
11519 RETURN(TNL);
11520 case PEOF:
11521 RETURN(TEOF);
11522 case '&':
11523 if (pgetc() == '&')
11524 RETURN(TAND);
11525 pungetc();
11526 RETURN(TBACKGND);
11527 case '|':
11528 if (pgetc() == '|')
11529 RETURN(TOR);
11530 pungetc();
11531 RETURN(TPIPE);
11532 case ';':
11533 if (pgetc() == ';')
11534 RETURN(TENDCASE);
11535 pungetc();
11536 RETURN(TSEMI);
11537 case '(':
11538 RETURN(TLP);
11539 case ')':
11540 RETURN(TRP);
11541 default:
11542 goto breakloop;
11543 }
11544 }
11545 breakloop:
11546 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11547#undef RETURN
11548}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011549#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011550
11551static int
11552readtoken(void)
11553{
11554 int t;
11555#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011556 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011557#endif
11558
11559#if ENABLE_ASH_ALIAS
11560 top:
11561#endif
11562
11563 t = xxreadtoken();
11564
11565 /*
11566 * eat newlines
11567 */
11568 if (checkkwd & CHKNL) {
11569 while (t == TNL) {
11570 parseheredoc();
11571 t = xxreadtoken();
11572 }
11573 }
11574
11575 if (t != TWORD || quoteflag) {
11576 goto out;
11577 }
11578
11579 /*
11580 * check for keywords
11581 */
11582 if (checkkwd & CHKKWD) {
11583 const char *const *pp;
11584
11585 pp = findkwd(wordtext);
11586 if (pp) {
11587 lasttoken = t = pp - tokname_array;
11588 TRACE(("keyword %s recognized\n", tokname(t)));
11589 goto out;
11590 }
11591 }
11592
11593 if (checkkwd & CHKALIAS) {
11594#if ENABLE_ASH_ALIAS
11595 struct alias *ap;
11596 ap = lookupalias(wordtext, 1);
11597 if (ap != NULL) {
11598 if (*ap->val) {
11599 pushstring(ap->val, ap);
11600 }
11601 goto top;
11602 }
11603#endif
11604 }
11605 out:
11606 checkkwd = 0;
11607#if DEBUG
11608 if (!alreadyseen)
11609 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11610 else
11611 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11612#endif
11613 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011614}
11615
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011616static char
11617peektoken(void)
11618{
11619 int t;
11620
11621 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011622 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011623 return tokname_array[t][0];
11624}
Eric Andersencb57d552001-06-28 07:25:16 +000011625
11626/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011627 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11628 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011629 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011630static union node *
11631parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011632{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011633 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011634
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011635 tokpushback = 0;
11636 doprompt = interact;
11637 if (doprompt)
11638 setprompt(doprompt);
11639 needprompt = 0;
11640 t = readtoken();
11641 if (t == TEOF)
11642 return NEOF;
11643 if (t == TNL)
11644 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011645 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011646 return list(1);
11647}
11648
11649/*
11650 * Input any here documents.
11651 */
11652static void
11653parseheredoc(void)
11654{
11655 struct heredoc *here;
11656 union node *n;
11657
11658 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011659 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011660
11661 while (here) {
11662 if (needprompt) {
11663 setprompt(2);
11664 }
11665 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11666 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011667 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011668 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011669 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011670 n->narg.text = wordtext;
11671 n->narg.backquote = backquotelist;
11672 here->here->nhere.doc = n;
11673 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011674 }
Eric Andersencb57d552001-06-28 07:25:16 +000011675}
11676
11677
11678/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011679 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011680 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011681#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011682static const char *
11683expandstr(const char *ps)
11684{
11685 union node n;
11686
11687 /* XXX Fix (char *) cast. */
11688 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011689 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011690 popfile();
11691
11692 n.narg.type = NARG;
11693 n.narg.next = NULL;
11694 n.narg.text = wordtext;
11695 n.narg.backquote = backquotelist;
11696
11697 expandarg(&n, NULL, 0);
11698 return stackblock();
11699}
11700#endif
11701
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011702/*
11703 * Execute a command or commands contained in a string.
11704 */
11705static int
11706evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011707{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011708 union node *n;
11709 struct stackmark smark;
11710 int skip;
11711
11712 setinputstring(s);
11713 setstackmark(&smark);
11714
11715 skip = 0;
11716 while ((n = parsecmd(0)) != NEOF) {
11717 evaltree(n, 0);
11718 popstackmark(&smark);
11719 skip = evalskip;
11720 if (skip)
11721 break;
11722 }
11723 popfile();
11724
11725 skip &= mask;
11726 evalskip = skip;
11727 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011728}
11729
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011730/*
11731 * The eval command.
11732 */
11733static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011734evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011735{
11736 char *p;
11737 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011738
Denis Vlasenko68404f12008-03-17 09:00:54 +000011739 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011740 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011741 argv += 2;
11742 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011743 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011744 for (;;) {
11745 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011746 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011747 if (p == NULL)
11748 break;
11749 STPUTC(' ', concat);
11750 }
11751 STPUTC('\0', concat);
11752 p = grabstackstr(concat);
11753 }
11754 evalstring(p, ~SKIPEVAL);
11755
11756 }
11757 return exitstatus;
11758}
11759
11760/*
11761 * Read and execute commands. "Top" is nonzero for the top level command
11762 * loop; it turns on prompting if the shell is interactive.
11763 */
11764static int
11765cmdloop(int top)
11766{
11767 union node *n;
11768 struct stackmark smark;
11769 int inter;
11770 int numeof = 0;
11771
11772 TRACE(("cmdloop(%d) called\n", top));
11773 for (;;) {
11774 int skip;
11775
11776 setstackmark(&smark);
11777#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011778 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011779 showjobs(stderr, SHOW_CHANGED);
11780#endif
11781 inter = 0;
11782 if (iflag && top) {
11783 inter++;
11784#if ENABLE_ASH_MAIL
11785 chkmail();
11786#endif
11787 }
11788 n = parsecmd(inter);
11789 /* showtree(n); DEBUG */
11790 if (n == NEOF) {
11791 if (!top || numeof >= 50)
11792 break;
11793 if (!stoppedjobs()) {
11794 if (!Iflag)
11795 break;
11796 out2str("\nUse \"exit\" to leave shell.\n");
11797 }
11798 numeof++;
11799 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011800 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11801 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011802 numeof = 0;
11803 evaltree(n, 0);
11804 }
11805 popstackmark(&smark);
11806 skip = evalskip;
11807
11808 if (skip) {
11809 evalskip = 0;
11810 return skip & SKIPEVAL;
11811 }
11812 }
11813 return 0;
11814}
11815
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011816/*
11817 * Take commands from a file. To be compatible we should do a path
11818 * search for the file, which is necessary to find sub-commands.
11819 */
11820static char *
11821find_dot_file(char *name)
11822{
11823 char *fullname;
11824 const char *path = pathval();
11825 struct stat statb;
11826
11827 /* don't try this for absolute or relative paths */
11828 if (strchr(name, '/'))
11829 return name;
11830
11831 while ((fullname = padvance(&path, name)) != NULL) {
11832 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11833 /*
11834 * Don't bother freeing here, since it will
11835 * be freed by the caller.
11836 */
11837 return fullname;
11838 }
11839 stunalloc(fullname);
11840 }
11841
11842 /* not found in the PATH */
11843 ash_msg_and_raise_error("%s: not found", name);
11844 /* NOTREACHED */
11845}
11846
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011847static int
11848dotcmd(int argc, char **argv)
11849{
11850 struct strlist *sp;
11851 volatile struct shparam saveparam;
11852 int status = 0;
11853
11854 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011855 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011856
Denis Vlasenko68404f12008-03-17 09:00:54 +000011857 if (argv[1]) { /* That's what SVR2 does */
11858 char *fullname = find_dot_file(argv[1]);
11859 argv += 2;
11860 argc -= 2;
11861 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011862 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011863 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011864 shellparam.nparam = argc;
11865 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011866 };
11867
11868 setinputfile(fullname, INPUT_PUSH_FILE);
11869 commandname = fullname;
11870 cmdloop(0);
11871 popfile();
11872
Denis Vlasenko68404f12008-03-17 09:00:54 +000011873 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011874 freeparam(&shellparam);
11875 shellparam = saveparam;
11876 };
11877 status = exitstatus;
11878 }
11879 return status;
11880}
11881
11882static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011883exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011884{
11885 if (stoppedjobs())
11886 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011887 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011888 exitstatus = number(argv[1]);
11889 raise_exception(EXEXIT);
11890 /* NOTREACHED */
11891}
11892
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011893/*
11894 * Read a file containing shell functions.
11895 */
11896static void
11897readcmdfile(char *name)
11898{
11899 setinputfile(name, INPUT_PUSH_FILE);
11900 cmdloop(0);
11901 popfile();
11902}
11903
11904
Denis Vlasenkocc571512007-02-23 21:10:35 +000011905/* ============ find_command inplementation */
11906
11907/*
11908 * Resolve a command name. If you change this routine, you may have to
11909 * change the shellexec routine as well.
11910 */
11911static void
11912find_command(char *name, struct cmdentry *entry, int act, const char *path)
11913{
11914 struct tblentry *cmdp;
11915 int idx;
11916 int prev;
11917 char *fullname;
11918 struct stat statb;
11919 int e;
11920 int updatetbl;
11921 struct builtincmd *bcmd;
11922
11923 /* If name contains a slash, don't use PATH or hash table */
11924 if (strchr(name, '/') != NULL) {
11925 entry->u.index = -1;
11926 if (act & DO_ABS) {
11927 while (stat(name, &statb) < 0) {
11928#ifdef SYSV
11929 if (errno == EINTR)
11930 continue;
11931#endif
11932 entry->cmdtype = CMDUNKNOWN;
11933 return;
11934 }
11935 }
11936 entry->cmdtype = CMDNORMAL;
11937 return;
11938 }
11939
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011940/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011941
11942 updatetbl = (path == pathval());
11943 if (!updatetbl) {
11944 act |= DO_ALTPATH;
11945 if (strstr(path, "%builtin") != NULL)
11946 act |= DO_ALTBLTIN;
11947 }
11948
11949 /* If name is in the table, check answer will be ok */
11950 cmdp = cmdlookup(name, 0);
11951 if (cmdp != NULL) {
11952 int bit;
11953
11954 switch (cmdp->cmdtype) {
11955 default:
11956#if DEBUG
11957 abort();
11958#endif
11959 case CMDNORMAL:
11960 bit = DO_ALTPATH;
11961 break;
11962 case CMDFUNCTION:
11963 bit = DO_NOFUNC;
11964 break;
11965 case CMDBUILTIN:
11966 bit = DO_ALTBLTIN;
11967 break;
11968 }
11969 if (act & bit) {
11970 updatetbl = 0;
11971 cmdp = NULL;
11972 } else if (cmdp->rehash == 0)
11973 /* if not invalidated by cd, we're done */
11974 goto success;
11975 }
11976
11977 /* If %builtin not in path, check for builtin next */
11978 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011979 if (bcmd) {
11980 if (IS_BUILTIN_REGULAR(bcmd))
11981 goto builtin_success;
11982 if (act & DO_ALTPATH) {
11983 if (!(act & DO_ALTBLTIN))
11984 goto builtin_success;
11985 } else if (builtinloc <= 0) {
11986 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011987 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011988 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011989
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011990#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011991 {
11992 int applet_no = find_applet_by_name(name);
11993 if (applet_no >= 0) {
11994 entry->cmdtype = CMDNORMAL;
11995 entry->u.index = -2 - applet_no;
11996 return;
11997 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011998 }
11999#endif
12000
Denis Vlasenkocc571512007-02-23 21:10:35 +000012001 /* We have to search path. */
12002 prev = -1; /* where to start */
12003 if (cmdp && cmdp->rehash) { /* doing a rehash */
12004 if (cmdp->cmdtype == CMDBUILTIN)
12005 prev = builtinloc;
12006 else
12007 prev = cmdp->param.index;
12008 }
12009
12010 e = ENOENT;
12011 idx = -1;
12012 loop:
12013 while ((fullname = padvance(&path, name)) != NULL) {
12014 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012015 /* NB: code below will still use fullname
12016 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012017 idx++;
12018 if (pathopt) {
12019 if (prefix(pathopt, "builtin")) {
12020 if (bcmd)
12021 goto builtin_success;
12022 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012023 }
12024 if ((act & DO_NOFUNC)
12025 || !prefix(pathopt, "func")
12026 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012027 continue;
12028 }
12029 }
12030 /* if rehash, don't redo absolute path names */
12031 if (fullname[0] == '/' && idx <= prev) {
12032 if (idx < prev)
12033 continue;
12034 TRACE(("searchexec \"%s\": no change\n", name));
12035 goto success;
12036 }
12037 while (stat(fullname, &statb) < 0) {
12038#ifdef SYSV
12039 if (errno == EINTR)
12040 continue;
12041#endif
12042 if (errno != ENOENT && errno != ENOTDIR)
12043 e = errno;
12044 goto loop;
12045 }
12046 e = EACCES; /* if we fail, this will be the error */
12047 if (!S_ISREG(statb.st_mode))
12048 continue;
12049 if (pathopt) { /* this is a %func directory */
12050 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012051 /* NB: stalloc will return space pointed by fullname
12052 * (because we don't have any intervening allocations
12053 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012054 readcmdfile(fullname);
12055 cmdp = cmdlookup(name, 0);
12056 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12057 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12058 stunalloc(fullname);
12059 goto success;
12060 }
12061 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12062 if (!updatetbl) {
12063 entry->cmdtype = CMDNORMAL;
12064 entry->u.index = idx;
12065 return;
12066 }
12067 INT_OFF;
12068 cmdp = cmdlookup(name, 1);
12069 cmdp->cmdtype = CMDNORMAL;
12070 cmdp->param.index = idx;
12071 INT_ON;
12072 goto success;
12073 }
12074
12075 /* We failed. If there was an entry for this command, delete it */
12076 if (cmdp && updatetbl)
12077 delete_cmd_entry();
12078 if (act & DO_ERR)
12079 ash_msg("%s: %s", name, errmsg(e, "not found"));
12080 entry->cmdtype = CMDUNKNOWN;
12081 return;
12082
12083 builtin_success:
12084 if (!updatetbl) {
12085 entry->cmdtype = CMDBUILTIN;
12086 entry->u.cmd = bcmd;
12087 return;
12088 }
12089 INT_OFF;
12090 cmdp = cmdlookup(name, 1);
12091 cmdp->cmdtype = CMDBUILTIN;
12092 cmdp->param.cmd = bcmd;
12093 INT_ON;
12094 success:
12095 cmdp->rehash = 0;
12096 entry->cmdtype = cmdp->cmdtype;
12097 entry->u = cmdp->param;
12098}
12099
12100
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012101/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012102
Eric Andersencb57d552001-06-28 07:25:16 +000012103/*
Eric Andersencb57d552001-06-28 07:25:16 +000012104 * The trap builtin.
12105 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012106static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012107trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012108{
12109 char *action;
12110 char **ap;
12111 int signo;
12112
Eric Andersenc470f442003-07-28 09:56:35 +000012113 nextopt(nullstr);
12114 ap = argptr;
12115 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012116 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012117 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012118 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012119 single_quote(trap[signo]),
12120 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012121 }
12122 }
12123 return 0;
12124 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012125 action = NULL;
12126 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012127 action = *ap++;
12128 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012129 signo = get_signum(*ap);
12130 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012131 ash_msg_and_raise_error("%s: bad trap", *ap);
12132 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012133 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012134 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012135 action = NULL;
12136 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012137 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012138 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012139 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012140 trap[signo] = action;
12141 if (signo != 0)
12142 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012143 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012144 ap++;
12145 }
12146 return 0;
12147}
12148
Eric Andersenc470f442003-07-28 09:56:35 +000012149
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012150/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012151
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012152#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012153/*
12154 * Lists available builtins
12155 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012156static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012157helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012158{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012159 unsigned col;
12160 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012161
12162 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012163 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012164 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012165 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012166 if (col > 60) {
12167 out1fmt("\n");
12168 col = 0;
12169 }
12170 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012171#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012172 {
12173 const char *a = applet_names;
12174 while (*a) {
12175 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12176 if (col > 60) {
12177 out1fmt("\n");
12178 col = 0;
12179 }
12180 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012181 }
12182 }
12183#endif
12184 out1fmt("\n\n");
12185 return EXIT_SUCCESS;
12186}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012187#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012188
Eric Andersencb57d552001-06-28 07:25:16 +000012189/*
Eric Andersencb57d552001-06-28 07:25:16 +000012190 * The export and readonly commands.
12191 */
Eric Andersenc470f442003-07-28 09:56:35 +000012192static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012193exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012194{
12195 struct var *vp;
12196 char *name;
12197 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012198 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012199 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012200
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012201 if (nextopt("p") != 'p') {
12202 aptr = argptr;
12203 name = *aptr;
12204 if (name) {
12205 do {
12206 p = strchr(name, '=');
12207 if (p != NULL) {
12208 p++;
12209 } else {
12210 vp = *findvar(hashvar(name), name);
12211 if (vp) {
12212 vp->flags |= flag;
12213 continue;
12214 }
Eric Andersencb57d552001-06-28 07:25:16 +000012215 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012216 setvar(name, p, flag);
12217 } while ((name = *++aptr) != NULL);
12218 return 0;
12219 }
Eric Andersencb57d552001-06-28 07:25:16 +000012220 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012221 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012222 return 0;
12223}
12224
Eric Andersencb57d552001-06-28 07:25:16 +000012225/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012226 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012227 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012228static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012229unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012230{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012231 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012232
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012233 cmdp = cmdlookup(name, 0);
12234 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12235 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012236}
12237
Eric Andersencb57d552001-06-28 07:25:16 +000012238/*
Eric Andersencb57d552001-06-28 07:25:16 +000012239 * The unset builtin command. We unset the function before we unset the
12240 * variable to allow a function to be unset when there is a readonly variable
12241 * with the same name.
12242 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012243static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012244unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012245{
12246 char **ap;
12247 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012248 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012249 int ret = 0;
12250
12251 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012252 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012253 }
Eric Andersencb57d552001-06-28 07:25:16 +000012254
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012255 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012256 if (flag != 'f') {
12257 i = unsetvar(*ap);
12258 ret |= i;
12259 if (!(i & 2))
12260 continue;
12261 }
12262 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012263 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012264 }
Eric Andersenc470f442003-07-28 09:56:35 +000012265 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012266}
12267
12268
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012269/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012270
Eric Andersenc470f442003-07-28 09:56:35 +000012271#include <sys/times.h>
12272
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012273static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012274 ' ', offsetof(struct tms, tms_utime),
12275 '\n', offsetof(struct tms, tms_stime),
12276 ' ', offsetof(struct tms, tms_cutime),
12277 '\n', offsetof(struct tms, tms_cstime),
12278 0
12279};
Eric Andersencb57d552001-06-28 07:25:16 +000012280
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012281static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012282timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012283{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012284 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012285 const unsigned char *p;
12286 struct tms buf;
12287
12288 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012289 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012290
12291 p = timescmd_str;
12292 do {
12293 t = *(clock_t *)(((char *) &buf) + p[1]);
12294 s = t / clk_tck;
12295 out1fmt("%ldm%ld.%.3lds%c",
12296 s/60, s%60,
12297 ((t - s * clk_tck) * 1000) / clk_tck,
12298 p[0]);
12299 } while (*(p += 2));
12300
Eric Andersencb57d552001-06-28 07:25:16 +000012301 return 0;
12302}
12303
Denis Vlasenko131ae172007-02-18 13:00:19 +000012304#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012305static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012306dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012307{
Eric Andersened9ecf72004-06-22 08:29:45 +000012308 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012309 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012310
Denis Vlasenkob012b102007-02-19 22:43:01 +000012311 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012312 result = arith(s, &errcode);
12313 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012314 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012315 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012316 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012317 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012318 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012319 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012320 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012321 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012322 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012323
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012324 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012325}
Eric Andersenc470f442003-07-28 09:56:35 +000012326
Eric Andersenc470f442003-07-28 09:56:35 +000012327/*
Eric Andersen90898442003-08-06 11:20:52 +000012328 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12329 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12330 *
12331 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012332 */
12333static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012334letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012335{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012336 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012337
Denis Vlasenko68404f12008-03-17 09:00:54 +000012338 argv++;
12339 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012340 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012341 do {
12342 i = dash_arith(*argv);
12343 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012344
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012345 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012346}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012347#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012348
Eric Andersenc470f442003-07-28 09:56:35 +000012349
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012350/* ============ miscbltin.c
12351 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012352 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012353 */
12354
12355#undef rflag
12356
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012357#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012358typedef enum __rlimit_resource rlim_t;
12359#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012360
Eric Andersenc470f442003-07-28 09:56:35 +000012361/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012362 * The read builtin. Options:
12363 * -r Do not interpret '\' specially
12364 * -s Turn off echo (tty only)
12365 * -n NCHARS Read NCHARS max
12366 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12367 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12368 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012369 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012370 * TODO: bash also has:
12371 * -a ARRAY Read into array[0],[1],etc
12372 * -d DELIM End on DELIM char, not newline
12373 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012374 */
Eric Andersenc470f442003-07-28 09:56:35 +000012375static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012376readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012377{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012378 static const char *const arg_REPLY[] = { "REPLY", NULL };
12379
Eric Andersenc470f442003-07-28 09:56:35 +000012380 char **ap;
12381 int backslash;
12382 char c;
12383 int rflag;
12384 char *prompt;
12385 const char *ifs;
12386 char *p;
12387 int startword;
12388 int status;
12389 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012390 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012391#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012392 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012393 int silent = 0;
12394 struct termios tty, old_tty;
12395#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012396#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012397 unsigned end_ms = 0;
12398 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012399#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012400
12401 rflag = 0;
12402 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012403 while ((i = nextopt("p:u:r"
12404 USE_ASH_READ_TIMEOUT("t:")
12405 USE_ASH_READ_NCHARS("n:s")
12406 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012407 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012408 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012409 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012410 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012411#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012412 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012413 nchars = bb_strtou(optionarg, NULL, 10);
12414 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012415 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012416 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012417 break;
12418 case 's':
12419 silent = 1;
12420 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012421#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012422#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012423 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012424 timeout = bb_strtou(optionarg, NULL, 10);
12425 if (errno || timeout > UINT_MAX / 2048)
12426 ash_msg_and_raise_error("invalid timeout");
12427 timeout *= 1000;
12428#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012429 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012430 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012431 /* EINVAL means number is ok, but not terminated by NUL */
12432 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012433 char *p2;
12434 if (*++p) {
12435 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012436 ts.tv_usec = bb_strtou(p, &p2, 10);
12437 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012438 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012439 scale = p2 - p;
12440 /* normalize to usec */
12441 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012442 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012443 while (scale++ < 6)
12444 ts.tv_usec *= 10;
12445 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012446 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012447 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012448 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012449 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012450 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012451 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012452#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012453 break;
12454#endif
12455 case 'r':
12456 rflag = 1;
12457 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012458 case 'u':
12459 fd = bb_strtou(optionarg, NULL, 10);
12460 if (fd < 0 || errno)
12461 ash_msg_and_raise_error("invalid file descriptor");
12462 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012463 default:
12464 break;
12465 }
Eric Andersenc470f442003-07-28 09:56:35 +000012466 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012467 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012468 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012469 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012470 ap = argptr;
12471 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012472 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012473 ifs = bltinlookup("IFS");
12474 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012475 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012476#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012477 tcgetattr(fd, &tty);
12478 old_tty = tty;
12479 if (nchars || silent) {
12480 if (nchars) {
12481 tty.c_lflag &= ~ICANON;
12482 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012483 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012484 if (silent) {
12485 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12486 }
12487 /* if tcgetattr failed, tcsetattr will fail too.
12488 * Ignoring, it's harmless. */
12489 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012490 }
12491#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012492
Eric Andersenc470f442003-07-28 09:56:35 +000012493 status = 0;
12494 startword = 1;
12495 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012496#if ENABLE_ASH_READ_TIMEOUT
12497 if (timeout) /* NB: ensuring end_ms is nonzero */
12498 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12499#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012500 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012501 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012502#if ENABLE_ASH_READ_TIMEOUT
12503 if (end_ms) {
12504 struct pollfd pfd[1];
12505 pfd[0].fd = fd;
12506 pfd[0].events = POLLIN;
12507 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12508 if ((int)timeout <= 0 /* already late? */
12509 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12510 ) { /* timed out! */
12511#if ENABLE_ASH_READ_NCHARS
12512 tcsetattr(fd, TCSANOW, &old_tty);
12513#endif
12514 return 1;
12515 }
12516 }
12517#endif
12518 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012519 status = 1;
12520 break;
12521 }
12522 if (c == '\0')
12523 continue;
12524 if (backslash) {
12525 backslash = 0;
12526 if (c != '\n')
12527 goto put;
12528 continue;
12529 }
12530 if (!rflag && c == '\\') {
12531 backslash++;
12532 continue;
12533 }
12534 if (c == '\n')
12535 break;
12536 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12537 continue;
12538 }
12539 startword = 0;
12540 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12541 STACKSTRNUL(p);
12542 setvar(*ap, stackblock(), 0);
12543 ap++;
12544 startword = 1;
12545 STARTSTACKSTR(p);
12546 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012547 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012548 STPUTC(c, p);
12549 }
12550 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012551/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012552#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012553 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012554#else
12555 while (1);
12556#endif
12557
12558#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012559 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012560#endif
12561
Eric Andersenc470f442003-07-28 09:56:35 +000012562 STACKSTRNUL(p);
12563 /* Remove trailing blanks */
12564 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12565 *p = '\0';
12566 setvar(*ap, stackblock(), 0);
12567 while (*++ap != NULL)
12568 setvar(*ap, nullstr, 0);
12569 return status;
12570}
12571
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012572static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012573umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012574{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012575 static const char permuser[3] ALIGN1 = "ugo";
12576 static const char permmode[3] ALIGN1 = "rwx";
12577 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012578 S_IRUSR, S_IWUSR, S_IXUSR,
12579 S_IRGRP, S_IWGRP, S_IXGRP,
12580 S_IROTH, S_IWOTH, S_IXOTH
12581 };
12582
12583 char *ap;
12584 mode_t mask;
12585 int i;
12586 int symbolic_mode = 0;
12587
12588 while (nextopt("S") != '\0') {
12589 symbolic_mode = 1;
12590 }
12591
Denis Vlasenkob012b102007-02-19 22:43:01 +000012592 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012593 mask = umask(0);
12594 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012595 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012596
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012597 ap = *argptr;
12598 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012599 if (symbolic_mode) {
12600 char buf[18];
12601 char *p = buf;
12602
12603 for (i = 0; i < 3; i++) {
12604 int j;
12605
12606 *p++ = permuser[i];
12607 *p++ = '=';
12608 for (j = 0; j < 3; j++) {
12609 if ((mask & permmask[3 * i + j]) == 0) {
12610 *p++ = permmode[j];
12611 }
12612 }
12613 *p++ = ',';
12614 }
12615 *--p = 0;
12616 puts(buf);
12617 } else {
12618 out1fmt("%.4o\n", mask);
12619 }
12620 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012621 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012622 mask = 0;
12623 do {
12624 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012625 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012626 mask = (mask << 3) + (*ap - '0');
12627 } while (*++ap != '\0');
12628 umask(mask);
12629 } else {
12630 mask = ~mask & 0777;
12631 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012632 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012633 }
12634 umask(~mask & 0777);
12635 }
12636 }
12637 return 0;
12638}
12639
12640/*
12641 * ulimit builtin
12642 *
12643 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12644 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12645 * ash by J.T. Conklin.
12646 *
12647 * Public domain.
12648 */
12649
12650struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012651 uint8_t cmd; /* RLIMIT_xxx fit into it */
12652 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012653 char option;
12654};
12655
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012656static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012657#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012658 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012659#endif
12660#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012661 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012662#endif
12663#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012664 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012665#endif
12666#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012667 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012668#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012669#ifdef RLIMIT_CORE
12670 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012671#endif
12672#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012673 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012674#endif
12675#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012676 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012677#endif
12678#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012679 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012680#endif
12681#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012682 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012683#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012684#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012685 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012686#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012687#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012688 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012689#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012690};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012691static const char limits_name[] =
12692#ifdef RLIMIT_CPU
12693 "time(seconds)" "\0"
12694#endif
12695#ifdef RLIMIT_FSIZE
12696 "file(blocks)" "\0"
12697#endif
12698#ifdef RLIMIT_DATA
12699 "data(kb)" "\0"
12700#endif
12701#ifdef RLIMIT_STACK
12702 "stack(kb)" "\0"
12703#endif
12704#ifdef RLIMIT_CORE
12705 "coredump(blocks)" "\0"
12706#endif
12707#ifdef RLIMIT_RSS
12708 "memory(kb)" "\0"
12709#endif
12710#ifdef RLIMIT_MEMLOCK
12711 "locked memory(kb)" "\0"
12712#endif
12713#ifdef RLIMIT_NPROC
12714 "process" "\0"
12715#endif
12716#ifdef RLIMIT_NOFILE
12717 "nofiles" "\0"
12718#endif
12719#ifdef RLIMIT_AS
12720 "vmemory(kb)" "\0"
12721#endif
12722#ifdef RLIMIT_LOCKS
12723 "locks" "\0"
12724#endif
12725;
Eric Andersenc470f442003-07-28 09:56:35 +000012726
Glenn L McGrath76620622004-01-13 10:19:37 +000012727enum limtype { SOFT = 0x1, HARD = 0x2 };
12728
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012729static void
12730printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012731 const struct limits *l)
12732{
12733 rlim_t val;
12734
12735 val = limit->rlim_max;
12736 if (how & SOFT)
12737 val = limit->rlim_cur;
12738
12739 if (val == RLIM_INFINITY)
12740 out1fmt("unlimited\n");
12741 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012742 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012743 out1fmt("%lld\n", (long long) val);
12744 }
12745}
12746
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012747static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012748ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012749{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012750 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012751 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012752 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012753 const struct limits *l;
12754 int set, all = 0;
12755 int optc, what;
12756 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012757
12758 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012759 while ((optc = nextopt("HSa"
12760#ifdef RLIMIT_CPU
12761 "t"
12762#endif
12763#ifdef RLIMIT_FSIZE
12764 "f"
12765#endif
12766#ifdef RLIMIT_DATA
12767 "d"
12768#endif
12769#ifdef RLIMIT_STACK
12770 "s"
12771#endif
12772#ifdef RLIMIT_CORE
12773 "c"
12774#endif
12775#ifdef RLIMIT_RSS
12776 "m"
12777#endif
12778#ifdef RLIMIT_MEMLOCK
12779 "l"
12780#endif
12781#ifdef RLIMIT_NPROC
12782 "p"
12783#endif
12784#ifdef RLIMIT_NOFILE
12785 "n"
12786#endif
12787#ifdef RLIMIT_AS
12788 "v"
12789#endif
12790#ifdef RLIMIT_LOCKS
12791 "w"
12792#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012793 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012794 switch (optc) {
12795 case 'H':
12796 how = HARD;
12797 break;
12798 case 'S':
12799 how = SOFT;
12800 break;
12801 case 'a':
12802 all = 1;
12803 break;
12804 default:
12805 what = optc;
12806 }
12807
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012808 for (l = limits_tbl; l->option != what; l++)
12809 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012810
12811 set = *argptr ? 1 : 0;
12812 if (set) {
12813 char *p = *argptr;
12814
12815 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012816 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012817 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012818 val = RLIM_INFINITY;
12819 else {
12820 val = (rlim_t) 0;
12821
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012822 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012823 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012824 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012825 if (val < (rlim_t) 0)
12826 break;
12827 }
12828 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012829 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012830 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012831 }
12832 }
12833 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012834 const char *lname = limits_name;
12835 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012836 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012837 out1fmt("%-20s ", lname);
12838 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012839 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012840 }
12841 return 0;
12842 }
12843
12844 getrlimit(l->cmd, &limit);
12845 if (set) {
12846 if (how & HARD)
12847 limit.rlim_max = val;
12848 if (how & SOFT)
12849 limit.rlim_cur = val;
12850 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012851 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012852 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012853 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012854 }
12855 return 0;
12856}
12857
Eric Andersen90898442003-08-06 11:20:52 +000012858
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012859/* ============ Math support */
12860
Denis Vlasenko131ae172007-02-18 13:00:19 +000012861#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012862
12863/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12864
12865 Permission is hereby granted, free of charge, to any person obtaining
12866 a copy of this software and associated documentation files (the
12867 "Software"), to deal in the Software without restriction, including
12868 without limitation the rights to use, copy, modify, merge, publish,
12869 distribute, sublicense, and/or sell copies of the Software, and to
12870 permit persons to whom the Software is furnished to do so, subject to
12871 the following conditions:
12872
12873 The above copyright notice and this permission notice shall be
12874 included in all copies or substantial portions of the Software.
12875
12876 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12877 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12878 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12879 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12880 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12881 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12882 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12883*/
12884
12885/* This is my infix parser/evaluator. It is optimized for size, intended
12886 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012887 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012888 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012889 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012890 * be that which POSIX specifies for shells. */
12891
12892/* The code uses a simple two-stack algorithm. See
12893 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012894 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012895 * this is based (this code differs in that it applies operators immediately
12896 * to the stack instead of adding them to a queue to end up with an
12897 * expression). */
12898
12899/* To use the routine, call it with an expression string and error return
12900 * pointer */
12901
12902/*
12903 * Aug 24, 2001 Manuel Novoa III
12904 *
12905 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12906 *
12907 * 1) In arith_apply():
12908 * a) Cached values of *numptr and &(numptr[-1]).
12909 * b) Removed redundant test for zero denominator.
12910 *
12911 * 2) In arith():
12912 * a) Eliminated redundant code for processing operator tokens by moving
12913 * to a table-based implementation. Also folded handling of parens
12914 * into the table.
12915 * b) Combined all 3 loops which called arith_apply to reduce generated
12916 * code size at the cost of speed.
12917 *
12918 * 3) The following expressions were treated as valid by the original code:
12919 * 1() , 0! , 1 ( *3 ) .
12920 * These bugs have been fixed by internally enclosing the expression in
12921 * parens and then checking that all binary ops and right parens are
12922 * preceded by a valid expression (NUM_TOKEN).
12923 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012924 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012925 * ctype's isspace() if it is used by another busybox applet or if additional
12926 * whitespace chars should be considered. Look below the "#include"s for a
12927 * precompiler test.
12928 */
12929
12930/*
12931 * Aug 26, 2001 Manuel Novoa III
12932 *
12933 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12934 *
12935 * Merge in Aaron's comments previously posted to the busybox list,
12936 * modified slightly to take account of my changes to the code.
12937 *
12938 */
12939
12940/*
12941 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12942 *
12943 * - allow access to variable,
12944 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12945 * - realize assign syntax (VAR=expr, +=, *= etc)
12946 * - realize exponentiation (** operator)
12947 * - realize comma separated - expr, expr
12948 * - realise ++expr --expr expr++ expr--
12949 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012950 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012951 * - was restored loses XOR operator
12952 * - remove one goto label, added three ;-)
12953 * - protect $((num num)) as true zero expr (Manuel`s error)
12954 * - always use special isspace(), see comment from bash ;-)
12955 */
12956
Eric Andersen90898442003-08-06 11:20:52 +000012957#define arith_isspace(arithval) \
12958 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12959
Eric Andersen90898442003-08-06 11:20:52 +000012960typedef unsigned char operator;
12961
12962/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012963 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012964 * precedence. The ID portion is so that multiple operators can have the
12965 * same precedence, ensuring that the leftmost one is evaluated first.
12966 * Consider * and /. */
12967
12968#define tok_decl(prec,id) (((id)<<5)|(prec))
12969#define PREC(op) ((op) & 0x1F)
12970
12971#define TOK_LPAREN tok_decl(0,0)
12972
12973#define TOK_COMMA tok_decl(1,0)
12974
12975#define TOK_ASSIGN tok_decl(2,0)
12976#define TOK_AND_ASSIGN tok_decl(2,1)
12977#define TOK_OR_ASSIGN tok_decl(2,2)
12978#define TOK_XOR_ASSIGN tok_decl(2,3)
12979#define TOK_PLUS_ASSIGN tok_decl(2,4)
12980#define TOK_MINUS_ASSIGN tok_decl(2,5)
12981#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12982#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12983
12984#define TOK_MUL_ASSIGN tok_decl(3,0)
12985#define TOK_DIV_ASSIGN tok_decl(3,1)
12986#define TOK_REM_ASSIGN tok_decl(3,2)
12987
12988/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012989#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012990
12991/* conditional is right associativity too */
12992#define TOK_CONDITIONAL tok_decl(4,0)
12993#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12994
12995#define TOK_OR tok_decl(5,0)
12996
12997#define TOK_AND tok_decl(6,0)
12998
12999#define TOK_BOR tok_decl(7,0)
13000
13001#define TOK_BXOR tok_decl(8,0)
13002
13003#define TOK_BAND tok_decl(9,0)
13004
13005#define TOK_EQ tok_decl(10,0)
13006#define TOK_NE tok_decl(10,1)
13007
13008#define TOK_LT tok_decl(11,0)
13009#define TOK_GT tok_decl(11,1)
13010#define TOK_GE tok_decl(11,2)
13011#define TOK_LE tok_decl(11,3)
13012
13013#define TOK_LSHIFT tok_decl(12,0)
13014#define TOK_RSHIFT tok_decl(12,1)
13015
13016#define TOK_ADD tok_decl(13,0)
13017#define TOK_SUB tok_decl(13,1)
13018
13019#define TOK_MUL tok_decl(14,0)
13020#define TOK_DIV tok_decl(14,1)
13021#define TOK_REM tok_decl(14,2)
13022
13023/* exponent is right associativity */
13024#define TOK_EXPONENT tok_decl(15,1)
13025
13026/* For now unary operators. */
13027#define UNARYPREC 16
13028#define TOK_BNOT tok_decl(UNARYPREC,0)
13029#define TOK_NOT tok_decl(UNARYPREC,1)
13030
13031#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13032#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13033
13034#define PREC_PRE (UNARYPREC+2)
13035
13036#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13037#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13038
13039#define PREC_POST (UNARYPREC+3)
13040
13041#define TOK_POST_INC tok_decl(PREC_POST, 0)
13042#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13043
13044#define SPEC_PREC (UNARYPREC+4)
13045
13046#define TOK_NUM tok_decl(SPEC_PREC, 0)
13047#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13048
13049#define NUMPTR (*numstackptr)
13050
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013051static int
13052tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013053{
13054 operator prec = PREC(op);
13055
13056 convert_prec_is_assing(prec);
13057 return (prec == PREC(TOK_ASSIGN) ||
13058 prec == PREC_PRE || prec == PREC_POST);
13059}
13060
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013061static int
13062is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013063{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013064 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13065 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013066}
13067
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013068typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013069 arith_t val;
13070 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013071 char contidional_second_val_initialized;
13072 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013073 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013074} v_n_t;
13075
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013076typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013077 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013078 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013079} chk_var_recursive_looped_t;
13080
13081static chk_var_recursive_looped_t *prev_chk_var_recursive;
13082
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013083static int
13084arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013085{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 if (t->var) {
13087 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013088
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013089 if (p) {
13090 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013091
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013092 /* recursive try as expression */
13093 chk_var_recursive_looped_t *cur;
13094 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013095
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13097 if (strcmp(cur->var, t->var) == 0) {
13098 /* expression recursion loop detected */
13099 return -5;
13100 }
13101 }
13102 /* save current lookuped var name */
13103 cur = prev_chk_var_recursive;
13104 cur_save.var = t->var;
13105 cur_save.next = cur;
13106 prev_chk_var_recursive = &cur_save;
13107
13108 t->val = arith (p, &errcode);
13109 /* restore previous ptr after recursiving */
13110 prev_chk_var_recursive = cur;
13111 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013112 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013113 /* allow undefined var as 0 */
13114 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013115 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013116 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013117}
13118
13119/* "applying" a token means performing it on the top elements on the integer
13120 * stack. For a unary operator it will only change the top element, but a
13121 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013122static int
13123arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013124{
Eric Andersen90898442003-08-06 11:20:52 +000013125 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013126 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013127 int ret_arith_lookup_val;
13128
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013129 /* There is no operator that can work without arguments */
13130 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013131 numptr_m1 = NUMPTR - 1;
13132
13133 /* check operand is var with noninteger value */
13134 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013135 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013136 return ret_arith_lookup_val;
13137
13138 rez = numptr_m1->val;
13139 if (op == TOK_UMINUS)
13140 rez *= -1;
13141 else if (op == TOK_NOT)
13142 rez = !rez;
13143 else if (op == TOK_BNOT)
13144 rez = ~rez;
13145 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13146 rez++;
13147 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13148 rez--;
13149 else if (op != TOK_UPLUS) {
13150 /* Binary operators */
13151
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013152 /* check and binary operators need two arguments */
13153 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013154
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013155 /* ... and they pop one */
13156 --NUMPTR;
13157 numptr_val = rez;
13158 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013159 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013160 /* protect $((expr1 ? expr2)) without ": expr" */
13161 goto err;
13162 }
13163 rez = numptr_m1->contidional_second_val;
13164 } else if (numptr_m1->contidional_second_val_initialized) {
13165 /* protect $((expr1 : expr2)) without "expr ? " */
13166 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013167 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013168 numptr_m1 = NUMPTR - 1;
13169 if (op != TOK_ASSIGN) {
13170 /* check operand is var with noninteger value for not '=' */
13171 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13172 if (ret_arith_lookup_val)
13173 return ret_arith_lookup_val;
13174 }
13175 if (op == TOK_CONDITIONAL) {
13176 numptr_m1->contidional_second_val = rez;
13177 }
13178 rez = numptr_m1->val;
13179 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013180 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013181 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013182 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013183 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013184 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013185 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013186 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013187 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013188 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013189 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013190 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013191 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013192 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013193 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013194 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013195 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013196 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013197 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013198 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013199 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013200 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013201 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013202 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013203 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013204 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013206 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013207 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013208 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013209 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013210 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013211 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013212 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013213 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013214 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013215 /* protect $((expr : expr)) without "expr ? " */
13216 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013217 }
13218 numptr_m1->contidional_second_val_initialized = op;
13219 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013220 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013221 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013222 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013223 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013224 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013225 return -3; /* exponent less than 0 */
13226 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013227 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013228
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013229 if (numptr_val)
13230 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013231 c *= rez;
13232 rez = c;
13233 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013234 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013235 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013236 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013237 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013238 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013239 rez %= numptr_val;
13240 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013241 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013242 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013243
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013244 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013245 /* Hmm, 1=2 ? */
13246 goto err;
13247 }
13248 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013249#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013250 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013251#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013252 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013253#endif
Eric Andersen90898442003-08-06 11:20:52 +000013254 setvar(numptr_m1->var, buf, 0);
13255 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013256 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013257 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013258 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013259 rez++;
13260 }
13261 numptr_m1->val = rez;
13262 /* protect geting var value, is number now */
13263 numptr_m1->var = NULL;
13264 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013265 err:
13266 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013267}
13268
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013269/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013270static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013271 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13272 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13273 '<','<', 0, TOK_LSHIFT,
13274 '>','>', 0, TOK_RSHIFT,
13275 '|','|', 0, TOK_OR,
13276 '&','&', 0, TOK_AND,
13277 '!','=', 0, TOK_NE,
13278 '<','=', 0, TOK_LE,
13279 '>','=', 0, TOK_GE,
13280 '=','=', 0, TOK_EQ,
13281 '|','=', 0, TOK_OR_ASSIGN,
13282 '&','=', 0, TOK_AND_ASSIGN,
13283 '*','=', 0, TOK_MUL_ASSIGN,
13284 '/','=', 0, TOK_DIV_ASSIGN,
13285 '%','=', 0, TOK_REM_ASSIGN,
13286 '+','=', 0, TOK_PLUS_ASSIGN,
13287 '-','=', 0, TOK_MINUS_ASSIGN,
13288 '-','-', 0, TOK_POST_DEC,
13289 '^','=', 0, TOK_XOR_ASSIGN,
13290 '+','+', 0, TOK_POST_INC,
13291 '*','*', 0, TOK_EXPONENT,
13292 '!', 0, TOK_NOT,
13293 '<', 0, TOK_LT,
13294 '>', 0, TOK_GT,
13295 '=', 0, TOK_ASSIGN,
13296 '|', 0, TOK_BOR,
13297 '&', 0, TOK_BAND,
13298 '*', 0, TOK_MUL,
13299 '/', 0, TOK_DIV,
13300 '%', 0, TOK_REM,
13301 '+', 0, TOK_ADD,
13302 '-', 0, TOK_SUB,
13303 '^', 0, TOK_BXOR,
13304 /* uniq */
13305 '~', 0, TOK_BNOT,
13306 ',', 0, TOK_COMMA,
13307 '?', 0, TOK_CONDITIONAL,
13308 ':', 0, TOK_CONDITIONAL_SEP,
13309 ')', 0, TOK_RPAREN,
13310 '(', 0, TOK_LPAREN,
13311 0
13312};
13313/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013314#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013315
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013316static arith_t
13317arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013318{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013319 char arithval; /* Current character under analysis */
13320 operator lasttok, op;
13321 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013322 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013323 const char *p = endexpression;
13324 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013325 v_n_t *numstack, *numstackptr;
13326 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013327
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013328 /* Stack of integers */
13329 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13330 * in any given correct or incorrect expression is left as an exercise to
13331 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013332 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013333 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013334 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013335
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013336 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13337 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013338
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013339 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013340 arithval = *expr;
13341 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013342 if (p == endexpression) {
13343 /* Null expression. */
13344 return 0;
13345 }
13346
13347 /* This is only reached after all tokens have been extracted from the
13348 * input stream. If there are still tokens on the operator stack, they
13349 * are to be applied in order. At the end, there should be a final
13350 * result on the integer stack */
13351
13352 if (expr != endexpression + 1) {
13353 /* If we haven't done so already, */
13354 /* append a closing right paren */
13355 expr = endexpression;
13356 /* and let the loop process it. */
13357 continue;
13358 }
13359 /* At this point, we're done with the expression. */
13360 if (numstackptr != numstack+1) {
13361 /* ... but if there isn't, it's bad */
13362 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013363 *perrcode = -1;
13364 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013365 }
13366 if (numstack->var) {
13367 /* expression is $((var)) only, lookup now */
13368 errcode = arith_lookup_val(numstack);
13369 }
13370 ret:
13371 *perrcode = errcode;
13372 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013373 }
13374
Eric Andersen90898442003-08-06 11:20:52 +000013375 /* Continue processing the expression. */
13376 if (arith_isspace(arithval)) {
13377 /* Skip whitespace */
13378 goto prologue;
13379 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013380 p = endofname(expr);
13381 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013382 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013383
13384 numstackptr->var = alloca(var_name_size);
13385 safe_strncpy(numstackptr->var, expr, var_name_size);
13386 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013387 num:
Eric Andersen90898442003-08-06 11:20:52 +000013388 numstackptr->contidional_second_val_initialized = 0;
13389 numstackptr++;
13390 lasttok = TOK_NUM;
13391 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013392 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013393 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013394 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013395#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013396 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013397#else
13398 numstackptr->val = strtol(expr, (char **) &expr, 0);
13399#endif
Eric Andersen90898442003-08-06 11:20:52 +000013400 goto num;
13401 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013402 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013403 const char *o;
13404
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013405 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013406 /* strange operator not found */
13407 goto err;
13408 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013409 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013410 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013411 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013412 /* found */
13413 expr = o - 1;
13414 break;
13415 }
13416 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013417 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013418 p++;
13419 /* skip zero delim */
13420 p++;
13421 }
13422 op = p[1];
13423
13424 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013425 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13426 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013427
13428 /* Plus and minus are binary (not unary) _only_ if the last
13429 * token was as number, or a right paren (which pretends to be
13430 * a number, since it evaluates to one). Think about it.
13431 * It makes sense. */
13432 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013433 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013434 case TOK_ADD:
13435 op = TOK_UPLUS;
13436 break;
13437 case TOK_SUB:
13438 op = TOK_UMINUS;
13439 break;
13440 case TOK_POST_INC:
13441 op = TOK_PRE_INC;
13442 break;
13443 case TOK_POST_DEC:
13444 op = TOK_PRE_DEC;
13445 break;
Eric Andersen90898442003-08-06 11:20:52 +000013446 }
13447 }
13448 /* We don't want a unary operator to cause recursive descent on the
13449 * stack, because there can be many in a row and it could cause an
13450 * operator to be evaluated before its argument is pushed onto the
13451 * integer stack. */
13452 /* But for binary operators, "apply" everything on the operator
13453 * stack until we find an operator with a lesser priority than the
13454 * one we have just extracted. */
13455 /* Left paren is given the lowest priority so it will never be
13456 * "applied" in this way.
13457 * if associativity is right and priority eq, applied also skip
13458 */
13459 prec = PREC(op);
13460 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13461 /* not left paren or unary */
13462 if (lasttok != TOK_NUM) {
13463 /* binary op must be preceded by a num */
13464 goto err;
13465 }
13466 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013467 if (op == TOK_RPAREN) {
13468 /* The algorithm employed here is simple: while we don't
13469 * hit an open paren nor the bottom of the stack, pop
13470 * tokens and apply them */
13471 if (stackptr[-1] == TOK_LPAREN) {
13472 --stackptr;
13473 /* Any operator directly after a */
13474 lasttok = TOK_NUM;
13475 /* close paren should consider itself binary */
13476 goto prologue;
13477 }
13478 } else {
13479 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013480
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013481 convert_prec_is_assing(prec);
13482 convert_prec_is_assing(prev_prec);
13483 if (prev_prec < prec)
13484 break;
13485 /* check right assoc */
13486 if (prev_prec == prec && is_right_associativity(prec))
13487 break;
13488 }
13489 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13490 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013491 }
13492 if (op == TOK_RPAREN) {
13493 goto err;
13494 }
13495 }
13496
13497 /* Push this operator to the stack and remember it. */
13498 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013499 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013500 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013501 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013502}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013503#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013504
13505
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013506/* ============ main() and helpers */
13507
13508/*
13509 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013510 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013511static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013512static void
13513exitshell(void)
13514{
13515 struct jmploc loc;
13516 char *p;
13517 int status;
13518
13519 status = exitstatus;
13520 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13521 if (setjmp(loc.loc)) {
13522 if (exception == EXEXIT)
13523/* dash bug: it just does _exit(exitstatus) here
13524 * but we have to do setjobctl(0) first!
13525 * (bug is still not fixed in dash-0.5.3 - if you run dash
13526 * under Midnight Commander, on exit from dash MC is backgrounded) */
13527 status = exitstatus;
13528 goto out;
13529 }
13530 exception_handler = &loc;
13531 p = trap[0];
13532 if (p) {
13533 trap[0] = NULL;
13534 evalstring(p, 0);
13535 }
13536 flush_stdout_stderr();
13537 out:
13538 setjobctl(0);
13539 _exit(status);
13540 /* NOTREACHED */
13541}
13542
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013543static void
13544init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013545{
13546 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013547 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013548
13549 /* from trap.c: */
13550 signal(SIGCHLD, SIG_DFL);
13551
13552 /* from var.c: */
13553 {
13554 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013555 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013556 const char *p;
13557 struct stat st1, st2;
13558
13559 initvar();
13560 for (envp = environ; envp && *envp; envp++) {
13561 if (strchr(*envp, '=')) {
13562 setvareq(*envp, VEXPORT|VTEXTFIXED);
13563 }
13564 }
13565
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013566 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013567 setvar("PPID", ppid, 0);
13568
13569 p = lookupvar("PWD");
13570 if (p)
13571 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13572 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13573 p = '\0';
13574 setpwd(p, 0);
13575 }
13576}
13577
13578/*
13579 * Process the shell command line arguments.
13580 */
13581static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013582procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013583{
13584 int i;
13585 const char *xminusc;
13586 char **xargv;
13587
13588 xargv = argv;
13589 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013590 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013591 xargv++;
13592 for (i = 0; i < NOPTS; i++)
13593 optlist[i] = 2;
13594 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013595 if (options(1)) {
13596 /* it already printed err message */
13597 raise_exception(EXERROR);
13598 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013599 xargv = argptr;
13600 xminusc = minusc;
13601 if (*xargv == NULL) {
13602 if (xminusc)
13603 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13604 sflag = 1;
13605 }
13606 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13607 iflag = 1;
13608 if (mflag == 2)
13609 mflag = iflag;
13610 for (i = 0; i < NOPTS; i++)
13611 if (optlist[i] == 2)
13612 optlist[i] = 0;
13613#if DEBUG == 2
13614 debug = 1;
13615#endif
13616 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13617 if (xminusc) {
13618 minusc = *xargv++;
13619 if (*xargv)
13620 goto setarg0;
13621 } else if (!sflag) {
13622 setinputfile(*xargv, 0);
13623 setarg0:
13624 arg0 = *xargv++;
13625 commandname = arg0;
13626 }
13627
13628 shellparam.p = xargv;
13629#if ENABLE_ASH_GETOPTS
13630 shellparam.optind = 1;
13631 shellparam.optoff = -1;
13632#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013633 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013634 while (*xargv) {
13635 shellparam.nparam++;
13636 xargv++;
13637 }
13638 optschanged();
13639}
13640
13641/*
13642 * Read /etc/profile or .profile.
13643 */
13644static void
13645read_profile(const char *name)
13646{
13647 int skip;
13648
13649 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13650 return;
13651 skip = cmdloop(0);
13652 popfile();
13653 if (skip)
13654 exitshell();
13655}
13656
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013657/*
13658 * This routine is called when an error or an interrupt occurs in an
13659 * interactive shell and control is returned to the main command loop.
13660 */
13661static void
13662reset(void)
13663{
13664 /* from eval.c: */
13665 evalskip = 0;
13666 loopnest = 0;
13667 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013668 g_parsefile->left_in_buffer = 0;
13669 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013670 popallfiles();
13671 /* from parser.c: */
13672 tokpushback = 0;
13673 checkkwd = 0;
13674 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013675 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013676}
13677
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013678#if PROFILE
13679static short profile_buf[16384];
13680extern int etext();
13681#endif
13682
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013683/*
13684 * Main routine. We initialize things, parse the arguments, execute
13685 * profiles if we're a login shell, and then call cmdloop to execute
13686 * commands. The setjmp call sets up the location to jump to when an
13687 * exception occurs. When an exception occurs the variable "state"
13688 * is used to figure out how far we had gotten.
13689 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013690int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013691int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013692{
13693 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013694 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013695 struct jmploc jmploc;
13696 struct stackmark smark;
13697
Denis Vlasenko01631112007-12-16 17:20:38 +000013698 /* Initialize global data */
13699 INIT_G_misc();
13700 INIT_G_memstack();
13701 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013702#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013703 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013704#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013705 INIT_G_cmdtable();
13706
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013707#if PROFILE
13708 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13709#endif
13710
13711#if ENABLE_FEATURE_EDITING
13712 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13713#endif
13714 state = 0;
13715 if (setjmp(jmploc.loc)) {
13716 int e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013717 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013718
13719 reset();
13720
13721 e = exception;
13722 if (e == EXERROR)
13723 exitstatus = 2;
13724 s = state;
13725 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13726 exitshell();
13727
13728 if (e == EXINT) {
13729 outcslow('\n', stderr);
13730 }
13731 popstackmark(&smark);
13732 FORCE_INT_ON; /* enable interrupts */
13733 if (s == 1)
13734 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013735 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013736 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013737 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013738 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013739 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013740 }
13741 exception_handler = &jmploc;
13742#if DEBUG
13743 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013744 trace_puts("Shell args: ");
13745 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013746#endif
13747 rootpid = getpid();
13748
13749#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013750 /* Can use monotonic_ns() for better randomness but for now it is
13751 * not used anywhere else in busybox... so avoid bloat */
13752 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013753#endif
13754 init();
13755 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013756 procargs(argv);
13757
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013758#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13759 if (iflag) {
13760 const char *hp = lookupvar("HISTFILE");
13761
13762 if (hp == NULL) {
13763 hp = lookupvar("HOME");
13764 if (hp != NULL) {
13765 char *defhp = concat_path_file(hp, ".ash_history");
13766 setvar("HISTFILE", defhp, 0);
13767 free(defhp);
13768 }
13769 }
13770 }
13771#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013772 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013773 isloginsh = 1;
13774 if (isloginsh) {
13775 state = 1;
13776 read_profile("/etc/profile");
13777 state1:
13778 state = 2;
13779 read_profile(".profile");
13780 }
13781 state2:
13782 state = 3;
13783 if (
13784#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013785 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013786#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013787 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013788 ) {
13789 shinit = lookupvar("ENV");
13790 if (shinit != NULL && *shinit != '\0') {
13791 read_profile(shinit);
13792 }
13793 }
13794 state3:
13795 state = 4;
13796 if (minusc)
13797 evalstring(minusc, 0);
13798
13799 if (sflag || minusc == NULL) {
13800#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013801 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013802 const char *hp = lookupvar("HISTFILE");
13803
13804 if (hp != NULL)
13805 line_input_state->hist_file = hp;
13806 }
13807#endif
13808 state4: /* XXX ??? - why isn't this before the "if" statement */
13809 cmdloop(1);
13810 }
13811#if PROFILE
13812 monitor(0);
13813#endif
13814#ifdef GPROF
13815 {
13816 extern void _mcleanup(void);
13817 _mcleanup();
13818 }
13819#endif
13820 exitshell();
13821 /* NOTREACHED */
13822}
13823
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013824#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013825const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013826int main(int argc, char **argv)
13827{
13828 return ash_main(argc, argv);
13829}
13830#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013831
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013832
Eric Andersendf82f612001-06-28 07:46:40 +000013833/*-
13834 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013835 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013836 *
13837 * This code is derived from software contributed to Berkeley by
13838 * Kenneth Almquist.
13839 *
13840 * Redistribution and use in source and binary forms, with or without
13841 * modification, are permitted provided that the following conditions
13842 * are met:
13843 * 1. Redistributions of source code must retain the above copyright
13844 * notice, this list of conditions and the following disclaimer.
13845 * 2. Redistributions in binary form must reproduce the above copyright
13846 * notice, this list of conditions and the following disclaimer in the
13847 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013848 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013849 * may be used to endorse or promote products derived from this software
13850 * without specific prior written permission.
13851 *
13852 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13853 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13854 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13855 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13856 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13857 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13858 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13859 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13860 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13861 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13862 * SUCH DAMAGE.
13863 */