blob: 9bb1d421ec3d0e182348215231906feff330d234 [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 */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000165// TODO: rename
166// pendingsig -> pending_sig
167// intpending -> pending_int
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000168 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
169 /* last pending signal */
170 volatile /*sig_atomic_t*/ smallint pendingsig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000171 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000172 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000173#define EXINT 0 /* SIGINT received */
174#define EXERROR 1 /* a generic error */
175#define EXSHELLPROC 2 /* execute a shell procedure */
176#define EXEXEC 3 /* command execution failed */
177#define EXEXIT 4 /* exit the shell */
178#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000179
Denis Vlasenko01631112007-12-16 17:20:38 +0000180 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000181 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000182
183 char optlist[NOPTS];
184#define eflag optlist[0]
185#define fflag optlist[1]
186#define Iflag optlist[2]
187#define iflag optlist[3]
188#define mflag optlist[4]
189#define nflag optlist[5]
190#define sflag optlist[6]
191#define xflag optlist[7]
192#define vflag optlist[8]
193#define Cflag optlist[9]
194#define aflag optlist[10]
195#define bflag optlist[11]
196#define uflag optlist[12]
197#define viflag optlist[13]
198#if DEBUG
199#define nolog optlist[14]
200#define debug optlist[15]
201#endif
202
203 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000204 /*
205 * Sigmode records the current value of the signal handlers for the various
206 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000207 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000208 */
209 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000210#define S_DFL 1 /* default signal handling (SIG_DFL) */
211#define S_CATCH 2 /* signal is caught */
212#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000213#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000214
Denis Vlasenko01631112007-12-16 17:20:38 +0000215 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000216 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000217 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000218
219 /* Rarely referenced stuff */
220#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000221 /* Random number generators */
222 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
223 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000224#endif
225 pid_t backgndpid; /* pid of last background process */
226 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000227};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000228extern struct globals_misc *const ash_ptr_to_globals_misc;
229#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000230#define rootpid (G_misc.rootpid )
231#define shlvl (G_misc.shlvl )
232#define minusc (G_misc.minusc )
233#define curdir (G_misc.curdir )
234#define physdir (G_misc.physdir )
235#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000236#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000237#define exception_type (G_misc.exception_type )
Denis Vlasenko01631112007-12-16 17:20:38 +0000238#define suppressint (G_misc.suppressint )
239#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000240//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000241#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000242#define isloginsh (G_misc.isloginsh )
243#define nullstr (G_misc.nullstr )
244#define optlist (G_misc.optlist )
245#define sigmode (G_misc.sigmode )
246#define gotsig (G_misc.gotsig )
247#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000248#define random_galois_LFSR (G_misc.random_galois_LFSR)
249#define random_LCG (G_misc.random_LCG )
250#define backgndpid (G_misc.backgndpid )
251#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000252#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000253 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
254 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000255 curdir = nullstr; \
256 physdir = nullstr; \
257} while (0)
258
259
Denis Vlasenko559691a2008-10-05 18:39:31 +0000260/* ============ Utility functions */
261static int isdigit_str9(const char *str)
262{
263 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
264 while (--maxlen && isdigit(*str))
265 str++;
266 return (*str == '\0');
267}
Denis Vlasenko01631112007-12-16 17:20:38 +0000268
Denis Vlasenko559691a2008-10-05 18:39:31 +0000269
270/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000271/*
Eric Andersen2870d962001-07-02 17:27:21 +0000272 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000273 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000274 * much more efficient and portable. (But hacking the kernel is so much
275 * more fun than worrying about efficiency and portability. :-))
276 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000277#define INT_OFF do { \
278 suppressint++; \
279 xbarrier(); \
280} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281
282/*
283 * Called to raise an exception. Since C doesn't include exceptions, we
284 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000285 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000287static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000288static void
289raise_exception(int e)
290{
291#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000292 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000293 abort();
294#endif
295 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000296 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000297 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000298}
299
300/*
301 * Called from trap.c when a SIGINT is received. (If the user specifies
302 * that SIGINT is to be trapped or ignored using the trap builtin, then
303 * this routine is not called.) Suppressint is nonzero when interrupts
304 * are held using the INT_OFF macro. (The test for iflag is just
305 * defensive programming.)
306 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000307static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000308static void
309raise_interrupt(void)
310{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000311 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000312
313 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000314 /* Signal is not automatically unmasked after it is raised,
315 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000316 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000317 /* pendingsig = 0; - now done in onsig() */
318
Denis Vlasenko4b875702009-03-19 13:30:04 +0000319 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000320 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
321 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000322 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323 signal(SIGINT, SIG_DFL);
324 raise(SIGINT);
325 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000326 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000327 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000328 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000329 /* NOTREACHED */
330}
331
332#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000333static void
334int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000335{
336 if (--suppressint == 0 && intpending) {
337 raise_interrupt();
338 }
339}
340#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000341static void
342force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000343{
344 suppressint = 0;
345 if (intpending)
346 raise_interrupt();
347}
348#define FORCE_INT_ON force_int_on()
349#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000350#define INT_ON do { \
351 xbarrier(); \
352 if (--suppressint == 0 && intpending) \
353 raise_interrupt(); \
354} while (0)
355#define FORCE_INT_ON do { \
356 xbarrier(); \
357 suppressint = 0; \
358 if (intpending) \
359 raise_interrupt(); \
360} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000361#endif /* ASH_OPTIMIZE_FOR_SIZE */
362
363#define SAVE_INT(v) ((v) = suppressint)
364
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000365#define RESTORE_INT(v) do { \
366 xbarrier(); \
367 suppressint = (v); \
368 if (suppressint == 0 && intpending) \
369 raise_interrupt(); \
370} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000371
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000372
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000373/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000374
Eric Andersenc470f442003-07-28 09:56:35 +0000375static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000376outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000377{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000378 INT_OFF;
379 fputs(p, file);
380 INT_ON;
381}
382
383static void
384flush_stdout_stderr(void)
385{
386 INT_OFF;
387 fflush(stdout);
388 fflush(stderr);
389 INT_ON;
390}
391
392static void
393flush_stderr(void)
394{
395 INT_OFF;
396 fflush(stderr);
397 INT_ON;
398}
399
400static void
401outcslow(int c, FILE *dest)
402{
403 INT_OFF;
404 putc(c, dest);
405 fflush(dest);
406 INT_ON;
407}
408
409static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
410static int
411out1fmt(const char *fmt, ...)
412{
413 va_list ap;
414 int r;
415
416 INT_OFF;
417 va_start(ap, fmt);
418 r = vprintf(fmt, ap);
419 va_end(ap);
420 INT_ON;
421 return r;
422}
423
424static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
425static int
426fmtstr(char *outbuf, size_t length, const char *fmt, ...)
427{
428 va_list ap;
429 int ret;
430
431 va_start(ap, fmt);
432 INT_OFF;
433 ret = vsnprintf(outbuf, length, fmt, ap);
434 va_end(ap);
435 INT_ON;
436 return ret;
437}
438
439static void
440out1str(const char *p)
441{
442 outstr(p, stdout);
443}
444
445static void
446out2str(const char *p)
447{
448 outstr(p, stderr);
449 flush_stderr();
450}
451
452
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000453/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000454
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000455/* control characters in argument strings */
456#define CTLESC '\201' /* escape next character */
457#define CTLVAR '\202' /* variable defn */
458#define CTLENDVAR '\203'
459#define CTLBACKQ '\204'
460#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
461/* CTLBACKQ | CTLQUOTE == '\205' */
462#define CTLARI '\206' /* arithmetic expression */
463#define CTLENDARI '\207'
464#define CTLQUOTEMARK '\210'
465
466/* variable substitution byte (follows CTLVAR) */
467#define VSTYPE 0x0f /* type of variable substitution */
468#define VSNUL 0x10 /* colon--treat the empty string as unset */
469#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
470
471/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000472#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
473#define VSMINUS 0x2 /* ${var-text} */
474#define VSPLUS 0x3 /* ${var+text} */
475#define VSQUESTION 0x4 /* ${var?message} */
476#define VSASSIGN 0x5 /* ${var=text} */
477#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
478#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
479#define VSTRIMLEFT 0x8 /* ${var#pattern} */
480#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
481#define VSLENGTH 0xa /* ${#var} */
482#if ENABLE_ASH_BASH_COMPAT
483#define VSSUBSTR 0xc /* ${var:position:length} */
484#define VSREPLACE 0xd /* ${var/pattern/replacement} */
485#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
486#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000487
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000488static const char dolatstr[] ALIGN1 = {
489 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
490};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000491
Denis Vlasenko559691a2008-10-05 18:39:31 +0000492#define NCMD 0
493#define NPIPE 1
494#define NREDIR 2
495#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000496#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000497#define NAND 5
498#define NOR 6
499#define NSEMI 7
500#define NIF 8
501#define NWHILE 9
502#define NUNTIL 10
503#define NFOR 11
504#define NCASE 12
505#define NCLIST 13
506#define NDEFUN 14
507#define NARG 15
508#define NTO 16
509#if ENABLE_ASH_BASH_COMPAT
510#define NTO2 17
511#endif
512#define NCLOBBER 18
513#define NFROM 19
514#define NFROMTO 20
515#define NAPPEND 21
516#define NTOFD 22
517#define NFROMFD 23
518#define NHERE 24
519#define NXHERE 25
520#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000521#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000522
523union node;
524
525struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000526 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000527 union node *assign;
528 union node *args;
529 union node *redirect;
530};
531
532struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000533 smallint type;
534 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000535 struct nodelist *cmdlist;
536};
537
538struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000539 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000540 union node *n;
541 union node *redirect;
542};
543
544struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000545 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000546 union node *ch1;
547 union node *ch2;
548};
549
550struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000551 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000552 union node *test;
553 union node *ifpart;
554 union node *elsepart;
555};
556
557struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000558 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000559 union node *args;
560 union node *body;
561 char *var;
562};
563
564struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000565 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000566 union node *expr;
567 union node *cases;
568};
569
570struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000571 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000572 union node *next;
573 union node *pattern;
574 union node *body;
575};
576
577struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000578 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000579 union node *next;
580 char *text;
581 struct nodelist *backquote;
582};
583
Denis Vlasenko559691a2008-10-05 18:39:31 +0000584/* nfile and ndup layout must match!
585 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
586 * that it is actually NTO2 (>&file), and change its type.
587 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000588struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000589 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000590 union node *next;
591 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000592 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000593 union node *fname;
594 char *expfname;
595};
596
597struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000598 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000599 union node *next;
600 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000601 int dupfd;
602 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000603 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000604};
605
606struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000607 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000608 union node *next;
609 int fd;
610 union node *doc;
611};
612
613struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000614 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000615 union node *com;
616};
617
618union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000619 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000620 struct ncmd ncmd;
621 struct npipe npipe;
622 struct nredir nredir;
623 struct nbinary nbinary;
624 struct nif nif;
625 struct nfor nfor;
626 struct ncase ncase;
627 struct nclist nclist;
628 struct narg narg;
629 struct nfile nfile;
630 struct ndup ndup;
631 struct nhere nhere;
632 struct nnot nnot;
633};
634
635struct nodelist {
636 struct nodelist *next;
637 union node *n;
638};
639
640struct funcnode {
641 int count;
642 union node n;
643};
644
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000645/*
646 * Free a parse tree.
647 */
648static void
649freefunc(struct funcnode *f)
650{
651 if (f && --f->count < 0)
652 free(f);
653}
654
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000655
656/* ============ Debugging output */
657
658#if DEBUG
659
660static FILE *tracefile;
661
662static void
663trace_printf(const char *fmt, ...)
664{
665 va_list va;
666
667 if (debug != 1)
668 return;
669 va_start(va, fmt);
670 vfprintf(tracefile, fmt, va);
671 va_end(va);
672}
673
674static void
675trace_vprintf(const char *fmt, va_list va)
676{
677 if (debug != 1)
678 return;
679 vfprintf(tracefile, fmt, va);
680}
681
682static void
683trace_puts(const char *s)
684{
685 if (debug != 1)
686 return;
687 fputs(s, tracefile);
688}
689
690static void
691trace_puts_quoted(char *s)
692{
693 char *p;
694 char c;
695
696 if (debug != 1)
697 return;
698 putc('"', tracefile);
699 for (p = s; *p; p++) {
700 switch (*p) {
701 case '\n': c = 'n'; goto backslash;
702 case '\t': c = 't'; goto backslash;
703 case '\r': c = 'r'; goto backslash;
704 case '"': c = '"'; goto backslash;
705 case '\\': c = '\\'; goto backslash;
706 case CTLESC: c = 'e'; goto backslash;
707 case CTLVAR: c = 'v'; goto backslash;
708 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
709 case CTLBACKQ: c = 'q'; goto backslash;
710 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
711 backslash:
712 putc('\\', tracefile);
713 putc(c, tracefile);
714 break;
715 default:
716 if (*p >= ' ' && *p <= '~')
717 putc(*p, tracefile);
718 else {
719 putc('\\', tracefile);
720 putc(*p >> 6 & 03, tracefile);
721 putc(*p >> 3 & 07, tracefile);
722 putc(*p & 07, tracefile);
723 }
724 break;
725 }
726 }
727 putc('"', tracefile);
728}
729
730static void
731trace_puts_args(char **ap)
732{
733 if (debug != 1)
734 return;
735 if (!*ap)
736 return;
737 while (1) {
738 trace_puts_quoted(*ap);
739 if (!*++ap) {
740 putc('\n', tracefile);
741 break;
742 }
743 putc(' ', tracefile);
744 }
745}
746
747static void
748opentrace(void)
749{
750 char s[100];
751#ifdef O_APPEND
752 int flags;
753#endif
754
755 if (debug != 1) {
756 if (tracefile)
757 fflush(tracefile);
758 /* leave open because libedit might be using it */
759 return;
760 }
761 strcpy(s, "./trace");
762 if (tracefile) {
763 if (!freopen(s, "a", tracefile)) {
764 fprintf(stderr, "Can't re-open %s\n", s);
765 debug = 0;
766 return;
767 }
768 } else {
769 tracefile = fopen(s, "a");
770 if (tracefile == NULL) {
771 fprintf(stderr, "Can't open %s\n", s);
772 debug = 0;
773 return;
774 }
775 }
776#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000777 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000778 if (flags >= 0)
779 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
780#endif
781 setlinebuf(tracefile);
782 fputs("\nTracing started.\n", tracefile);
783}
784
785static void
786indent(int amount, char *pfx, FILE *fp)
787{
788 int i;
789
790 for (i = 0; i < amount; i++) {
791 if (pfx && i == amount - 1)
792 fputs(pfx, fp);
793 putc('\t', fp);
794 }
795}
796
797/* little circular references here... */
798static void shtree(union node *n, int ind, char *pfx, FILE *fp);
799
800static void
801sharg(union node *arg, FILE *fp)
802{
803 char *p;
804 struct nodelist *bqlist;
805 int subtype;
806
807 if (arg->type != NARG) {
808 out1fmt("<node type %d>\n", arg->type);
809 abort();
810 }
811 bqlist = arg->narg.backquote;
812 for (p = arg->narg.text; *p; p++) {
813 switch (*p) {
814 case CTLESC:
815 putc(*++p, fp);
816 break;
817 case CTLVAR:
818 putc('$', fp);
819 putc('{', fp);
820 subtype = *++p;
821 if (subtype == VSLENGTH)
822 putc('#', fp);
823
824 while (*p != '=')
825 putc(*p++, fp);
826
827 if (subtype & VSNUL)
828 putc(':', fp);
829
830 switch (subtype & VSTYPE) {
831 case VSNORMAL:
832 putc('}', fp);
833 break;
834 case VSMINUS:
835 putc('-', fp);
836 break;
837 case VSPLUS:
838 putc('+', fp);
839 break;
840 case VSQUESTION:
841 putc('?', fp);
842 break;
843 case VSASSIGN:
844 putc('=', fp);
845 break;
846 case VSTRIMLEFT:
847 putc('#', fp);
848 break;
849 case VSTRIMLEFTMAX:
850 putc('#', fp);
851 putc('#', fp);
852 break;
853 case VSTRIMRIGHT:
854 putc('%', fp);
855 break;
856 case VSTRIMRIGHTMAX:
857 putc('%', fp);
858 putc('%', fp);
859 break;
860 case VSLENGTH:
861 break;
862 default:
863 out1fmt("<subtype %d>", subtype);
864 }
865 break;
866 case CTLENDVAR:
867 putc('}', fp);
868 break;
869 case CTLBACKQ:
870 case CTLBACKQ|CTLQUOTE:
871 putc('$', fp);
872 putc('(', fp);
873 shtree(bqlist->n, -1, NULL, fp);
874 putc(')', fp);
875 break;
876 default:
877 putc(*p, fp);
878 break;
879 }
880 }
881}
882
883static void
884shcmd(union node *cmd, FILE *fp)
885{
886 union node *np;
887 int first;
888 const char *s;
889 int dftfd;
890
891 first = 1;
892 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000893 if (!first)
894 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000895 sharg(np, fp);
896 first = 0;
897 }
898 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000899 if (!first)
900 putc(' ', fp);
901 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000902 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000903 case NTO: s = ">>"+1; dftfd = 1; break;
904 case NCLOBBER: s = ">|"; dftfd = 1; break;
905 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000906#if ENABLE_ASH_BASH_COMPAT
907 case NTO2:
908#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000909 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000910 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000911 case NFROMFD: s = "<&"; break;
912 case NFROMTO: s = "<>"; break;
913 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000914 }
915 if (np->nfile.fd != dftfd)
916 fprintf(fp, "%d", np->nfile.fd);
917 fputs(s, fp);
918 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
919 fprintf(fp, "%d", np->ndup.dupfd);
920 } else {
921 sharg(np->nfile.fname, fp);
922 }
923 first = 0;
924 }
925}
926
927static void
928shtree(union node *n, int ind, char *pfx, FILE *fp)
929{
930 struct nodelist *lp;
931 const char *s;
932
933 if (n == NULL)
934 return;
935
936 indent(ind, pfx, fp);
937 switch (n->type) {
938 case NSEMI:
939 s = "; ";
940 goto binop;
941 case NAND:
942 s = " && ";
943 goto binop;
944 case NOR:
945 s = " || ";
946 binop:
947 shtree(n->nbinary.ch1, ind, NULL, fp);
948 /* if (ind < 0) */
949 fputs(s, fp);
950 shtree(n->nbinary.ch2, ind, NULL, fp);
951 break;
952 case NCMD:
953 shcmd(n, fp);
954 if (ind >= 0)
955 putc('\n', fp);
956 break;
957 case NPIPE:
958 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
959 shcmd(lp->n, fp);
960 if (lp->next)
961 fputs(" | ", fp);
962 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000963 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000964 fputs(" &", fp);
965 if (ind >= 0)
966 putc('\n', fp);
967 break;
968 default:
969 fprintf(fp, "<node type %d>", n->type);
970 if (ind >= 0)
971 putc('\n', fp);
972 break;
973 }
974}
975
976static void
977showtree(union node *n)
978{
979 trace_puts("showtree called\n");
980 shtree(n, 1, NULL, stdout);
981}
982
983#define TRACE(param) trace_printf param
984#define TRACEV(param) trace_vprintf param
985
986#else
987
988#define TRACE(param)
989#define TRACEV(param)
990
991#endif /* DEBUG */
992
993
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000994/* ============ Parser data */
995
996/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000997 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
998 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000999struct strlist {
1000 struct strlist *next;
1001 char *text;
1002};
1003
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001004struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001005
Denis Vlasenkob012b102007-02-19 22:43:01 +00001006struct strpush {
1007 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001008 char *prev_string;
1009 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001010#if ENABLE_ASH_ALIAS
1011 struct alias *ap; /* if push was associated with an alias */
1012#endif
1013 char *string; /* remember the string since it may change */
1014};
1015
1016struct parsefile {
1017 struct parsefile *prev; /* preceding file on stack */
1018 int linno; /* current line */
1019 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001020 int left_in_line; /* number of chars left in this line */
1021 int left_in_buffer; /* number of chars left in this buffer past the line */
1022 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001023 char *buf; /* input buffer */
1024 struct strpush *strpush; /* for pushing strings at this level */
1025 struct strpush basestrpush; /* so pushing one is fast */
1026};
1027
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001028static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001029static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001030static int startlinno; /* line # where last token started */
1031static char *commandname; /* currently executing command */
1032static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001033static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034
1035
1036/* ============ Message printing */
1037
1038static void
1039ash_vmsg(const char *msg, va_list ap)
1040{
1041 fprintf(stderr, "%s: ", arg0);
1042 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001043 if (strcmp(arg0, commandname))
1044 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001045 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001046 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001047 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001048 vfprintf(stderr, msg, ap);
1049 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001050}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001051
1052/*
1053 * Exverror is called to raise the error exception. If the second argument
1054 * is not NULL then error prints an error message using printf style
1055 * formatting. It then raises the error exception.
1056 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001057static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001058static void
1059ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001060{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001061#if DEBUG
1062 if (msg) {
1063 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1064 TRACEV((msg, ap));
1065 TRACE(("\") pid=%d\n", getpid()));
1066 } else
1067 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1068 if (msg)
1069#endif
1070 ash_vmsg(msg, ap);
1071
1072 flush_stdout_stderr();
1073 raise_exception(cond);
1074 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001075}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001076
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001077static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001078static void
1079ash_msg_and_raise_error(const char *msg, ...)
1080{
1081 va_list ap;
1082
1083 va_start(ap, msg);
1084 ash_vmsg_and_raise(EXERROR, msg, ap);
1085 /* NOTREACHED */
1086 va_end(ap);
1087}
1088
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001089static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001090static void
1091ash_msg_and_raise(int cond, const char *msg, ...)
1092{
1093 va_list ap;
1094
1095 va_start(ap, msg);
1096 ash_vmsg_and_raise(cond, msg, ap);
1097 /* NOTREACHED */
1098 va_end(ap);
1099}
1100
1101/*
1102 * error/warning routines for external builtins
1103 */
1104static void
1105ash_msg(const char *fmt, ...)
1106{
1107 va_list ap;
1108
1109 va_start(ap, fmt);
1110 ash_vmsg(fmt, ap);
1111 va_end(ap);
1112}
1113
1114/*
1115 * Return a string describing an error. The returned string may be a
1116 * pointer to a static buffer that will be overwritten on the next call.
1117 * Action describes the operation that got the error.
1118 */
1119static const char *
1120errmsg(int e, const char *em)
1121{
1122 if (e == ENOENT || e == ENOTDIR) {
1123 return em;
1124 }
1125 return strerror(e);
1126}
1127
1128
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001129/* ============ Memory allocation */
1130
1131/*
1132 * It appears that grabstackstr() will barf with such alignments
1133 * because stalloc() will return a string allocated in a new stackblock.
1134 */
1135#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1136enum {
1137 /* Most machines require the value returned from malloc to be aligned
1138 * in some way. The following macro will get this right
1139 * on many machines. */
1140 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1141 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001142 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001143};
1144
1145struct stack_block {
1146 struct stack_block *prev;
1147 char space[MINSIZE];
1148};
1149
1150struct stackmark {
1151 struct stack_block *stackp;
1152 char *stacknxt;
1153 size_t stacknleft;
1154 struct stackmark *marknext;
1155};
1156
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001157
Denis Vlasenko01631112007-12-16 17:20:38 +00001158struct globals_memstack {
1159 struct stack_block *g_stackp; // = &stackbase;
1160 struct stackmark *markp;
1161 char *g_stacknxt; // = stackbase.space;
1162 char *sstrend; // = stackbase.space + MINSIZE;
1163 size_t g_stacknleft; // = MINSIZE;
1164 int herefd; // = -1;
1165 struct stack_block stackbase;
1166};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001167extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1168#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001169#define g_stackp (G_memstack.g_stackp )
1170#define markp (G_memstack.markp )
1171#define g_stacknxt (G_memstack.g_stacknxt )
1172#define sstrend (G_memstack.sstrend )
1173#define g_stacknleft (G_memstack.g_stacknleft)
1174#define herefd (G_memstack.herefd )
1175#define stackbase (G_memstack.stackbase )
1176#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001177 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1178 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001179 g_stackp = &stackbase; \
1180 g_stacknxt = stackbase.space; \
1181 g_stacknleft = MINSIZE; \
1182 sstrend = stackbase.space + MINSIZE; \
1183 herefd = -1; \
1184} while (0)
1185
1186#define stackblock() ((void *)g_stacknxt)
1187#define stackblocksize() g_stacknleft
1188
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001189
1190static void *
1191ckrealloc(void * p, size_t nbytes)
1192{
1193 p = realloc(p, nbytes);
1194 if (!p)
1195 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1196 return p;
1197}
1198
1199static void *
1200ckmalloc(size_t nbytes)
1201{
1202 return ckrealloc(NULL, nbytes);
1203}
1204
Denis Vlasenko597906c2008-02-20 16:38:54 +00001205static void *
1206ckzalloc(size_t nbytes)
1207{
1208 return memset(ckmalloc(nbytes), 0, nbytes);
1209}
1210
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001211/*
1212 * Make a copy of a string in safe storage.
1213 */
1214static char *
1215ckstrdup(const char *s)
1216{
1217 char *p = strdup(s);
1218 if (!p)
1219 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1220 return p;
1221}
1222
1223/*
1224 * Parse trees for commands are allocated in lifo order, so we use a stack
1225 * to make this more efficient, and also to avoid all sorts of exception
1226 * handling code to handle interrupts in the middle of a parse.
1227 *
1228 * The size 504 was chosen because the Ultrix malloc handles that size
1229 * well.
1230 */
1231static void *
1232stalloc(size_t nbytes)
1233{
1234 char *p;
1235 size_t aligned;
1236
1237 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001238 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001239 size_t len;
1240 size_t blocksize;
1241 struct stack_block *sp;
1242
1243 blocksize = aligned;
1244 if (blocksize < MINSIZE)
1245 blocksize = MINSIZE;
1246 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1247 if (len < blocksize)
1248 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1249 INT_OFF;
1250 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001251 sp->prev = g_stackp;
1252 g_stacknxt = sp->space;
1253 g_stacknleft = blocksize;
1254 sstrend = g_stacknxt + blocksize;
1255 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256 INT_ON;
1257 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001258 p = g_stacknxt;
1259 g_stacknxt += aligned;
1260 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001261 return p;
1262}
1263
Denis Vlasenko597906c2008-02-20 16:38:54 +00001264static void *
1265stzalloc(size_t nbytes)
1266{
1267 return memset(stalloc(nbytes), 0, nbytes);
1268}
1269
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001270static void
1271stunalloc(void *p)
1272{
1273#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001274 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001275 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001276 abort();
1277 }
1278#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001279 g_stacknleft += g_stacknxt - (char *)p;
1280 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001281}
1282
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001283/*
1284 * Like strdup but works with the ash stack.
1285 */
1286static char *
1287ststrdup(const char *p)
1288{
1289 size_t len = strlen(p) + 1;
1290 return memcpy(stalloc(len), p, len);
1291}
1292
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001293static void
1294setstackmark(struct stackmark *mark)
1295{
Denis Vlasenko01631112007-12-16 17:20:38 +00001296 mark->stackp = g_stackp;
1297 mark->stacknxt = g_stacknxt;
1298 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001299 mark->marknext = markp;
1300 markp = mark;
1301}
1302
1303static void
1304popstackmark(struct stackmark *mark)
1305{
1306 struct stack_block *sp;
1307
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001308 if (!mark->stackp)
1309 return;
1310
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001311 INT_OFF;
1312 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001313 while (g_stackp != mark->stackp) {
1314 sp = g_stackp;
1315 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001316 free(sp);
1317 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001318 g_stacknxt = mark->stacknxt;
1319 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001320 sstrend = mark->stacknxt + mark->stacknleft;
1321 INT_ON;
1322}
1323
1324/*
1325 * When the parser reads in a string, it wants to stick the string on the
1326 * stack and only adjust the stack pointer when it knows how big the
1327 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1328 * of space on top of the stack and stackblocklen returns the length of
1329 * this block. Growstackblock will grow this space by at least one byte,
1330 * possibly moving it (like realloc). Grabstackblock actually allocates the
1331 * part of the block that has been used.
1332 */
1333static void
1334growstackblock(void)
1335{
1336 size_t newlen;
1337
Denis Vlasenko01631112007-12-16 17:20:38 +00001338 newlen = g_stacknleft * 2;
1339 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001340 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1341 if (newlen < 128)
1342 newlen += 128;
1343
Denis Vlasenko01631112007-12-16 17:20:38 +00001344 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001345 struct stack_block *oldstackp;
1346 struct stackmark *xmark;
1347 struct stack_block *sp;
1348 struct stack_block *prevstackp;
1349 size_t grosslen;
1350
1351 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001352 oldstackp = g_stackp;
1353 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001354 prevstackp = sp->prev;
1355 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1356 sp = ckrealloc(sp, grosslen);
1357 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001358 g_stackp = sp;
1359 g_stacknxt = sp->space;
1360 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001361 sstrend = sp->space + newlen;
1362
1363 /*
1364 * Stack marks pointing to the start of the old block
1365 * must be relocated to point to the new block
1366 */
1367 xmark = markp;
1368 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 xmark->stackp = g_stackp;
1370 xmark->stacknxt = g_stacknxt;
1371 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001372 xmark = xmark->marknext;
1373 }
1374 INT_ON;
1375 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001377 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378 char *p = stalloc(newlen);
1379
1380 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001381 g_stacknxt = memcpy(p, oldspace, oldlen);
1382 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001383 }
1384}
1385
1386static void
1387grabstackblock(size_t len)
1388{
1389 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001390 g_stacknxt += len;
1391 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001392}
1393
1394/*
1395 * The following routines are somewhat easier to use than the above.
1396 * The user declares a variable of type STACKSTR, which may be declared
1397 * to be a register. The macro STARTSTACKSTR initializes things. Then
1398 * the user uses the macro STPUTC to add characters to the string. In
1399 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1400 * grown as necessary. When the user is done, she can just leave the
1401 * string there and refer to it using stackblock(). Or she can allocate
1402 * the space for it using grabstackstr(). If it is necessary to allow
1403 * someone else to use the stack temporarily and then continue to grow
1404 * the string, the user should use grabstack to allocate the space, and
1405 * then call ungrabstr(p) to return to the previous mode of operation.
1406 *
1407 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1408 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1409 * is space for at least one character.
1410 */
1411static void *
1412growstackstr(void)
1413{
1414 size_t len = stackblocksize();
1415 if (herefd >= 0 && len >= 1024) {
1416 full_write(herefd, stackblock(), len);
1417 return stackblock();
1418 }
1419 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001420 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001421}
1422
1423/*
1424 * Called from CHECKSTRSPACE.
1425 */
1426static char *
1427makestrspace(size_t newlen, char *p)
1428{
Denis Vlasenko01631112007-12-16 17:20:38 +00001429 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001430 size_t size = stackblocksize();
1431
1432 for (;;) {
1433 size_t nleft;
1434
1435 size = stackblocksize();
1436 nleft = size - len;
1437 if (nleft >= newlen)
1438 break;
1439 growstackblock();
1440 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001441 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001442}
1443
1444static char *
1445stack_nputstr(const char *s, size_t n, char *p)
1446{
1447 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001448 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001449 return p;
1450}
1451
1452static char *
1453stack_putstr(const char *s, char *p)
1454{
1455 return stack_nputstr(s, strlen(s), p);
1456}
1457
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001458static char *
1459_STPUTC(int c, char *p)
1460{
1461 if (p == sstrend)
1462 p = growstackstr();
1463 *p++ = c;
1464 return p;
1465}
1466
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001467#define STARTSTACKSTR(p) ((p) = stackblock())
1468#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001469#define CHECKSTRSPACE(n, p) do { \
1470 char *q = (p); \
1471 size_t l = (n); \
1472 size_t m = sstrend - q; \
1473 if (l > m) \
1474 (p) = makestrspace(l, q); \
1475} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001476#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001477#define STACKSTRNUL(p) do { \
1478 if ((p) == sstrend) \
1479 (p) = growstackstr(); \
1480 *(p) = '\0'; \
1481} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001482#define STUNPUTC(p) (--(p))
1483#define STTOPC(p) ((p)[-1])
1484#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001485
1486#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001487#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001488#define stackstrend() ((void *)sstrend)
1489
1490
1491/* ============ String helpers */
1492
1493/*
1494 * prefix -- see if pfx is a prefix of string.
1495 */
1496static char *
1497prefix(const char *string, const char *pfx)
1498{
1499 while (*pfx) {
1500 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001501 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001502 }
1503 return (char *) string;
1504}
1505
1506/*
1507 * Check for a valid number. This should be elsewhere.
1508 */
1509static int
1510is_number(const char *p)
1511{
1512 do {
1513 if (!isdigit(*p))
1514 return 0;
1515 } while (*++p != '\0');
1516 return 1;
1517}
1518
1519/*
1520 * Convert a string of digits to an integer, printing an error message on
1521 * failure.
1522 */
1523static int
1524number(const char *s)
1525{
1526 if (!is_number(s))
1527 ash_msg_and_raise_error(illnum, s);
1528 return atoi(s);
1529}
1530
1531/*
1532 * Produce a possibly single quoted string suitable as input to the shell.
1533 * The return string is allocated on the stack.
1534 */
1535static char *
1536single_quote(const char *s)
1537{
1538 char *p;
1539
1540 STARTSTACKSTR(p);
1541
1542 do {
1543 char *q;
1544 size_t len;
1545
1546 len = strchrnul(s, '\'') - s;
1547
1548 q = p = makestrspace(len + 3, p);
1549
1550 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001551 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001552 *q++ = '\'';
1553 s += len;
1554
1555 STADJUST(q - p, p);
1556
1557 len = strspn(s, "'");
1558 if (!len)
1559 break;
1560
1561 q = p = makestrspace(len + 3, p);
1562
1563 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001564 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001565 *q++ = '"';
1566 s += len;
1567
1568 STADJUST(q - p, p);
1569 } while (*s);
1570
1571 USTPUTC(0, p);
1572
1573 return stackblock();
1574}
1575
1576
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001577/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001578
1579static char **argptr; /* argument list for builtin commands */
1580static char *optionarg; /* set by nextopt (like getopt) */
1581static char *optptr; /* used by nextopt */
1582
1583/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001584 * XXX - should get rid of. Have all builtins use getopt(3).
1585 * The library getopt must have the BSD extension static variable
1586 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001587 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001588 * Standard option processing (a la getopt) for builtin routines.
1589 * The only argument that is passed to nextopt is the option string;
1590 * the other arguments are unnecessary. It returns the character,
1591 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001592 */
1593static int
1594nextopt(const char *optstring)
1595{
1596 char *p;
1597 const char *q;
1598 char c;
1599
1600 p = optptr;
1601 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001602 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001603 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001604 if (p == NULL)
1605 return '\0';
1606 if (*p != '-')
1607 return '\0';
1608 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001609 return '\0';
1610 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001611 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001612 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001613 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001614 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001615 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001616 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001617 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001618 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001619 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620 if (*++q == ':')
1621 q++;
1622 }
1623 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001624 if (*p == '\0') {
1625 p = *argptr++;
1626 if (p == NULL)
1627 ash_msg_and_raise_error("no arg for -%c option", c);
1628 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001629 optionarg = p;
1630 p = NULL;
1631 }
1632 optptr = p;
1633 return c;
1634}
1635
1636
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001637/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001638
Denis Vlasenko01631112007-12-16 17:20:38 +00001639/*
1640 * The parsefile structure pointed to by the global variable parsefile
1641 * contains information about the current file being read.
1642 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001643struct shparam {
1644 int nparam; /* # of positional parameters (without $0) */
1645#if ENABLE_ASH_GETOPTS
1646 int optind; /* next parameter to be processed by getopts */
1647 int optoff; /* used by getopts */
1648#endif
1649 unsigned char malloced; /* if parameter list dynamically allocated */
1650 char **p; /* parameter list */
1651};
1652
1653/*
1654 * Free the list of positional parameters.
1655 */
1656static void
1657freeparam(volatile struct shparam *param)
1658{
Denis Vlasenko01631112007-12-16 17:20:38 +00001659 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001660 char **ap, **ap1;
1661 ap = ap1 = param->p;
1662 while (*ap)
1663 free(*ap++);
1664 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001665 }
1666}
1667
1668#if ENABLE_ASH_GETOPTS
1669static void getoptsreset(const char *value);
1670#endif
1671
1672struct var {
1673 struct var *next; /* next entry in hash list */
1674 int flags; /* flags are defined above */
1675 const char *text; /* name=value */
1676 void (*func)(const char *); /* function to be called when */
1677 /* the variable gets set/unset */
1678};
1679
1680struct localvar {
1681 struct localvar *next; /* next local variable in list */
1682 struct var *vp; /* the variable that was made local */
1683 int flags; /* saved flags */
1684 const char *text; /* saved text */
1685};
1686
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687/* flags */
1688#define VEXPORT 0x01 /* variable is exported */
1689#define VREADONLY 0x02 /* variable cannot be modified */
1690#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1691#define VTEXTFIXED 0x08 /* text is statically allocated */
1692#define VSTACK 0x10 /* text is allocated on the stack */
1693#define VUNSET 0x20 /* the variable is not set */
1694#define VNOFUNC 0x40 /* don't call the callback function */
1695#define VNOSET 0x80 /* do not set variable - just readonly test */
1696#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001697#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001698# define VDYNAMIC 0x200 /* dynamic variable */
1699#else
1700# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#endif
1702
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001703#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001704static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705#define defifs (defifsvar + 4)
1706#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001707static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001708#endif
1709
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001710
Denis Vlasenko01631112007-12-16 17:20:38 +00001711/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001712#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001713static void
1714change_lc_all(const char *value)
1715{
1716 if (value && *value != '\0')
1717 setlocale(LC_ALL, value);
1718}
1719static void
1720change_lc_ctype(const char *value)
1721{
1722 if (value && *value != '\0')
1723 setlocale(LC_CTYPE, value);
1724}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001725#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726#if ENABLE_ASH_MAIL
1727static void chkmail(void);
1728static void changemail(const char *);
1729#endif
1730static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001731#if ENABLE_ASH_RANDOM_SUPPORT
1732static void change_random(const char *);
1733#endif
1734
Denis Vlasenko01631112007-12-16 17:20:38 +00001735static const struct {
1736 int flags;
1737 const char *text;
1738 void (*func)(const char *);
1739} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001741 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001746 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1747 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1750 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1751 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1752 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755#endif
1756#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001757 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#endif
1759#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1761 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
1766};
1767
Denis Vlasenko0b769642008-07-24 07:54:57 +00001768struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001769
1770struct globals_var {
1771 struct shparam shellparam; /* $@ current positional parameters */
1772 struct redirtab *redirlist;
1773 int g_nullredirs;
1774 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1775 struct var *vartab[VTABSIZE];
1776 struct var varinit[ARRAY_SIZE(varinit_data)];
1777};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001778extern struct globals_var *const ash_ptr_to_globals_var;
1779#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001780#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001781//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001782#define g_nullredirs (G_var.g_nullredirs )
1783#define preverrout_fd (G_var.preverrout_fd)
1784#define vartab (G_var.vartab )
1785#define varinit (G_var.varinit )
1786#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001787 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001788 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1789 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001790 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1791 varinit[i].flags = varinit_data[i].flags; \
1792 varinit[i].text = varinit_data[i].text; \
1793 varinit[i].func = varinit_data[i].func; \
1794 } \
1795} while (0)
1796
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001797#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001798#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001799# define vmail (&vifs)[1]
1800# define vmpath (&vmail)[1]
1801# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001802#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001803# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001804#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001805#define vps1 (&vpath)[1]
1806#define vps2 (&vps1)[1]
1807#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001808#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001809# define voptind (&vps4)[1]
1810# if ENABLE_ASH_RANDOM_SUPPORT
1811# define vrandom (&voptind)[1]
1812# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001813#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001814# if ENABLE_ASH_RANDOM_SUPPORT
1815# define vrandom (&vps4)[1]
1816# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001817#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001818
1819/*
1820 * The following macros access the values of the above variables.
1821 * They have to skip over the name. They return the null string
1822 * for unset variables.
1823 */
1824#define ifsval() (vifs.text + 4)
1825#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001826#if ENABLE_ASH_MAIL
1827# define mailval() (vmail.text + 5)
1828# define mpathval() (vmpath.text + 9)
1829# define mpathset() ((vmpath.flags & VUNSET) == 0)
1830#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001831#define pathval() (vpath.text + 5)
1832#define ps1val() (vps1.text + 4)
1833#define ps2val() (vps2.text + 4)
1834#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001835#if ENABLE_ASH_GETOPTS
1836# define optindval() (voptind.text + 7)
1837#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001838
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001839
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1841#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1842
Denis Vlasenko01631112007-12-16 17:20:38 +00001843#if ENABLE_ASH_GETOPTS
1844static void
1845getoptsreset(const char *value)
1846{
1847 shellparam.optind = number(value);
1848 shellparam.optoff = -1;
1849}
1850#endif
1851
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001852/*
1853 * Return of a legal variable name (a letter or underscore followed by zero or
1854 * more letters, underscores, and digits).
1855 */
1856static char *
1857endofname(const char *name)
1858{
1859 char *p;
1860
1861 p = (char *) name;
1862 if (!is_name(*p))
1863 return p;
1864 while (*++p) {
1865 if (!is_in_name(*p))
1866 break;
1867 }
1868 return p;
1869}
1870
1871/*
1872 * Compares two strings up to the first = or '\0'. The first
1873 * string must be terminated by '='; the second may be terminated by
1874 * either '=' or '\0'.
1875 */
1876static int
1877varcmp(const char *p, const char *q)
1878{
1879 int c, d;
1880
1881 while ((c = *p) == (d = *q)) {
1882 if (!c || c == '=')
1883 goto out;
1884 p++;
1885 q++;
1886 }
1887 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001888 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001889 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001890 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891 out:
1892 return c - d;
1893}
1894
1895static int
1896varequal(const char *a, const char *b)
1897{
1898 return !varcmp(a, b);
1899}
1900
1901/*
1902 * Find the appropriate entry in the hash table from the name.
1903 */
1904static struct var **
1905hashvar(const char *p)
1906{
1907 unsigned hashval;
1908
1909 hashval = ((unsigned char) *p) << 4;
1910 while (*p && *p != '=')
1911 hashval += (unsigned char) *p++;
1912 return &vartab[hashval % VTABSIZE];
1913}
1914
1915static int
1916vpcmp(const void *a, const void *b)
1917{
1918 return varcmp(*(const char **)a, *(const char **)b);
1919}
1920
1921/*
1922 * This routine initializes the builtin variables.
1923 */
1924static void
1925initvar(void)
1926{
1927 struct var *vp;
1928 struct var *end;
1929 struct var **vpp;
1930
1931 /*
1932 * PS1 depends on uid
1933 */
1934#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1935 vps1.text = "PS1=\\w \\$ ";
1936#else
1937 if (!geteuid())
1938 vps1.text = "PS1=# ";
1939#endif
1940 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001941 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001942 do {
1943 vpp = hashvar(vp->text);
1944 vp->next = *vpp;
1945 *vpp = vp;
1946 } while (++vp < end);
1947}
1948
1949static struct var **
1950findvar(struct var **vpp, const char *name)
1951{
1952 for (; *vpp; vpp = &(*vpp)->next) {
1953 if (varequal((*vpp)->text, name)) {
1954 break;
1955 }
1956 }
1957 return vpp;
1958}
1959
1960/*
1961 * Find the value of a variable. Returns NULL if not set.
1962 */
1963static char *
1964lookupvar(const char *name)
1965{
1966 struct var *v;
1967
1968 v = *findvar(hashvar(name), name);
1969 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001970#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971 /*
1972 * Dynamic variables are implemented roughly the same way they are
1973 * in bash. Namely, they're "special" so long as they aren't unset.
1974 * As soon as they're unset, they're no longer dynamic, and dynamic
1975 * lookup will no longer happen at that point. -- PFM.
1976 */
1977 if ((v->flags & VDYNAMIC))
1978 (*v->func)(NULL);
1979#endif
1980 if (!(v->flags & VUNSET))
1981 return strchrnul(v->text, '=') + 1;
1982 }
1983 return NULL;
1984}
1985
1986/*
1987 * Search the environment of a builtin command.
1988 */
1989static char *
1990bltinlookup(const char *name)
1991{
1992 struct strlist *sp;
1993
1994 for (sp = cmdenviron; sp; sp = sp->next) {
1995 if (varequal(sp->text, name))
1996 return strchrnul(sp->text, '=') + 1;
1997 }
1998 return lookupvar(name);
1999}
2000
2001/*
2002 * Same as setvar except that the variable and value are passed in
2003 * the first argument as name=value. Since the first argument will
2004 * be actually stored in the table, it should not be a string that
2005 * will go away.
2006 * Called with interrupts off.
2007 */
2008static void
2009setvareq(char *s, int flags)
2010{
2011 struct var *vp, **vpp;
2012
2013 vpp = hashvar(s);
2014 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2015 vp = *findvar(vpp, s);
2016 if (vp) {
2017 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2018 const char *n;
2019
2020 if (flags & VNOSAVE)
2021 free(s);
2022 n = vp->text;
2023 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2024 }
2025
2026 if (flags & VNOSET)
2027 return;
2028
2029 if (vp->func && (flags & VNOFUNC) == 0)
2030 (*vp->func)(strchrnul(s, '=') + 1);
2031
2032 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2033 free((char*)vp->text);
2034
2035 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2036 } else {
2037 if (flags & VNOSET)
2038 return;
2039 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002040 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002041 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002042 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002043 *vpp = vp;
2044 }
2045 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2046 s = ckstrdup(s);
2047 vp->text = s;
2048 vp->flags = flags;
2049}
2050
2051/*
2052 * Set the value of a variable. The flags argument is ored with the
2053 * flags of the variable. If val is NULL, the variable is unset.
2054 */
2055static void
2056setvar(const char *name, const char *val, int flags)
2057{
2058 char *p, *q;
2059 size_t namelen;
2060 char *nameeq;
2061 size_t vallen;
2062
2063 q = endofname(name);
2064 p = strchrnul(q, '=');
2065 namelen = p - name;
2066 if (!namelen || p != q)
2067 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2068 vallen = 0;
2069 if (val == NULL) {
2070 flags |= VUNSET;
2071 } else {
2072 vallen = strlen(val);
2073 }
2074 INT_OFF;
2075 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002076 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002077 if (val) {
2078 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002079 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002080 }
2081 *p = '\0';
2082 setvareq(nameeq, flags | VNOSAVE);
2083 INT_ON;
2084}
2085
2086#if ENABLE_ASH_GETOPTS
2087/*
2088 * Safe version of setvar, returns 1 on success 0 on failure.
2089 */
2090static int
2091setvarsafe(const char *name, const char *val, int flags)
2092{
2093 int err;
2094 volatile int saveint;
2095 struct jmploc *volatile savehandler = exception_handler;
2096 struct jmploc jmploc;
2097
2098 SAVE_INT(saveint);
2099 if (setjmp(jmploc.loc))
2100 err = 1;
2101 else {
2102 exception_handler = &jmploc;
2103 setvar(name, val, flags);
2104 err = 0;
2105 }
2106 exception_handler = savehandler;
2107 RESTORE_INT(saveint);
2108 return err;
2109}
2110#endif
2111
2112/*
2113 * Unset the specified variable.
2114 */
2115static int
2116unsetvar(const char *s)
2117{
2118 struct var **vpp;
2119 struct var *vp;
2120 int retval;
2121
2122 vpp = findvar(hashvar(s), s);
2123 vp = *vpp;
2124 retval = 2;
2125 if (vp) {
2126 int flags = vp->flags;
2127
2128 retval = 1;
2129 if (flags & VREADONLY)
2130 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002131#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002132 vp->flags &= ~VDYNAMIC;
2133#endif
2134 if (flags & VUNSET)
2135 goto ok;
2136 if ((flags & VSTRFIXED) == 0) {
2137 INT_OFF;
2138 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2139 free((char*)vp->text);
2140 *vpp = vp->next;
2141 free(vp);
2142 INT_ON;
2143 } else {
2144 setvar(s, 0, 0);
2145 vp->flags &= ~VEXPORT;
2146 }
2147 ok:
2148 retval = 0;
2149 }
2150 out:
2151 return retval;
2152}
2153
2154/*
2155 * Process a linked list of variable assignments.
2156 */
2157static void
2158listsetvar(struct strlist *list_set_var, int flags)
2159{
2160 struct strlist *lp = list_set_var;
2161
2162 if (!lp)
2163 return;
2164 INT_OFF;
2165 do {
2166 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002167 lp = lp->next;
2168 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002169 INT_ON;
2170}
2171
2172/*
2173 * Generate a list of variables satisfying the given conditions.
2174 */
2175static char **
2176listvars(int on, int off, char ***end)
2177{
2178 struct var **vpp;
2179 struct var *vp;
2180 char **ep;
2181 int mask;
2182
2183 STARTSTACKSTR(ep);
2184 vpp = vartab;
2185 mask = on | off;
2186 do {
2187 for (vp = *vpp; vp; vp = vp->next) {
2188 if ((vp->flags & mask) == on) {
2189 if (ep == stackstrend())
2190 ep = growstackstr();
2191 *ep++ = (char *) vp->text;
2192 }
2193 }
2194 } while (++vpp < vartab + VTABSIZE);
2195 if (ep == stackstrend())
2196 ep = growstackstr();
2197 if (end)
2198 *end = ep;
2199 *ep++ = NULL;
2200 return grabstackstr(ep);
2201}
2202
2203
2204/* ============ Path search helper
2205 *
2206 * The variable path (passed by reference) should be set to the start
2207 * of the path before the first call; padvance will update
2208 * this value as it proceeds. Successive calls to padvance will return
2209 * the possible path expansions in sequence. If an option (indicated by
2210 * a percent sign) appears in the path entry then the global variable
2211 * pathopt will be set to point to it; otherwise pathopt will be set to
2212 * NULL.
2213 */
2214static const char *pathopt; /* set by padvance */
2215
2216static char *
2217padvance(const char **path, const char *name)
2218{
2219 const char *p;
2220 char *q;
2221 const char *start;
2222 size_t len;
2223
2224 if (*path == NULL)
2225 return NULL;
2226 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002227 for (p = start; *p && *p != ':' && *p != '%'; p++)
2228 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002229 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2230 while (stackblocksize() < len)
2231 growstackblock();
2232 q = stackblock();
2233 if (p != start) {
2234 memcpy(q, start, p - start);
2235 q += p - start;
2236 *q++ = '/';
2237 }
2238 strcpy(q, name);
2239 pathopt = NULL;
2240 if (*p == '%') {
2241 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002242 while (*p && *p != ':')
2243 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002244 }
2245 if (*p == ':')
2246 *path = p + 1;
2247 else
2248 *path = NULL;
2249 return stalloc(len);
2250}
2251
2252
2253/* ============ Prompt */
2254
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002255static smallint doprompt; /* if set, prompt the user */
2256static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002257
2258#if ENABLE_FEATURE_EDITING
2259static line_input_t *line_input_state;
2260static const char *cmdedit_prompt;
2261static void
2262putprompt(const char *s)
2263{
2264 if (ENABLE_ASH_EXPAND_PRMT) {
2265 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002266 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002267 return;
2268 }
2269 cmdedit_prompt = s;
2270}
2271#else
2272static void
2273putprompt(const char *s)
2274{
2275 out2str(s);
2276}
2277#endif
2278
2279#if ENABLE_ASH_EXPAND_PRMT
2280/* expandstr() needs parsing machinery, so it is far away ahead... */
2281static const char *expandstr(const char *ps);
2282#else
2283#define expandstr(s) s
2284#endif
2285
2286static void
2287setprompt(int whichprompt)
2288{
2289 const char *prompt;
2290#if ENABLE_ASH_EXPAND_PRMT
2291 struct stackmark smark;
2292#endif
2293
2294 needprompt = 0;
2295
2296 switch (whichprompt) {
2297 case 1:
2298 prompt = ps1val();
2299 break;
2300 case 2:
2301 prompt = ps2val();
2302 break;
2303 default: /* 0 */
2304 prompt = nullstr;
2305 }
2306#if ENABLE_ASH_EXPAND_PRMT
2307 setstackmark(&smark);
2308 stalloc(stackblocksize());
2309#endif
2310 putprompt(expandstr(prompt));
2311#if ENABLE_ASH_EXPAND_PRMT
2312 popstackmark(&smark);
2313#endif
2314}
2315
2316
2317/* ============ The cd and pwd commands */
2318
2319#define CD_PHYSICAL 1
2320#define CD_PRINT 2
2321
2322static int docd(const char *, int);
2323
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002324static int
2325cdopt(void)
2326{
2327 int flags = 0;
2328 int i, j;
2329
2330 j = 'L';
2331 while ((i = nextopt("LP"))) {
2332 if (i != j) {
2333 flags ^= CD_PHYSICAL;
2334 j = i;
2335 }
2336 }
2337
2338 return flags;
2339}
2340
2341/*
2342 * Update curdir (the name of the current directory) in response to a
2343 * cd command.
2344 */
2345static const char *
2346updatepwd(const char *dir)
2347{
2348 char *new;
2349 char *p;
2350 char *cdcomppath;
2351 const char *lim;
2352
2353 cdcomppath = ststrdup(dir);
2354 STARTSTACKSTR(new);
2355 if (*dir != '/') {
2356 if (curdir == nullstr)
2357 return 0;
2358 new = stack_putstr(curdir, new);
2359 }
2360 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002361 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002362 if (*dir != '/') {
2363 if (new[-1] != '/')
2364 USTPUTC('/', new);
2365 if (new > lim && *lim == '/')
2366 lim++;
2367 } else {
2368 USTPUTC('/', new);
2369 cdcomppath++;
2370 if (dir[1] == '/' && dir[2] != '/') {
2371 USTPUTC('/', new);
2372 cdcomppath++;
2373 lim++;
2374 }
2375 }
2376 p = strtok(cdcomppath, "/");
2377 while (p) {
2378 switch (*p) {
2379 case '.':
2380 if (p[1] == '.' && p[2] == '\0') {
2381 while (new > lim) {
2382 STUNPUTC(new);
2383 if (new[-1] == '/')
2384 break;
2385 }
2386 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002387 }
2388 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002389 break;
2390 /* fall through */
2391 default:
2392 new = stack_putstr(p, new);
2393 USTPUTC('/', new);
2394 }
2395 p = strtok(0, "/");
2396 }
2397 if (new > lim)
2398 STUNPUTC(new);
2399 *new = 0;
2400 return stackblock();
2401}
2402
2403/*
2404 * Find out what the current directory is. If we already know the current
2405 * directory, this routine returns immediately.
2406 */
2407static char *
2408getpwd(void)
2409{
Denis Vlasenko01631112007-12-16 17:20:38 +00002410 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002411 return dir ? dir : nullstr;
2412}
2413
2414static void
2415setpwd(const char *val, int setold)
2416{
2417 char *oldcur, *dir;
2418
2419 oldcur = dir = curdir;
2420
2421 if (setold) {
2422 setvar("OLDPWD", oldcur, VEXPORT);
2423 }
2424 INT_OFF;
2425 if (physdir != nullstr) {
2426 if (physdir != oldcur)
2427 free(physdir);
2428 physdir = nullstr;
2429 }
2430 if (oldcur == val || !val) {
2431 char *s = getpwd();
2432 physdir = s;
2433 if (!val)
2434 dir = s;
2435 } else
2436 dir = ckstrdup(val);
2437 if (oldcur != dir && oldcur != nullstr) {
2438 free(oldcur);
2439 }
2440 curdir = dir;
2441 INT_ON;
2442 setvar("PWD", dir, VEXPORT);
2443}
2444
2445static void hashcd(void);
2446
2447/*
2448 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2449 * know that the current directory has changed.
2450 */
2451static int
2452docd(const char *dest, int flags)
2453{
2454 const char *dir = 0;
2455 int err;
2456
2457 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2458
2459 INT_OFF;
2460 if (!(flags & CD_PHYSICAL)) {
2461 dir = updatepwd(dest);
2462 if (dir)
2463 dest = dir;
2464 }
2465 err = chdir(dest);
2466 if (err)
2467 goto out;
2468 setpwd(dir, 1);
2469 hashcd();
2470 out:
2471 INT_ON;
2472 return err;
2473}
2474
2475static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002476cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002477{
2478 const char *dest;
2479 const char *path;
2480 const char *p;
2481 char c;
2482 struct stat statb;
2483 int flags;
2484
2485 flags = cdopt();
2486 dest = *argptr;
2487 if (!dest)
2488 dest = bltinlookup(homestr);
2489 else if (LONE_DASH(dest)) {
2490 dest = bltinlookup("OLDPWD");
2491 flags |= CD_PRINT;
2492 }
2493 if (!dest)
2494 dest = nullstr;
2495 if (*dest == '/')
2496 goto step7;
2497 if (*dest == '.') {
2498 c = dest[1];
2499 dotdot:
2500 switch (c) {
2501 case '\0':
2502 case '/':
2503 goto step6;
2504 case '.':
2505 c = dest[2];
2506 if (c != '.')
2507 goto dotdot;
2508 }
2509 }
2510 if (!*dest)
2511 dest = ".";
2512 path = bltinlookup("CDPATH");
2513 if (!path) {
2514 step6:
2515 step7:
2516 p = dest;
2517 goto docd;
2518 }
2519 do {
2520 c = *path;
2521 p = padvance(&path, dest);
2522 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2523 if (c && c != ':')
2524 flags |= CD_PRINT;
2525 docd:
2526 if (!docd(p, flags))
2527 goto out;
2528 break;
2529 }
2530 } while (path);
2531 ash_msg_and_raise_error("can't cd to %s", dest);
2532 /* NOTREACHED */
2533 out:
2534 if (flags & CD_PRINT)
2535 out1fmt(snlfmt, curdir);
2536 return 0;
2537}
2538
2539static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002540pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002541{
2542 int flags;
2543 const char *dir = curdir;
2544
2545 flags = cdopt();
2546 if (flags) {
2547 if (physdir == nullstr)
2548 setpwd(dir, 0);
2549 dir = physdir;
2550 }
2551 out1fmt(snlfmt, dir);
2552 return 0;
2553}
2554
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002555
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002556/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002557
Denis Vlasenko834dee72008-10-07 09:18:30 +00002558
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002559#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002560/* buffer for top level input file */
2561#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002562
Eric Andersenc470f442003-07-28 09:56:35 +00002563/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002564#define CWORD 0 /* character is nothing special */
2565#define CNL 1 /* newline character */
2566#define CBACK 2 /* a backslash character */
2567#define CSQUOTE 3 /* single quote */
2568#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002569#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002570#define CBQUOTE 6 /* backwards single quote */
2571#define CVAR 7 /* a dollar sign */
2572#define CENDVAR 8 /* a '}' character */
2573#define CLP 9 /* a left paren in arithmetic */
2574#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002575#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002576#define CCTL 12 /* like CWORD, except it must be escaped */
2577#define CSPCL 13 /* these terminate a word */
2578#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002579
Denis Vlasenko131ae172007-02-18 13:00:19 +00002580#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002581#define SYNBASE 130
2582#define PEOF -130
2583#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002584#define PEOA_OR_PEOF PEOA
2585#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002586#define SYNBASE 129
2587#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002588#define PEOA_OR_PEOF PEOF
2589#endif
2590
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591/* number syntax index */
2592#define BASESYNTAX 0 /* not in quotes */
2593#define DQSYNTAX 1 /* in double quotes */
2594#define SQSYNTAX 2 /* in single quotes */
2595#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002596#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002597
Denis Vlasenko131ae172007-02-18 13:00:19 +00002598#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002599#define USE_SIT_FUNCTION
2600#endif
2601
Denis Vlasenko131ae172007-02-18 13:00:19 +00002602#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002603static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002604#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002605 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002606#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002607 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2608 { CNL, CNL, CNL, CNL }, /* 2, \n */
2609 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2610 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2611 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2612 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2613 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2614 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2615 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2616 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2617 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002618#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002619 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2620 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2621 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002622#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002623};
Eric Andersenc470f442003-07-28 09:56:35 +00002624#else
2625static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002626#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002627 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002628#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002629 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2630 { CNL, CNL, CNL }, /* 2, \n */
2631 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2632 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2633 { CVAR, CVAR, CWORD }, /* 5, $ */
2634 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2635 { CSPCL, CWORD, CWORD }, /* 7, ( */
2636 { CSPCL, CWORD, CWORD }, /* 8, ) */
2637 { CBACK, CBACK, CCTL }, /* 9, \ */
2638 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2639 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002640#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002641 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2642 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2643 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002644#endif
2645};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002646#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002647
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002648#ifdef USE_SIT_FUNCTION
2649
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002650static int
2651SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002652{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002653 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002654#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002655 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002656 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2657 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2658 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2659 11, 3 /* "}~" */
2660 };
2661#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002662 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002663 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2664 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2665 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2666 10, 2 /* "}~" */
2667 };
2668#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669 const char *s;
2670 int indx;
2671
Eric Andersenc470f442003-07-28 09:56:35 +00002672 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002674#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002675 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002676 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002677 else
2678#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002679
2680 if ((unsigned char)c >= (unsigned char)(CTLESC)
2681 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2682 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002683 return CCTL;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002684 }
Denis Vlasenko68819d12008-12-15 11:26:36 +00002685 s = strchrnul(spec_symbls, c);
2686 if (*s == '\0')
2687 return CWORD;
2688 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002689 return S_I_T[indx][syntax];
2690}
2691
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002692#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002693
Denis Vlasenko131ae172007-02-18 13:00:19 +00002694#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002695#define CSPCL_CIGN_CIGN_CIGN 0
2696#define CSPCL_CWORD_CWORD_CWORD 1
2697#define CNL_CNL_CNL_CNL 2
2698#define CWORD_CCTL_CCTL_CWORD 3
2699#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2700#define CVAR_CVAR_CWORD_CVAR 5
2701#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2702#define CSPCL_CWORD_CWORD_CLP 7
2703#define CSPCL_CWORD_CWORD_CRP 8
2704#define CBACK_CBACK_CCTL_CBACK 9
2705#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2706#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2707#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2708#define CWORD_CWORD_CWORD_CWORD 13
2709#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002710#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002711#define CSPCL_CWORD_CWORD_CWORD 0
2712#define CNL_CNL_CNL_CNL 1
2713#define CWORD_CCTL_CCTL_CWORD 2
2714#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2715#define CVAR_CVAR_CWORD_CVAR 4
2716#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2717#define CSPCL_CWORD_CWORD_CLP 6
2718#define CSPCL_CWORD_CWORD_CRP 7
2719#define CBACK_CBACK_CCTL_CBACK 8
2720#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2721#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2722#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2723#define CWORD_CWORD_CWORD_CWORD 12
2724#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002725#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002726
2727static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002728 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002729 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002730#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002731 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2732#endif
2733 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2735 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2736 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2737 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2738 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2739 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2740 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2741 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002742 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2871 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2872 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2894 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002895 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002896 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2898 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002900 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002901 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2902 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2903 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2904 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2906 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2907 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2908 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2909 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2920 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2921 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2922 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2923 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2924 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2925 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2953 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2954 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2955 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2958 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2986 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2987 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2988 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002989};
2990
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002991#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2992
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002993#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002994
Eric Andersen2870d962001-07-02 17:27:21 +00002995
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002996/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002997
Denis Vlasenko131ae172007-02-18 13:00:19 +00002998#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002999
3000#define ALIASINUSE 1
3001#define ALIASDEAD 2
3002
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003003struct alias {
3004 struct alias *next;
3005 char *name;
3006 char *val;
3007 int flag;
3008};
3009
Denis Vlasenko01631112007-12-16 17:20:38 +00003010
3011static struct alias **atab; // [ATABSIZE];
3012#define INIT_G_alias() do { \
3013 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3014} while (0)
3015
Eric Andersen2870d962001-07-02 17:27:21 +00003016
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003017static struct alias **
3018__lookupalias(const char *name) {
3019 unsigned int hashval;
3020 struct alias **app;
3021 const char *p;
3022 unsigned int ch;
3023
3024 p = name;
3025
3026 ch = (unsigned char)*p;
3027 hashval = ch << 4;
3028 while (ch) {
3029 hashval += ch;
3030 ch = (unsigned char)*++p;
3031 }
3032 app = &atab[hashval % ATABSIZE];
3033
3034 for (; *app; app = &(*app)->next) {
3035 if (strcmp(name, (*app)->name) == 0) {
3036 break;
3037 }
3038 }
3039
3040 return app;
3041}
3042
3043static struct alias *
3044lookupalias(const char *name, int check)
3045{
3046 struct alias *ap = *__lookupalias(name);
3047
3048 if (check && ap && (ap->flag & ALIASINUSE))
3049 return NULL;
3050 return ap;
3051}
3052
3053static struct alias *
3054freealias(struct alias *ap)
3055{
3056 struct alias *next;
3057
3058 if (ap->flag & ALIASINUSE) {
3059 ap->flag |= ALIASDEAD;
3060 return ap;
3061 }
3062
3063 next = ap->next;
3064 free(ap->name);
3065 free(ap->val);
3066 free(ap);
3067 return next;
3068}
Eric Andersencb57d552001-06-28 07:25:16 +00003069
Eric Andersenc470f442003-07-28 09:56:35 +00003070static void
3071setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003072{
3073 struct alias *ap, **app;
3074
3075 app = __lookupalias(name);
3076 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003077 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003078 if (ap) {
3079 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003080 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003081 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003082 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003083 ap->flag &= ~ALIASDEAD;
3084 } else {
3085 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003086 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003087 ap->name = ckstrdup(name);
3088 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003089 /*ap->flag = 0; - ckzalloc did it */
3090 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003091 *app = ap;
3092 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003093 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003094}
3095
Eric Andersenc470f442003-07-28 09:56:35 +00003096static int
3097unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003098{
Eric Andersencb57d552001-06-28 07:25:16 +00003099 struct alias **app;
3100
3101 app = __lookupalias(name);
3102
3103 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003104 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003105 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003106 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003107 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003108 }
3109
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003110 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003111}
3112
Eric Andersenc470f442003-07-28 09:56:35 +00003113static void
3114rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003115{
Eric Andersencb57d552001-06-28 07:25:16 +00003116 struct alias *ap, **app;
3117 int i;
3118
Denis Vlasenkob012b102007-02-19 22:43:01 +00003119 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003120 for (i = 0; i < ATABSIZE; i++) {
3121 app = &atab[i];
3122 for (ap = *app; ap; ap = *app) {
3123 *app = freealias(*app);
3124 if (ap == *app) {
3125 app = &ap->next;
3126 }
3127 }
3128 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003129 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003130}
3131
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003132static void
3133printalias(const struct alias *ap)
3134{
3135 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3136}
3137
Eric Andersencb57d552001-06-28 07:25:16 +00003138/*
3139 * TODO - sort output
3140 */
Eric Andersenc470f442003-07-28 09:56:35 +00003141static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003142aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003143{
3144 char *n, *v;
3145 int ret = 0;
3146 struct alias *ap;
3147
Denis Vlasenko68404f12008-03-17 09:00:54 +00003148 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003149 int i;
3150
Denis Vlasenko68404f12008-03-17 09:00:54 +00003151 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003152 for (ap = atab[i]; ap; ap = ap->next) {
3153 printalias(ap);
3154 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003155 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003156 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003157 }
3158 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003159 v = strchr(n+1, '=');
3160 if (v == NULL) { /* n+1: funny ksh stuff */
3161 ap = *__lookupalias(n);
3162 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003163 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003164 ret = 1;
3165 } else
3166 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003167 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003168 *v++ = '\0';
3169 setalias(n, v);
3170 }
3171 }
3172
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003173 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003174}
3175
Eric Andersenc470f442003-07-28 09:56:35 +00003176static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003177unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003178{
3179 int i;
3180
3181 while ((i = nextopt("a")) != '\0') {
3182 if (i == 'a') {
3183 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003184 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003185 }
3186 }
3187 for (i = 0; *argptr; argptr++) {
3188 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003189 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003190 i = 1;
3191 }
3192 }
3193
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003194 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003195}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003196
Denis Vlasenko131ae172007-02-18 13:00:19 +00003197#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003198
Eric Andersenc470f442003-07-28 09:56:35 +00003199
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003200/* ============ jobs.c */
3201
3202/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3203#define FORK_FG 0
3204#define FORK_BG 1
3205#define FORK_NOJOB 2
3206
3207/* mode flags for showjob(s) */
3208#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3209#define SHOW_PID 0x04 /* include process pid */
3210#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3211
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003212/*
3213 * A job structure contains information about a job. A job is either a
3214 * single process or a set of processes contained in a pipeline. In the
3215 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3216 * array of pids.
3217 */
3218
3219struct procstat {
3220 pid_t pid; /* process id */
3221 int status; /* last process status from wait() */
3222 char *cmd; /* text of command being run */
3223};
3224
3225struct job {
3226 struct procstat ps0; /* status of process */
3227 struct procstat *ps; /* status or processes when more than one */
3228#if JOBS
3229 int stopstatus; /* status of a stopped job */
3230#endif
3231 uint32_t
3232 nprocs: 16, /* number of processes */
3233 state: 8,
3234#define JOBRUNNING 0 /* at least one proc running */
3235#define JOBSTOPPED 1 /* all procs are stopped */
3236#define JOBDONE 2 /* all procs are completed */
3237#if JOBS
3238 sigint: 1, /* job was killed by SIGINT */
3239 jobctl: 1, /* job running under job control */
3240#endif
3241 waited: 1, /* true if this entry has been waited for */
3242 used: 1, /* true if this entry is in used */
3243 changed: 1; /* true if status has changed */
3244 struct job *prev_job; /* previous job */
3245};
3246
Denis Vlasenko68404f12008-03-17 09:00:54 +00003247static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003248#if !JOBS
3249#define forkshell(job, node, mode) forkshell(job, mode)
3250#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003251static int forkshell(struct job *, union node *, int);
3252static int waitforjob(struct job *);
3253
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003254#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003255enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003256#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003257#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003258static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003260#endif
3261
3262/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003263 * Ignore a signal.
3264 */
3265static void
3266ignoresig(int signo)
3267{
3268 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3269 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3270 /* No, need to do it */
3271 signal(signo, SIG_IGN);
3272 }
3273 sigmode[signo - 1] = S_HARD_IGN;
3274}
3275
3276/*
3277 * Signal handler. Only one usage site - in setsignal()
3278 */
3279static void
3280onsig(int signo)
3281{
3282 gotsig[signo - 1] = 1;
3283
3284 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
3285 if (!suppressint) {
3286 pendingsig = 0;
3287 raise_interrupt(); /* does not return */
3288 }
3289 intpending = 1;
3290 } else {
3291 pendingsig = signo;
3292 }
3293}
3294
3295/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003296 * Set the signal handler for the specified signal. The routine figures
3297 * out what it should be set to.
3298 */
3299static void
3300setsignal(int signo)
3301{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003302 char *t;
3303 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003304 struct sigaction act;
3305
3306 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003307 new_act = S_DFL;
3308 if (t != NULL) { /* trap for this sig is set */
3309 new_act = S_CATCH;
3310 if (t[0] == '\0') /* trap is "": ignore this sig */
3311 new_act = S_IGN;
3312 }
3313
3314 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003315 switch (signo) {
3316 case SIGINT:
3317 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003318 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003319 break;
3320 case SIGQUIT:
3321#if DEBUG
3322 if (debug)
3323 break;
3324#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003325 /* man bash:
3326 * "In all cases, bash ignores SIGQUIT. Non-builtin
3327 * commands run by bash have signal handlers
3328 * set to the values inherited by the shell
3329 * from its parent". */
3330 new_act = S_IGN;
3331 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332 case SIGTERM:
3333 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003334 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003335 break;
3336#if JOBS
3337 case SIGTSTP:
3338 case SIGTTOU:
3339 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003340 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 break;
3342#endif
3343 }
3344 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003345//TODO: if !rootshell, we reset SIGQUIT to DFL,
3346//whereas we have to restore it to what shell got on entry
3347//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348
3349 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003350 cur_act = *t;
3351 if (cur_act == 0) {
3352 /* current setting is not yet known */
3353 if (sigaction(signo, NULL, &act)) {
3354 /* pretend it worked; maybe we should give a warning,
3355 * but other shells don't. We don't alter sigmode,
3356 * so we retry every time.
3357 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003358 return;
3359 }
3360 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003361 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362 if (mflag
3363 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3364 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003365 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003366 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003367 }
3368 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003369 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003370 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003371
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003372 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003373 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003374 case S_CATCH:
3375 act.sa_handler = onsig;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003376 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3377 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003378 break;
3379 case S_IGN:
3380 act.sa_handler = SIG_IGN;
3381 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003383 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003384
3385 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003386}
3387
3388/* mode flags for set_curjob */
3389#define CUR_DELETE 2
3390#define CUR_RUNNING 1
3391#define CUR_STOPPED 0
3392
3393/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003394#define DOWAIT_NONBLOCK WNOHANG
3395#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003396
3397#if JOBS
3398/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003399static int initialpgrp; //references:2
3400static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003401#endif
3402/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003403static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003404/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003405static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003406/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003407static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003408/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003409static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003410
3411static void
3412set_curjob(struct job *jp, unsigned mode)
3413{
3414 struct job *jp1;
3415 struct job **jpp, **curp;
3416
3417 /* first remove from list */
3418 jpp = curp = &curjob;
3419 do {
3420 jp1 = *jpp;
3421 if (jp1 == jp)
3422 break;
3423 jpp = &jp1->prev_job;
3424 } while (1);
3425 *jpp = jp1->prev_job;
3426
3427 /* Then re-insert in correct position */
3428 jpp = curp;
3429 switch (mode) {
3430 default:
3431#if DEBUG
3432 abort();
3433#endif
3434 case CUR_DELETE:
3435 /* job being deleted */
3436 break;
3437 case CUR_RUNNING:
3438 /* newly created job or backgrounded job,
3439 put after all stopped jobs. */
3440 do {
3441 jp1 = *jpp;
3442#if JOBS
3443 if (!jp1 || jp1->state != JOBSTOPPED)
3444#endif
3445 break;
3446 jpp = &jp1->prev_job;
3447 } while (1);
3448 /* FALLTHROUGH */
3449#if JOBS
3450 case CUR_STOPPED:
3451#endif
3452 /* newly stopped job - becomes curjob */
3453 jp->prev_job = *jpp;
3454 *jpp = jp;
3455 break;
3456 }
3457}
3458
3459#if JOBS || DEBUG
3460static int
3461jobno(const struct job *jp)
3462{
3463 return jp - jobtab + 1;
3464}
3465#endif
3466
3467/*
3468 * Convert a job name to a job structure.
3469 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003470#if !JOBS
3471#define getjob(name, getctl) getjob(name)
3472#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003473static struct job *
3474getjob(const char *name, int getctl)
3475{
3476 struct job *jp;
3477 struct job *found;
3478 const char *err_msg = "No such job: %s";
3479 unsigned num;
3480 int c;
3481 const char *p;
3482 char *(*match)(const char *, const char *);
3483
3484 jp = curjob;
3485 p = name;
3486 if (!p)
3487 goto currentjob;
3488
3489 if (*p != '%')
3490 goto err;
3491
3492 c = *++p;
3493 if (!c)
3494 goto currentjob;
3495
3496 if (!p[1]) {
3497 if (c == '+' || c == '%') {
3498 currentjob:
3499 err_msg = "No current job";
3500 goto check;
3501 }
3502 if (c == '-') {
3503 if (jp)
3504 jp = jp->prev_job;
3505 err_msg = "No previous job";
3506 check:
3507 if (!jp)
3508 goto err;
3509 goto gotit;
3510 }
3511 }
3512
3513 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003514// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003515 num = atoi(p);
3516 if (num < njobs) {
3517 jp = jobtab + num - 1;
3518 if (jp->used)
3519 goto gotit;
3520 goto err;
3521 }
3522 }
3523
3524 match = prefix;
3525 if (*p == '?') {
3526 match = strstr;
3527 p++;
3528 }
3529
3530 found = 0;
3531 while (1) {
3532 if (!jp)
3533 goto err;
3534 if (match(jp->ps[0].cmd, p)) {
3535 if (found)
3536 goto err;
3537 found = jp;
3538 err_msg = "%s: ambiguous";
3539 }
3540 jp = jp->prev_job;
3541 }
3542
3543 gotit:
3544#if JOBS
3545 err_msg = "job %s not created under job control";
3546 if (getctl && jp->jobctl == 0)
3547 goto err;
3548#endif
3549 return jp;
3550 err:
3551 ash_msg_and_raise_error(err_msg, name);
3552}
3553
3554/*
3555 * Mark a job structure as unused.
3556 */
3557static void
3558freejob(struct job *jp)
3559{
3560 struct procstat *ps;
3561 int i;
3562
3563 INT_OFF;
3564 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3565 if (ps->cmd != nullstr)
3566 free(ps->cmd);
3567 }
3568 if (jp->ps != &jp->ps0)
3569 free(jp->ps);
3570 jp->used = 0;
3571 set_curjob(jp, CUR_DELETE);
3572 INT_ON;
3573}
3574
3575#if JOBS
3576static void
3577xtcsetpgrp(int fd, pid_t pgrp)
3578{
3579 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003580 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003581}
3582
3583/*
3584 * Turn job control on and off.
3585 *
3586 * Note: This code assumes that the third arg to ioctl is a character
3587 * pointer, which is true on Berkeley systems but not System V. Since
3588 * System V doesn't have job control yet, this isn't a problem now.
3589 *
3590 * Called with interrupts off.
3591 */
3592static void
3593setjobctl(int on)
3594{
3595 int fd;
3596 int pgrp;
3597
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003598 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003599 return;
3600 if (on) {
3601 int ofd;
3602 ofd = fd = open(_PATH_TTY, O_RDWR);
3603 if (fd < 0) {
3604 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3605 * That sometimes helps to acquire controlling tty.
3606 * Obviously, a workaround for bugs when someone
3607 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003608 fd = 2;
3609 while (!isatty(fd))
3610 if (--fd < 0)
3611 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003612 }
3613 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003614 if (ofd >= 0)
3615 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003616 if (fd < 0)
3617 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003618 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003619 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003620 do { /* while we are in the background */
3621 pgrp = tcgetpgrp(fd);
3622 if (pgrp < 0) {
3623 out:
3624 ash_msg("can't access tty; job control turned off");
3625 mflag = on = 0;
3626 goto close;
3627 }
3628 if (pgrp == getpgrp())
3629 break;
3630 killpg(0, SIGTTIN);
3631 } while (1);
3632 initialpgrp = pgrp;
3633
3634 setsignal(SIGTSTP);
3635 setsignal(SIGTTOU);
3636 setsignal(SIGTTIN);
3637 pgrp = rootpid;
3638 setpgid(0, pgrp);
3639 xtcsetpgrp(fd, pgrp);
3640 } else {
3641 /* turning job control off */
3642 fd = ttyfd;
3643 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003644 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003645 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003646 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003647 setpgid(0, pgrp);
3648 setsignal(SIGTSTP);
3649 setsignal(SIGTTOU);
3650 setsignal(SIGTTIN);
3651 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003652 if (fd >= 0)
3653 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654 fd = -1;
3655 }
3656 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003657 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003658}
3659
3660static int
3661killcmd(int argc, char **argv)
3662{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003663 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003664 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003665 do {
3666 if (argv[i][0] == '%') {
3667 struct job *jp = getjob(argv[i], 0);
3668 unsigned pid = jp->ps[0].pid;
3669 /* Enough space for ' -NNN<nul>' */
3670 argv[i] = alloca(sizeof(int)*3 + 3);
3671 /* kill_main has matching code to expect
3672 * leading space. Needed to not confuse
3673 * negative pids with "kill -SIGNAL_NO" syntax */
3674 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003675 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003676 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003677 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003678 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003679}
3680
3681static void
3682showpipe(struct job *jp, FILE *out)
3683{
3684 struct procstat *sp;
3685 struct procstat *spend;
3686
3687 spend = jp->ps + jp->nprocs;
3688 for (sp = jp->ps + 1; sp < spend; sp++)
3689 fprintf(out, " | %s", sp->cmd);
3690 outcslow('\n', out);
3691 flush_stdout_stderr();
3692}
3693
3694
3695static int
3696restartjob(struct job *jp, int mode)
3697{
3698 struct procstat *ps;
3699 int i;
3700 int status;
3701 pid_t pgid;
3702
3703 INT_OFF;
3704 if (jp->state == JOBDONE)
3705 goto out;
3706 jp->state = JOBRUNNING;
3707 pgid = jp->ps->pid;
3708 if (mode == FORK_FG)
3709 xtcsetpgrp(ttyfd, pgid);
3710 killpg(pgid, SIGCONT);
3711 ps = jp->ps;
3712 i = jp->nprocs;
3713 do {
3714 if (WIFSTOPPED(ps->status)) {
3715 ps->status = -1;
3716 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003717 ps++;
3718 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003719 out:
3720 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3721 INT_ON;
3722 return status;
3723}
3724
3725static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003726fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003727{
3728 struct job *jp;
3729 FILE *out;
3730 int mode;
3731 int retval;
3732
3733 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3734 nextopt(nullstr);
3735 argv = argptr;
3736 out = stdout;
3737 do {
3738 jp = getjob(*argv, 1);
3739 if (mode == FORK_BG) {
3740 set_curjob(jp, CUR_RUNNING);
3741 fprintf(out, "[%d] ", jobno(jp));
3742 }
3743 outstr(jp->ps->cmd, out);
3744 showpipe(jp, out);
3745 retval = restartjob(jp, mode);
3746 } while (*argv && *++argv);
3747 return retval;
3748}
3749#endif
3750
3751static int
3752sprint_status(char *s, int status, int sigonly)
3753{
3754 int col;
3755 int st;
3756
3757 col = 0;
3758 if (!WIFEXITED(status)) {
3759#if JOBS
3760 if (WIFSTOPPED(status))
3761 st = WSTOPSIG(status);
3762 else
3763#endif
3764 st = WTERMSIG(status);
3765 if (sigonly) {
3766 if (st == SIGINT || st == SIGPIPE)
3767 goto out;
3768#if JOBS
3769 if (WIFSTOPPED(status))
3770 goto out;
3771#endif
3772 }
3773 st &= 0x7f;
3774 col = fmtstr(s, 32, strsignal(st));
3775 if (WCOREDUMP(status)) {
3776 col += fmtstr(s + col, 16, " (core dumped)");
3777 }
3778 } else if (!sigonly) {
3779 st = WEXITSTATUS(status);
3780 if (st)
3781 col = fmtstr(s, 16, "Done(%d)", st);
3782 else
3783 col = fmtstr(s, 16, "Done");
3784 }
3785 out:
3786 return col;
3787}
3788
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003789static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003790dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791{
3792 int pid;
3793 int status;
3794 struct job *jp;
3795 struct job *thisjob;
3796 int state;
3797
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003798 TRACE(("dowait(0x%x) called\n", wait_flags));
3799
3800 /* Do a wait system call. If job control is compiled in, we accept
3801 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3802 * NB: _not_ safe_waitpid, we need to detect EINTR */
3803 pid = waitpid(-1, &status,
3804 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3805 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003806 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003807 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003808
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809 INT_OFF;
3810 thisjob = NULL;
3811 for (jp = curjob; jp; jp = jp->prev_job) {
3812 struct procstat *sp;
3813 struct procstat *spend;
3814 if (jp->state == JOBDONE)
3815 continue;
3816 state = JOBDONE;
3817 spend = jp->ps + jp->nprocs;
3818 sp = jp->ps;
3819 do {
3820 if (sp->pid == pid) {
3821 TRACE(("Job %d: changing status of proc %d "
3822 "from 0x%x to 0x%x\n",
3823 jobno(jp), pid, sp->status, status));
3824 sp->status = status;
3825 thisjob = jp;
3826 }
3827 if (sp->status == -1)
3828 state = JOBRUNNING;
3829#if JOBS
3830 if (state == JOBRUNNING)
3831 continue;
3832 if (WIFSTOPPED(sp->status)) {
3833 jp->stopstatus = sp->status;
3834 state = JOBSTOPPED;
3835 }
3836#endif
3837 } while (++sp < spend);
3838 if (thisjob)
3839 goto gotjob;
3840 }
3841#if JOBS
3842 if (!WIFSTOPPED(status))
3843#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844 jobless--;
3845 goto out;
3846
3847 gotjob:
3848 if (state != JOBRUNNING) {
3849 thisjob->changed = 1;
3850
3851 if (thisjob->state != state) {
3852 TRACE(("Job %d: changing state from %d to %d\n",
3853 jobno(thisjob), thisjob->state, state));
3854 thisjob->state = state;
3855#if JOBS
3856 if (state == JOBSTOPPED) {
3857 set_curjob(thisjob, CUR_STOPPED);
3858 }
3859#endif
3860 }
3861 }
3862
3863 out:
3864 INT_ON;
3865
3866 if (thisjob && thisjob == job) {
3867 char s[48 + 1];
3868 int len;
3869
3870 len = sprint_status(s, status, 1);
3871 if (len) {
3872 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003873 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003874 out2str(s);
3875 }
3876 }
3877 return pid;
3878}
3879
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003880static int
3881blocking_wait_with_raise_on_sig(struct job *job)
3882{
3883 pid_t pid = dowait(DOWAIT_BLOCK, job);
3884 if (pid <= 0 && pendingsig)
3885 raise_exception(EXSIG);
3886 return pid;
3887}
3888
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889#if JOBS
3890static void
3891showjob(FILE *out, struct job *jp, int mode)
3892{
3893 struct procstat *ps;
3894 struct procstat *psend;
3895 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003896 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003897 char s[80];
3898
3899 ps = jp->ps;
3900
3901 if (mode & SHOW_PGID) {
3902 /* just output process (group) id of pipeline */
3903 fprintf(out, "%d\n", ps->pid);
3904 return;
3905 }
3906
3907 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003908 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003909
3910 if (jp == curjob)
3911 s[col - 2] = '+';
3912 else if (curjob && jp == curjob->prev_job)
3913 s[col - 2] = '-';
3914
3915 if (mode & SHOW_PID)
3916 col += fmtstr(s + col, 16, "%d ", ps->pid);
3917
3918 psend = ps + jp->nprocs;
3919
3920 if (jp->state == JOBRUNNING) {
3921 strcpy(s + col, "Running");
3922 col += sizeof("Running") - 1;
3923 } else {
3924 int status = psend[-1].status;
3925 if (jp->state == JOBSTOPPED)
3926 status = jp->stopstatus;
3927 col += sprint_status(s + col, status, 0);
3928 }
3929
3930 goto start;
3931
3932 do {
3933 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003934 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003935 start:
3936 fprintf(out, "%s%*c%s",
3937 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3938 );
3939 if (!(mode & SHOW_PID)) {
3940 showpipe(jp, out);
3941 break;
3942 }
3943 if (++ps == psend) {
3944 outcslow('\n', out);
3945 break;
3946 }
3947 } while (1);
3948
3949 jp->changed = 0;
3950
3951 if (jp->state == JOBDONE) {
3952 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3953 freejob(jp);
3954 }
3955}
3956
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957/*
3958 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3959 * statuses have changed since the last call to showjobs.
3960 */
3961static void
3962showjobs(FILE *out, int mode)
3963{
3964 struct job *jp;
3965
3966 TRACE(("showjobs(%x) called\n", mode));
3967
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003968 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003969 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 continue;
3971
3972 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003975 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 }
3977}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003978
3979static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003980jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003981{
3982 int mode, m;
3983
3984 mode = 0;
3985 while ((m = nextopt("lp"))) {
3986 if (m == 'l')
3987 mode = SHOW_PID;
3988 else
3989 mode = SHOW_PGID;
3990 }
3991
3992 argv = argptr;
3993 if (*argv) {
3994 do
3995 showjob(stdout, getjob(*argv,0), mode);
3996 while (*++argv);
3997 } else
3998 showjobs(stdout, mode);
3999
4000 return 0;
4001}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002#endif /* JOBS */
4003
4004static int
4005getstatus(struct job *job)
4006{
4007 int status;
4008 int retval;
4009
4010 status = job->ps[job->nprocs - 1].status;
4011 retval = WEXITSTATUS(status);
4012 if (!WIFEXITED(status)) {
4013#if JOBS
4014 retval = WSTOPSIG(status);
4015 if (!WIFSTOPPED(status))
4016#endif
4017 {
4018 /* XXX: limits number of signals */
4019 retval = WTERMSIG(status);
4020#if JOBS
4021 if (retval == SIGINT)
4022 job->sigint = 1;
4023#endif
4024 }
4025 retval += 128;
4026 }
4027 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4028 jobno(job), job->nprocs, status, retval));
4029 return retval;
4030}
4031
4032static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004033waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034{
4035 struct job *job;
4036 int retval;
4037 struct job *jp;
4038
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004039// exsig++;
4040// xbarrier();
4041 if (pendingsig)
4042 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043
4044 nextopt(nullstr);
4045 retval = 0;
4046
4047 argv = argptr;
4048 if (!*argv) {
4049 /* wait for all jobs */
4050 for (;;) {
4051 jp = curjob;
4052 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004053 if (!jp) /* no running procs */
4054 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 if (jp->state == JOBRUNNING)
4056 break;
4057 jp->waited = 1;
4058 jp = jp->prev_job;
4059 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004060 /* man bash:
4061 * "When bash is waiting for an asynchronous command via
4062 * the wait builtin, the reception of a signal for which a trap
4063 * has been set will cause the wait builtin to return immediately
4064 * with an exit status greater than 128, immediately after which
4065 * the trap is executed."
4066 * Do we do it that way? */
4067 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 }
4069 }
4070
4071 retval = 127;
4072 do {
4073 if (**argv != '%') {
4074 pid_t pid = number(*argv);
4075 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004076 while (1) {
4077 if (!job)
4078 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079 if (job->ps[job->nprocs - 1].pid == pid)
4080 break;
4081 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004082 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004083 } else
4084 job = getjob(*argv, 0);
4085 /* loop until process terminated or stopped */
4086 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004087 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 job->waited = 1;
4089 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004090 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 } while (*++argv);
4092
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004093 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004094 return retval;
4095}
4096
4097static struct job *
4098growjobtab(void)
4099{
4100 size_t len;
4101 ptrdiff_t offset;
4102 struct job *jp, *jq;
4103
4104 len = njobs * sizeof(*jp);
4105 jq = jobtab;
4106 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4107
4108 offset = (char *)jp - (char *)jq;
4109 if (offset) {
4110 /* Relocate pointers */
4111 size_t l = len;
4112
4113 jq = (struct job *)((char *)jq + l);
4114 while (l) {
4115 l -= sizeof(*jp);
4116 jq--;
4117#define joff(p) ((struct job *)((char *)(p) + l))
4118#define jmove(p) (p) = (void *)((char *)(p) + offset)
4119 if (joff(jp)->ps == &jq->ps0)
4120 jmove(joff(jp)->ps);
4121 if (joff(jp)->prev_job)
4122 jmove(joff(jp)->prev_job);
4123 }
4124 if (curjob)
4125 jmove(curjob);
4126#undef joff
4127#undef jmove
4128 }
4129
4130 njobs += 4;
4131 jobtab = jp;
4132 jp = (struct job *)((char *)jp + len);
4133 jq = jp + 3;
4134 do {
4135 jq->used = 0;
4136 } while (--jq >= jp);
4137 return jp;
4138}
4139
4140/*
4141 * Return a new job structure.
4142 * Called with interrupts off.
4143 */
4144static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004145makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004146{
4147 int i;
4148 struct job *jp;
4149
4150 for (i = njobs, jp = jobtab; ; jp++) {
4151 if (--i < 0) {
4152 jp = growjobtab();
4153 break;
4154 }
4155 if (jp->used == 0)
4156 break;
4157 if (jp->state != JOBDONE || !jp->waited)
4158 continue;
4159#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004160 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004161 continue;
4162#endif
4163 freejob(jp);
4164 break;
4165 }
4166 memset(jp, 0, sizeof(*jp));
4167#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004168 /* jp->jobctl is a bitfield.
4169 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004170 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004171 jp->jobctl = 1;
4172#endif
4173 jp->prev_job = curjob;
4174 curjob = jp;
4175 jp->used = 1;
4176 jp->ps = &jp->ps0;
4177 if (nprocs > 1) {
4178 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4179 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004180 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004181 jobno(jp)));
4182 return jp;
4183}
4184
4185#if JOBS
4186/*
4187 * Return a string identifying a command (to be printed by the
4188 * jobs command).
4189 */
4190static char *cmdnextc;
4191
4192static void
4193cmdputs(const char *s)
4194{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004195 static const char vstype[VSTYPE + 1][3] = {
4196 "", "}", "-", "+", "?", "=",
4197 "%", "%%", "#", "##"
4198 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4199 };
4200
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004201 const char *p, *str;
4202 char c, cc[2] = " ";
4203 char *nextc;
4204 int subtype = 0;
4205 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004206
4207 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4208 p = s;
4209 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004210 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211 switch (c) {
4212 case CTLESC:
4213 c = *p++;
4214 break;
4215 case CTLVAR:
4216 subtype = *p++;
4217 if ((subtype & VSTYPE) == VSLENGTH)
4218 str = "${#";
4219 else
4220 str = "${";
4221 if (!(subtype & VSQUOTE) == !(quoted & 1))
4222 goto dostr;
4223 quoted ^= 1;
4224 c = '"';
4225 break;
4226 case CTLENDVAR:
4227 str = "\"}" + !(quoted & 1);
4228 quoted >>= 1;
4229 subtype = 0;
4230 goto dostr;
4231 case CTLBACKQ:
4232 str = "$(...)";
4233 goto dostr;
4234 case CTLBACKQ+CTLQUOTE:
4235 str = "\"$(...)\"";
4236 goto dostr;
4237#if ENABLE_ASH_MATH_SUPPORT
4238 case CTLARI:
4239 str = "$((";
4240 goto dostr;
4241 case CTLENDARI:
4242 str = "))";
4243 goto dostr;
4244#endif
4245 case CTLQUOTEMARK:
4246 quoted ^= 1;
4247 c = '"';
4248 break;
4249 case '=':
4250 if (subtype == 0)
4251 break;
4252 if ((subtype & VSTYPE) != VSNORMAL)
4253 quoted <<= 1;
4254 str = vstype[subtype & VSTYPE];
4255 if (subtype & VSNUL)
4256 c = ':';
4257 else
4258 goto checkstr;
4259 break;
4260 case '\'':
4261 case '\\':
4262 case '"':
4263 case '$':
4264 /* These can only happen inside quotes */
4265 cc[0] = c;
4266 str = cc;
4267 c = '\\';
4268 break;
4269 default:
4270 break;
4271 }
4272 USTPUTC(c, nextc);
4273 checkstr:
4274 if (!str)
4275 continue;
4276 dostr:
4277 while ((c = *str++)) {
4278 USTPUTC(c, nextc);
4279 }
4280 }
4281 if (quoted & 1) {
4282 USTPUTC('"', nextc);
4283 }
4284 *nextc = 0;
4285 cmdnextc = nextc;
4286}
4287
4288/* cmdtxt() and cmdlist() call each other */
4289static void cmdtxt(union node *n);
4290
4291static void
4292cmdlist(union node *np, int sep)
4293{
4294 for (; np; np = np->narg.next) {
4295 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004296 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004297 cmdtxt(np);
4298 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004299 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004300 }
4301}
4302
4303static void
4304cmdtxt(union node *n)
4305{
4306 union node *np;
4307 struct nodelist *lp;
4308 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004309
4310 if (!n)
4311 return;
4312 switch (n->type) {
4313 default:
4314#if DEBUG
4315 abort();
4316#endif
4317 case NPIPE:
4318 lp = n->npipe.cmdlist;
4319 for (;;) {
4320 cmdtxt(lp->n);
4321 lp = lp->next;
4322 if (!lp)
4323 break;
4324 cmdputs(" | ");
4325 }
4326 break;
4327 case NSEMI:
4328 p = "; ";
4329 goto binop;
4330 case NAND:
4331 p = " && ";
4332 goto binop;
4333 case NOR:
4334 p = " || ";
4335 binop:
4336 cmdtxt(n->nbinary.ch1);
4337 cmdputs(p);
4338 n = n->nbinary.ch2;
4339 goto donode;
4340 case NREDIR:
4341 case NBACKGND:
4342 n = n->nredir.n;
4343 goto donode;
4344 case NNOT:
4345 cmdputs("!");
4346 n = n->nnot.com;
4347 donode:
4348 cmdtxt(n);
4349 break;
4350 case NIF:
4351 cmdputs("if ");
4352 cmdtxt(n->nif.test);
4353 cmdputs("; then ");
4354 n = n->nif.ifpart;
4355 if (n->nif.elsepart) {
4356 cmdtxt(n);
4357 cmdputs("; else ");
4358 n = n->nif.elsepart;
4359 }
4360 p = "; fi";
4361 goto dotail;
4362 case NSUBSHELL:
4363 cmdputs("(");
4364 n = n->nredir.n;
4365 p = ")";
4366 goto dotail;
4367 case NWHILE:
4368 p = "while ";
4369 goto until;
4370 case NUNTIL:
4371 p = "until ";
4372 until:
4373 cmdputs(p);
4374 cmdtxt(n->nbinary.ch1);
4375 n = n->nbinary.ch2;
4376 p = "; done";
4377 dodo:
4378 cmdputs("; do ");
4379 dotail:
4380 cmdtxt(n);
4381 goto dotail2;
4382 case NFOR:
4383 cmdputs("for ");
4384 cmdputs(n->nfor.var);
4385 cmdputs(" in ");
4386 cmdlist(n->nfor.args, 1);
4387 n = n->nfor.body;
4388 p = "; done";
4389 goto dodo;
4390 case NDEFUN:
4391 cmdputs(n->narg.text);
4392 p = "() { ... }";
4393 goto dotail2;
4394 case NCMD:
4395 cmdlist(n->ncmd.args, 1);
4396 cmdlist(n->ncmd.redirect, 0);
4397 break;
4398 case NARG:
4399 p = n->narg.text;
4400 dotail2:
4401 cmdputs(p);
4402 break;
4403 case NHERE:
4404 case NXHERE:
4405 p = "<<...";
4406 goto dotail2;
4407 case NCASE:
4408 cmdputs("case ");
4409 cmdputs(n->ncase.expr->narg.text);
4410 cmdputs(" in ");
4411 for (np = n->ncase.cases; np; np = np->nclist.next) {
4412 cmdtxt(np->nclist.pattern);
4413 cmdputs(") ");
4414 cmdtxt(np->nclist.body);
4415 cmdputs(";; ");
4416 }
4417 p = "esac";
4418 goto dotail2;
4419 case NTO:
4420 p = ">";
4421 goto redir;
4422 case NCLOBBER:
4423 p = ">|";
4424 goto redir;
4425 case NAPPEND:
4426 p = ">>";
4427 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004428#if ENABLE_ASH_BASH_COMPAT
4429 case NTO2:
4430#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004431 case NTOFD:
4432 p = ">&";
4433 goto redir;
4434 case NFROM:
4435 p = "<";
4436 goto redir;
4437 case NFROMFD:
4438 p = "<&";
4439 goto redir;
4440 case NFROMTO:
4441 p = "<>";
4442 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004443 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004444 cmdputs(p);
4445 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004446 cmdputs(utoa(n->ndup.dupfd));
4447 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004448 }
4449 n = n->nfile.fname;
4450 goto donode;
4451 }
4452}
4453
4454static char *
4455commandtext(union node *n)
4456{
4457 char *name;
4458
4459 STARTSTACKSTR(cmdnextc);
4460 cmdtxt(n);
4461 name = stackblock();
4462 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4463 name, cmdnextc, cmdnextc));
4464 return ckstrdup(name);
4465}
4466#endif /* JOBS */
4467
4468/*
4469 * Fork off a subshell. If we are doing job control, give the subshell its
4470 * own process group. Jp is a job structure that the job is to be added to.
4471 * N is the command that will be evaluated by the child. Both jp and n may
4472 * be NULL. The mode parameter can be one of the following:
4473 * FORK_FG - Fork off a foreground process.
4474 * FORK_BG - Fork off a background process.
4475 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4476 * process group even if job control is on.
4477 *
4478 * When job control is turned off, background processes have their standard
4479 * input redirected to /dev/null (except for the second and later processes
4480 * in a pipeline).
4481 *
4482 * Called with interrupts off.
4483 */
4484/*
4485 * Clear traps on a fork.
4486 */
4487static void
4488clear_traps(void)
4489{
4490 char **tp;
4491
4492 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004493 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004494 INT_OFF;
4495 free(*tp);
4496 *tp = NULL;
4497 if (tp != &trap[0])
4498 setsignal(tp - trap);
4499 INT_ON;
4500 }
4501 }
4502}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004503
4504/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004506
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004507/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004508static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004509forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510{
4511 int oldlvl;
4512
4513 TRACE(("Child shell %d\n", getpid()));
4514 oldlvl = shlvl;
4515 shlvl++;
4516
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004517 /* man bash: "Non-builtin commands run by bash have signal handlers
4518 * set to the values inherited by the shell from its parent".
4519 * Do we do it correctly? */
4520
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004521 closescript();
4522 clear_traps();
4523#if JOBS
4524 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004525 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4527 pid_t pgrp;
4528
4529 if (jp->nprocs == 0)
4530 pgrp = getpid();
4531 else
4532 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004533 /* this can fail because we are doing it in the parent also */
4534 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535 if (mode == FORK_FG)
4536 xtcsetpgrp(ttyfd, pgrp);
4537 setsignal(SIGTSTP);
4538 setsignal(SIGTTOU);
4539 } else
4540#endif
4541 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004542 /* man bash: "When job control is not in effect,
4543 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 ignoresig(SIGINT);
4545 ignoresig(SIGQUIT);
4546 if (jp->nprocs == 0) {
4547 close(0);
4548 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004549 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004550 }
4551 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004552 if (!oldlvl) {
4553 if (iflag) { /* why if iflag only? */
4554 setsignal(SIGINT);
4555 setsignal(SIGTERM);
4556 }
4557 /* man bash:
4558 * "In all cases, bash ignores SIGQUIT. Non-builtin
4559 * commands run by bash have signal handlers
4560 * set to the values inherited by the shell
4561 * from its parent".
4562 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004563 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004564 }
4565 for (jp = curjob; jp; jp = jp->prev_job)
4566 freejob(jp);
4567 jobless = 0;
4568}
4569
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004570/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004571#if !JOBS
4572#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4573#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004574static void
4575forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4576{
4577 TRACE(("In parent shell: child = %d\n", pid));
4578 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004579 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4580 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004581 jobless++;
4582 return;
4583 }
4584#if JOBS
4585 if (mode != FORK_NOJOB && jp->jobctl) {
4586 int pgrp;
4587
4588 if (jp->nprocs == 0)
4589 pgrp = pid;
4590 else
4591 pgrp = jp->ps[0].pid;
4592 /* This can fail because we are doing it in the child also */
4593 setpgid(pid, pgrp);
4594 }
4595#endif
4596 if (mode == FORK_BG) {
4597 backgndpid = pid; /* set $! */
4598 set_curjob(jp, CUR_RUNNING);
4599 }
4600 if (jp) {
4601 struct procstat *ps = &jp->ps[jp->nprocs++];
4602 ps->pid = pid;
4603 ps->status = -1;
4604 ps->cmd = nullstr;
4605#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004606 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004607 ps->cmd = commandtext(n);
4608#endif
4609 }
4610}
4611
4612static int
4613forkshell(struct job *jp, union node *n, int mode)
4614{
4615 int pid;
4616
4617 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4618 pid = fork();
4619 if (pid < 0) {
4620 TRACE(("Fork failed, errno=%d", errno));
4621 if (jp)
4622 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004623 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004624 }
4625 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004626 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004627 else
4628 forkparent(jp, n, mode, pid);
4629 return pid;
4630}
4631
4632/*
4633 * Wait for job to finish.
4634 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004635 * Under job control we have the problem that while a child process
4636 * is running interrupts generated by the user are sent to the child
4637 * but not to the shell. This means that an infinite loop started by
4638 * an interactive user may be hard to kill. With job control turned off,
4639 * an interactive user may place an interactive program inside a loop.
4640 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004641 * these interrupts to also abort the loop. The approach we take here
4642 * is to have the shell ignore interrupt signals while waiting for a
4643 * foreground process to terminate, and then send itself an interrupt
4644 * signal if the child process was terminated by an interrupt signal.
4645 * Unfortunately, some programs want to do a bit of cleanup and then
4646 * exit on interrupt; unless these processes terminate themselves by
4647 * sending a signal to themselves (instead of calling exit) they will
4648 * confuse this approach.
4649 *
4650 * Called with interrupts off.
4651 */
4652static int
4653waitforjob(struct job *jp)
4654{
4655 int st;
4656
4657 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004658
4659 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004660 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004661 /* In non-interactive shells, we _can_ get
4662 * a keyboard signal here and be EINTRed,
4663 * but we just loop back, waiting for command to complete.
4664 *
4665 * man bash:
4666 * "If bash is waiting for a command to complete and receives
4667 * a signal for which a trap has been set, the trap
4668 * will not be executed until the command completes."
4669 *
4670 * Reality is that even if trap is not set, bash
4671 * will not act on the signal until command completes.
4672 * Try this. sleep5intoff.c:
4673 * #include <signal.h>
4674 * #include <unistd.h>
4675 * int main() {
4676 * sigset_t set;
4677 * sigemptyset(&set);
4678 * sigaddset(&set, SIGINT);
4679 * sigaddset(&set, SIGQUIT);
4680 * sigprocmask(SIG_BLOCK, &set, NULL);
4681 * sleep(5);
4682 * return 0;
4683 * }
4684 * $ bash -c './sleep5intoff; echo hi'
4685 * ^C^C^C^C <--- pressing ^C once a second
4686 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004687 * $ bash -c './sleep5intoff; echo hi'
4688 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4689 * $ _
4690 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004691 dowait(DOWAIT_BLOCK, jp);
4692 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004693 INT_ON;
4694
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004695 st = getstatus(jp);
4696#if JOBS
4697 if (jp->jobctl) {
4698 xtcsetpgrp(ttyfd, rootpid);
4699 /*
4700 * This is truly gross.
4701 * If we're doing job control, then we did a TIOCSPGRP which
4702 * caused us (the shell) to no longer be in the controlling
4703 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4704 * intuit from the subprocess exit status whether a SIGINT
4705 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4706 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004707 if (jp->sigint) /* TODO: do the same with all signals */
4708 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709 }
4710 if (jp->state == JOBDONE)
4711#endif
4712 freejob(jp);
4713 return st;
4714}
4715
4716/*
4717 * return 1 if there are stopped jobs, otherwise 0
4718 */
4719static int
4720stoppedjobs(void)
4721{
4722 struct job *jp;
4723 int retval;
4724
4725 retval = 0;
4726 if (job_warning)
4727 goto out;
4728 jp = curjob;
4729 if (jp && jp->state == JOBSTOPPED) {
4730 out2str("You have stopped jobs.\n");
4731 job_warning = 2;
4732 retval++;
4733 }
4734 out:
4735 return retval;
4736}
4737
4738
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004739/* ============ redir.c
4740 *
4741 * Code for dealing with input/output redirection.
4742 */
4743
4744#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004745#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004746
4747/*
4748 * Open a file in noclobber mode.
4749 * The code was copied from bash.
4750 */
4751static int
4752noclobberopen(const char *fname)
4753{
4754 int r, fd;
4755 struct stat finfo, finfo2;
4756
4757 /*
4758 * If the file exists and is a regular file, return an error
4759 * immediately.
4760 */
4761 r = stat(fname, &finfo);
4762 if (r == 0 && S_ISREG(finfo.st_mode)) {
4763 errno = EEXIST;
4764 return -1;
4765 }
4766
4767 /*
4768 * If the file was not present (r != 0), make sure we open it
4769 * exclusively so that if it is created before we open it, our open
4770 * will fail. Make sure that we do not truncate an existing file.
4771 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4772 * file was not a regular file, we leave O_EXCL off.
4773 */
4774 if (r != 0)
4775 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4776 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4777
4778 /* If the open failed, return the file descriptor right away. */
4779 if (fd < 0)
4780 return fd;
4781
4782 /*
4783 * OK, the open succeeded, but the file may have been changed from a
4784 * non-regular file to a regular file between the stat and the open.
4785 * We are assuming that the O_EXCL open handles the case where FILENAME
4786 * did not exist and is symlinked to an existing file between the stat
4787 * and open.
4788 */
4789
4790 /*
4791 * If we can open it and fstat the file descriptor, and neither check
4792 * revealed that it was a regular file, and the file has not been
4793 * replaced, return the file descriptor.
4794 */
4795 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4796 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4797 return fd;
4798
4799 /* The file has been replaced. badness. */
4800 close(fd);
4801 errno = EEXIST;
4802 return -1;
4803}
4804
4805/*
4806 * Handle here documents. Normally we fork off a process to write the
4807 * data to a pipe. If the document is short, we can stuff the data in
4808 * the pipe without forking.
4809 */
4810/* openhere needs this forward reference */
4811static void expandhere(union node *arg, int fd);
4812static int
4813openhere(union node *redir)
4814{
4815 int pip[2];
4816 size_t len = 0;
4817
4818 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004819 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004820 if (redir->type == NHERE) {
4821 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004822 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004823 full_write(pip[1], redir->nhere.doc->narg.text, len);
4824 goto out;
4825 }
4826 }
4827 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004828 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004829 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004830 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4831 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4832 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4833 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004834 signal(SIGPIPE, SIG_DFL);
4835 if (redir->type == NHERE)
4836 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004837 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004838 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004839 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004840 }
4841 out:
4842 close(pip[1]);
4843 return pip[0];
4844}
4845
4846static int
4847openredirect(union node *redir)
4848{
4849 char *fname;
4850 int f;
4851
4852 switch (redir->nfile.type) {
4853 case NFROM:
4854 fname = redir->nfile.expfname;
4855 f = open(fname, O_RDONLY);
4856 if (f < 0)
4857 goto eopen;
4858 break;
4859 case NFROMTO:
4860 fname = redir->nfile.expfname;
4861 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4862 if (f < 0)
4863 goto ecreate;
4864 break;
4865 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004866#if ENABLE_ASH_BASH_COMPAT
4867 case NTO2:
4868#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004869 /* Take care of noclobber mode. */
4870 if (Cflag) {
4871 fname = redir->nfile.expfname;
4872 f = noclobberopen(fname);
4873 if (f < 0)
4874 goto ecreate;
4875 break;
4876 }
4877 /* FALLTHROUGH */
4878 case NCLOBBER:
4879 fname = redir->nfile.expfname;
4880 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4881 if (f < 0)
4882 goto ecreate;
4883 break;
4884 case NAPPEND:
4885 fname = redir->nfile.expfname;
4886 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4887 if (f < 0)
4888 goto ecreate;
4889 break;
4890 default:
4891#if DEBUG
4892 abort();
4893#endif
4894 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004895/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004896// case NTOFD:
4897// case NFROMFD:
4898// f = -1;
4899// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004900 case NHERE:
4901 case NXHERE:
4902 f = openhere(redir);
4903 break;
4904 }
4905
4906 return f;
4907 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004908 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004909 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004910 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004911}
4912
4913/*
4914 * Copy a file descriptor to be >= to. Returns -1
4915 * if the source file descriptor is closed, EMPTY if there are no unused
4916 * file descriptors left.
4917 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004918/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4919 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004920enum {
4921 COPYFD_EXACT = (int)~(INT_MAX),
4922 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4923};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004924static int
4925copyfd(int from, int to)
4926{
4927 int newfd;
4928
Denis Vlasenko5a867312008-07-24 19:46:38 +00004929 if (to & COPYFD_EXACT) {
4930 to &= ~COPYFD_EXACT;
4931 /*if (from != to)*/
4932 newfd = dup2(from, to);
4933 } else {
4934 newfd = fcntl(from, F_DUPFD, to);
4935 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936 if (newfd < 0) {
4937 if (errno == EMFILE)
4938 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004939 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004940 ash_msg_and_raise_error("%d: %m", from);
4941 }
4942 return newfd;
4943}
4944
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004945/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004946struct two_fd_t {
4947 int orig, copy;
4948};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004949struct redirtab {
4950 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004951 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004952 int pair_count;
4953 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004954};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004955#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004956
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004957static int need_to_remember(struct redirtab *rp, int fd)
4958{
4959 int i;
4960
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004961 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004962 return 0;
4963
4964 for (i = 0; i < rp->pair_count; i++) {
4965 if (rp->two_fd[i].orig == fd) {
4966 /* already remembered */
4967 return 0;
4968 }
4969 }
4970 return 1;
4971}
4972
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004973/* "hidden" fd is a fd used to read scripts, or a copy of such */
4974static int is_hidden_fd(struct redirtab *rp, int fd)
4975{
4976 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004977 struct parsefile *pf;
4978
4979 if (fd == -1)
4980 return 0;
4981 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004982 while (pf) {
4983 if (fd == pf->fd) {
4984 return 1;
4985 }
4986 pf = pf->prev;
4987 }
4988 if (!rp)
4989 return 0;
4990 fd |= COPYFD_RESTORE;
4991 for (i = 0; i < rp->pair_count; i++) {
4992 if (rp->two_fd[i].copy == fd) {
4993 return 1;
4994 }
4995 }
4996 return 0;
4997}
4998
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004999/*
5000 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5001 * old file descriptors are stashed away so that the redirection can be
5002 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
5003 * standard output, and the standard error if it becomes a duplicate of
5004 * stdout, is saved in memory.
5005 */
5006/* flags passed to redirect */
5007#define REDIR_PUSH 01 /* save previous values of file descriptors */
5008#define REDIR_SAVEFD2 03 /* set preverrout */
5009static void
5010redirect(union node *redir, int flags)
5011{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005012 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005013 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005014 int i;
5015 int fd;
5016 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005017 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005018
Denis Vlasenko01631112007-12-16 17:20:38 +00005019 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005020 if (!redir) {
5021 return;
5022 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005023
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005024 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005025 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005026 INT_OFF;
5027 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005028 union node *tmp = redir;
5029 do {
5030 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005031#if ENABLE_ASH_BASH_COMPAT
5032 if (redir->nfile.type == NTO2)
5033 sv_pos++;
5034#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005035 tmp = tmp->nfile.next;
5036 } while (tmp);
5037 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005038 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005039 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005040 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005041 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005042 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005043 while (sv_pos > 0) {
5044 sv_pos--;
5045 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5046 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005048
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005049 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005050 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005051 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005052 int right_fd = redir->ndup.dupfd;
5053 /* redirect from/to same file descriptor? */
5054 if (right_fd == fd)
5055 continue;
5056 /* echo >&10 and 10 is a fd opened to the sh script? */
5057 if (is_hidden_fd(sv, right_fd)) {
5058 errno = EBADF; /* as if it is closed */
5059 ash_msg_and_raise_error("%d: %m", right_fd);
5060 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005061 newfd = -1;
5062 } else {
5063 newfd = openredirect(redir); /* always >= 0 */
5064 if (fd == newfd) {
5065 /* Descriptor wasn't open before redirect.
5066 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005067 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005068 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005069 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005070 continue;
5071 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005072 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005073#if ENABLE_ASH_BASH_COMPAT
5074 redirect_more:
5075#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005076 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005077 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005079/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5080 * are closed in popredir() in the child, preventing them from leaking
5081 * into child. (popredir() also cleans up the mess in case of failures)
5082 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 if (i == -1) {
5084 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005085 if (i != EBADF) {
5086 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005087 if (newfd >= 0)
5088 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 errno = i;
5090 ash_msg_and_raise_error("%d: %m", fd);
5091 /* NOTREACHED */
5092 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005093 /* EBADF: it is not open - good, remember to close it */
5094 remember_to_close:
5095 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005096 } else { /* fd is open, save its copy */
5097 /* "exec fd>&-" should not close fds
5098 * which point to script file(s).
5099 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005100 if (is_hidden_fd(sv, fd))
5101 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005102 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005103 if (fd == 2)
5104 copied_fd2 = i;
5105 sv->two_fd[sv_pos].orig = fd;
5106 sv->two_fd[sv_pos].copy = i;
5107 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005108 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005109 if (newfd < 0) {
5110 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005111 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005112 close(fd);
5113 } else {
5114 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005115 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005116 } else if (fd != newfd) { /* move newfd to fd */
5117 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005118#if ENABLE_ASH_BASH_COMPAT
5119 if (!(redir->nfile.type == NTO2 && fd == 2))
5120#endif
5121 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005122 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005123#if ENABLE_ASH_BASH_COMPAT
5124 if (redir->nfile.type == NTO2 && fd == 1) {
5125 /* We already redirected it to fd 1, now copy it to 2 */
5126 newfd = 1;
5127 fd = 2;
5128 goto redirect_more;
5129 }
5130#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005131 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005132
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005133 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005134 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5135 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005136}
5137
5138/*
5139 * Undo the effects of the last redirection.
5140 */
5141static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005142popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005143{
5144 struct redirtab *rp;
5145 int i;
5146
Denis Vlasenko01631112007-12-16 17:20:38 +00005147 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005148 return;
5149 INT_OFF;
5150 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005151 for (i = 0; i < rp->pair_count; i++) {
5152 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005153 int copy = rp->two_fd[i].copy;
5154 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005155 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005156 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005157 continue;
5158 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005159 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005160 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005161 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005162 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005163 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005164 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005165 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005166 }
5167 }
5168 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005169 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005170 free(rp);
5171 INT_ON;
5172}
5173
5174/*
5175 * Undo all redirections. Called on error or interrupt.
5176 */
5177
5178/*
5179 * Discard all saved file descriptors.
5180 */
5181static void
5182clearredir(int drop)
5183{
5184 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005185 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005186 if (!redirlist)
5187 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005188 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005189 }
5190}
5191
5192static int
5193redirectsafe(union node *redir, int flags)
5194{
5195 int err;
5196 volatile int saveint;
5197 struct jmploc *volatile savehandler = exception_handler;
5198 struct jmploc jmploc;
5199
5200 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005201 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5202 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005203 if (!err) {
5204 exception_handler = &jmploc;
5205 redirect(redir, flags);
5206 }
5207 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005208 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005209 longjmp(exception_handler->loc, 1);
5210 RESTORE_INT(saveint);
5211 return err;
5212}
5213
5214
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005215/* ============ Routines to expand arguments to commands
5216 *
5217 * We have to deal with backquotes, shell variables, and file metacharacters.
5218 */
5219
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005220#if ENABLE_ASH_MATH_SUPPORT_64
5221typedef int64_t arith_t;
5222#define arith_t_type long long
5223#else
5224typedef long arith_t;
5225#define arith_t_type long
5226#endif
5227
5228#if ENABLE_ASH_MATH_SUPPORT
5229static arith_t dash_arith(const char *);
5230static arith_t arith(const char *expr, int *perrcode);
5231#endif
5232
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005233/*
5234 * expandarg flags
5235 */
5236#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5237#define EXP_TILDE 0x2 /* do normal tilde expansion */
5238#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5239#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5240#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5241#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5242#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5243#define EXP_WORD 0x80 /* expand word in parameter expansion */
5244#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5245/*
5246 * _rmescape() flags
5247 */
5248#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5249#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5250#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5251#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5252#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5253
5254/*
5255 * Structure specifying which parts of the string should be searched
5256 * for IFS characters.
5257 */
5258struct ifsregion {
5259 struct ifsregion *next; /* next region in list */
5260 int begoff; /* offset of start of region */
5261 int endoff; /* offset of end of region */
5262 int nulonly; /* search for nul bytes only */
5263};
5264
5265struct arglist {
5266 struct strlist *list;
5267 struct strlist **lastp;
5268};
5269
5270/* output of current string */
5271static char *expdest;
5272/* list of back quote expressions */
5273static struct nodelist *argbackq;
5274/* first struct in list of ifs regions */
5275static struct ifsregion ifsfirst;
5276/* last struct in list */
5277static struct ifsregion *ifslastp;
5278/* holds expanded arg list */
5279static struct arglist exparg;
5280
5281/*
5282 * Our own itoa().
5283 */
5284static int
5285cvtnum(arith_t num)
5286{
5287 int len;
5288
5289 expdest = makestrspace(32, expdest);
5290#if ENABLE_ASH_MATH_SUPPORT_64
5291 len = fmtstr(expdest, 32, "%lld", (long long) num);
5292#else
5293 len = fmtstr(expdest, 32, "%ld", num);
5294#endif
5295 STADJUST(len, expdest);
5296 return len;
5297}
5298
5299static size_t
5300esclen(const char *start, const char *p)
5301{
5302 size_t esc = 0;
5303
5304 while (p > start && *--p == CTLESC) {
5305 esc++;
5306 }
5307 return esc;
5308}
5309
5310/*
5311 * Remove any CTLESC characters from a string.
5312 */
5313static char *
5314_rmescapes(char *str, int flag)
5315{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005316 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005317
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005318 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005319 unsigned inquotes;
5320 int notescaped;
5321 int globbing;
5322
5323 p = strpbrk(str, qchars);
5324 if (!p) {
5325 return str;
5326 }
5327 q = p;
5328 r = str;
5329 if (flag & RMESCAPE_ALLOC) {
5330 size_t len = p - str;
5331 size_t fulllen = len + strlen(p) + 1;
5332
5333 if (flag & RMESCAPE_GROW) {
5334 r = makestrspace(fulllen, expdest);
5335 } else if (flag & RMESCAPE_HEAP) {
5336 r = ckmalloc(fulllen);
5337 } else {
5338 r = stalloc(fulllen);
5339 }
5340 q = r;
5341 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005342 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005343 }
5344 }
5345 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5346 globbing = flag & RMESCAPE_GLOB;
5347 notescaped = globbing;
5348 while (*p) {
5349 if (*p == CTLQUOTEMARK) {
5350 inquotes = ~inquotes;
5351 p++;
5352 notescaped = globbing;
5353 continue;
5354 }
5355 if (*p == '\\') {
5356 /* naked back slash */
5357 notescaped = 0;
5358 goto copy;
5359 }
5360 if (*p == CTLESC) {
5361 p++;
5362 if (notescaped && inquotes && *p != '/') {
5363 *q++ = '\\';
5364 }
5365 }
5366 notescaped = globbing;
5367 copy:
5368 *q++ = *p++;
5369 }
5370 *q = '\0';
5371 if (flag & RMESCAPE_GROW) {
5372 expdest = r;
5373 STADJUST(q - r + 1, expdest);
5374 }
5375 return r;
5376}
5377#define rmescapes(p) _rmescapes((p), 0)
5378
5379#define pmatch(a, b) !fnmatch((a), (b), 0)
5380
5381/*
5382 * Prepare a pattern for a expmeta (internal glob(3)) call.
5383 *
5384 * Returns an stalloced string.
5385 */
5386static char *
5387preglob(const char *pattern, int quoted, int flag)
5388{
5389 flag |= RMESCAPE_GLOB;
5390 if (quoted) {
5391 flag |= RMESCAPE_QUOTED;
5392 }
5393 return _rmescapes((char *)pattern, flag);
5394}
5395
5396/*
5397 * Put a string on the stack.
5398 */
5399static void
5400memtodest(const char *p, size_t len, int syntax, int quotes)
5401{
5402 char *q = expdest;
5403
5404 q = makestrspace(len * 2, q);
5405
5406 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005407 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005408 if (!c)
5409 continue;
5410 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5411 USTPUTC(CTLESC, q);
5412 USTPUTC(c, q);
5413 }
5414
5415 expdest = q;
5416}
5417
5418static void
5419strtodest(const char *p, int syntax, int quotes)
5420{
5421 memtodest(p, strlen(p), syntax, quotes);
5422}
5423
5424/*
5425 * Record the fact that we have to scan this region of the
5426 * string for IFS characters.
5427 */
5428static void
5429recordregion(int start, int end, int nulonly)
5430{
5431 struct ifsregion *ifsp;
5432
5433 if (ifslastp == NULL) {
5434 ifsp = &ifsfirst;
5435 } else {
5436 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005437 ifsp = ckzalloc(sizeof(*ifsp));
5438 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005439 ifslastp->next = ifsp;
5440 INT_ON;
5441 }
5442 ifslastp = ifsp;
5443 ifslastp->begoff = start;
5444 ifslastp->endoff = end;
5445 ifslastp->nulonly = nulonly;
5446}
5447
5448static void
5449removerecordregions(int endoff)
5450{
5451 if (ifslastp == NULL)
5452 return;
5453
5454 if (ifsfirst.endoff > endoff) {
5455 while (ifsfirst.next != NULL) {
5456 struct ifsregion *ifsp;
5457 INT_OFF;
5458 ifsp = ifsfirst.next->next;
5459 free(ifsfirst.next);
5460 ifsfirst.next = ifsp;
5461 INT_ON;
5462 }
5463 if (ifsfirst.begoff > endoff)
5464 ifslastp = NULL;
5465 else {
5466 ifslastp = &ifsfirst;
5467 ifsfirst.endoff = endoff;
5468 }
5469 return;
5470 }
5471
5472 ifslastp = &ifsfirst;
5473 while (ifslastp->next && ifslastp->next->begoff < endoff)
5474 ifslastp=ifslastp->next;
5475 while (ifslastp->next != NULL) {
5476 struct ifsregion *ifsp;
5477 INT_OFF;
5478 ifsp = ifslastp->next->next;
5479 free(ifslastp->next);
5480 ifslastp->next = ifsp;
5481 INT_ON;
5482 }
5483 if (ifslastp->endoff > endoff)
5484 ifslastp->endoff = endoff;
5485}
5486
5487static char *
5488exptilde(char *startp, char *p, int flag)
5489{
5490 char c;
5491 char *name;
5492 struct passwd *pw;
5493 const char *home;
5494 int quotes = flag & (EXP_FULL | EXP_CASE);
5495 int startloc;
5496
5497 name = p + 1;
5498
5499 while ((c = *++p) != '\0') {
5500 switch (c) {
5501 case CTLESC:
5502 return startp;
5503 case CTLQUOTEMARK:
5504 return startp;
5505 case ':':
5506 if (flag & EXP_VARTILDE)
5507 goto done;
5508 break;
5509 case '/':
5510 case CTLENDVAR:
5511 goto done;
5512 }
5513 }
5514 done:
5515 *p = '\0';
5516 if (*name == '\0') {
5517 home = lookupvar(homestr);
5518 } else {
5519 pw = getpwnam(name);
5520 if (pw == NULL)
5521 goto lose;
5522 home = pw->pw_dir;
5523 }
5524 if (!home || !*home)
5525 goto lose;
5526 *p = c;
5527 startloc = expdest - (char *)stackblock();
5528 strtodest(home, SQSYNTAX, quotes);
5529 recordregion(startloc, expdest - (char *)stackblock(), 0);
5530 return p;
5531 lose:
5532 *p = c;
5533 return startp;
5534}
5535
5536/*
5537 * Execute a command inside back quotes. If it's a builtin command, we
5538 * want to save its output in a block obtained from malloc. Otherwise
5539 * we fork off a subprocess and get the output of the command via a pipe.
5540 * Should be called with interrupts off.
5541 */
5542struct backcmd { /* result of evalbackcmd */
5543 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005544 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005545 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005546 struct job *jp; /* job structure for command */
5547};
5548
5549/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005550static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005551#define EV_EXIT 01 /* exit after evaluating tree */
5552static void evaltree(union node *, int);
5553
5554static void
5555evalbackcmd(union node *n, struct backcmd *result)
5556{
5557 int saveherefd;
5558
5559 result->fd = -1;
5560 result->buf = NULL;
5561 result->nleft = 0;
5562 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005563 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005564 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005565
5566 saveherefd = herefd;
5567 herefd = -1;
5568
5569 {
5570 int pip[2];
5571 struct job *jp;
5572
5573 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005574 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005575 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5577 FORCE_INT_ON;
5578 close(pip[0]);
5579 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005580 /*close(1);*/
5581 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005582 close(pip[1]);
5583 }
5584 eflag = 0;
5585 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5586 /* NOTREACHED */
5587 }
5588 close(pip[1]);
5589 result->fd = pip[0];
5590 result->jp = jp;
5591 }
5592 herefd = saveherefd;
5593 out:
5594 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5595 result->fd, result->buf, result->nleft, result->jp));
5596}
5597
5598/*
5599 * Expand stuff in backwards quotes.
5600 */
5601static void
5602expbackq(union node *cmd, int quoted, int quotes)
5603{
5604 struct backcmd in;
5605 int i;
5606 char buf[128];
5607 char *p;
5608 char *dest;
5609 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005610 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005611 struct stackmark smark;
5612
5613 INT_OFF;
5614 setstackmark(&smark);
5615 dest = expdest;
5616 startloc = dest - (char *)stackblock();
5617 grabstackstr(dest);
5618 evalbackcmd(cmd, &in);
5619 popstackmark(&smark);
5620
5621 p = in.buf;
5622 i = in.nleft;
5623 if (i == 0)
5624 goto read;
5625 for (;;) {
5626 memtodest(p, i, syntax, quotes);
5627 read:
5628 if (in.fd < 0)
5629 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005630 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005631 TRACE(("expbackq: read returns %d\n", i));
5632 if (i <= 0)
5633 break;
5634 p = buf;
5635 }
5636
Denis Vlasenko60818682007-09-28 22:07:23 +00005637 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005638 if (in.fd >= 0) {
5639 close(in.fd);
5640 back_exitstatus = waitforjob(in.jp);
5641 }
5642 INT_ON;
5643
5644 /* Eat all trailing newlines */
5645 dest = expdest;
5646 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5647 STUNPUTC(dest);
5648 expdest = dest;
5649
5650 if (quoted == 0)
5651 recordregion(startloc, dest - (char *)stackblock(), 0);
5652 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5653 (dest - (char *)stackblock()) - startloc,
5654 (dest - (char *)stackblock()) - startloc,
5655 stackblock() + startloc));
5656}
5657
5658#if ENABLE_ASH_MATH_SUPPORT
5659/*
5660 * Expand arithmetic expression. Backup to start of expression,
5661 * evaluate, place result in (backed up) result, adjust string position.
5662 */
5663static void
5664expari(int quotes)
5665{
5666 char *p, *start;
5667 int begoff;
5668 int flag;
5669 int len;
5670
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005671 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005672
5673 /*
5674 * This routine is slightly over-complicated for
5675 * efficiency. Next we scan backwards looking for the
5676 * start of arithmetic.
5677 */
5678 start = stackblock();
5679 p = expdest - 1;
5680 *p = '\0';
5681 p--;
5682 do {
5683 int esc;
5684
5685 while (*p != CTLARI) {
5686 p--;
5687#if DEBUG
5688 if (p < start) {
5689 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5690 }
5691#endif
5692 }
5693
5694 esc = esclen(start, p);
5695 if (!(esc % 2)) {
5696 break;
5697 }
5698
5699 p -= esc + 1;
5700 } while (1);
5701
5702 begoff = p - start;
5703
5704 removerecordregions(begoff);
5705
5706 flag = p[1];
5707
5708 expdest = p;
5709
5710 if (quotes)
5711 rmescapes(p + 2);
5712
5713 len = cvtnum(dash_arith(p + 2));
5714
5715 if (flag != '"')
5716 recordregion(begoff, begoff + len, 0);
5717}
5718#endif
5719
5720/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005721static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722
5723/*
5724 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5725 * characters to allow for further processing. Otherwise treat
5726 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005727 *
5728 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5729 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5730 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005731 */
5732static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005733argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005734{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005735 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005736 '=',
5737 ':',
5738 CTLQUOTEMARK,
5739 CTLENDVAR,
5740 CTLESC,
5741 CTLVAR,
5742 CTLBACKQ,
5743 CTLBACKQ | CTLQUOTE,
5744#if ENABLE_ASH_MATH_SUPPORT
5745 CTLENDARI,
5746#endif
5747 0
5748 };
5749 const char *reject = spclchars;
5750 int c;
5751 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5752 int breakall = flag & EXP_WORD;
5753 int inquotes;
5754 size_t length;
5755 int startloc;
5756
5757 if (!(flag & EXP_VARTILDE)) {
5758 reject += 2;
5759 } else if (flag & EXP_VARTILDE2) {
5760 reject++;
5761 }
5762 inquotes = 0;
5763 length = 0;
5764 if (flag & EXP_TILDE) {
5765 char *q;
5766
5767 flag &= ~EXP_TILDE;
5768 tilde:
5769 q = p;
5770 if (*q == CTLESC && (flag & EXP_QWORD))
5771 q++;
5772 if (*q == '~')
5773 p = exptilde(p, q, flag);
5774 }
5775 start:
5776 startloc = expdest - (char *)stackblock();
5777 for (;;) {
5778 length += strcspn(p + length, reject);
5779 c = p[length];
5780 if (c && (!(c & 0x80)
5781#if ENABLE_ASH_MATH_SUPPORT
5782 || c == CTLENDARI
5783#endif
5784 )) {
5785 /* c == '=' || c == ':' || c == CTLENDARI */
5786 length++;
5787 }
5788 if (length > 0) {
5789 int newloc;
5790 expdest = stack_nputstr(p, length, expdest);
5791 newloc = expdest - (char *)stackblock();
5792 if (breakall && !inquotes && newloc > startloc) {
5793 recordregion(startloc, newloc, 0);
5794 }
5795 startloc = newloc;
5796 }
5797 p += length + 1;
5798 length = 0;
5799
5800 switch (c) {
5801 case '\0':
5802 goto breakloop;
5803 case '=':
5804 if (flag & EXP_VARTILDE2) {
5805 p--;
5806 continue;
5807 }
5808 flag |= EXP_VARTILDE2;
5809 reject++;
5810 /* fall through */
5811 case ':':
5812 /*
5813 * sort of a hack - expand tildes in variable
5814 * assignments (after the first '=' and after ':'s).
5815 */
5816 if (*--p == '~') {
5817 goto tilde;
5818 }
5819 continue;
5820 }
5821
5822 switch (c) {
5823 case CTLENDVAR: /* ??? */
5824 goto breakloop;
5825 case CTLQUOTEMARK:
5826 /* "$@" syntax adherence hack */
5827 if (
5828 !inquotes &&
5829 !memcmp(p, dolatstr, 4) &&
5830 (p[4] == CTLQUOTEMARK || (
5831 p[4] == CTLENDVAR &&
5832 p[5] == CTLQUOTEMARK
5833 ))
5834 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005835 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005836 goto start;
5837 }
5838 inquotes = !inquotes;
5839 addquote:
5840 if (quotes) {
5841 p--;
5842 length++;
5843 startloc++;
5844 }
5845 break;
5846 case CTLESC:
5847 startloc++;
5848 length++;
5849 goto addquote;
5850 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005851 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005852 goto start;
5853 case CTLBACKQ:
5854 c = 0;
5855 case CTLBACKQ|CTLQUOTE:
5856 expbackq(argbackq->n, c, quotes);
5857 argbackq = argbackq->next;
5858 goto start;
5859#if ENABLE_ASH_MATH_SUPPORT
5860 case CTLENDARI:
5861 p--;
5862 expari(quotes);
5863 goto start;
5864#endif
5865 }
5866 }
5867 breakloop:
5868 ;
5869}
5870
5871static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005872scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005873 int zero)
5874{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005875// This commented out code was added by James Simmons <jsimmons@infradead.org>
5876// as part of a larger change when he added support for ${var/a/b}.
5877// However, it broke # and % operators:
5878//
5879//var=ababcdcd
5880// ok bad
5881//echo ${var#ab} abcdcd abcdcd
5882//echo ${var##ab} abcdcd abcdcd
5883//echo ${var#a*b} abcdcd ababcdcd (!)
5884//echo ${var##a*b} cdcd cdcd
5885//echo ${var#?} babcdcd ababcdcd (!)
5886//echo ${var##?} babcdcd babcdcd
5887//echo ${var#*} ababcdcd babcdcd (!)
5888//echo ${var##*}
5889//echo ${var%cd} ababcd ababcd
5890//echo ${var%%cd} ababcd abab (!)
5891//echo ${var%c*d} ababcd ababcd
5892//echo ${var%%c*d} abab ababcdcd (!)
5893//echo ${var%?} ababcdc ababcdc
5894//echo ${var%%?} ababcdc ababcdcd (!)
5895//echo ${var%*} ababcdcd ababcdcd
5896//echo ${var%%*}
5897//
5898// Commenting it back out helped. Remove it completely if it really
5899// is not needed.
5900
5901 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005902 char c;
5903
5904 loc = startp;
5905 loc2 = rmesc;
5906 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005907 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005908 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005909
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005910 c = *loc2;
5911 if (zero) {
5912 *loc2 = '\0';
5913 s = rmesc;
5914 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005915 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005916
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005917// // chop off end if its '*'
5918// full = strrchr(str, '*');
5919// if (full && full != str)
5920// match--;
5921//
5922// // If str starts with '*' replace with s.
5923// if ((*str == '*') && strlen(s) >= match) {
5924// full = xstrdup(s);
5925// strncpy(full+strlen(s)-match+1, str+1, match-1);
5926// } else
5927// full = xstrndup(str, match);
5928// match = strncmp(s, full, strlen(full));
5929// free(full);
5930//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005931 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005932 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005933 return loc;
5934 if (quotes && *loc == CTLESC)
5935 loc++;
5936 loc++;
5937 loc2++;
5938 } while (c);
5939 return 0;
5940}
5941
5942static char *
5943scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5944 int zero)
5945{
5946 int esc = 0;
5947 char *loc;
5948 char *loc2;
5949
5950 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5951 int match;
5952 char c = *loc2;
5953 const char *s = loc2;
5954 if (zero) {
5955 *loc2 = '\0';
5956 s = rmesc;
5957 }
5958 match = pmatch(str, s);
5959 *loc2 = c;
5960 if (match)
5961 return loc;
5962 loc--;
5963 if (quotes) {
5964 if (--esc < 0) {
5965 esc = esclen(startp, loc);
5966 }
5967 if (esc % 2) {
5968 esc--;
5969 loc--;
5970 }
5971 }
5972 }
5973 return 0;
5974}
5975
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005976static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977static void
5978varunset(const char *end, const char *var, const char *umsg, int varflags)
5979{
5980 const char *msg;
5981 const char *tail;
5982
5983 tail = nullstr;
5984 msg = "parameter not set";
5985 if (umsg) {
5986 if (*end == CTLENDVAR) {
5987 if (varflags & VSNUL)
5988 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005989 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005990 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005991 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992 }
5993 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5994}
5995
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005996#if ENABLE_ASH_BASH_COMPAT
5997static char *
5998parse_sub_pattern(char *arg, int inquotes)
5999{
6000 char *idx, *repl = NULL;
6001 unsigned char c;
6002
Denis Vlasenko2659c632008-06-14 06:04:59 +00006003 idx = arg;
6004 while (1) {
6005 c = *arg;
6006 if (!c)
6007 break;
6008 if (c == '/') {
6009 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006010 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006011 repl = idx + 1;
6012 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006013 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006014 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006015 *idx++ = c;
6016 if (!inquotes && c == '\\' && arg[1] == '\\')
6017 arg++; /* skip both \\, not just first one */
6018 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006019 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006020 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006021
6022 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006023}
6024#endif /* ENABLE_ASH_BASH_COMPAT */
6025
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006026static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006027subevalvar(char *p, char *str, int strloc, int subtype,
6028 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006030 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006031 char *startp;
6032 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006033 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006034 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6035 USE_ASH_BASH_COMPAT(char null = '\0';)
6036 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6037 int saveherefd = herefd;
6038 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006040 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006041
6042 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006043 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6044 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 STPUTC('\0', expdest);
6046 herefd = saveherefd;
6047 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006048 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006049
6050 switch (subtype) {
6051 case VSASSIGN:
6052 setvar(str, startp, 0);
6053 amount = startp - expdest;
6054 STADJUST(amount, expdest);
6055 return startp;
6056
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006057#if ENABLE_ASH_BASH_COMPAT
6058 case VSSUBSTR:
6059 loc = str = stackblock() + strloc;
6060// TODO: number() instead? It does error checking...
6061 pos = atoi(loc);
6062 len = str - startp - 1;
6063
6064 /* *loc != '\0', guaranteed by parser */
6065 if (quotes) {
6066 char *ptr;
6067
6068 /* We must adjust the length by the number of escapes we find. */
6069 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006070 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006071 len--;
6072 ptr++;
6073 }
6074 }
6075 }
6076 orig_len = len;
6077
6078 if (*loc++ == ':') {
6079// TODO: number() instead? It does error checking...
6080 len = atoi(loc);
6081 } else {
6082 len = orig_len;
6083 while (*loc && *loc != ':')
6084 loc++;
6085 if (*loc++ == ':')
6086// TODO: number() instead? It does error checking...
6087 len = atoi(loc);
6088 }
6089 if (pos >= orig_len) {
6090 pos = 0;
6091 len = 0;
6092 }
6093 if (len > (orig_len - pos))
6094 len = orig_len - pos;
6095
6096 for (str = startp; pos; str++, pos--) {
6097 if (quotes && *str == CTLESC)
6098 str++;
6099 }
6100 for (loc = startp; len; len--) {
6101 if (quotes && *str == CTLESC)
6102 *loc++ = *str++;
6103 *loc++ = *str++;
6104 }
6105 *loc = '\0';
6106 amount = loc - expdest;
6107 STADJUST(amount, expdest);
6108 return loc;
6109#endif
6110
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111 case VSQUESTION:
6112 varunset(p, str, startp, varflags);
6113 /* NOTREACHED */
6114 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006115 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006116
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006117 /* We'll comeback here if we grow the stack while handling
6118 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6119 * stack will need rebasing, and we'll need to remove our work
6120 * areas each time
6121 */
6122 USE_ASH_BASH_COMPAT(restart:)
6123
6124 amount = expdest - ((char *)stackblock() + resetloc);
6125 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006126 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006127
6128 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006129 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006130 if (quotes) {
6131 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6132 if (rmesc != startp) {
6133 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006134 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 }
6136 }
6137 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006138 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006139 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006140 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006141
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006142#if ENABLE_ASH_BASH_COMPAT
6143 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6144 char *idx, *end, *restart_detect;
6145
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006146 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006147 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6148 if (!repl)
6149 repl = &null;
6150 }
6151
6152 /* If there's no pattern to match, return the expansion unmolested */
6153 if (*str == '\0')
6154 return 0;
6155
6156 len = 0;
6157 idx = startp;
6158 end = str - 1;
6159 while (idx < end) {
6160 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6161 if (!loc) {
6162 /* No match, advance */
6163 restart_detect = stackblock();
6164 STPUTC(*idx, expdest);
6165 if (quotes && *idx == CTLESC) {
6166 idx++;
6167 len++;
6168 STPUTC(*idx, expdest);
6169 }
6170 if (stackblock() != restart_detect)
6171 goto restart;
6172 idx++;
6173 len++;
6174 rmesc++;
6175 continue;
6176 }
6177
6178 if (subtype == VSREPLACEALL) {
6179 while (idx < loc) {
6180 if (quotes && *idx == CTLESC)
6181 idx++;
6182 idx++;
6183 rmesc++;
6184 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006185 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006186 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006187 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006188
6189 for (loc = repl; *loc; loc++) {
6190 restart_detect = stackblock();
6191 STPUTC(*loc, expdest);
6192 if (stackblock() != restart_detect)
6193 goto restart;
6194 len++;
6195 }
6196
6197 if (subtype == VSREPLACE) {
6198 while (*idx) {
6199 restart_detect = stackblock();
6200 STPUTC(*idx, expdest);
6201 if (stackblock() != restart_detect)
6202 goto restart;
6203 len++;
6204 idx++;
6205 }
6206 break;
6207 }
6208 }
6209
6210 /* We've put the replaced text into a buffer at workloc, now
6211 * move it to the right place and adjust the stack.
6212 */
6213 startp = stackblock() + startloc;
6214 STPUTC('\0', expdest);
6215 memmove(startp, stackblock() + workloc, len);
6216 startp[len++] = '\0';
6217 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6218 STADJUST(-amount, expdest);
6219 return startp;
6220 }
6221#endif /* ENABLE_ASH_BASH_COMPAT */
6222
6223 subtype -= VSTRIMRIGHT;
6224#if DEBUG
6225 if (subtype < 0 || subtype > 7)
6226 abort();
6227#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006228 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6229 zero = subtype >> 1;
6230 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6231 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6232
6233 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6234 if (loc) {
6235 if (zero) {
6236 memmove(startp, loc, str - loc);
6237 loc = startp + (str - loc) - 1;
6238 }
6239 *loc = '\0';
6240 amount = loc - expdest;
6241 STADJUST(amount, expdest);
6242 }
6243 return loc;
6244}
6245
6246/*
6247 * Add the value of a specialized variable to the stack string.
6248 */
6249static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006250varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006251{
6252 int num;
6253 char *p;
6254 int i;
6255 int sep = 0;
6256 int sepq = 0;
6257 ssize_t len = 0;
6258 char **ap;
6259 int syntax;
6260 int quoted = varflags & VSQUOTE;
6261 int subtype = varflags & VSTYPE;
6262 int quotes = flags & (EXP_FULL | EXP_CASE);
6263
6264 if (quoted && (flags & EXP_FULL))
6265 sep = 1 << CHAR_BIT;
6266
6267 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6268 switch (*name) {
6269 case '$':
6270 num = rootpid;
6271 goto numvar;
6272 case '?':
6273 num = exitstatus;
6274 goto numvar;
6275 case '#':
6276 num = shellparam.nparam;
6277 goto numvar;
6278 case '!':
6279 num = backgndpid;
6280 if (num == 0)
6281 return -1;
6282 numvar:
6283 len = cvtnum(num);
6284 break;
6285 case '-':
6286 p = makestrspace(NOPTS, expdest);
6287 for (i = NOPTS - 1; i >= 0; i--) {
6288 if (optlist[i]) {
6289 USTPUTC(optletters(i), p);
6290 len++;
6291 }
6292 }
6293 expdest = p;
6294 break;
6295 case '@':
6296 if (sep)
6297 goto param;
6298 /* fall through */
6299 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006300 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006301 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6302 sepq = 1;
6303 param:
6304 ap = shellparam.p;
6305 if (!ap)
6306 return -1;
6307 while ((p = *ap++)) {
6308 size_t partlen;
6309
6310 partlen = strlen(p);
6311 len += partlen;
6312
6313 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6314 memtodest(p, partlen, syntax, quotes);
6315
6316 if (*ap && sep) {
6317 char *q;
6318
6319 len++;
6320 if (subtype == VSPLUS || subtype == VSLENGTH) {
6321 continue;
6322 }
6323 q = expdest;
6324 if (sepq)
6325 STPUTC(CTLESC, q);
6326 STPUTC(sep, q);
6327 expdest = q;
6328 }
6329 }
6330 return len;
6331 case '0':
6332 case '1':
6333 case '2':
6334 case '3':
6335 case '4':
6336 case '5':
6337 case '6':
6338 case '7':
6339 case '8':
6340 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006341// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006342 num = atoi(name);
6343 if (num < 0 || num > shellparam.nparam)
6344 return -1;
6345 p = num ? shellparam.p[num - 1] : arg0;
6346 goto value;
6347 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006348 /* NB: name has form "VAR=..." */
6349
6350 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6351 * which should be considered before we check variables. */
6352 if (var_str_list) {
6353 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6354 p = NULL;
6355 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006356 char *str, *eq;
6357 str = var_str_list->text;
6358 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006359 if (!eq) /* stop at first non-assignment */
6360 break;
6361 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006362 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006363 && strncmp(str, name, name_len) == 0) {
6364 p = eq;
6365 /* goto value; - WRONG! */
6366 /* think "A=1 A=2 B=$A" */
6367 }
6368 var_str_list = var_str_list->next;
6369 } while (var_str_list);
6370 if (p)
6371 goto value;
6372 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006373 p = lookupvar(name);
6374 value:
6375 if (!p)
6376 return -1;
6377
6378 len = strlen(p);
6379 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6380 memtodest(p, len, syntax, quotes);
6381 return len;
6382 }
6383
6384 if (subtype == VSPLUS || subtype == VSLENGTH)
6385 STADJUST(-len, expdest);
6386 return len;
6387}
6388
6389/*
6390 * Expand a variable, and return a pointer to the next character in the
6391 * input string.
6392 */
6393static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006394evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006395{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006396 char varflags;
6397 char subtype;
6398 char quoted;
6399 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006400 char *var;
6401 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006402 int startloc;
6403 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405 varflags = *p++;
6406 subtype = varflags & VSTYPE;
6407 quoted = varflags & VSQUOTE;
6408 var = p;
6409 easy = (!quoted || (*var == '@' && shellparam.nparam));
6410 startloc = expdest - (char *)stackblock();
6411 p = strchr(p, '=') + 1;
6412
6413 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006414 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415 if (varflags & VSNUL)
6416 varlen--;
6417
6418 if (subtype == VSPLUS) {
6419 varlen = -1 - varlen;
6420 goto vsplus;
6421 }
6422
6423 if (subtype == VSMINUS) {
6424 vsplus:
6425 if (varlen < 0) {
6426 argstr(
6427 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006428 (quoted ? EXP_QWORD : EXP_WORD),
6429 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006430 );
6431 goto end;
6432 }
6433 if (easy)
6434 goto record;
6435 goto end;
6436 }
6437
6438 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6439 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006440 if (subevalvar(p, var, /* strloc: */ 0,
6441 subtype, startloc, varflags,
6442 /* quotes: */ 0,
6443 var_str_list)
6444 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445 varflags &= ~VSNUL;
6446 /*
6447 * Remove any recorded regions beyond
6448 * start of variable
6449 */
6450 removerecordregions(startloc);
6451 goto again;
6452 }
6453 goto end;
6454 }
6455 if (easy)
6456 goto record;
6457 goto end;
6458 }
6459
6460 if (varlen < 0 && uflag)
6461 varunset(p, var, 0, 0);
6462
6463 if (subtype == VSLENGTH) {
6464 cvtnum(varlen > 0 ? varlen : 0);
6465 goto record;
6466 }
6467
6468 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006469 if (easy)
6470 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006471 goto end;
6472 }
6473
6474#if DEBUG
6475 switch (subtype) {
6476 case VSTRIMLEFT:
6477 case VSTRIMLEFTMAX:
6478 case VSTRIMRIGHT:
6479 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006480#if ENABLE_ASH_BASH_COMPAT
6481 case VSSUBSTR:
6482 case VSREPLACE:
6483 case VSREPLACEALL:
6484#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006485 break;
6486 default:
6487 abort();
6488 }
6489#endif
6490
6491 if (varlen >= 0) {
6492 /*
6493 * Terminate the string and start recording the pattern
6494 * right after it
6495 */
6496 STPUTC('\0', expdest);
6497 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006498 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6499 startloc, varflags,
6500 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6501 var_str_list)
6502 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006503 int amount = expdest - (
6504 (char *)stackblock() + patloc - 1
6505 );
6506 STADJUST(-amount, expdest);
6507 }
6508 /* Remove any recorded regions beyond start of variable */
6509 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006510 record:
6511 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006512 }
6513
6514 end:
6515 if (subtype != VSNORMAL) { /* skip to end of alternative */
6516 int nesting = 1;
6517 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006518 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006519 if (c == CTLESC)
6520 p++;
6521 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6522 if (varlen >= 0)
6523 argbackq = argbackq->next;
6524 } else if (c == CTLVAR) {
6525 if ((*p++ & VSTYPE) != VSNORMAL)
6526 nesting++;
6527 } else if (c == CTLENDVAR) {
6528 if (--nesting == 0)
6529 break;
6530 }
6531 }
6532 }
6533 return p;
6534}
6535
6536/*
6537 * Break the argument string into pieces based upon IFS and add the
6538 * strings to the argument list. The regions of the string to be
6539 * searched for IFS characters have been stored by recordregion.
6540 */
6541static void
6542ifsbreakup(char *string, struct arglist *arglist)
6543{
6544 struct ifsregion *ifsp;
6545 struct strlist *sp;
6546 char *start;
6547 char *p;
6548 char *q;
6549 const char *ifs, *realifs;
6550 int ifsspc;
6551 int nulonly;
6552
6553 start = string;
6554 if (ifslastp != NULL) {
6555 ifsspc = 0;
6556 nulonly = 0;
6557 realifs = ifsset() ? ifsval() : defifs;
6558 ifsp = &ifsfirst;
6559 do {
6560 p = string + ifsp->begoff;
6561 nulonly = ifsp->nulonly;
6562 ifs = nulonly ? nullstr : realifs;
6563 ifsspc = 0;
6564 while (p < string + ifsp->endoff) {
6565 q = p;
6566 if (*p == CTLESC)
6567 p++;
6568 if (!strchr(ifs, *p)) {
6569 p++;
6570 continue;
6571 }
6572 if (!nulonly)
6573 ifsspc = (strchr(defifs, *p) != NULL);
6574 /* Ignore IFS whitespace at start */
6575 if (q == start && ifsspc) {
6576 p++;
6577 start = p;
6578 continue;
6579 }
6580 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006581 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006582 sp->text = start;
6583 *arglist->lastp = sp;
6584 arglist->lastp = &sp->next;
6585 p++;
6586 if (!nulonly) {
6587 for (;;) {
6588 if (p >= string + ifsp->endoff) {
6589 break;
6590 }
6591 q = p;
6592 if (*p == CTLESC)
6593 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006594 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 p = q;
6596 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006597 }
6598 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006599 if (ifsspc) {
6600 p++;
6601 ifsspc = 0;
6602 } else {
6603 p = q;
6604 break;
6605 }
6606 } else
6607 p++;
6608 }
6609 }
6610 start = p;
6611 } /* while */
6612 ifsp = ifsp->next;
6613 } while (ifsp != NULL);
6614 if (nulonly)
6615 goto add;
6616 }
6617
6618 if (!*start)
6619 return;
6620
6621 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006622 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006623 sp->text = start;
6624 *arglist->lastp = sp;
6625 arglist->lastp = &sp->next;
6626}
6627
6628static void
6629ifsfree(void)
6630{
6631 struct ifsregion *p;
6632
6633 INT_OFF;
6634 p = ifsfirst.next;
6635 do {
6636 struct ifsregion *ifsp;
6637 ifsp = p->next;
6638 free(p);
6639 p = ifsp;
6640 } while (p);
6641 ifslastp = NULL;
6642 ifsfirst.next = NULL;
6643 INT_ON;
6644}
6645
6646/*
6647 * Add a file name to the list.
6648 */
6649static void
6650addfname(const char *name)
6651{
6652 struct strlist *sp;
6653
Denis Vlasenko597906c2008-02-20 16:38:54 +00006654 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655 sp->text = ststrdup(name);
6656 *exparg.lastp = sp;
6657 exparg.lastp = &sp->next;
6658}
6659
6660static char *expdir;
6661
6662/*
6663 * Do metacharacter (i.e. *, ?, [...]) expansion.
6664 */
6665static void
6666expmeta(char *enddir, char *name)
6667{
6668 char *p;
6669 const char *cp;
6670 char *start;
6671 char *endname;
6672 int metaflag;
6673 struct stat statb;
6674 DIR *dirp;
6675 struct dirent *dp;
6676 int atend;
6677 int matchdot;
6678
6679 metaflag = 0;
6680 start = name;
6681 for (p = name; *p; p++) {
6682 if (*p == '*' || *p == '?')
6683 metaflag = 1;
6684 else if (*p == '[') {
6685 char *q = p + 1;
6686 if (*q == '!')
6687 q++;
6688 for (;;) {
6689 if (*q == '\\')
6690 q++;
6691 if (*q == '/' || *q == '\0')
6692 break;
6693 if (*++q == ']') {
6694 metaflag = 1;
6695 break;
6696 }
6697 }
6698 } else if (*p == '\\')
6699 p++;
6700 else if (*p == '/') {
6701 if (metaflag)
6702 goto out;
6703 start = p + 1;
6704 }
6705 }
6706 out:
6707 if (metaflag == 0) { /* we've reached the end of the file name */
6708 if (enddir != expdir)
6709 metaflag++;
6710 p = name;
6711 do {
6712 if (*p == '\\')
6713 p++;
6714 *enddir++ = *p;
6715 } while (*p++);
6716 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6717 addfname(expdir);
6718 return;
6719 }
6720 endname = p;
6721 if (name < start) {
6722 p = name;
6723 do {
6724 if (*p == '\\')
6725 p++;
6726 *enddir++ = *p++;
6727 } while (p < start);
6728 }
6729 if (enddir == expdir) {
6730 cp = ".";
6731 } else if (enddir == expdir + 1 && *expdir == '/') {
6732 cp = "/";
6733 } else {
6734 cp = expdir;
6735 enddir[-1] = '\0';
6736 }
6737 dirp = opendir(cp);
6738 if (dirp == NULL)
6739 return;
6740 if (enddir != expdir)
6741 enddir[-1] = '/';
6742 if (*endname == 0) {
6743 atend = 1;
6744 } else {
6745 atend = 0;
6746 *endname++ = '\0';
6747 }
6748 matchdot = 0;
6749 p = start;
6750 if (*p == '\\')
6751 p++;
6752 if (*p == '.')
6753 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006754 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006755 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006756 continue;
6757 if (pmatch(start, dp->d_name)) {
6758 if (atend) {
6759 strcpy(enddir, dp->d_name);
6760 addfname(expdir);
6761 } else {
6762 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6763 continue;
6764 p[-1] = '/';
6765 expmeta(p, endname);
6766 }
6767 }
6768 }
6769 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006770 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006771 endname[-1] = '/';
6772}
6773
6774static struct strlist *
6775msort(struct strlist *list, int len)
6776{
6777 struct strlist *p, *q = NULL;
6778 struct strlist **lpp;
6779 int half;
6780 int n;
6781
6782 if (len <= 1)
6783 return list;
6784 half = len >> 1;
6785 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006786 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787 q = p;
6788 p = p->next;
6789 }
6790 q->next = NULL; /* terminate first half of list */
6791 q = msort(list, half); /* sort first half of list */
6792 p = msort(p, len - half); /* sort second half */
6793 lpp = &list;
6794 for (;;) {
6795#if ENABLE_LOCALE_SUPPORT
6796 if (strcoll(p->text, q->text) < 0)
6797#else
6798 if (strcmp(p->text, q->text) < 0)
6799#endif
6800 {
6801 *lpp = p;
6802 lpp = &p->next;
6803 p = *lpp;
6804 if (p == NULL) {
6805 *lpp = q;
6806 break;
6807 }
6808 } else {
6809 *lpp = q;
6810 lpp = &q->next;
6811 q = *lpp;
6812 if (q == NULL) {
6813 *lpp = p;
6814 break;
6815 }
6816 }
6817 }
6818 return list;
6819}
6820
6821/*
6822 * Sort the results of file name expansion. It calculates the number of
6823 * strings to sort and then calls msort (short for merge sort) to do the
6824 * work.
6825 */
6826static struct strlist *
6827expsort(struct strlist *str)
6828{
6829 int len;
6830 struct strlist *sp;
6831
6832 len = 0;
6833 for (sp = str; sp; sp = sp->next)
6834 len++;
6835 return msort(str, len);
6836}
6837
6838static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006839expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006840{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006841 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006842 '*', '?', '[', 0
6843 };
6844 /* TODO - EXP_REDIR */
6845
6846 while (str) {
6847 struct strlist **savelastp;
6848 struct strlist *sp;
6849 char *p;
6850
6851 if (fflag)
6852 goto nometa;
6853 if (!strpbrk(str->text, metachars))
6854 goto nometa;
6855 savelastp = exparg.lastp;
6856
6857 INT_OFF;
6858 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6859 {
6860 int i = strlen(str->text);
6861 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6862 }
6863
6864 expmeta(expdir, p);
6865 free(expdir);
6866 if (p != str->text)
6867 free(p);
6868 INT_ON;
6869 if (exparg.lastp == savelastp) {
6870 /*
6871 * no matches
6872 */
6873 nometa:
6874 *exparg.lastp = str;
6875 rmescapes(str->text);
6876 exparg.lastp = &str->next;
6877 } else {
6878 *exparg.lastp = NULL;
6879 *savelastp = sp = expsort(*savelastp);
6880 while (sp->next != NULL)
6881 sp = sp->next;
6882 exparg.lastp = &sp->next;
6883 }
6884 str = str->next;
6885 }
6886}
6887
6888/*
6889 * Perform variable substitution and command substitution on an argument,
6890 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6891 * perform splitting and file name expansion. When arglist is NULL, perform
6892 * here document expansion.
6893 */
6894static void
6895expandarg(union node *arg, struct arglist *arglist, int flag)
6896{
6897 struct strlist *sp;
6898 char *p;
6899
6900 argbackq = arg->narg.backquote;
6901 STARTSTACKSTR(expdest);
6902 ifsfirst.next = NULL;
6903 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006904 argstr(arg->narg.text, flag,
6905 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006906 p = _STPUTC('\0', expdest);
6907 expdest = p - 1;
6908 if (arglist == NULL) {
6909 return; /* here document expanded */
6910 }
6911 p = grabstackstr(p);
6912 exparg.lastp = &exparg.list;
6913 /*
6914 * TODO - EXP_REDIR
6915 */
6916 if (flag & EXP_FULL) {
6917 ifsbreakup(p, &exparg);
6918 *exparg.lastp = NULL;
6919 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006920 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006921 } else {
6922 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6923 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006924 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006925 sp->text = p;
6926 *exparg.lastp = sp;
6927 exparg.lastp = &sp->next;
6928 }
6929 if (ifsfirst.next)
6930 ifsfree();
6931 *exparg.lastp = NULL;
6932 if (exparg.list) {
6933 *arglist->lastp = exparg.list;
6934 arglist->lastp = exparg.lastp;
6935 }
6936}
6937
6938/*
6939 * Expand shell variables and backquotes inside a here document.
6940 */
6941static void
6942expandhere(union node *arg, int fd)
6943{
6944 herefd = fd;
6945 expandarg(arg, (struct arglist *)NULL, 0);
6946 full_write(fd, stackblock(), expdest - (char *)stackblock());
6947}
6948
6949/*
6950 * Returns true if the pattern matches the string.
6951 */
6952static int
6953patmatch(char *pattern, const char *string)
6954{
6955 return pmatch(preglob(pattern, 0, 0), string);
6956}
6957
6958/*
6959 * See if a pattern matches in a case statement.
6960 */
6961static int
6962casematch(union node *pattern, char *val)
6963{
6964 struct stackmark smark;
6965 int result;
6966
6967 setstackmark(&smark);
6968 argbackq = pattern->narg.backquote;
6969 STARTSTACKSTR(expdest);
6970 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006971 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6972 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006973 STACKSTRNUL(expdest);
6974 result = patmatch(stackblock(), val);
6975 popstackmark(&smark);
6976 return result;
6977}
6978
6979
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006980/* ============ find_command */
6981
6982struct builtincmd {
6983 const char *name;
6984 int (*builtin)(int, char **);
6985 /* unsigned flags; */
6986};
6987#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006988/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006989 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006990#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006991#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992
6993struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006994 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006995 union param {
6996 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006997 /* index >= 0 for commands without path (slashes) */
6998 /* (TODO: what exactly does the value mean? PATH position?) */
6999 /* index == -1 for commands with slashes */
7000 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007001 const struct builtincmd *cmd;
7002 struct funcnode *func;
7003 } u;
7004};
7005/* values of cmdtype */
7006#define CMDUNKNOWN -1 /* no entry in table for command */
7007#define CMDNORMAL 0 /* command is an executable program */
7008#define CMDFUNCTION 1 /* command is a shell function */
7009#define CMDBUILTIN 2 /* command is a shell builtin */
7010
7011/* action to find_command() */
7012#define DO_ERR 0x01 /* prints errors */
7013#define DO_ABS 0x02 /* checks absolute paths */
7014#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7015#define DO_ALTPATH 0x08 /* using alternate path */
7016#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7017
7018static void find_command(char *, struct cmdentry *, int, const char *);
7019
7020
7021/* ============ Hashing commands */
7022
7023/*
7024 * When commands are first encountered, they are entered in a hash table.
7025 * This ensures that a full path search will not have to be done for them
7026 * on each invocation.
7027 *
7028 * We should investigate converting to a linear search, even though that
7029 * would make the command name "hash" a misnomer.
7030 */
7031
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007032struct tblentry {
7033 struct tblentry *next; /* next entry in hash chain */
7034 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007035 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007036 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007037 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007038};
7039
Denis Vlasenko01631112007-12-16 17:20:38 +00007040static struct tblentry **cmdtable;
7041#define INIT_G_cmdtable() do { \
7042 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7043} while (0)
7044
7045static int builtinloc = -1; /* index in path of %builtin, or -1 */
7046
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007047
7048static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007049tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050{
7051 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007052
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007053#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007054 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007055 if (APPLET_IS_NOEXEC(applet_no)) {
7056 while (*envp)
7057 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007058 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007059 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007060 /* re-exec ourselves with the new arguments */
7061 execve(bb_busybox_exec_path, argv, envp);
7062 /* If they called chroot or otherwise made the binary no longer
7063 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007064 }
7065#endif
7066
7067 repeat:
7068#ifdef SYSV
7069 do {
7070 execve(cmd, argv, envp);
7071 } while (errno == EINTR);
7072#else
7073 execve(cmd, argv, envp);
7074#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007075 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007076 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007077 return;
7078 }
7079 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007080 char **ap;
7081 char **new;
7082
7083 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007084 continue;
7085 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007086 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007087 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007088 ap += 2;
7089 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007090 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007091 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007092 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007093 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007094 goto repeat;
7095 }
7096}
7097
7098/*
7099 * Exec a program. Never returns. If you change this routine, you may
7100 * have to change the find_command routine as well.
7101 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007102static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007103static void
7104shellexec(char **argv, const char *path, int idx)
7105{
7106 char *cmdname;
7107 int e;
7108 char **envp;
7109 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007110#if ENABLE_FEATURE_SH_STANDALONE
7111 int applet_no = -1;
7112#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007113
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007114 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007115 envp = listvars(VEXPORT, VUNSET, 0);
7116 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007117#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007118 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007119#endif
7120 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007121 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007122 e = errno;
7123 } else {
7124 e = ENOENT;
7125 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7126 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007127 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007128 if (errno != ENOENT && errno != ENOTDIR)
7129 e = errno;
7130 }
7131 stunalloc(cmdname);
7132 }
7133 }
7134
7135 /* Map to POSIX errors */
7136 switch (e) {
7137 case EACCES:
7138 exerrno = 126;
7139 break;
7140 case ENOENT:
7141 exerrno = 127;
7142 break;
7143 default:
7144 exerrno = 2;
7145 break;
7146 }
7147 exitstatus = exerrno;
7148 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007149 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007150 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7151 /* NOTREACHED */
7152}
7153
7154static void
7155printentry(struct tblentry *cmdp)
7156{
7157 int idx;
7158 const char *path;
7159 char *name;
7160
7161 idx = cmdp->param.index;
7162 path = pathval();
7163 do {
7164 name = padvance(&path, cmdp->cmdname);
7165 stunalloc(name);
7166 } while (--idx >= 0);
7167 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7168}
7169
7170/*
7171 * Clear out command entries. The argument specifies the first entry in
7172 * PATH which has changed.
7173 */
7174static void
7175clearcmdentry(int firstchange)
7176{
7177 struct tblentry **tblp;
7178 struct tblentry **pp;
7179 struct tblentry *cmdp;
7180
7181 INT_OFF;
7182 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7183 pp = tblp;
7184 while ((cmdp = *pp) != NULL) {
7185 if ((cmdp->cmdtype == CMDNORMAL &&
7186 cmdp->param.index >= firstchange)
7187 || (cmdp->cmdtype == CMDBUILTIN &&
7188 builtinloc >= firstchange)
7189 ) {
7190 *pp = cmdp->next;
7191 free(cmdp);
7192 } else {
7193 pp = &cmdp->next;
7194 }
7195 }
7196 }
7197 INT_ON;
7198}
7199
7200/*
7201 * Locate a command in the command hash table. If "add" is nonzero,
7202 * add the command to the table if it is not already present. The
7203 * variable "lastcmdentry" is set to point to the address of the link
7204 * pointing to the entry, so that delete_cmd_entry can delete the
7205 * entry.
7206 *
7207 * Interrupts must be off if called with add != 0.
7208 */
7209static struct tblentry **lastcmdentry;
7210
7211static struct tblentry *
7212cmdlookup(const char *name, int add)
7213{
7214 unsigned int hashval;
7215 const char *p;
7216 struct tblentry *cmdp;
7217 struct tblentry **pp;
7218
7219 p = name;
7220 hashval = (unsigned char)*p << 4;
7221 while (*p)
7222 hashval += (unsigned char)*p++;
7223 hashval &= 0x7FFF;
7224 pp = &cmdtable[hashval % CMDTABLESIZE];
7225 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7226 if (strcmp(cmdp->cmdname, name) == 0)
7227 break;
7228 pp = &cmdp->next;
7229 }
7230 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007231 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7232 + strlen(name)
7233 /* + 1 - already done because
7234 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007235 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007236 cmdp->cmdtype = CMDUNKNOWN;
7237 strcpy(cmdp->cmdname, name);
7238 }
7239 lastcmdentry = pp;
7240 return cmdp;
7241}
7242
7243/*
7244 * Delete the command entry returned on the last lookup.
7245 */
7246static void
7247delete_cmd_entry(void)
7248{
7249 struct tblentry *cmdp;
7250
7251 INT_OFF;
7252 cmdp = *lastcmdentry;
7253 *lastcmdentry = cmdp->next;
7254 if (cmdp->cmdtype == CMDFUNCTION)
7255 freefunc(cmdp->param.func);
7256 free(cmdp);
7257 INT_ON;
7258}
7259
7260/*
7261 * Add a new command entry, replacing any existing command entry for
7262 * the same name - except special builtins.
7263 */
7264static void
7265addcmdentry(char *name, struct cmdentry *entry)
7266{
7267 struct tblentry *cmdp;
7268
7269 cmdp = cmdlookup(name, 1);
7270 if (cmdp->cmdtype == CMDFUNCTION) {
7271 freefunc(cmdp->param.func);
7272 }
7273 cmdp->cmdtype = entry->cmdtype;
7274 cmdp->param = entry->u;
7275 cmdp->rehash = 0;
7276}
7277
7278static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007279hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007280{
7281 struct tblentry **pp;
7282 struct tblentry *cmdp;
7283 int c;
7284 struct cmdentry entry;
7285 char *name;
7286
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007287 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007288 clearcmdentry(0);
7289 return 0;
7290 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007291
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007292 if (*argptr == NULL) {
7293 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7294 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7295 if (cmdp->cmdtype == CMDNORMAL)
7296 printentry(cmdp);
7297 }
7298 }
7299 return 0;
7300 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007301
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302 c = 0;
7303 while ((name = *argptr) != NULL) {
7304 cmdp = cmdlookup(name, 0);
7305 if (cmdp != NULL
7306 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007307 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7308 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007309 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007310 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007311 find_command(name, &entry, DO_ERR, pathval());
7312 if (entry.cmdtype == CMDUNKNOWN)
7313 c = 1;
7314 argptr++;
7315 }
7316 return c;
7317}
7318
7319/*
7320 * Called when a cd is done. Marks all commands so the next time they
7321 * are executed they will be rehashed.
7322 */
7323static void
7324hashcd(void)
7325{
7326 struct tblentry **pp;
7327 struct tblentry *cmdp;
7328
7329 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7330 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007331 if (cmdp->cmdtype == CMDNORMAL
7332 || (cmdp->cmdtype == CMDBUILTIN
7333 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7334 && builtinloc > 0)
7335 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007336 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007337 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007338 }
7339 }
7340}
7341
7342/*
7343 * Fix command hash table when PATH changed.
7344 * Called before PATH is changed. The argument is the new value of PATH;
7345 * pathval() still returns the old value at this point.
7346 * Called with interrupts off.
7347 */
7348static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007349changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007351 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007352 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007353 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007354 int idx_bltin;
7355
7356 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007357 firstchange = 9999; /* assume no change */
7358 idx = 0;
7359 idx_bltin = -1;
7360 for (;;) {
7361 if (*old != *new) {
7362 firstchange = idx;
7363 if ((*old == '\0' && *new == ':')
7364 || (*old == ':' && *new == '\0'))
7365 firstchange++;
7366 old = new; /* ignore subsequent differences */
7367 }
7368 if (*new == '\0')
7369 break;
7370 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7371 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007372 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007373 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007374 new++, old++;
7375 }
7376 if (builtinloc < 0 && idx_bltin >= 0)
7377 builtinloc = idx_bltin; /* zap builtins */
7378 if (builtinloc >= 0 && idx_bltin < 0)
7379 firstchange = 0;
7380 clearcmdentry(firstchange);
7381 builtinloc = idx_bltin;
7382}
7383
7384#define TEOF 0
7385#define TNL 1
7386#define TREDIR 2
7387#define TWORD 3
7388#define TSEMI 4
7389#define TBACKGND 5
7390#define TAND 6
7391#define TOR 7
7392#define TPIPE 8
7393#define TLP 9
7394#define TRP 10
7395#define TENDCASE 11
7396#define TENDBQUOTE 12
7397#define TNOT 13
7398#define TCASE 14
7399#define TDO 15
7400#define TDONE 16
7401#define TELIF 17
7402#define TELSE 18
7403#define TESAC 19
7404#define TFI 20
7405#define TFOR 21
7406#define TIF 22
7407#define TIN 23
7408#define TTHEN 24
7409#define TUNTIL 25
7410#define TWHILE 26
7411#define TBEGIN 27
7412#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007413typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007414
7415/* first char is indicating which tokens mark the end of a list */
7416static const char *const tokname_array[] = {
7417 "\1end of file",
7418 "\0newline",
7419 "\0redirection",
7420 "\0word",
7421 "\0;",
7422 "\0&",
7423 "\0&&",
7424 "\0||",
7425 "\0|",
7426 "\0(",
7427 "\1)",
7428 "\1;;",
7429 "\1`",
7430#define KWDOFFSET 13
7431 /* the following are keywords */
7432 "\0!",
7433 "\0case",
7434 "\1do",
7435 "\1done",
7436 "\1elif",
7437 "\1else",
7438 "\1esac",
7439 "\1fi",
7440 "\0for",
7441 "\0if",
7442 "\0in",
7443 "\1then",
7444 "\0until",
7445 "\0while",
7446 "\0{",
7447 "\1}",
7448};
7449
7450static const char *
7451tokname(int tok)
7452{
7453 static char buf[16];
7454
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007455//try this:
7456//if (tok < TSEMI) return tokname_array[tok] + 1;
7457//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7458//return buf;
7459
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007460 if (tok >= TSEMI)
7461 buf[0] = '"';
7462 sprintf(buf + (tok >= TSEMI), "%s%c",
7463 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7464 return buf;
7465}
7466
7467/* Wrapper around strcmp for qsort/bsearch/... */
7468static int
7469pstrcmp(const void *a, const void *b)
7470{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007471 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007472}
7473
7474static const char *const *
7475findkwd(const char *s)
7476{
7477 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007478 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7479 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007480}
7481
7482/*
7483 * Locate and print what a word is...
7484 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007485static int
7486describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007487{
7488 struct cmdentry entry;
7489 struct tblentry *cmdp;
7490#if ENABLE_ASH_ALIAS
7491 const struct alias *ap;
7492#endif
7493 const char *path = pathval();
7494
7495 if (describe_command_verbose) {
7496 out1str(command);
7497 }
7498
7499 /* First look at the keywords */
7500 if (findkwd(command)) {
7501 out1str(describe_command_verbose ? " is a shell keyword" : command);
7502 goto out;
7503 }
7504
7505#if ENABLE_ASH_ALIAS
7506 /* Then look at the aliases */
7507 ap = lookupalias(command, 0);
7508 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007509 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007510 out1str("alias ");
7511 printalias(ap);
7512 return 0;
7513 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007514 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007515 goto out;
7516 }
7517#endif
7518 /* Then check if it is a tracked alias */
7519 cmdp = cmdlookup(command, 0);
7520 if (cmdp != NULL) {
7521 entry.cmdtype = cmdp->cmdtype;
7522 entry.u = cmdp->param;
7523 } else {
7524 /* Finally use brute force */
7525 find_command(command, &entry, DO_ABS, path);
7526 }
7527
7528 switch (entry.cmdtype) {
7529 case CMDNORMAL: {
7530 int j = entry.u.index;
7531 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007532 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007533 p = command;
7534 } else {
7535 do {
7536 p = padvance(&path, command);
7537 stunalloc(p);
7538 } while (--j >= 0);
7539 }
7540 if (describe_command_verbose) {
7541 out1fmt(" is%s %s",
7542 (cmdp ? " a tracked alias for" : nullstr), p
7543 );
7544 } else {
7545 out1str(p);
7546 }
7547 break;
7548 }
7549
7550 case CMDFUNCTION:
7551 if (describe_command_verbose) {
7552 out1str(" is a shell function");
7553 } else {
7554 out1str(command);
7555 }
7556 break;
7557
7558 case CMDBUILTIN:
7559 if (describe_command_verbose) {
7560 out1fmt(" is a %sshell builtin",
7561 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7562 "special " : nullstr
7563 );
7564 } else {
7565 out1str(command);
7566 }
7567 break;
7568
7569 default:
7570 if (describe_command_verbose) {
7571 out1str(": not found\n");
7572 }
7573 return 127;
7574 }
7575 out:
7576 outstr("\n", stdout);
7577 return 0;
7578}
7579
7580static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007581typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007582{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007583 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007584 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007585 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007586
Denis Vlasenko46846e22007-05-20 13:08:31 +00007587 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007588 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007589 i++;
7590 verbose = 0;
7591 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007592 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007593 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007594 }
7595 return err;
7596}
7597
7598#if ENABLE_ASH_CMDCMD
7599static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007600commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007601{
7602 int c;
7603 enum {
7604 VERIFY_BRIEF = 1,
7605 VERIFY_VERBOSE = 2,
7606 } verify = 0;
7607
7608 while ((c = nextopt("pvV")) != '\0')
7609 if (c == 'V')
7610 verify |= VERIFY_VERBOSE;
7611 else if (c == 'v')
7612 verify |= VERIFY_BRIEF;
7613#if DEBUG
7614 else if (c != 'p')
7615 abort();
7616#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007617 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7618 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007619 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007620 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007621
7622 return 0;
7623}
7624#endif
7625
7626
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007627/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007628
Denis Vlasenko340299a2008-11-21 10:36:36 +00007629static int funcblocksize; /* size of structures in function */
7630static int funcstringsize; /* size of strings in node */
7631static void *funcblock; /* block to allocate function from */
7632static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007633
Eric Andersencb57d552001-06-28 07:25:16 +00007634/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007635#define EV_EXIT 01 /* exit after evaluating tree */
7636#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007637#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007638
Denis Vlasenko340299a2008-11-21 10:36:36 +00007639static const short nodesize[N_NUMBER] = {
7640 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7641 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7642 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7643 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7644 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7645 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7646 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7647 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7648 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7649 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7650 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7651 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7652 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7653 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7654 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7655 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7656 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007657#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007658 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007659#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007660 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7661 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7662 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7663 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7664 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7665 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7666 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7667 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7668 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007669};
7670
7671static void calcsize(union node *n);
7672
7673static void
7674sizenodelist(struct nodelist *lp)
7675{
7676 while (lp) {
7677 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7678 calcsize(lp->n);
7679 lp = lp->next;
7680 }
7681}
7682
7683static void
7684calcsize(union node *n)
7685{
7686 if (n == NULL)
7687 return;
7688 funcblocksize += nodesize[n->type];
7689 switch (n->type) {
7690 case NCMD:
7691 calcsize(n->ncmd.redirect);
7692 calcsize(n->ncmd.args);
7693 calcsize(n->ncmd.assign);
7694 break;
7695 case NPIPE:
7696 sizenodelist(n->npipe.cmdlist);
7697 break;
7698 case NREDIR:
7699 case NBACKGND:
7700 case NSUBSHELL:
7701 calcsize(n->nredir.redirect);
7702 calcsize(n->nredir.n);
7703 break;
7704 case NAND:
7705 case NOR:
7706 case NSEMI:
7707 case NWHILE:
7708 case NUNTIL:
7709 calcsize(n->nbinary.ch2);
7710 calcsize(n->nbinary.ch1);
7711 break;
7712 case NIF:
7713 calcsize(n->nif.elsepart);
7714 calcsize(n->nif.ifpart);
7715 calcsize(n->nif.test);
7716 break;
7717 case NFOR:
7718 funcstringsize += strlen(n->nfor.var) + 1;
7719 calcsize(n->nfor.body);
7720 calcsize(n->nfor.args);
7721 break;
7722 case NCASE:
7723 calcsize(n->ncase.cases);
7724 calcsize(n->ncase.expr);
7725 break;
7726 case NCLIST:
7727 calcsize(n->nclist.body);
7728 calcsize(n->nclist.pattern);
7729 calcsize(n->nclist.next);
7730 break;
7731 case NDEFUN:
7732 case NARG:
7733 sizenodelist(n->narg.backquote);
7734 funcstringsize += strlen(n->narg.text) + 1;
7735 calcsize(n->narg.next);
7736 break;
7737 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007738#if ENABLE_ASH_BASH_COMPAT
7739 case NTO2:
7740#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007741 case NCLOBBER:
7742 case NFROM:
7743 case NFROMTO:
7744 case NAPPEND:
7745 calcsize(n->nfile.fname);
7746 calcsize(n->nfile.next);
7747 break;
7748 case NTOFD:
7749 case NFROMFD:
7750 calcsize(n->ndup.vname);
7751 calcsize(n->ndup.next);
7752 break;
7753 case NHERE:
7754 case NXHERE:
7755 calcsize(n->nhere.doc);
7756 calcsize(n->nhere.next);
7757 break;
7758 case NNOT:
7759 calcsize(n->nnot.com);
7760 break;
7761 };
7762}
7763
7764static char *
7765nodeckstrdup(char *s)
7766{
7767 char *rtn = funcstring;
7768
7769 strcpy(funcstring, s);
7770 funcstring += strlen(s) + 1;
7771 return rtn;
7772}
7773
7774static union node *copynode(union node *);
7775
7776static struct nodelist *
7777copynodelist(struct nodelist *lp)
7778{
7779 struct nodelist *start;
7780 struct nodelist **lpp;
7781
7782 lpp = &start;
7783 while (lp) {
7784 *lpp = funcblock;
7785 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7786 (*lpp)->n = copynode(lp->n);
7787 lp = lp->next;
7788 lpp = &(*lpp)->next;
7789 }
7790 *lpp = NULL;
7791 return start;
7792}
7793
7794static union node *
7795copynode(union node *n)
7796{
7797 union node *new;
7798
7799 if (n == NULL)
7800 return NULL;
7801 new = funcblock;
7802 funcblock = (char *) funcblock + nodesize[n->type];
7803
7804 switch (n->type) {
7805 case NCMD:
7806 new->ncmd.redirect = copynode(n->ncmd.redirect);
7807 new->ncmd.args = copynode(n->ncmd.args);
7808 new->ncmd.assign = copynode(n->ncmd.assign);
7809 break;
7810 case NPIPE:
7811 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007812 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007813 break;
7814 case NREDIR:
7815 case NBACKGND:
7816 case NSUBSHELL:
7817 new->nredir.redirect = copynode(n->nredir.redirect);
7818 new->nredir.n = copynode(n->nredir.n);
7819 break;
7820 case NAND:
7821 case NOR:
7822 case NSEMI:
7823 case NWHILE:
7824 case NUNTIL:
7825 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7826 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7827 break;
7828 case NIF:
7829 new->nif.elsepart = copynode(n->nif.elsepart);
7830 new->nif.ifpart = copynode(n->nif.ifpart);
7831 new->nif.test = copynode(n->nif.test);
7832 break;
7833 case NFOR:
7834 new->nfor.var = nodeckstrdup(n->nfor.var);
7835 new->nfor.body = copynode(n->nfor.body);
7836 new->nfor.args = copynode(n->nfor.args);
7837 break;
7838 case NCASE:
7839 new->ncase.cases = copynode(n->ncase.cases);
7840 new->ncase.expr = copynode(n->ncase.expr);
7841 break;
7842 case NCLIST:
7843 new->nclist.body = copynode(n->nclist.body);
7844 new->nclist.pattern = copynode(n->nclist.pattern);
7845 new->nclist.next = copynode(n->nclist.next);
7846 break;
7847 case NDEFUN:
7848 case NARG:
7849 new->narg.backquote = copynodelist(n->narg.backquote);
7850 new->narg.text = nodeckstrdup(n->narg.text);
7851 new->narg.next = copynode(n->narg.next);
7852 break;
7853 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007854#if ENABLE_ASH_BASH_COMPAT
7855 case NTO2:
7856#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007857 case NCLOBBER:
7858 case NFROM:
7859 case NFROMTO:
7860 case NAPPEND:
7861 new->nfile.fname = copynode(n->nfile.fname);
7862 new->nfile.fd = n->nfile.fd;
7863 new->nfile.next = copynode(n->nfile.next);
7864 break;
7865 case NTOFD:
7866 case NFROMFD:
7867 new->ndup.vname = copynode(n->ndup.vname);
7868 new->ndup.dupfd = n->ndup.dupfd;
7869 new->ndup.fd = n->ndup.fd;
7870 new->ndup.next = copynode(n->ndup.next);
7871 break;
7872 case NHERE:
7873 case NXHERE:
7874 new->nhere.doc = copynode(n->nhere.doc);
7875 new->nhere.fd = n->nhere.fd;
7876 new->nhere.next = copynode(n->nhere.next);
7877 break;
7878 case NNOT:
7879 new->nnot.com = copynode(n->nnot.com);
7880 break;
7881 };
7882 new->type = n->type;
7883 return new;
7884}
7885
7886/*
7887 * Make a copy of a parse tree.
7888 */
7889static struct funcnode *
7890copyfunc(union node *n)
7891{
7892 struct funcnode *f;
7893 size_t blocksize;
7894
7895 funcblocksize = offsetof(struct funcnode, n);
7896 funcstringsize = 0;
7897 calcsize(n);
7898 blocksize = funcblocksize;
7899 f = ckmalloc(blocksize + funcstringsize);
7900 funcblock = (char *) f + offsetof(struct funcnode, n);
7901 funcstring = (char *) f + blocksize;
7902 copynode(n);
7903 f->count = 0;
7904 return f;
7905}
7906
7907/*
7908 * Define a shell function.
7909 */
7910static void
7911defun(char *name, union node *func)
7912{
7913 struct cmdentry entry;
7914
7915 INT_OFF;
7916 entry.cmdtype = CMDFUNCTION;
7917 entry.u.func = copyfunc(func);
7918 addcmdentry(name, &entry);
7919 INT_ON;
7920}
7921
Denis Vlasenko4b875702009-03-19 13:30:04 +00007922/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923#define SKIPBREAK (1 << 0)
7924#define SKIPCONT (1 << 1)
7925#define SKIPFUNC (1 << 2)
7926#define SKIPFILE (1 << 3)
7927#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00007928static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007929static int skipcount; /* number of levels to skip */
7930static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007931static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007932
Denis Vlasenko4b875702009-03-19 13:30:04 +00007933/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007934static int evalstring(char *s, int mask);
7935
Denis Vlasenko4b875702009-03-19 13:30:04 +00007936/* Called to execute a trap.
7937 * Single callsite - at the end of evaltree().
7938 * If we return non-zero, exaltree raises EXEXIT exception.
7939 *
7940 * Perhaps we should avoid entering new trap handlers
7941 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007942 */
7943static int
7944dotrap(void)
7945{
Denis Vlasenko4b875702009-03-19 13:30:04 +00007946 uint8_t *g;
7947 int sig;
7948 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007949
7950 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007951 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007952 xbarrier();
7953
Denis Vlasenko4b875702009-03-19 13:30:04 +00007954 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
7955 int want_exexit;
7956 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007957
Denis Vlasenko4b875702009-03-19 13:30:04 +00007958 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007959 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00007960 t = trap[sig];
7961 /* non-trapped SIGINT is handled separately by raise_interrupt,
7962 * don't upset it by resetting gotsig[SIGINT-1] */
7963 if (sig == SIGINT && !t)
7964 continue;
7965 *g = 0;
7966 if (!t)
7967 continue;
7968 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007969 exitstatus = savestatus;
Denis Vlasenko4b875702009-03-19 13:30:04 +00007970 if (want_exexit)
7971 return want_exexit;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007972 }
7973
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007974 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007975}
7976
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007977/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007978static void evalloop(union node *, int);
7979static void evalfor(union node *, int);
7980static void evalcase(union node *, int);
7981static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007982static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007983static void evalpipe(union node *, int);
7984static void evalcommand(union node *, int);
7985static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007986static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007987
Eric Andersen62483552001-07-10 06:09:16 +00007988/*
Eric Andersenc470f442003-07-28 09:56:35 +00007989 * Evaluate a parse tree. The value is left in the global variable
7990 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007991 */
Eric Andersenc470f442003-07-28 09:56:35 +00007992static void
7993evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007994{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007995 struct jmploc *volatile savehandler = exception_handler;
7996 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007997 int checkexit = 0;
7998 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007999 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008000
Eric Andersenc470f442003-07-28 09:56:35 +00008001 if (n == NULL) {
8002 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008003 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008004 }
Eric Andersenc470f442003-07-28 09:56:35 +00008005 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008006 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008007
8008 exception_handler = &jmploc;
8009 {
8010 int err = setjmp(jmploc.loc);
8011 if (err) {
8012 /* if it was a signal, check for trap handlers */
Denis Vlasenko7f88e342009-03-19 03:36:18 +00008013 if (exception_type == EXSIG)
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008014 goto out;
8015 /* continue on the way out */
8016 exception_handler = savehandler;
8017 longjmp(exception_handler->loc, err);
8018 }
8019 }
8020
Eric Andersenc470f442003-07-28 09:56:35 +00008021 switch (n->type) {
8022 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008023#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008024 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008025 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008026 break;
8027#endif
8028 case NNOT:
8029 evaltree(n->nnot.com, EV_TESTED);
8030 status = !exitstatus;
8031 goto setstatus;
8032 case NREDIR:
8033 expredir(n->nredir.redirect);
8034 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8035 if (!status) {
8036 evaltree(n->nredir.n, flags & EV_TESTED);
8037 status = exitstatus;
8038 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008039 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008040 goto setstatus;
8041 case NCMD:
8042 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008043 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008044 if (eflag && !(flags & EV_TESTED))
8045 checkexit = ~0;
8046 goto calleval;
8047 case NFOR:
8048 evalfn = evalfor;
8049 goto calleval;
8050 case NWHILE:
8051 case NUNTIL:
8052 evalfn = evalloop;
8053 goto calleval;
8054 case NSUBSHELL:
8055 case NBACKGND:
8056 evalfn = evalsubshell;
8057 goto calleval;
8058 case NPIPE:
8059 evalfn = evalpipe;
8060 goto checkexit;
8061 case NCASE:
8062 evalfn = evalcase;
8063 goto calleval;
8064 case NAND:
8065 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008066 case NSEMI: {
8067
Eric Andersenc470f442003-07-28 09:56:35 +00008068#if NAND + 1 != NOR
8069#error NAND + 1 != NOR
8070#endif
8071#if NOR + 1 != NSEMI
8072#error NOR + 1 != NSEMI
8073#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008074 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008075 evaltree(
8076 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008077 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008078 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008079 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008080 break;
8081 if (!evalskip) {
8082 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008083 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008084 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008085 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008086 evalfn(n, flags);
8087 break;
8088 }
8089 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008090 }
Eric Andersenc470f442003-07-28 09:56:35 +00008091 case NIF:
8092 evaltree(n->nif.test, EV_TESTED);
8093 if (evalskip)
8094 break;
8095 if (exitstatus == 0) {
8096 n = n->nif.ifpart;
8097 goto evaln;
8098 } else if (n->nif.elsepart) {
8099 n = n->nif.elsepart;
8100 goto evaln;
8101 }
8102 goto success;
8103 case NDEFUN:
8104 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008105 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008106 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008107 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008108 exitstatus = status;
8109 break;
8110 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008111
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008112 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008113 exception_handler = savehandler;
8114 out1:
8115 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008116 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008117 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008118 goto exexit;
8119
8120 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008121 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008122 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008123 }
Eric Andersen62483552001-07-10 06:09:16 +00008124}
8125
Eric Andersenc470f442003-07-28 09:56:35 +00008126#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8127static
8128#endif
8129void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8130
Eric Andersenc470f442003-07-28 09:56:35 +00008131static void
8132evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008133{
8134 int status;
8135
8136 loopnest++;
8137 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008138 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008139 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008140 int i;
8141
Eric Andersencb57d552001-06-28 07:25:16 +00008142 evaltree(n->nbinary.ch1, EV_TESTED);
8143 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008144 skipping:
8145 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008146 evalskip = 0;
8147 continue;
8148 }
8149 if (evalskip == SKIPBREAK && --skipcount <= 0)
8150 evalskip = 0;
8151 break;
8152 }
Eric Andersenc470f442003-07-28 09:56:35 +00008153 i = exitstatus;
8154 if (n->type != NWHILE)
8155 i = !i;
8156 if (i != 0)
8157 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008158 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008159 status = exitstatus;
8160 if (evalskip)
8161 goto skipping;
8162 }
8163 loopnest--;
8164 exitstatus = status;
8165}
8166
Eric Andersenc470f442003-07-28 09:56:35 +00008167static void
8168evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008169{
8170 struct arglist arglist;
8171 union node *argp;
8172 struct strlist *sp;
8173 struct stackmark smark;
8174
8175 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008176 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008177 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008178 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008179 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008180 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008181 if (evalskip)
8182 goto out;
8183 }
8184 *arglist.lastp = NULL;
8185
8186 exitstatus = 0;
8187 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008188 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008189 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008190 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008191 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008192 if (evalskip) {
8193 if (evalskip == SKIPCONT && --skipcount <= 0) {
8194 evalskip = 0;
8195 continue;
8196 }
8197 if (evalskip == SKIPBREAK && --skipcount <= 0)
8198 evalskip = 0;
8199 break;
8200 }
8201 }
8202 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008203 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008204 popstackmark(&smark);
8205}
8206
Eric Andersenc470f442003-07-28 09:56:35 +00008207static void
8208evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008209{
8210 union node *cp;
8211 union node *patp;
8212 struct arglist arglist;
8213 struct stackmark smark;
8214
8215 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008216 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008217 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008218 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008219 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008220 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8221 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008222 if (casematch(patp, arglist.list->text)) {
8223 if (evalskip == 0) {
8224 evaltree(cp->nclist.body, flags);
8225 }
8226 goto out;
8227 }
8228 }
8229 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008230 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008231 popstackmark(&smark);
8232}
8233
Eric Andersenc470f442003-07-28 09:56:35 +00008234/*
8235 * Kick off a subshell to evaluate a tree.
8236 */
Eric Andersenc470f442003-07-28 09:56:35 +00008237static void
8238evalsubshell(union node *n, int flags)
8239{
8240 struct job *jp;
8241 int backgnd = (n->type == NBACKGND);
8242 int status;
8243
8244 expredir(n->nredir.redirect);
8245 if (!backgnd && flags & EV_EXIT && !trap[0])
8246 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008247 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008248 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008249 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008250 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008251 flags |= EV_EXIT;
8252 if (backgnd)
8253 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008254 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008255 redirect(n->nredir.redirect, 0);
8256 evaltreenr(n->nredir.n, flags);
8257 /* never returns */
8258 }
8259 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008260 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008261 status = waitforjob(jp);
8262 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008263 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008264}
8265
Eric Andersenc470f442003-07-28 09:56:35 +00008266/*
8267 * Compute the names of the files in a redirection list.
8268 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008269static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008270static void
8271expredir(union node *n)
8272{
8273 union node *redir;
8274
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008275 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008276 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008277
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008278 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008279 fn.lastp = &fn.list;
8280 switch (redir->type) {
8281 case NFROMTO:
8282 case NFROM:
8283 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008284#if ENABLE_ASH_BASH_COMPAT
8285 case NTO2:
8286#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008287 case NCLOBBER:
8288 case NAPPEND:
8289 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008290#if ENABLE_ASH_BASH_COMPAT
8291 store_expfname:
8292#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008293 redir->nfile.expfname = fn.list->text;
8294 break;
8295 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008296 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008297 if (redir->ndup.vname) {
8298 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008299 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008300 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008301#if ENABLE_ASH_BASH_COMPAT
8302//FIXME: we used expandarg with different args!
8303 if (!isdigit_str9(fn.list->text)) {
8304 /* >&file, not >&fd */
8305 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8306 ash_msg_and_raise_error("redir error");
8307 redir->type = NTO2;
8308 goto store_expfname;
8309 }
8310#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008311 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008312 }
8313 break;
8314 }
8315 }
8316}
8317
Eric Andersencb57d552001-06-28 07:25:16 +00008318/*
Eric Andersencb57d552001-06-28 07:25:16 +00008319 * Evaluate a pipeline. All the processes in the pipeline are children
8320 * of the process creating the pipeline. (This differs from some versions
8321 * of the shell, which make the last process in a pipeline the parent
8322 * of all the rest.)
8323 */
Eric Andersenc470f442003-07-28 09:56:35 +00008324static void
8325evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008326{
8327 struct job *jp;
8328 struct nodelist *lp;
8329 int pipelen;
8330 int prevfd;
8331 int pip[2];
8332
Eric Andersenc470f442003-07-28 09:56:35 +00008333 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008334 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008335 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008336 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008337 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008338 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008339 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008340 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008341 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008342 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008343 pip[1] = -1;
8344 if (lp->next) {
8345 if (pipe(pip) < 0) {
8346 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008347 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008348 }
8349 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008350 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008351 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008352 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008353 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008354 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008355 if (prevfd > 0) {
8356 dup2(prevfd, 0);
8357 close(prevfd);
8358 }
8359 if (pip[1] > 1) {
8360 dup2(pip[1], 1);
8361 close(pip[1]);
8362 }
Eric Andersenc470f442003-07-28 09:56:35 +00008363 evaltreenr(lp->n, flags);
8364 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008365 }
8366 if (prevfd >= 0)
8367 close(prevfd);
8368 prevfd = pip[0];
8369 close(pip[1]);
8370 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008371 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008372 exitstatus = waitforjob(jp);
8373 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008374 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008375 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008376}
8377
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008378/*
8379 * Controls whether the shell is interactive or not.
8380 */
8381static void
8382setinteractive(int on)
8383{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008384 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008385
8386 if (++on == is_interactive)
8387 return;
8388 is_interactive = on;
8389 setsignal(SIGINT);
8390 setsignal(SIGQUIT);
8391 setsignal(SIGTERM);
8392#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8393 if (is_interactive > 1) {
8394 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008395 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008396
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008397 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008398 out1fmt(
8399 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008400 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008401 "Enter 'help' for a list of built-in commands."
8402 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008403 bb_banner);
8404 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008405 }
8406 }
8407#endif
8408}
8409
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008410static void
8411optschanged(void)
8412{
8413#if DEBUG
8414 opentrace();
8415#endif
8416 setinteractive(iflag);
8417 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008418#if ENABLE_FEATURE_EDITING_VI
8419 if (viflag)
8420 line_input_state->flags |= VI_MODE;
8421 else
8422 line_input_state->flags &= ~VI_MODE;
8423#else
8424 viflag = 0; /* forcibly keep the option off */
8425#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008426}
8427
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008428static struct localvar *localvars;
8429
8430/*
8431 * Called after a function returns.
8432 * Interrupts must be off.
8433 */
8434static void
8435poplocalvars(void)
8436{
8437 struct localvar *lvp;
8438 struct var *vp;
8439
8440 while ((lvp = localvars) != NULL) {
8441 localvars = lvp->next;
8442 vp = lvp->vp;
8443 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8444 if (vp == NULL) { /* $- saved */
8445 memcpy(optlist, lvp->text, sizeof(optlist));
8446 free((char*)lvp->text);
8447 optschanged();
8448 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8449 unsetvar(vp->text);
8450 } else {
8451 if (vp->func)
8452 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8453 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8454 free((char*)vp->text);
8455 vp->flags = lvp->flags;
8456 vp->text = lvp->text;
8457 }
8458 free(lvp);
8459 }
8460}
8461
8462static int
8463evalfun(struct funcnode *func, int argc, char **argv, int flags)
8464{
8465 volatile struct shparam saveparam;
8466 struct localvar *volatile savelocalvars;
8467 struct jmploc *volatile savehandler;
8468 struct jmploc jmploc;
8469 int e;
8470
8471 saveparam = shellparam;
8472 savelocalvars = localvars;
8473 e = setjmp(jmploc.loc);
8474 if (e) {
8475 goto funcdone;
8476 }
8477 INT_OFF;
8478 savehandler = exception_handler;
8479 exception_handler = &jmploc;
8480 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008481 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008482 func->count++;
8483 funcnest++;
8484 INT_ON;
8485 shellparam.nparam = argc - 1;
8486 shellparam.p = argv + 1;
8487#if ENABLE_ASH_GETOPTS
8488 shellparam.optind = 1;
8489 shellparam.optoff = -1;
8490#endif
8491 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008492 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008493 INT_OFF;
8494 funcnest--;
8495 freefunc(func);
8496 poplocalvars();
8497 localvars = savelocalvars;
8498 freeparam(&shellparam);
8499 shellparam = saveparam;
8500 exception_handler = savehandler;
8501 INT_ON;
8502 evalskip &= ~SKIPFUNC;
8503 return e;
8504}
8505
Denis Vlasenko131ae172007-02-18 13:00:19 +00008506#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008507static char **
8508parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008509{
8510 char *cp, c;
8511
8512 for (;;) {
8513 cp = *++argv;
8514 if (!cp)
8515 return 0;
8516 if (*cp++ != '-')
8517 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008518 c = *cp++;
8519 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008520 break;
8521 if (c == '-' && !*cp) {
8522 argv++;
8523 break;
8524 }
8525 do {
8526 switch (c) {
8527 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008528 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008529 break;
8530 default:
8531 /* run 'typecmd' for other options */
8532 return 0;
8533 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008534 c = *cp++;
8535 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008536 }
8537 return argv;
8538}
8539#endif
8540
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008541/*
8542 * Make a variable a local variable. When a variable is made local, it's
8543 * value and flags are saved in a localvar structure. The saved values
8544 * will be restored when the shell function returns. We handle the name
8545 * "-" as a special case.
8546 */
8547static void
8548mklocal(char *name)
8549{
8550 struct localvar *lvp;
8551 struct var **vpp;
8552 struct var *vp;
8553
8554 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008555 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008556 if (LONE_DASH(name)) {
8557 char *p;
8558 p = ckmalloc(sizeof(optlist));
8559 lvp->text = memcpy(p, optlist, sizeof(optlist));
8560 vp = NULL;
8561 } else {
8562 char *eq;
8563
8564 vpp = hashvar(name);
8565 vp = *findvar(vpp, name);
8566 eq = strchr(name, '=');
8567 if (vp == NULL) {
8568 if (eq)
8569 setvareq(name, VSTRFIXED);
8570 else
8571 setvar(name, NULL, VSTRFIXED);
8572 vp = *vpp; /* the new variable */
8573 lvp->flags = VUNSET;
8574 } else {
8575 lvp->text = vp->text;
8576 lvp->flags = vp->flags;
8577 vp->flags |= VSTRFIXED|VTEXTFIXED;
8578 if (eq)
8579 setvareq(name, 0);
8580 }
8581 }
8582 lvp->vp = vp;
8583 lvp->next = localvars;
8584 localvars = lvp;
8585 INT_ON;
8586}
8587
8588/*
8589 * The "local" command.
8590 */
8591static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008592localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008593{
8594 char *name;
8595
8596 argv = argptr;
8597 while ((name = *argv++) != NULL) {
8598 mklocal(name);
8599 }
8600 return 0;
8601}
8602
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008603static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008604falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008605{
8606 return 1;
8607}
8608
8609static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008610truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008611{
8612 return 0;
8613}
8614
8615static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008616execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008617{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008618 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008619 iflag = 0; /* exit on error */
8620 mflag = 0;
8621 optschanged();
8622 shellexec(argv + 1, pathval(), 0);
8623 }
8624 return 0;
8625}
8626
8627/*
8628 * The return command.
8629 */
8630static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008631returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008632{
8633 /*
8634 * If called outside a function, do what ksh does;
8635 * skip the rest of the file.
8636 */
8637 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8638 return argv[1] ? number(argv[1]) : exitstatus;
8639}
8640
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008641/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008642static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008643static int dotcmd(int, char **);
8644static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008645static int exitcmd(int, char **);
8646static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008647#if ENABLE_ASH_GETOPTS
8648static int getoptscmd(int, char **);
8649#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008650#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008651static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008652#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008653#if ENABLE_ASH_MATH_SUPPORT
8654static int letcmd(int, char **);
8655#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008656static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008657static int setcmd(int, char **);
8658static int shiftcmd(int, char **);
8659static int timescmd(int, char **);
8660static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008661static int umaskcmd(int, char **);
8662static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008663static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008664
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008665#define BUILTIN_NOSPEC "0"
8666#define BUILTIN_SPECIAL "1"
8667#define BUILTIN_REGULAR "2"
8668#define BUILTIN_SPEC_REG "3"
8669#define BUILTIN_ASSIGN "4"
8670#define BUILTIN_SPEC_ASSG "5"
8671#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008672#define BUILTIN_SPEC_REG_ASSG "7"
8673
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008674/* We do not handle [[ expr ]] bashism bash-compatibly,
8675 * we make it a synonym of [ expr ].
8676 * Basically, word splitting and pathname expansion should NOT be performed
8677 * Examples:
8678 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8679 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8680 * Additional operators:
8681 * || and && should work as -o and -a
8682 * =~ regexp match
8683 * Apart from the above, [[ expr ]] should work as [ expr ]
8684 */
8685
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008686#define echocmd echo_main
8687#define printfcmd printf_main
8688#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008689
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008690/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008691static const struct builtincmd builtintab[] = {
8692 { BUILTIN_SPEC_REG ".", dotcmd },
8693 { BUILTIN_SPEC_REG ":", truecmd },
8694#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008695 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008696#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008697 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008698#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008699#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008700#if ENABLE_ASH_ALIAS
8701 { BUILTIN_REG_ASSG "alias", aliascmd },
8702#endif
8703#if JOBS
8704 { BUILTIN_REGULAR "bg", fg_bgcmd },
8705#endif
8706 { BUILTIN_SPEC_REG "break", breakcmd },
8707 { BUILTIN_REGULAR "cd", cdcmd },
8708 { BUILTIN_NOSPEC "chdir", cdcmd },
8709#if ENABLE_ASH_CMDCMD
8710 { BUILTIN_REGULAR "command", commandcmd },
8711#endif
8712 { BUILTIN_SPEC_REG "continue", breakcmd },
8713#if ENABLE_ASH_BUILTIN_ECHO
8714 { BUILTIN_REGULAR "echo", echocmd },
8715#endif
8716 { BUILTIN_SPEC_REG "eval", evalcmd },
8717 { BUILTIN_SPEC_REG "exec", execcmd },
8718 { BUILTIN_SPEC_REG "exit", exitcmd },
8719 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8720 { BUILTIN_REGULAR "false", falsecmd },
8721#if JOBS
8722 { BUILTIN_REGULAR "fg", fg_bgcmd },
8723#endif
8724#if ENABLE_ASH_GETOPTS
8725 { BUILTIN_REGULAR "getopts", getoptscmd },
8726#endif
8727 { BUILTIN_NOSPEC "hash", hashcmd },
8728#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8729 { BUILTIN_NOSPEC "help", helpcmd },
8730#endif
8731#if JOBS
8732 { BUILTIN_REGULAR "jobs", jobscmd },
8733 { BUILTIN_REGULAR "kill", killcmd },
8734#endif
8735#if ENABLE_ASH_MATH_SUPPORT
8736 { BUILTIN_NOSPEC "let", letcmd },
8737#endif
8738 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008739#if ENABLE_ASH_BUILTIN_PRINTF
8740 { BUILTIN_REGULAR "printf", printfcmd },
8741#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008742 { BUILTIN_NOSPEC "pwd", pwdcmd },
8743 { BUILTIN_REGULAR "read", readcmd },
8744 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8745 { BUILTIN_SPEC_REG "return", returncmd },
8746 { BUILTIN_SPEC_REG "set", setcmd },
8747 { BUILTIN_SPEC_REG "shift", shiftcmd },
8748 { BUILTIN_SPEC_REG "source", dotcmd },
8749#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008750 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008751#endif
8752 { BUILTIN_SPEC_REG "times", timescmd },
8753 { BUILTIN_SPEC_REG "trap", trapcmd },
8754 { BUILTIN_REGULAR "true", truecmd },
8755 { BUILTIN_NOSPEC "type", typecmd },
8756 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8757 { BUILTIN_REGULAR "umask", umaskcmd },
8758#if ENABLE_ASH_ALIAS
8759 { BUILTIN_REGULAR "unalias", unaliascmd },
8760#endif
8761 { BUILTIN_SPEC_REG "unset", unsetcmd },
8762 { BUILTIN_REGULAR "wait", waitcmd },
8763};
8764
Denis Vlasenko80591b02008-03-25 07:49:43 +00008765/* Should match the above table! */
8766#define COMMANDCMD (builtintab + \
8767 2 + \
8768 1 * ENABLE_ASH_BUILTIN_TEST + \
8769 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8770 1 * ENABLE_ASH_ALIAS + \
8771 1 * ENABLE_ASH_JOB_CONTROL + \
8772 3)
8773#define EXECCMD (builtintab + \
8774 2 + \
8775 1 * ENABLE_ASH_BUILTIN_TEST + \
8776 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8777 1 * ENABLE_ASH_ALIAS + \
8778 1 * ENABLE_ASH_JOB_CONTROL + \
8779 3 + \
8780 1 * ENABLE_ASH_CMDCMD + \
8781 1 + \
8782 ENABLE_ASH_BUILTIN_ECHO + \
8783 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008784
8785/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008786 * Search the table of builtin commands.
8787 */
8788static struct builtincmd *
8789find_builtin(const char *name)
8790{
8791 struct builtincmd *bp;
8792
8793 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008794 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008795 pstrcmp
8796 );
8797 return bp;
8798}
8799
8800/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008801 * Execute a simple command.
8802 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008803static int
8804isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008805{
8806 const char *q = endofname(p);
8807 if (p == q)
8808 return 0;
8809 return *q == '=';
8810}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008811static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008812bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008813{
8814 /* Preserve exitstatus of a previous possible redirection
8815 * as POSIX mandates */
8816 return back_exitstatus;
8817}
Eric Andersenc470f442003-07-28 09:56:35 +00008818static void
8819evalcommand(union node *cmd, int flags)
8820{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008821 static const struct builtincmd null_bltin = {
8822 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008823 };
Eric Andersenc470f442003-07-28 09:56:35 +00008824 struct stackmark smark;
8825 union node *argp;
8826 struct arglist arglist;
8827 struct arglist varlist;
8828 char **argv;
8829 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008830 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008831 struct cmdentry cmdentry;
8832 struct job *jp;
8833 char *lastarg;
8834 const char *path;
8835 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008836 int status;
8837 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008838 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008839 smallint cmd_is_exec;
8840 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008841
8842 /* First expand the arguments. */
8843 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8844 setstackmark(&smark);
8845 back_exitstatus = 0;
8846
8847 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008848 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008849 varlist.lastp = &varlist.list;
8850 *varlist.lastp = NULL;
8851 arglist.lastp = &arglist.list;
8852 *arglist.lastp = NULL;
8853
8854 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008855 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008856 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8857 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8858 }
8859
Eric Andersenc470f442003-07-28 09:56:35 +00008860 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8861 struct strlist **spp;
8862
8863 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008864 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008865 expandarg(argp, &arglist, EXP_VARTILDE);
8866 else
8867 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8868
Eric Andersenc470f442003-07-28 09:56:35 +00008869 for (sp = *spp; sp; sp = sp->next)
8870 argc++;
8871 }
8872
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008873 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008874 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008875 TRACE(("evalcommand arg: %s\n", sp->text));
8876 *nargv++ = sp->text;
8877 }
8878 *nargv = NULL;
8879
8880 lastarg = NULL;
8881 if (iflag && funcnest == 0 && argc > 0)
8882 lastarg = nargv[-1];
8883
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008884 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008885 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008886 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008887
8888 path = vpath.text;
8889 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8890 struct strlist **spp;
8891 char *p;
8892
8893 spp = varlist.lastp;
8894 expandarg(argp, &varlist, EXP_VARTILDE);
8895
8896 /*
8897 * Modify the command lookup path, if a PATH= assignment
8898 * is present
8899 */
8900 p = (*spp)->text;
8901 if (varequal(p, path))
8902 path = p;
8903 }
8904
8905 /* Print the command if xflag is set. */
8906 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008907 int n;
8908 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008909
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008910 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008911 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008912
8913 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008914 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008915 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008916 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008917 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008918 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008919 p--;
8920 }
8921 }
8922 sp = arglist.list;
8923 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008924 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008925 }
8926
8927 cmd_is_exec = 0;
8928 spclbltin = -1;
8929
8930 /* Now locate the command. */
8931 if (argc) {
8932 const char *oldpath;
8933 int cmd_flag = DO_ERR;
8934
8935 path += 5;
8936 oldpath = path;
8937 for (;;) {
8938 find_command(argv[0], &cmdentry, cmd_flag, path);
8939 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008940 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008941 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008942 goto bail;
8943 }
8944
8945 /* implement bltin and command here */
8946 if (cmdentry.cmdtype != CMDBUILTIN)
8947 break;
8948 if (spclbltin < 0)
8949 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8950 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008951 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008952#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008953 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008954 path = oldpath;
8955 nargv = parse_command_args(argv, &path);
8956 if (!nargv)
8957 break;
8958 argc -= nargv - argv;
8959 argv = nargv;
8960 cmd_flag |= DO_NOFUNC;
8961 } else
8962#endif
8963 break;
8964 }
8965 }
8966
8967 if (status) {
8968 /* We have a redirection error. */
8969 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008970 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008971 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008972 exitstatus = status;
8973 goto out;
8974 }
8975
8976 /* Execute the command. */
8977 switch (cmdentry.cmdtype) {
8978 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008979
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008980#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008981/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8982 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008983 {
8984 /* find_command() encodes applet_no as (-2 - applet_no) */
8985 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008986 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008987 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008988 /* run <applet>_main() */
8989 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008990 break;
8991 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008992 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008993#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008994 /* Fork off a child process if necessary. */
8995 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008996 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008997 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008998 if (forkshell(jp, cmd, FORK_FG) != 0) {
8999 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009000 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009001 break;
9002 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009003 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009004 }
9005 listsetvar(varlist.list, VEXPORT|VSTACK);
9006 shellexec(argv, path, cmdentry.u.index);
9007 /* NOTREACHED */
9008
9009 case CMDBUILTIN:
9010 cmdenviron = varlist.list;
9011 if (cmdenviron) {
9012 struct strlist *list = cmdenviron;
9013 int i = VNOSET;
9014 if (spclbltin > 0 || argc == 0) {
9015 i = 0;
9016 if (cmd_is_exec && argc > 1)
9017 i = VEXPORT;
9018 }
9019 listsetvar(list, i);
9020 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009021 /* Tight loop with builtins only:
9022 * "while kill -0 $child; do true; done"
9023 * will never exit even if $child died, unless we do this
9024 * to reap the zombie and make kill detect that it's gone: */
9025 dowait(DOWAIT_NONBLOCK, NULL);
9026
Eric Andersenc470f442003-07-28 09:56:35 +00009027 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9028 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009029 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009030 if (i == EXEXIT)
9031 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009032 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009033 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009034 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009035 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009036 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009037 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009038 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009039 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009040 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009041 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009042 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009043 }
9044 break;
9045
9046 case CMDFUNCTION:
9047 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009048 /* See above for the rationale */
9049 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009050 if (evalfun(cmdentry.u.func, argc, argv, flags))
9051 goto raise;
9052 break;
9053 }
9054
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009055 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009056 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009057 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009058 /* dsl: I think this is intended to be used to support
9059 * '_' in 'vi' command mode during line editing...
9060 * However I implemented that within libedit itself.
9061 */
9062 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009063 }
Eric Andersenc470f442003-07-28 09:56:35 +00009064 popstackmark(&smark);
9065}
9066
9067static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009068evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9069{
Eric Andersenc470f442003-07-28 09:56:35 +00009070 char *volatile savecmdname;
9071 struct jmploc *volatile savehandler;
9072 struct jmploc jmploc;
9073 int i;
9074
9075 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009076 i = setjmp(jmploc.loc);
9077 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009078 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009079 savehandler = exception_handler;
9080 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009081 commandname = argv[0];
9082 argptr = argv + 1;
9083 optptr = NULL; /* initialize nextopt */
9084 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009085 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009086 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009087 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009088 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009089 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009090// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009091 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009092
9093 return i;
9094}
9095
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009096static int
9097goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009098{
9099 return !*endofname(p);
9100}
9101
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009102
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009103/*
9104 * Search for a command. This is called before we fork so that the
9105 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009106 * the child. The check for "goodname" is an overly conservative
9107 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009108 */
Eric Andersenc470f442003-07-28 09:56:35 +00009109static void
9110prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009111{
9112 struct cmdentry entry;
9113
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009114 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9115 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009116}
9117
Eric Andersencb57d552001-06-28 07:25:16 +00009118
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009119/* ============ Builtin commands
9120 *
9121 * Builtin commands whose functions are closely tied to evaluation
9122 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009123 */
9124
9125/*
Eric Andersencb57d552001-06-28 07:25:16 +00009126 * Handle break and continue commands. Break, continue, and return are
9127 * all handled by setting the evalskip flag. The evaluation routines
9128 * above all check this flag, and if it is set they start skipping
9129 * commands rather than executing them. The variable skipcount is
9130 * the number of loops to break/continue, or the number of function
9131 * levels to return. (The latter is always 1.) It should probably
9132 * be an error to break out of more loops than exist, but it isn't
9133 * in the standard shell so we don't make it one here.
9134 */
Eric Andersenc470f442003-07-28 09:56:35 +00009135static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009136breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009137{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009138 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009139
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009140 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009141 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009142 if (n > loopnest)
9143 n = loopnest;
9144 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009145 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009146 skipcount = n;
9147 }
9148 return 0;
9149}
9150
Eric Andersenc470f442003-07-28 09:56:35 +00009151
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009152/* ============ input.c
9153 *
Eric Andersen90898442003-08-06 11:20:52 +00009154 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009155 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009156
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009157enum {
9158 INPUT_PUSH_FILE = 1,
9159 INPUT_NOFILE_OK = 2,
9160};
Eric Andersencb57d552001-06-28 07:25:16 +00009161
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009162static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009163/* values of checkkwd variable */
9164#define CHKALIAS 0x1
9165#define CHKKWD 0x2
9166#define CHKNL 0x4
9167
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009168/*
9169 * Push a string back onto the input at this current parsefile level.
9170 * We handle aliases this way.
9171 */
9172#if !ENABLE_ASH_ALIAS
9173#define pushstring(s, ap) pushstring(s)
9174#endif
9175static void
9176pushstring(char *s, struct alias *ap)
9177{
9178 struct strpush *sp;
9179 int len;
9180
9181 len = strlen(s);
9182 INT_OFF;
9183 if (g_parsefile->strpush) {
9184 sp = ckzalloc(sizeof(*sp));
9185 sp->prev = g_parsefile->strpush;
9186 } else {
9187 sp = &(g_parsefile->basestrpush);
9188 }
9189 g_parsefile->strpush = sp;
9190 sp->prev_string = g_parsefile->next_to_pgetc;
9191 sp->prev_left_in_line = g_parsefile->left_in_line;
9192#if ENABLE_ASH_ALIAS
9193 sp->ap = ap;
9194 if (ap) {
9195 ap->flag |= ALIASINUSE;
9196 sp->string = s;
9197 }
9198#endif
9199 g_parsefile->next_to_pgetc = s;
9200 g_parsefile->left_in_line = len;
9201 INT_ON;
9202}
9203
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009204static void
9205popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009206{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009207 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009208
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009209 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009210#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009211 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009212 if (g_parsefile->next_to_pgetc[-1] == ' '
9213 || g_parsefile->next_to_pgetc[-1] == '\t'
9214 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009216 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009217 if (sp->string != sp->ap->val) {
9218 free(sp->string);
9219 }
9220 sp->ap->flag &= ~ALIASINUSE;
9221 if (sp->ap->flag & ALIASDEAD) {
9222 unalias(sp->ap->name);
9223 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009224 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009225#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009226 g_parsefile->next_to_pgetc = sp->prev_string;
9227 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009228 g_parsefile->strpush = sp->prev;
9229 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009230 free(sp);
9231 INT_ON;
9232}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009233
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009234//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9235//it peeks whether it is &>, and then pushes back both chars.
9236//This function needs to save last *next_to_pgetc to buf[0]
9237//to make two pungetc() reliable. Currently,
9238// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009239static int
9240preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009241{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009242 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009243 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009244
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009245 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009246#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009247 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009248 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009249 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009250 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009251#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009252 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009253#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009254 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9255 if (nr == 0) {
9256 /* Ctrl+C pressed */
9257 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009258 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009259 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009260 raise(SIGINT);
9261 return 1;
9262 }
Eric Andersenc470f442003-07-28 09:56:35 +00009263 goto retry;
9264 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009265 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009266 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009267 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009268 }
Eric Andersencb57d552001-06-28 07:25:16 +00009269 }
9270#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009271 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009272#endif
9273
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009274#if 0
9275/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009276 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009277 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009278 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009279 if (flags >= 0 && (flags & O_NONBLOCK)) {
9280 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009281 if (fcntl(0, F_SETFL, flags) >= 0) {
9282 out2str("sh: turning off NDELAY mode\n");
9283 goto retry;
9284 }
9285 }
9286 }
9287 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009288#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009289 return nr;
9290}
9291
9292/*
9293 * Refill the input buffer and return the next input character:
9294 *
9295 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009296 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9297 * or we are reading from a string so we can't refill the buffer,
9298 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009299 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9300 * 4) Process input up to the next newline, deleting nul characters.
9301 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009302//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9303#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009304/*
9305 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9306 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9307 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9308 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9309 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009310static int
Eric Andersenc470f442003-07-28 09:56:35 +00009311preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009312{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009313 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009314 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009315
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009316 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009317#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009318 if (g_parsefile->left_in_line == -1
9319 && g_parsefile->strpush->ap
9320 && g_parsefile->next_to_pgetc[-1] != ' '
9321 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009322 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009323 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009324 return PEOA;
9325 }
Eric Andersen2870d962001-07-02 17:27:21 +00009326#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009327 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009328 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009329 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9330 g_parsefile->left_in_line,
9331 g_parsefile->next_to_pgetc,
9332 g_parsefile->next_to_pgetc);
9333 if (--g_parsefile->left_in_line >= 0)
9334 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009335 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009336 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009337 * "pgetc" needs refilling.
9338 */
9339
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009340 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009341 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009342 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009343 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009344 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009345 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009346 /* even in failure keep left_in_line and next_to_pgetc
9347 * in lock step, for correct multi-layer pungetc.
9348 * left_in_line was decremented before preadbuffer(),
9349 * must inc next_to_pgetc: */
9350 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009351 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009352 }
Eric Andersencb57d552001-06-28 07:25:16 +00009353
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009354 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009355 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009356 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009357 again:
9358 more = preadfd();
9359 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009360 /* don't try reading again */
9361 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009362 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009363 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009364 return PEOF;
9365 }
9366 }
9367
Denis Vlasenko727752d2008-11-28 03:41:47 +00009368 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009369 * Set g_parsefile->left_in_line
9370 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009371 * NUL chars are deleted.
9372 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009373 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009374 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009375 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009376
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009377 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009378
Denis Vlasenko727752d2008-11-28 03:41:47 +00009379 c = *q;
9380 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009381 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009382 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009383 q++;
9384 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009385 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009386 break;
9387 }
Eric Andersencb57d552001-06-28 07:25:16 +00009388 }
9389
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009390 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009391 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9392 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009393 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009394 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009395 }
9396 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009397 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009398
Eric Andersencb57d552001-06-28 07:25:16 +00009399 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009400 char save = *q;
9401 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009402 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009403 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009404 }
9405
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009406 pgetc_debug("preadbuffer at %d:%p'%s'",
9407 g_parsefile->left_in_line,
9408 g_parsefile->next_to_pgetc,
9409 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009410 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009411}
9412
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009413#define pgetc_as_macro() \
9414 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009415 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009416 : preadbuffer() \
9417 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009418
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009419static int
9420pgetc(void)
9421{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009422 pgetc_debug("pgetc_fast at %d:%p'%s'",
9423 g_parsefile->left_in_line,
9424 g_parsefile->next_to_pgetc,
9425 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009426 return pgetc_as_macro();
9427}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009428
9429#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009430#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009431#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009432#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009433#endif
9434
9435/*
9436 * Same as pgetc(), but ignores PEOA.
9437 */
9438#if ENABLE_ASH_ALIAS
9439static int
9440pgetc2(void)
9441{
9442 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009443 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009444 pgetc_debug("pgetc_fast at %d:%p'%s'",
9445 g_parsefile->left_in_line,
9446 g_parsefile->next_to_pgetc,
9447 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009448 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009449 } while (c == PEOA);
9450 return c;
9451}
9452#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009453#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009454#endif
9455
9456/*
9457 * Read a line from the script.
9458 */
9459static char *
9460pfgets(char *line, int len)
9461{
9462 char *p = line;
9463 int nleft = len;
9464 int c;
9465
9466 while (--nleft > 0) {
9467 c = pgetc2();
9468 if (c == PEOF) {
9469 if (p == line)
9470 return NULL;
9471 break;
9472 }
9473 *p++ = c;
9474 if (c == '\n')
9475 break;
9476 }
9477 *p = '\0';
9478 return line;
9479}
9480
Eric Andersenc470f442003-07-28 09:56:35 +00009481/*
9482 * Undo the last call to pgetc. Only one character may be pushed back.
9483 * PEOF may be pushed back.
9484 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009485static void
Eric Andersenc470f442003-07-28 09:56:35 +00009486pungetc(void)
9487{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009488 g_parsefile->left_in_line++;
9489 g_parsefile->next_to_pgetc--;
9490 pgetc_debug("pushed back to %d:%p'%s'",
9491 g_parsefile->left_in_line,
9492 g_parsefile->next_to_pgetc,
9493 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009494}
9495
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009496/*
9497 * To handle the "." command, a stack of input files is used. Pushfile
9498 * adds a new entry to the stack and popfile restores the previous level.
9499 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009500static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009501pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009502{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009503 struct parsefile *pf;
9504
Denis Vlasenko597906c2008-02-20 16:38:54 +00009505 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009506 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009507 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009508 /*pf->strpush = NULL; - ckzalloc did it */
9509 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009510 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009511}
9512
9513static void
9514popfile(void)
9515{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009516 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009517
Denis Vlasenkob012b102007-02-19 22:43:01 +00009518 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009519 if (pf->fd >= 0)
9520 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009521 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009522 while (pf->strpush)
9523 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009524 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009525 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009526 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009527}
9528
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009529/*
9530 * Return to top level.
9531 */
9532static void
9533popallfiles(void)
9534{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009535 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009536 popfile();
9537}
9538
9539/*
9540 * Close the file(s) that the shell is reading commands from. Called
9541 * after a fork is done.
9542 */
9543static void
9544closescript(void)
9545{
9546 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009547 if (g_parsefile->fd > 0) {
9548 close(g_parsefile->fd);
9549 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009550 }
9551}
9552
9553/*
9554 * Like setinputfile, but takes an open file descriptor. Call this with
9555 * interrupts off.
9556 */
9557static void
9558setinputfd(int fd, int push)
9559{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009560 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009561 if (push) {
9562 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009563 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009564 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009565 g_parsefile->fd = fd;
9566 if (g_parsefile->buf == NULL)
9567 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009568 g_parsefile->left_in_buffer = 0;
9569 g_parsefile->left_in_line = 0;
9570 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009571}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009572
Eric Andersenc470f442003-07-28 09:56:35 +00009573/*
9574 * Set the input to take input from a file. If push is set, push the
9575 * old input onto the stack first.
9576 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009577static int
9578setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009579{
9580 int fd;
9581 int fd2;
9582
Denis Vlasenkob012b102007-02-19 22:43:01 +00009583 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009584 fd = open(fname, O_RDONLY);
9585 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009586 if (flags & INPUT_NOFILE_OK)
9587 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009588 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009589 }
Eric Andersenc470f442003-07-28 09:56:35 +00009590 if (fd < 10) {
9591 fd2 = copyfd(fd, 10);
9592 close(fd);
9593 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009594 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009595 fd = fd2;
9596 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009597 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009598 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009599 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009600 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009601}
9602
Eric Andersencb57d552001-06-28 07:25:16 +00009603/*
9604 * Like setinputfile, but takes input from a string.
9605 */
Eric Andersenc470f442003-07-28 09:56:35 +00009606static void
9607setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009608{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009609 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009610 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009611 g_parsefile->next_to_pgetc = string;
9612 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009613 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009614 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009615 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009616}
9617
9618
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009619/* ============ mail.c
9620 *
9621 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009622 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009623
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009624#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009625
Eric Andersencb57d552001-06-28 07:25:16 +00009626#define MAXMBOXES 10
9627
Eric Andersenc470f442003-07-28 09:56:35 +00009628/* times of mailboxes */
9629static time_t mailtime[MAXMBOXES];
9630/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009631static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009632
Eric Andersencb57d552001-06-28 07:25:16 +00009633/*
Eric Andersenc470f442003-07-28 09:56:35 +00009634 * Print appropriate message(s) if mail has arrived.
9635 * If mail_var_path_changed is set,
9636 * then the value of MAIL has mail_var_path_changed,
9637 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009638 */
Eric Andersenc470f442003-07-28 09:56:35 +00009639static void
9640chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009641{
Eric Andersencb57d552001-06-28 07:25:16 +00009642 const char *mpath;
9643 char *p;
9644 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009645 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009646 struct stackmark smark;
9647 struct stat statb;
9648
Eric Andersencb57d552001-06-28 07:25:16 +00009649 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009650 mpath = mpathset() ? mpathval() : mailval();
9651 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009652 p = padvance(&mpath, nullstr);
9653 if (p == NULL)
9654 break;
9655 if (*p == '\0')
9656 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009657 for (q = p; *q; q++)
9658 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009659#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009660 if (q[-1] != '/')
9661 abort();
9662#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009663 q[-1] = '\0'; /* delete trailing '/' */
9664 if (stat(p, &statb) < 0) {
9665 *mtp = 0;
9666 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009667 }
Eric Andersenc470f442003-07-28 09:56:35 +00009668 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9669 fprintf(
9670 stderr, snlfmt,
9671 pathopt ? pathopt : "you have mail"
9672 );
9673 }
9674 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009675 }
Eric Andersenc470f442003-07-28 09:56:35 +00009676 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009677 popstackmark(&smark);
9678}
Eric Andersencb57d552001-06-28 07:25:16 +00009679
Eric Andersenc470f442003-07-28 09:56:35 +00009680static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009681changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009682{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009683 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009684}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009685
Denis Vlasenko131ae172007-02-18 13:00:19 +00009686#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009687
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009688
9689/* ============ ??? */
9690
Eric Andersencb57d552001-06-28 07:25:16 +00009691/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009692 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009693 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009694static void
9695setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009696{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009697 char **newparam;
9698 char **ap;
9699 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009700
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009701 for (nparam = 0; argv[nparam]; nparam++)
9702 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009703 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9704 while (*argv) {
9705 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009706 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009707 *ap = NULL;
9708 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009709 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009710 shellparam.nparam = nparam;
9711 shellparam.p = newparam;
9712#if ENABLE_ASH_GETOPTS
9713 shellparam.optind = 1;
9714 shellparam.optoff = -1;
9715#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009716}
9717
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009718/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009719 * Process shell options. The global variable argptr contains a pointer
9720 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009721 *
9722 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9723 * For a non-interactive shell, an error condition encountered
9724 * by a special built-in ... shall cause the shell to write a diagnostic message
9725 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009726 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009727 * ...
9728 * Utility syntax error (option or operand error) Shall exit
9729 * ...
9730 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9731 * we see that bash does not do that (set "finishes" with error code 1 instead,
9732 * and shell continues), and people rely on this behavior!
9733 * Testcase:
9734 * set -o barfoo 2>/dev/null
9735 * echo $?
9736 *
9737 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009738 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009739static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009740plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009741{
9742 int i;
9743
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009744 if (name) {
9745 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009746 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009747 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009748 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009749 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009750 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009751 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009752 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009753 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009754 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009755 if (val) {
9756 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9757 } else {
9758 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9759 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009760 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009761 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009762}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009763static void
9764setoption(int flag, int val)
9765{
9766 int i;
9767
9768 for (i = 0; i < NOPTS; i++) {
9769 if (optletters(i) == flag) {
9770 optlist[i] = val;
9771 return;
9772 }
9773 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009774 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009775 /* NOTREACHED */
9776}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009777static int
Eric Andersenc470f442003-07-28 09:56:35 +00009778options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009779{
9780 char *p;
9781 int val;
9782 int c;
9783
9784 if (cmdline)
9785 minusc = NULL;
9786 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009787 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009788 if (c != '-' && c != '+')
9789 break;
9790 argptr++;
9791 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009792 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009793 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009794 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009795 if (!cmdline) {
9796 /* "-" means turn off -x and -v */
9797 if (p[0] == '\0')
9798 xflag = vflag = 0;
9799 /* "--" means reset params */
9800 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009801 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009802 }
Eric Andersenc470f442003-07-28 09:56:35 +00009803 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009804 }
Eric Andersencb57d552001-06-28 07:25:16 +00009805 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009806 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009807 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009808 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009809 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009810 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009811 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009812 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009813 /* it already printed err message */
9814 return 1; /* error */
9815 }
Eric Andersencb57d552001-06-28 07:25:16 +00009816 if (*argptr)
9817 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009818 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9819 isloginsh = 1;
9820 /* bash does not accept +-login, we also won't */
9821 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009822 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009823 isloginsh = 1;
9824 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009825 } else {
9826 setoption(c, val);
9827 }
9828 }
9829 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009830 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009831}
9832
Eric Andersencb57d552001-06-28 07:25:16 +00009833/*
Eric Andersencb57d552001-06-28 07:25:16 +00009834 * The shift builtin command.
9835 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009836static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009837shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009838{
9839 int n;
9840 char **ap1, **ap2;
9841
9842 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009843 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009844 n = number(argv[1]);
9845 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009846 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009847 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009848 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009849 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009850 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009851 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009852 }
9853 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009854 while ((*ap2++ = *ap1++) != NULL)
9855 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009856#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009857 shellparam.optind = 1;
9858 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009859#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009860 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009861 return 0;
9862}
9863
Eric Andersencb57d552001-06-28 07:25:16 +00009864/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009865 * POSIX requires that 'set' (but not export or readonly) output the
9866 * variables in lexicographic order - by the locale's collating order (sigh).
9867 * Maybe we could keep them in an ordered balanced binary tree
9868 * instead of hashed lists.
9869 * For now just roll 'em through qsort for printing...
9870 */
9871static int
9872showvars(const char *sep_prefix, int on, int off)
9873{
9874 const char *sep;
9875 char **ep, **epend;
9876
9877 ep = listvars(on, off, &epend);
9878 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9879
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009880 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009881
9882 for (; ep < epend; ep++) {
9883 const char *p;
9884 const char *q;
9885
9886 p = strchrnul(*ep, '=');
9887 q = nullstr;
9888 if (*p)
9889 q = single_quote(++p);
9890 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9891 }
9892 return 0;
9893}
9894
9895/*
Eric Andersencb57d552001-06-28 07:25:16 +00009896 * The set command builtin.
9897 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009898static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009899setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009900{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009901 int retval;
9902
Denis Vlasenko68404f12008-03-17 09:00:54 +00009903 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009904 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009905 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009906 retval = 1;
9907 if (!options(0)) { /* if no parse error... */
9908 retval = 0;
9909 optschanged();
9910 if (*argptr != NULL) {
9911 setparam(argptr);
9912 }
Eric Andersencb57d552001-06-28 07:25:16 +00009913 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009914 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009915 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009916}
9917
Denis Vlasenko131ae172007-02-18 13:00:19 +00009918#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009919static void
9920change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009921{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009922 /* Galois LFSR parameter */
9923 /* Taps at 32 31 29 1: */
9924 enum { MASK = 0x8000000b };
9925 /* Another example - taps at 32 31 30 10: */
9926 /* MASK = 0x00400007 */
9927
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009928 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009929 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009930 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009931
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009932 /* LCG has period of 2^32 and alternating lowest bit */
9933 random_LCG = 1664525 * random_LCG + 1013904223;
9934 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9935 t = (random_galois_LFSR << 1);
9936 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9937 t ^= MASK;
9938 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009939 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009940 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009941 * for $RANDOM range. Combining with subtraction is
9942 * just for fun. + and ^ would work equally well. */
9943 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009944 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009945 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009946 vrandom.flags &= ~VNOFUNC;
9947 } else {
9948 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009949 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009950 }
Eric Andersenef02f822004-03-11 13:34:24 +00009951}
Eric Andersen16767e22004-03-16 05:14:10 +00009952#endif
9953
Denis Vlasenko131ae172007-02-18 13:00:19 +00009954#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009955static int
Eric Andersenc470f442003-07-28 09:56:35 +00009956getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009957{
9958 char *p, *q;
9959 char c = '?';
9960 int done = 0;
9961 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009962 char s[12];
9963 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009964
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009965 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009966 return 1;
9967 optnext = optfirst + *param_optind - 1;
9968
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009969 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009970 p = NULL;
9971 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009972 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 if (p == NULL || *p == '\0') {
9974 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009975 p = *optnext;
9976 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009977 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009978 p = NULL;
9979 done = 1;
9980 goto out;
9981 }
9982 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009983 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009984 goto atend;
9985 }
9986
9987 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009988 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009989 if (*q == '\0') {
9990 if (optstr[0] == ':') {
9991 s[0] = c;
9992 s[1] = '\0';
9993 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009994 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009995 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009996 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009997 }
9998 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009999 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010000 }
10001 if (*++q == ':')
10002 q++;
10003 }
10004
10005 if (*++q == ':') {
10006 if (*p == '\0' && (p = *optnext) == NULL) {
10007 if (optstr[0] == ':') {
10008 s[0] = c;
10009 s[1] = '\0';
10010 err |= setvarsafe("OPTARG", s, 0);
10011 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010012 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010013 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010014 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010015 c = '?';
10016 }
Eric Andersenc470f442003-07-28 09:56:35 +000010017 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010018 }
10019
10020 if (p == *optnext)
10021 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010022 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010023 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010024 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010025 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010026 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010027 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010028 *param_optind = optnext - optfirst + 1;
10029 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010030 err |= setvarsafe("OPTIND", s, VNOFUNC);
10031 s[0] = c;
10032 s[1] = '\0';
10033 err |= setvarsafe(optvar, s, 0);
10034 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010035 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010036 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010037 flush_stdout_stderr();
10038 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010039 }
10040 return done;
10041}
Eric Andersenc470f442003-07-28 09:56:35 +000010042
10043/*
10044 * The getopts builtin. Shellparam.optnext points to the next argument
10045 * to be processed. Shellparam.optptr points to the next character to
10046 * be processed in the current argument. If shellparam.optnext is NULL,
10047 * then it's the first time getopts has been called.
10048 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010049static int
Eric Andersenc470f442003-07-28 09:56:35 +000010050getoptscmd(int argc, char **argv)
10051{
10052 char **optbase;
10053
10054 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010055 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010056 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010057 optbase = shellparam.p;
10058 if (shellparam.optind > shellparam.nparam + 1) {
10059 shellparam.optind = 1;
10060 shellparam.optoff = -1;
10061 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010062 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010063 optbase = &argv[3];
10064 if (shellparam.optind > argc - 2) {
10065 shellparam.optind = 1;
10066 shellparam.optoff = -1;
10067 }
10068 }
10069
10070 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010071 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010072}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010073#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010074
Eric Andersencb57d552001-06-28 07:25:16 +000010075
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010076/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010077
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010078struct heredoc {
10079 struct heredoc *next; /* next here document in list */
10080 union node *here; /* redirection node */
10081 char *eofmark; /* string indicating end of input */
10082 smallint striptabs; /* if set, strip leading tabs */
10083};
10084
10085static smallint tokpushback; /* last token pushed back */
10086static smallint parsebackquote; /* nonzero if we are inside backquotes */
10087static smallint quoteflag; /* set if (part of) last token was quoted */
10088static token_id_t lasttoken; /* last token read (integer id Txxx) */
10089static struct heredoc *heredoclist; /* list of here documents to read */
10090static char *wordtext; /* text of last word returned by readtoken */
10091static struct nodelist *backquotelist;
10092static union node *redirnode;
10093static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010094/*
10095 * NEOF is returned by parsecmd when it encounters an end of file. It
10096 * must be distinct from NULL, so we use the address of a variable that
10097 * happens to be handy.
10098 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010099#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010100
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010101static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010102static void
10103raise_error_syntax(const char *msg)
10104{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010105 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010106 /* NOTREACHED */
10107}
10108
10109/*
10110 * Called when an unexpected token is read during the parse. The argument
10111 * is the token that is expected, or -1 if more than one type of token can
10112 * occur at this point.
10113 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010114static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010115static void
10116raise_error_unexpected_syntax(int token)
10117{
10118 char msg[64];
10119 int l;
10120
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010121 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010122 if (token >= 0)
10123 sprintf(msg + l, " (expecting %s)", tokname(token));
10124 raise_error_syntax(msg);
10125 /* NOTREACHED */
10126}
Eric Andersencb57d552001-06-28 07:25:16 +000010127
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010128#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010129
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010130/* parsing is heavily cross-recursive, need these forward decls */
10131static union node *andor(void);
10132static union node *pipeline(void);
10133static union node *parse_command(void);
10134static void parseheredoc(void);
10135static char peektoken(void);
10136static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010137
Eric Andersenc470f442003-07-28 09:56:35 +000010138static union node *
10139list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010140{
10141 union node *n1, *n2, *n3;
10142 int tok;
10143
Eric Andersenc470f442003-07-28 09:56:35 +000010144 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10145 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010146 return NULL;
10147 n1 = NULL;
10148 for (;;) {
10149 n2 = andor();
10150 tok = readtoken();
10151 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010152 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010153 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010154 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010155 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010156 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010157 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010158 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010159 n2 = n3;
10160 }
10161 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010162 }
10163 }
10164 if (n1 == NULL) {
10165 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010166 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010167 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010168 n3->type = NSEMI;
10169 n3->nbinary.ch1 = n1;
10170 n3->nbinary.ch2 = n2;
10171 n1 = n3;
10172 }
10173 switch (tok) {
10174 case TBACKGND:
10175 case TSEMI:
10176 tok = readtoken();
10177 /* fall through */
10178 case TNL:
10179 if (tok == TNL) {
10180 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010181 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010182 return n1;
10183 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010184 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010185 }
Eric Andersenc470f442003-07-28 09:56:35 +000010186 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010187 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010188 return n1;
10189 break;
10190 case TEOF:
10191 if (heredoclist)
10192 parseheredoc();
10193 else
Eric Andersenc470f442003-07-28 09:56:35 +000010194 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010195 return n1;
10196 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010197 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010198 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010199 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010200 return n1;
10201 }
10202 }
10203}
10204
Eric Andersenc470f442003-07-28 09:56:35 +000010205static union node *
10206andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010207{
Eric Andersencb57d552001-06-28 07:25:16 +000010208 union node *n1, *n2, *n3;
10209 int t;
10210
Eric Andersencb57d552001-06-28 07:25:16 +000010211 n1 = pipeline();
10212 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010213 t = readtoken();
10214 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010215 t = NAND;
10216 } else if (t == TOR) {
10217 t = NOR;
10218 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010219 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010220 return n1;
10221 }
Eric Andersenc470f442003-07-28 09:56:35 +000010222 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010223 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010224 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010225 n3->type = t;
10226 n3->nbinary.ch1 = n1;
10227 n3->nbinary.ch2 = n2;
10228 n1 = n3;
10229 }
10230}
10231
Eric Andersenc470f442003-07-28 09:56:35 +000010232static union node *
10233pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010234{
Eric Andersencb57d552001-06-28 07:25:16 +000010235 union node *n1, *n2, *pipenode;
10236 struct nodelist *lp, *prev;
10237 int negate;
10238
10239 negate = 0;
10240 TRACE(("pipeline: entered\n"));
10241 if (readtoken() == TNOT) {
10242 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010243 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010244 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010245 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010247 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010248 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010249 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010250 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010251 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010252 pipenode->npipe.cmdlist = lp;
10253 lp->n = n1;
10254 do {
10255 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010256 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010257 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010258 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010259 prev->next = lp;
10260 } while (readtoken() == TPIPE);
10261 lp->next = NULL;
10262 n1 = pipenode;
10263 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010264 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010265 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010266 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010267 n2->type = NNOT;
10268 n2->nnot.com = n1;
10269 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010270 }
10271 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010272}
10273
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010274static union node *
10275makename(void)
10276{
10277 union node *n;
10278
Denis Vlasenko597906c2008-02-20 16:38:54 +000010279 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010280 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010281 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010282 n->narg.text = wordtext;
10283 n->narg.backquote = backquotelist;
10284 return n;
10285}
10286
10287static void
10288fixredir(union node *n, const char *text, int err)
10289{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010290 int fd;
10291
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010292 TRACE(("Fix redir %s %d\n", text, err));
10293 if (!err)
10294 n->ndup.vname = NULL;
10295
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010296 fd = bb_strtou(text, NULL, 10);
10297 if (!errno && fd >= 0)
10298 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010299 else if (LONE_DASH(text))
10300 n->ndup.dupfd = -1;
10301 else {
10302 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010303 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010304 n->ndup.vname = makename();
10305 }
10306}
10307
10308/*
10309 * Returns true if the text contains nothing to expand (no dollar signs
10310 * or backquotes).
10311 */
10312static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010313noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010314{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010315 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010316 char c;
10317
10318 p = text;
10319 while ((c = *p++) != '\0') {
10320 if (c == CTLQUOTEMARK)
10321 continue;
10322 if (c == CTLESC)
10323 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010324 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010325 return 0;
10326 }
10327 return 1;
10328}
10329
10330static void
10331parsefname(void)
10332{
10333 union node *n = redirnode;
10334
10335 if (readtoken() != TWORD)
10336 raise_error_unexpected_syntax(-1);
10337 if (n->type == NHERE) {
10338 struct heredoc *here = heredoc;
10339 struct heredoc *p;
10340 int i;
10341
10342 if (quoteflag == 0)
10343 n->type = NXHERE;
10344 TRACE(("Here document %d\n", n->type));
10345 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010346 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010347 rmescapes(wordtext);
10348 here->eofmark = wordtext;
10349 here->next = NULL;
10350 if (heredoclist == NULL)
10351 heredoclist = here;
10352 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010353 for (p = heredoclist; p->next; p = p->next)
10354 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010355 p->next = here;
10356 }
10357 } else if (n->type == NTOFD || n->type == NFROMFD) {
10358 fixredir(n, wordtext, 0);
10359 } else {
10360 n->nfile.fname = makename();
10361 }
10362}
Eric Andersencb57d552001-06-28 07:25:16 +000010363
Eric Andersenc470f442003-07-28 09:56:35 +000010364static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010365simplecmd(void)
10366{
10367 union node *args, **app;
10368 union node *n = NULL;
10369 union node *vars, **vpp;
10370 union node **rpp, *redir;
10371 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010372#if ENABLE_ASH_BASH_COMPAT
10373 smallint double_brackets_flag = 0;
10374#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010375
10376 args = NULL;
10377 app = &args;
10378 vars = NULL;
10379 vpp = &vars;
10380 redir = NULL;
10381 rpp = &redir;
10382
10383 savecheckkwd = CHKALIAS;
10384 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010385 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010386 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010387 t = readtoken();
10388 switch (t) {
10389#if ENABLE_ASH_BASH_COMPAT
10390 case TAND: /* "&&" */
10391 case TOR: /* "||" */
10392 if (!double_brackets_flag) {
10393 tokpushback = 1;
10394 goto out;
10395 }
10396 wordtext = (char *) (t == TAND ? "-a" : "-o");
10397#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010398 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010399 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010400 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010401 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010402 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010403#if ENABLE_ASH_BASH_COMPAT
10404 if (strcmp("[[", wordtext) == 0)
10405 double_brackets_flag = 1;
10406 else if (strcmp("]]", wordtext) == 0)
10407 double_brackets_flag = 0;
10408#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010409 n->narg.backquote = backquotelist;
10410 if (savecheckkwd && isassignment(wordtext)) {
10411 *vpp = n;
10412 vpp = &n->narg.next;
10413 } else {
10414 *app = n;
10415 app = &n->narg.next;
10416 savecheckkwd = 0;
10417 }
10418 break;
10419 case TREDIR:
10420 *rpp = n = redirnode;
10421 rpp = &n->nfile.next;
10422 parsefname(); /* read name of redirection file */
10423 break;
10424 case TLP:
10425 if (args && app == &args->narg.next
10426 && !vars && !redir
10427 ) {
10428 struct builtincmd *bcmd;
10429 const char *name;
10430
10431 /* We have a function */
10432 if (readtoken() != TRP)
10433 raise_error_unexpected_syntax(TRP);
10434 name = n->narg.text;
10435 if (!goodname(name)
10436 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10437 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010438 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010439 }
10440 n->type = NDEFUN;
10441 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10442 n->narg.next = parse_command();
10443 return n;
10444 }
10445 /* fall through */
10446 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010447 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010448 goto out;
10449 }
10450 }
10451 out:
10452 *app = NULL;
10453 *vpp = NULL;
10454 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010455 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010456 n->type = NCMD;
10457 n->ncmd.args = args;
10458 n->ncmd.assign = vars;
10459 n->ncmd.redirect = redir;
10460 return n;
10461}
10462
10463static union node *
10464parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010465{
Eric Andersencb57d552001-06-28 07:25:16 +000010466 union node *n1, *n2;
10467 union node *ap, **app;
10468 union node *cp, **cpp;
10469 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010470 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010471 int t;
10472
10473 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010474 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010475
Eric Andersencb57d552001-06-28 07:25:16 +000010476 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010477 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010478 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010479 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010480 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010481 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010482 n1->type = NIF;
10483 n1->nif.test = list(0);
10484 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010485 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010486 n1->nif.ifpart = list(0);
10487 n2 = n1;
10488 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010489 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010490 n2 = n2->nif.elsepart;
10491 n2->type = NIF;
10492 n2->nif.test = list(0);
10493 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010494 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010495 n2->nif.ifpart = list(0);
10496 }
10497 if (lasttoken == TELSE)
10498 n2->nif.elsepart = list(0);
10499 else {
10500 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010501 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 }
Eric Andersenc470f442003-07-28 09:56:35 +000010503 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010504 break;
10505 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010506 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010507 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010508 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010509 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010510 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010511 got = readtoken();
10512 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010513 TRACE(("expecting DO got %s %s\n", tokname(got),
10514 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010515 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010516 }
10517 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010518 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010519 break;
10520 }
10521 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010522 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010523 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010524 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010525 n1->type = NFOR;
10526 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010527 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010528 if (readtoken() == TIN) {
10529 app = &ap;
10530 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010531 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010532 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010533 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010534 n2->narg.text = wordtext;
10535 n2->narg.backquote = backquotelist;
10536 *app = n2;
10537 app = &n2->narg.next;
10538 }
10539 *app = NULL;
10540 n1->nfor.args = ap;
10541 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010542 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010543 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010544 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010545 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010546 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010547 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010548 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010549 n1->nfor.args = n2;
10550 /*
10551 * Newline or semicolon here is optional (but note
10552 * that the original Bourne shell only allowed NL).
10553 */
10554 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010555 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010556 }
Eric Andersenc470f442003-07-28 09:56:35 +000010557 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010558 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010559 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010560 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010561 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010562 break;
10563 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010564 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010565 n1->type = NCASE;
10566 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010567 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010568 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010569 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010570 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010571 n2->narg.text = wordtext;
10572 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010573 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010574 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010575 } while (readtoken() == TNL);
10576 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010577 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010578 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010579 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010580 checkkwd = CHKNL | CHKKWD;
10581 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010582 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010583 if (lasttoken == TLP)
10584 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010585 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010586 cp->type = NCLIST;
10587 app = &cp->nclist.pattern;
10588 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010589 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010590 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010591 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010592 ap->narg.text = wordtext;
10593 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010594 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010595 break;
10596 app = &ap->narg.next;
10597 readtoken();
10598 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010599 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010600 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010601 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010602 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010603
Eric Andersenc470f442003-07-28 09:56:35 +000010604 cpp = &cp->nclist.next;
10605
10606 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010607 t = readtoken();
10608 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010609 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010610 raise_error_unexpected_syntax(TENDCASE);
10611 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010612 }
Eric Andersenc470f442003-07-28 09:56:35 +000010613 }
Eric Andersencb57d552001-06-28 07:25:16 +000010614 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010615 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010616 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010617 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010618 n1->type = NSUBSHELL;
10619 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010620 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010621 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010622 break;
10623 case TBEGIN:
10624 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010625 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010626 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010627 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010628 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010629 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010630 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010631 }
10632
Eric Andersenc470f442003-07-28 09:56:35 +000010633 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010634 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010635
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010636 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010637 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010638 checkkwd = CHKKWD | CHKALIAS;
10639 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010640 while (readtoken() == TREDIR) {
10641 *rpp = n2 = redirnode;
10642 rpp = &n2->nfile.next;
10643 parsefname();
10644 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010645 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010646 *rpp = NULL;
10647 if (redir) {
10648 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010649 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010650 n2->type = NREDIR;
10651 n2->nredir.n = n1;
10652 n1 = n2;
10653 }
10654 n1->nredir.redirect = redir;
10655 }
Eric Andersencb57d552001-06-28 07:25:16 +000010656 return n1;
10657}
10658
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010659#if ENABLE_ASH_BASH_COMPAT
10660static int decode_dollar_squote(void)
10661{
10662 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10663 int c, cnt;
10664 char *p;
10665 char buf[4];
10666
10667 c = pgetc();
10668 p = strchr(C_escapes, c);
10669 if (p) {
10670 buf[0] = c;
10671 p = buf;
10672 cnt = 3;
10673 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10674 do {
10675 c = pgetc();
10676 *++p = c;
10677 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10678 pungetc();
10679 } else if (c == 'x') { /* \xHH */
10680 do {
10681 c = pgetc();
10682 *++p = c;
10683 } while (isxdigit(c) && --cnt);
10684 pungetc();
10685 if (cnt == 3) { /* \x but next char is "bad" */
10686 c = 'x';
10687 goto unrecognized;
10688 }
10689 } else { /* simple seq like \\ or \t */
10690 p++;
10691 }
10692 *p = '\0';
10693 p = buf;
10694 c = bb_process_escape_sequence((void*)&p);
10695 } else { /* unrecognized "\z": print both chars unless ' or " */
10696 if (c != '\'' && c != '"') {
10697 unrecognized:
10698 c |= 0x100; /* "please encode \, then me" */
10699 }
10700 }
10701 return c;
10702}
10703#endif
10704
Eric Andersencb57d552001-06-28 07:25:16 +000010705/*
10706 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10707 * is not NULL, read a here document. In the latter case, eofmark is the
10708 * word which marks the end of the document and striptabs is true if
10709 * leading tabs should be stripped from the document. The argument firstc
10710 * is the first character of the input token or document.
10711 *
10712 * Because C does not have internal subroutines, I have simulated them
10713 * using goto's to implement the subroutine linkage. The following macros
10714 * will run code that appears at the end of readtoken1.
10715 */
Eric Andersen2870d962001-07-02 17:27:21 +000010716#define CHECKEND() {goto checkend; checkend_return:;}
10717#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10718#define PARSESUB() {goto parsesub; parsesub_return:;}
10719#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10720#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10721#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010722static int
Eric Andersenc470f442003-07-28 09:56:35 +000010723readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010724{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010725 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010726 int c = firstc;
10727 char *out;
10728 int len;
10729 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010730 struct nodelist *bqlist;
10731 smallint quotef;
10732 smallint dblquote;
10733 smallint oldstyle;
10734 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010735#if ENABLE_ASH_EXPAND_PRMT
10736 smallint pssyntax; /* we are expanding a prompt string */
10737#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010738 int varnest; /* levels of variables expansion */
10739 int arinest; /* levels of arithmetic expansion */
10740 int parenlevel; /* levels of parens in arithmetic */
10741 int dqvarnest; /* levels of variables expansion within double quotes */
10742
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010743 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10744
Eric Andersencb57d552001-06-28 07:25:16 +000010745#if __GNUC__
10746 /* Avoid longjmp clobbering */
10747 (void) &out;
10748 (void) &quotef;
10749 (void) &dblquote;
10750 (void) &varnest;
10751 (void) &arinest;
10752 (void) &parenlevel;
10753 (void) &dqvarnest;
10754 (void) &oldstyle;
10755 (void) &prevsyntax;
10756 (void) &syntax;
10757#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010758 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010759 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010760 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010761 oldstyle = 0;
10762 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010763#if ENABLE_ASH_EXPAND_PRMT
10764 pssyntax = (syntax == PSSYNTAX);
10765 if (pssyntax)
10766 syntax = DQSYNTAX;
10767#endif
10768 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010769 varnest = 0;
10770 arinest = 0;
10771 parenlevel = 0;
10772 dqvarnest = 0;
10773
10774 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010775 loop:
10776 /* For each line, until end of word */
10777 {
Eric Andersenc470f442003-07-28 09:56:35 +000010778 CHECKEND(); /* set c to PEOF if at end of here document */
10779 for (;;) { /* until end of line or end of word */
10780 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010781 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010782 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010783 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010784 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010785 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010786 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010787 if (doprompt)
10788 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010789 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010790 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010791 case CWORD:
10792 USTPUTC(c, out);
10793 break;
10794 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010795 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010796 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010797#if ENABLE_ASH_BASH_COMPAT
10798 if (c == '\\' && bash_dollar_squote) {
10799 c = decode_dollar_squote();
10800 if (c & 0x100) {
10801 USTPUTC('\\', out);
10802 c = (unsigned char)c;
10803 }
10804 }
10805#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010806 USTPUTC(c, out);
10807 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010808 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010809 c = pgetc2();
10810 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010811 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010812 USTPUTC('\\', out);
10813 pungetc();
10814 } else if (c == '\n') {
10815 if (doprompt)
10816 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010818#if ENABLE_ASH_EXPAND_PRMT
10819 if (c == '$' && pssyntax) {
10820 USTPUTC(CTLESC, out);
10821 USTPUTC('\\', out);
10822 }
10823#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010824 if (dblquote && c != '\\'
10825 && c != '`' && c != '$'
10826 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010827 ) {
10828 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010829 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010830 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010831 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010832 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010833 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010834 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010835 }
10836 break;
10837 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010838 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010839 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010840 if (eofmark == NULL) {
10841 USTPUTC(CTLQUOTEMARK, out);
10842 }
Eric Andersencb57d552001-06-28 07:25:16 +000010843 break;
10844 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010845 syntax = DQSYNTAX;
10846 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010847 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010848 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010849 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010850 if (eofmark != NULL && arinest == 0
10851 && varnest == 0
10852 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010853 USTPUTC(c, out);
10854 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010855 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010856 syntax = BASESYNTAX;
10857 dblquote = 0;
10858 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010859 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010860 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010861 }
10862 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010863 case CVAR: /* '$' */
10864 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010865 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010866 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010867 if (varnest > 0) {
10868 varnest--;
10869 if (dqvarnest > 0) {
10870 dqvarnest--;
10871 }
10872 USTPUTC(CTLENDVAR, out);
10873 } else {
10874 USTPUTC(c, out);
10875 }
10876 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010877#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010878 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010879 parenlevel++;
10880 USTPUTC(c, out);
10881 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010882 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010883 if (parenlevel > 0) {
10884 USTPUTC(c, out);
10885 --parenlevel;
10886 } else {
10887 if (pgetc() == ')') {
10888 if (--arinest == 0) {
10889 USTPUTC(CTLENDARI, out);
10890 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010891 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010892 } else
10893 USTPUTC(')', out);
10894 } else {
10895 /*
10896 * unbalanced parens
10897 * (don't 2nd guess - no error)
10898 */
10899 pungetc();
10900 USTPUTC(')', out);
10901 }
10902 }
10903 break;
10904#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010905 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010906 PARSEBACKQOLD();
10907 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010908 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010909 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010910 case CIGN:
10911 break;
10912 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010913 if (varnest == 0) {
10914#if ENABLE_ASH_BASH_COMPAT
10915 if (c == '&') {
10916 if (pgetc() == '>')
10917 c = 0x100 + '>'; /* flag &> */
10918 pungetc();
10919 }
10920#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010921 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010922 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010923#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010924 if (c != PEOA)
10925#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010926 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010927
Eric Andersencb57d552001-06-28 07:25:16 +000010928 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010929 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010930 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010931 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010932 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010933#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010934 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010935 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010936#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010937 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010938 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010939 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010940 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010941 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010942 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010943 }
10944 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010945 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010946 out = stackblock();
10947 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010948 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10949 && quotef == 0
10950 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010951 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010952 PARSEREDIR(); /* passed as params: out, c */
10953 lasttoken = TREDIR;
10954 return lasttoken;
10955 }
10956 /* else: non-number X seen, interpret it
10957 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010958 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010959 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010960 }
10961 quoteflag = quotef;
10962 backquotelist = bqlist;
10963 grabstackblock(len);
10964 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010965 lasttoken = TWORD;
10966 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010967/* end of readtoken routine */
10968
Eric Andersencb57d552001-06-28 07:25:16 +000010969/*
10970 * Check to see whether we are at the end of the here document. When this
10971 * is called, c is set to the first character of the next input line. If
10972 * we are at the end of the here document, this routine sets the c to PEOF.
10973 */
Eric Andersenc470f442003-07-28 09:56:35 +000010974checkend: {
10975 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010976#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010977 if (c == PEOA) {
10978 c = pgetc2();
10979 }
10980#endif
10981 if (striptabs) {
10982 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010983 c = pgetc2();
10984 }
Eric Andersenc470f442003-07-28 09:56:35 +000010985 }
10986 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010987 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010988 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010989
Eric Andersenc470f442003-07-28 09:56:35 +000010990 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010991 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10992 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010993 if (*p == '\n' && *q == '\0') {
10994 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010995 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000010996 needprompt = doprompt;
10997 } else {
10998 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010999 }
11000 }
11001 }
11002 }
Eric Andersenc470f442003-07-28 09:56:35 +000011003 goto checkend_return;
11004}
Eric Andersencb57d552001-06-28 07:25:16 +000011005
Eric Andersencb57d552001-06-28 07:25:16 +000011006/*
11007 * Parse a redirection operator. The variable "out" points to a string
11008 * specifying the fd to be redirected. The variable "c" contains the
11009 * first character of the redirection operator.
11010 */
Eric Andersenc470f442003-07-28 09:56:35 +000011011parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011012 /* out is already checked to be a valid number or "" */
11013 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011014 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011015
Denis Vlasenko597906c2008-02-20 16:38:54 +000011016 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011017 if (c == '>') {
11018 np->nfile.fd = 1;
11019 c = pgetc();
11020 if (c == '>')
11021 np->type = NAPPEND;
11022 else if (c == '|')
11023 np->type = NCLOBBER;
11024 else if (c == '&')
11025 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011026 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011027 else {
11028 np->type = NTO;
11029 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011030 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011031 }
11032#if ENABLE_ASH_BASH_COMPAT
11033 else if (c == 0x100 + '>') { /* this flags &> redirection */
11034 np->nfile.fd = 1;
11035 pgetc(); /* this is '>', no need to check */
11036 np->type = NTO2;
11037 }
11038#endif
11039 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011040 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011041 c = pgetc();
11042 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011043 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011044 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011045 np = stzalloc(sizeof(struct nhere));
11046 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011047 }
11048 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011049 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011050 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011051 c = pgetc();
11052 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011053 heredoc->striptabs = 1;
11054 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011055 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011056 pungetc();
11057 }
11058 break;
11059
11060 case '&':
11061 np->type = NFROMFD;
11062 break;
11063
11064 case '>':
11065 np->type = NFROMTO;
11066 break;
11067
11068 default:
11069 np->type = NFROM;
11070 pungetc();
11071 break;
11072 }
Eric Andersencb57d552001-06-28 07:25:16 +000011073 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011074 if (fd >= 0)
11075 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011076 redirnode = np;
11077 goto parseredir_return;
11078}
Eric Andersencb57d552001-06-28 07:25:16 +000011079
Eric Andersencb57d552001-06-28 07:25:16 +000011080/*
11081 * Parse a substitution. At this point, we have read the dollar sign
11082 * and nothing else.
11083 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011084
11085/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11086 * (assuming ascii char codes, as the original implementation did) */
11087#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011088 (((unsigned)(c) - 33 < 32) \
11089 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011090parsesub: {
11091 int subtype;
11092 int typeloc;
11093 int flags;
11094 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011095 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011096
Eric Andersenc470f442003-07-28 09:56:35 +000011097 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011098 if (c <= PEOA_OR_PEOF
11099 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011100 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011101#if ENABLE_ASH_BASH_COMPAT
11102 if (c == '\'')
11103 bash_dollar_squote = 1;
11104 else
11105#endif
11106 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011107 pungetc();
11108 } else if (c == '(') { /* $(command) or $((arith)) */
11109 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011110#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011111 PARSEARITH();
11112#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011113 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011114#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011115 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011116 pungetc();
11117 PARSEBACKQNEW();
11118 }
11119 } else {
11120 USTPUTC(CTLVAR, out);
11121 typeloc = out - (char *)stackblock();
11122 USTPUTC(VSNORMAL, out);
11123 subtype = VSNORMAL;
11124 if (c == '{') {
11125 c = pgetc();
11126 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011127 c = pgetc();
11128 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011129 c = '#';
11130 else
11131 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011132 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011133 subtype = 0;
11134 }
11135 if (c > PEOA_OR_PEOF && is_name(c)) {
11136 do {
11137 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011138 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011139 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011140 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011141 do {
11142 STPUTC(c, out);
11143 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011144 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011145 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011146 USTPUTC(c, out);
11147 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011148 } else {
11149 badsub:
11150 raise_error_syntax("bad substitution");
11151 }
Eric Andersencb57d552001-06-28 07:25:16 +000011152
Eric Andersenc470f442003-07-28 09:56:35 +000011153 STPUTC('=', out);
11154 flags = 0;
11155 if (subtype == 0) {
11156 switch (c) {
11157 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011158 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011159#if ENABLE_ASH_BASH_COMPAT
11160 if (c == ':' || c == '$' || isdigit(c)) {
11161 pungetc();
11162 subtype = VSSUBSTR;
11163 break;
11164 }
11165#endif
11166 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011167 /*FALLTHROUGH*/
11168 default:
11169 p = strchr(types, c);
11170 if (p == NULL)
11171 goto badsub;
11172 subtype = p - types + VSNORMAL;
11173 break;
11174 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011175 case '#': {
11176 int cc = c;
11177 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11178 c = pgetc();
11179 if (c == cc)
11180 subtype++;
11181 else
11182 pungetc();
11183 break;
11184 }
11185#if ENABLE_ASH_BASH_COMPAT
11186 case '/':
11187 subtype = VSREPLACE;
11188 c = pgetc();
11189 if (c == '/')
11190 subtype++; /* VSREPLACEALL */
11191 else
11192 pungetc();
11193 break;
11194#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011195 }
Eric Andersenc470f442003-07-28 09:56:35 +000011196 } else {
11197 pungetc();
11198 }
11199 if (dblquote || arinest)
11200 flags |= VSQUOTE;
11201 *((char *)stackblock() + typeloc) = subtype | flags;
11202 if (subtype != VSNORMAL) {
11203 varnest++;
11204 if (dblquote || arinest) {
11205 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011206 }
11207 }
11208 }
Eric Andersenc470f442003-07-28 09:56:35 +000011209 goto parsesub_return;
11210}
Eric Andersencb57d552001-06-28 07:25:16 +000011211
Eric Andersencb57d552001-06-28 07:25:16 +000011212/*
11213 * Called to parse command substitutions. Newstyle is set if the command
11214 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11215 * list of commands (passed by reference), and savelen is the number of
11216 * characters on the top of the stack which must be preserved.
11217 */
Eric Andersenc470f442003-07-28 09:56:35 +000011218parsebackq: {
11219 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011220 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011221 union node *n;
11222 char *volatile str;
11223 struct jmploc jmploc;
11224 struct jmploc *volatile savehandler;
11225 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011226 smallint saveprompt = 0;
11227
Eric Andersencb57d552001-06-28 07:25:16 +000011228#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011229 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011230#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011231 savepbq = parsebackquote;
11232 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011233 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011234 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011235 exception_handler = savehandler;
11236 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011237 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011238 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011239 str = NULL;
11240 savelen = out - (char *)stackblock();
11241 if (savelen > 0) {
11242 str = ckmalloc(savelen);
11243 memcpy(str, stackblock(), savelen);
11244 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011245 savehandler = exception_handler;
11246 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011247 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011248 if (oldstyle) {
11249 /* We must read until the closing backquote, giving special
11250 treatment to some slashes, and then push the string and
11251 reread it as input, interpreting it normally. */
11252 char *pout;
11253 int pc;
11254 size_t psavelen;
11255 char *pstr;
11256
11257
11258 STARTSTACKSTR(pout);
11259 for (;;) {
11260 if (needprompt) {
11261 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011262 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011263 pc = pgetc();
11264 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011265 case '`':
11266 goto done;
11267
11268 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011269 pc = pgetc();
11270 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011271 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011272 if (doprompt)
11273 setprompt(2);
11274 /*
11275 * If eating a newline, avoid putting
11276 * the newline into the new character
11277 * stream (via the STPUTC after the
11278 * switch).
11279 */
11280 continue;
11281 }
11282 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011283 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011284 STPUTC('\\', pout);
11285 if (pc > PEOA_OR_PEOF) {
11286 break;
11287 }
11288 /* fall through */
11289
11290 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011291#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011292 case PEOA:
11293#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011294 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011295 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011296
11297 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011298 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011299 needprompt = doprompt;
11300 break;
11301
11302 default:
11303 break;
11304 }
11305 STPUTC(pc, pout);
11306 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011307 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011308 STPUTC('\0', pout);
11309 psavelen = pout - (char *)stackblock();
11310 if (psavelen > 0) {
11311 pstr = grabstackstr(pout);
11312 setinputstring(pstr);
11313 }
11314 }
11315 nlpp = &bqlist;
11316 while (*nlpp)
11317 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011318 *nlpp = stzalloc(sizeof(**nlpp));
11319 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011320 parsebackquote = oldstyle;
11321
11322 if (oldstyle) {
11323 saveprompt = doprompt;
11324 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011325 }
11326
Eric Andersenc470f442003-07-28 09:56:35 +000011327 n = list(2);
11328
11329 if (oldstyle)
11330 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011331 else if (readtoken() != TRP)
11332 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011333
11334 (*nlpp)->n = n;
11335 if (oldstyle) {
11336 /*
11337 * Start reading from old file again, ignoring any pushed back
11338 * tokens left from the backquote parsing
11339 */
11340 popfile();
11341 tokpushback = 0;
11342 }
11343 while (stackblocksize() <= savelen)
11344 growstackblock();
11345 STARTSTACKSTR(out);
11346 if (str) {
11347 memcpy(out, str, savelen);
11348 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011349 INT_OFF;
11350 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011351 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011352 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011353 }
11354 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011355 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011356 if (arinest || dblquote)
11357 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11358 else
11359 USTPUTC(CTLBACKQ, out);
11360 if (oldstyle)
11361 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011362 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011363}
11364
Denis Vlasenko131ae172007-02-18 13:00:19 +000011365#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011366/*
11367 * Parse an arithmetic expansion (indicate start of one and set state)
11368 */
Eric Andersenc470f442003-07-28 09:56:35 +000011369parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011370 if (++arinest == 1) {
11371 prevsyntax = syntax;
11372 syntax = ARISYNTAX;
11373 USTPUTC(CTLARI, out);
11374 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011375 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011376 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011377 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011378 } else {
11379 /*
11380 * we collapse embedded arithmetic expansion to
11381 * parenthesis, which should be equivalent
11382 */
11383 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011384 }
Eric Andersenc470f442003-07-28 09:56:35 +000011385 goto parsearith_return;
11386}
11387#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011388
Eric Andersenc470f442003-07-28 09:56:35 +000011389} /* end of readtoken */
11390
Eric Andersencb57d552001-06-28 07:25:16 +000011391/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011392 * Read the next input token.
11393 * If the token is a word, we set backquotelist to the list of cmds in
11394 * backquotes. We set quoteflag to true if any part of the word was
11395 * quoted.
11396 * If the token is TREDIR, then we set redirnode to a structure containing
11397 * the redirection.
11398 * In all cases, the variable startlinno is set to the number of the line
11399 * on which the token starts.
11400 *
11401 * [Change comment: here documents and internal procedures]
11402 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11403 * word parsing code into a separate routine. In this case, readtoken
11404 * doesn't need to have any internal procedures, but parseword does.
11405 * We could also make parseoperator in essence the main routine, and
11406 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011407 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011408#define NEW_xxreadtoken
11409#ifdef NEW_xxreadtoken
11410/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011411static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011412 '\n', '(', ')', /* singles */
11413 '&', '|', ';', /* doubles */
11414 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011415};
Eric Andersencb57d552001-06-28 07:25:16 +000011416
Denis Vlasenko834dee72008-10-07 09:18:30 +000011417#define xxreadtoken_singles 3
11418#define xxreadtoken_doubles 3
11419
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011420static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011421 TNL, TLP, TRP, /* only single occurrence allowed */
11422 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11423 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011424 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011425};
11426
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011427static int
11428xxreadtoken(void)
11429{
11430 int c;
11431
11432 if (tokpushback) {
11433 tokpushback = 0;
11434 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011435 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011436 if (needprompt) {
11437 setprompt(2);
11438 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011439 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011440 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011441 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011442 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11443 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011444
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011445 if (c == '#') {
11446 while ((c = pgetc()) != '\n' && c != PEOF)
11447 continue;
11448 pungetc();
11449 } else if (c == '\\') {
11450 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011451 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011452 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011453 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011454 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011455 if (doprompt)
11456 setprompt(2);
11457 } else {
11458 const char *p;
11459
11460 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11461 if (c != PEOF) {
11462 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011463 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011464 needprompt = doprompt;
11465 }
11466
11467 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011468 if (p == NULL)
11469 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011470
Denis Vlasenko834dee72008-10-07 09:18:30 +000011471 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11472 int cc = pgetc();
11473 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011474 p += xxreadtoken_doubles + 1;
11475 } else {
11476 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011477#if ENABLE_ASH_BASH_COMPAT
11478 if (c == '&' && cc == '>') /* &> */
11479 break; /* return readtoken1(...) */
11480#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011481 }
11482 }
11483 }
11484 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11485 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011486 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011487 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011488
11489 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011490}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011491#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011492#define RETURN(token) return lasttoken = token
11493static int
11494xxreadtoken(void)
11495{
11496 int c;
11497
11498 if (tokpushback) {
11499 tokpushback = 0;
11500 return lasttoken;
11501 }
11502 if (needprompt) {
11503 setprompt(2);
11504 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011505 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011506 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011507 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011508 switch (c) {
11509 case ' ': case '\t':
11510#if ENABLE_ASH_ALIAS
11511 case PEOA:
11512#endif
11513 continue;
11514 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011515 while ((c = pgetc()) != '\n' && c != PEOF)
11516 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011517 pungetc();
11518 continue;
11519 case '\\':
11520 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011521 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011522 if (doprompt)
11523 setprompt(2);
11524 continue;
11525 }
11526 pungetc();
11527 goto breakloop;
11528 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011529 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011530 needprompt = doprompt;
11531 RETURN(TNL);
11532 case PEOF:
11533 RETURN(TEOF);
11534 case '&':
11535 if (pgetc() == '&')
11536 RETURN(TAND);
11537 pungetc();
11538 RETURN(TBACKGND);
11539 case '|':
11540 if (pgetc() == '|')
11541 RETURN(TOR);
11542 pungetc();
11543 RETURN(TPIPE);
11544 case ';':
11545 if (pgetc() == ';')
11546 RETURN(TENDCASE);
11547 pungetc();
11548 RETURN(TSEMI);
11549 case '(':
11550 RETURN(TLP);
11551 case ')':
11552 RETURN(TRP);
11553 default:
11554 goto breakloop;
11555 }
11556 }
11557 breakloop:
11558 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11559#undef RETURN
11560}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011561#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011562
11563static int
11564readtoken(void)
11565{
11566 int t;
11567#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011568 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011569#endif
11570
11571#if ENABLE_ASH_ALIAS
11572 top:
11573#endif
11574
11575 t = xxreadtoken();
11576
11577 /*
11578 * eat newlines
11579 */
11580 if (checkkwd & CHKNL) {
11581 while (t == TNL) {
11582 parseheredoc();
11583 t = xxreadtoken();
11584 }
11585 }
11586
11587 if (t != TWORD || quoteflag) {
11588 goto out;
11589 }
11590
11591 /*
11592 * check for keywords
11593 */
11594 if (checkkwd & CHKKWD) {
11595 const char *const *pp;
11596
11597 pp = findkwd(wordtext);
11598 if (pp) {
11599 lasttoken = t = pp - tokname_array;
11600 TRACE(("keyword %s recognized\n", tokname(t)));
11601 goto out;
11602 }
11603 }
11604
11605 if (checkkwd & CHKALIAS) {
11606#if ENABLE_ASH_ALIAS
11607 struct alias *ap;
11608 ap = lookupalias(wordtext, 1);
11609 if (ap != NULL) {
11610 if (*ap->val) {
11611 pushstring(ap->val, ap);
11612 }
11613 goto top;
11614 }
11615#endif
11616 }
11617 out:
11618 checkkwd = 0;
11619#if DEBUG
11620 if (!alreadyseen)
11621 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11622 else
11623 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11624#endif
11625 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011626}
11627
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011628static char
11629peektoken(void)
11630{
11631 int t;
11632
11633 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011634 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011635 return tokname_array[t][0];
11636}
Eric Andersencb57d552001-06-28 07:25:16 +000011637
11638/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011639 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11640 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011641 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011642static union node *
11643parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011644{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011645 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011646
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011647 tokpushback = 0;
11648 doprompt = interact;
11649 if (doprompt)
11650 setprompt(doprompt);
11651 needprompt = 0;
11652 t = readtoken();
11653 if (t == TEOF)
11654 return NEOF;
11655 if (t == TNL)
11656 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011657 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011658 return list(1);
11659}
11660
11661/*
11662 * Input any here documents.
11663 */
11664static void
11665parseheredoc(void)
11666{
11667 struct heredoc *here;
11668 union node *n;
11669
11670 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011671 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011672
11673 while (here) {
11674 if (needprompt) {
11675 setprompt(2);
11676 }
11677 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11678 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011679 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011680 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011681 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011682 n->narg.text = wordtext;
11683 n->narg.backquote = backquotelist;
11684 here->here->nhere.doc = n;
11685 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011686 }
Eric Andersencb57d552001-06-28 07:25:16 +000011687}
11688
11689
11690/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011691 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011692 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011693#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011694static const char *
11695expandstr(const char *ps)
11696{
11697 union node n;
11698
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011699 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11700 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011701 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011702 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011703 popfile();
11704
11705 n.narg.type = NARG;
11706 n.narg.next = NULL;
11707 n.narg.text = wordtext;
11708 n.narg.backquote = backquotelist;
11709
11710 expandarg(&n, NULL, 0);
11711 return stackblock();
11712}
11713#endif
11714
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011715/*
11716 * Execute a command or commands contained in a string.
11717 */
11718static int
11719evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011720{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011721 union node *n;
11722 struct stackmark smark;
11723 int skip;
11724
11725 setinputstring(s);
11726 setstackmark(&smark);
11727
11728 skip = 0;
11729 while ((n = parsecmd(0)) != NEOF) {
11730 evaltree(n, 0);
11731 popstackmark(&smark);
11732 skip = evalskip;
11733 if (skip)
11734 break;
11735 }
11736 popfile();
11737
11738 skip &= mask;
11739 evalskip = skip;
11740 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011741}
11742
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011743/*
11744 * The eval command.
11745 */
11746static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011747evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011748{
11749 char *p;
11750 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011751
Denis Vlasenko68404f12008-03-17 09:00:54 +000011752 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011753 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011754 argv += 2;
11755 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011756 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011757 for (;;) {
11758 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011759 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011760 if (p == NULL)
11761 break;
11762 STPUTC(' ', concat);
11763 }
11764 STPUTC('\0', concat);
11765 p = grabstackstr(concat);
11766 }
11767 evalstring(p, ~SKIPEVAL);
11768
11769 }
11770 return exitstatus;
11771}
11772
11773/*
11774 * Read and execute commands. "Top" is nonzero for the top level command
11775 * loop; it turns on prompting if the shell is interactive.
11776 */
11777static int
11778cmdloop(int top)
11779{
11780 union node *n;
11781 struct stackmark smark;
11782 int inter;
11783 int numeof = 0;
11784
11785 TRACE(("cmdloop(%d) called\n", top));
11786 for (;;) {
11787 int skip;
11788
11789 setstackmark(&smark);
11790#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011791 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011792 showjobs(stderr, SHOW_CHANGED);
11793#endif
11794 inter = 0;
11795 if (iflag && top) {
11796 inter++;
11797#if ENABLE_ASH_MAIL
11798 chkmail();
11799#endif
11800 }
11801 n = parsecmd(inter);
11802 /* showtree(n); DEBUG */
11803 if (n == NEOF) {
11804 if (!top || numeof >= 50)
11805 break;
11806 if (!stoppedjobs()) {
11807 if (!Iflag)
11808 break;
11809 out2str("\nUse \"exit\" to leave shell.\n");
11810 }
11811 numeof++;
11812 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011813 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11814 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011815 numeof = 0;
11816 evaltree(n, 0);
11817 }
11818 popstackmark(&smark);
11819 skip = evalskip;
11820
11821 if (skip) {
11822 evalskip = 0;
11823 return skip & SKIPEVAL;
11824 }
11825 }
11826 return 0;
11827}
11828
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011829/*
11830 * Take commands from a file. To be compatible we should do a path
11831 * search for the file, which is necessary to find sub-commands.
11832 */
11833static char *
11834find_dot_file(char *name)
11835{
11836 char *fullname;
11837 const char *path = pathval();
11838 struct stat statb;
11839
11840 /* don't try this for absolute or relative paths */
11841 if (strchr(name, '/'))
11842 return name;
11843
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011844 /* IIRC standards do not say whether . is to be searched.
11845 * And it is even smaller this way, making it unconditional for now:
11846 */
11847 if (1) { /* ENABLE_ASH_BASH_COMPAT */
11848 fullname = name;
11849 goto try_cur_dir;
11850 }
11851
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011852 while ((fullname = padvance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011853 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011854 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11855 /*
11856 * Don't bother freeing here, since it will
11857 * be freed by the caller.
11858 */
11859 return fullname;
11860 }
11861 stunalloc(fullname);
11862 }
11863
11864 /* not found in the PATH */
11865 ash_msg_and_raise_error("%s: not found", name);
11866 /* NOTREACHED */
11867}
11868
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011869static int
11870dotcmd(int argc, char **argv)
11871{
11872 struct strlist *sp;
11873 volatile struct shparam saveparam;
11874 int status = 0;
11875
11876 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011877 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011878
Denis Vlasenko68404f12008-03-17 09:00:54 +000011879 if (argv[1]) { /* That's what SVR2 does */
11880 char *fullname = find_dot_file(argv[1]);
11881 argv += 2;
11882 argc -= 2;
11883 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011884 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011885 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011886 shellparam.nparam = argc;
11887 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011888 };
11889
11890 setinputfile(fullname, INPUT_PUSH_FILE);
11891 commandname = fullname;
11892 cmdloop(0);
11893 popfile();
11894
Denis Vlasenko68404f12008-03-17 09:00:54 +000011895 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011896 freeparam(&shellparam);
11897 shellparam = saveparam;
11898 };
11899 status = exitstatus;
11900 }
11901 return status;
11902}
11903
11904static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011905exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011906{
11907 if (stoppedjobs())
11908 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011909 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011910 exitstatus = number(argv[1]);
11911 raise_exception(EXEXIT);
11912 /* NOTREACHED */
11913}
11914
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011915/*
11916 * Read a file containing shell functions.
11917 */
11918static void
11919readcmdfile(char *name)
11920{
11921 setinputfile(name, INPUT_PUSH_FILE);
11922 cmdloop(0);
11923 popfile();
11924}
11925
11926
Denis Vlasenkocc571512007-02-23 21:10:35 +000011927/* ============ find_command inplementation */
11928
11929/*
11930 * Resolve a command name. If you change this routine, you may have to
11931 * change the shellexec routine as well.
11932 */
11933static void
11934find_command(char *name, struct cmdentry *entry, int act, const char *path)
11935{
11936 struct tblentry *cmdp;
11937 int idx;
11938 int prev;
11939 char *fullname;
11940 struct stat statb;
11941 int e;
11942 int updatetbl;
11943 struct builtincmd *bcmd;
11944
11945 /* If name contains a slash, don't use PATH or hash table */
11946 if (strchr(name, '/') != NULL) {
11947 entry->u.index = -1;
11948 if (act & DO_ABS) {
11949 while (stat(name, &statb) < 0) {
11950#ifdef SYSV
11951 if (errno == EINTR)
11952 continue;
11953#endif
11954 entry->cmdtype = CMDUNKNOWN;
11955 return;
11956 }
11957 }
11958 entry->cmdtype = CMDNORMAL;
11959 return;
11960 }
11961
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011962/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011963
11964 updatetbl = (path == pathval());
11965 if (!updatetbl) {
11966 act |= DO_ALTPATH;
11967 if (strstr(path, "%builtin") != NULL)
11968 act |= DO_ALTBLTIN;
11969 }
11970
11971 /* If name is in the table, check answer will be ok */
11972 cmdp = cmdlookup(name, 0);
11973 if (cmdp != NULL) {
11974 int bit;
11975
11976 switch (cmdp->cmdtype) {
11977 default:
11978#if DEBUG
11979 abort();
11980#endif
11981 case CMDNORMAL:
11982 bit = DO_ALTPATH;
11983 break;
11984 case CMDFUNCTION:
11985 bit = DO_NOFUNC;
11986 break;
11987 case CMDBUILTIN:
11988 bit = DO_ALTBLTIN;
11989 break;
11990 }
11991 if (act & bit) {
11992 updatetbl = 0;
11993 cmdp = NULL;
11994 } else if (cmdp->rehash == 0)
11995 /* if not invalidated by cd, we're done */
11996 goto success;
11997 }
11998
11999 /* If %builtin not in path, check for builtin next */
12000 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012001 if (bcmd) {
12002 if (IS_BUILTIN_REGULAR(bcmd))
12003 goto builtin_success;
12004 if (act & DO_ALTPATH) {
12005 if (!(act & DO_ALTBLTIN))
12006 goto builtin_success;
12007 } else if (builtinloc <= 0) {
12008 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012009 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012010 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012011
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012012#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012013 {
12014 int applet_no = find_applet_by_name(name);
12015 if (applet_no >= 0) {
12016 entry->cmdtype = CMDNORMAL;
12017 entry->u.index = -2 - applet_no;
12018 return;
12019 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012020 }
12021#endif
12022
Denis Vlasenkocc571512007-02-23 21:10:35 +000012023 /* We have to search path. */
12024 prev = -1; /* where to start */
12025 if (cmdp && cmdp->rehash) { /* doing a rehash */
12026 if (cmdp->cmdtype == CMDBUILTIN)
12027 prev = builtinloc;
12028 else
12029 prev = cmdp->param.index;
12030 }
12031
12032 e = ENOENT;
12033 idx = -1;
12034 loop:
12035 while ((fullname = padvance(&path, name)) != NULL) {
12036 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012037 /* NB: code below will still use fullname
12038 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012039 idx++;
12040 if (pathopt) {
12041 if (prefix(pathopt, "builtin")) {
12042 if (bcmd)
12043 goto builtin_success;
12044 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012045 }
12046 if ((act & DO_NOFUNC)
12047 || !prefix(pathopt, "func")
12048 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012049 continue;
12050 }
12051 }
12052 /* if rehash, don't redo absolute path names */
12053 if (fullname[0] == '/' && idx <= prev) {
12054 if (idx < prev)
12055 continue;
12056 TRACE(("searchexec \"%s\": no change\n", name));
12057 goto success;
12058 }
12059 while (stat(fullname, &statb) < 0) {
12060#ifdef SYSV
12061 if (errno == EINTR)
12062 continue;
12063#endif
12064 if (errno != ENOENT && errno != ENOTDIR)
12065 e = errno;
12066 goto loop;
12067 }
12068 e = EACCES; /* if we fail, this will be the error */
12069 if (!S_ISREG(statb.st_mode))
12070 continue;
12071 if (pathopt) { /* this is a %func directory */
12072 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012073 /* NB: stalloc will return space pointed by fullname
12074 * (because we don't have any intervening allocations
12075 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012076 readcmdfile(fullname);
12077 cmdp = cmdlookup(name, 0);
12078 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12079 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12080 stunalloc(fullname);
12081 goto success;
12082 }
12083 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12084 if (!updatetbl) {
12085 entry->cmdtype = CMDNORMAL;
12086 entry->u.index = idx;
12087 return;
12088 }
12089 INT_OFF;
12090 cmdp = cmdlookup(name, 1);
12091 cmdp->cmdtype = CMDNORMAL;
12092 cmdp->param.index = idx;
12093 INT_ON;
12094 goto success;
12095 }
12096
12097 /* We failed. If there was an entry for this command, delete it */
12098 if (cmdp && updatetbl)
12099 delete_cmd_entry();
12100 if (act & DO_ERR)
12101 ash_msg("%s: %s", name, errmsg(e, "not found"));
12102 entry->cmdtype = CMDUNKNOWN;
12103 return;
12104
12105 builtin_success:
12106 if (!updatetbl) {
12107 entry->cmdtype = CMDBUILTIN;
12108 entry->u.cmd = bcmd;
12109 return;
12110 }
12111 INT_OFF;
12112 cmdp = cmdlookup(name, 1);
12113 cmdp->cmdtype = CMDBUILTIN;
12114 cmdp->param.cmd = bcmd;
12115 INT_ON;
12116 success:
12117 cmdp->rehash = 0;
12118 entry->cmdtype = cmdp->cmdtype;
12119 entry->u = cmdp->param;
12120}
12121
12122
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012123/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012124
Eric Andersencb57d552001-06-28 07:25:16 +000012125/*
Eric Andersencb57d552001-06-28 07:25:16 +000012126 * The trap builtin.
12127 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012128static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012129trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012130{
12131 char *action;
12132 char **ap;
12133 int signo;
12134
Eric Andersenc470f442003-07-28 09:56:35 +000012135 nextopt(nullstr);
12136 ap = argptr;
12137 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012138 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012139 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012140 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012141 single_quote(trap[signo]),
12142 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012143 }
12144 }
12145 return 0;
12146 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012147 action = NULL;
12148 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012149 action = *ap++;
12150 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012151 signo = get_signum(*ap);
12152 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012153 ash_msg_and_raise_error("%s: bad trap", *ap);
12154 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012155 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012156 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012157 action = NULL;
12158 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012159 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012160 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012161 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012162 trap[signo] = action;
12163 if (signo != 0)
12164 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012165 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012166 ap++;
12167 }
12168 return 0;
12169}
12170
Eric Andersenc470f442003-07-28 09:56:35 +000012171
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012172/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012173
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012174#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012175/*
12176 * Lists available builtins
12177 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012178static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012179helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012180{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012181 unsigned col;
12182 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012183
12184 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012185 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012186 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012187 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012188 if (col > 60) {
12189 out1fmt("\n");
12190 col = 0;
12191 }
12192 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012193#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012194 {
12195 const char *a = applet_names;
12196 while (*a) {
12197 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12198 if (col > 60) {
12199 out1fmt("\n");
12200 col = 0;
12201 }
12202 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012203 }
12204 }
12205#endif
12206 out1fmt("\n\n");
12207 return EXIT_SUCCESS;
12208}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012209#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012210
Eric Andersencb57d552001-06-28 07:25:16 +000012211/*
Eric Andersencb57d552001-06-28 07:25:16 +000012212 * The export and readonly commands.
12213 */
Eric Andersenc470f442003-07-28 09:56:35 +000012214static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012215exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012216{
12217 struct var *vp;
12218 char *name;
12219 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012220 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012221 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012222
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012223 if (nextopt("p") != 'p') {
12224 aptr = argptr;
12225 name = *aptr;
12226 if (name) {
12227 do {
12228 p = strchr(name, '=');
12229 if (p != NULL) {
12230 p++;
12231 } else {
12232 vp = *findvar(hashvar(name), name);
12233 if (vp) {
12234 vp->flags |= flag;
12235 continue;
12236 }
Eric Andersencb57d552001-06-28 07:25:16 +000012237 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012238 setvar(name, p, flag);
12239 } while ((name = *++aptr) != NULL);
12240 return 0;
12241 }
Eric Andersencb57d552001-06-28 07:25:16 +000012242 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012243 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012244 return 0;
12245}
12246
Eric Andersencb57d552001-06-28 07:25:16 +000012247/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012248 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012249 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012250static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012251unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012252{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012253 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012254
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012255 cmdp = cmdlookup(name, 0);
12256 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12257 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012258}
12259
Eric Andersencb57d552001-06-28 07:25:16 +000012260/*
Eric Andersencb57d552001-06-28 07:25:16 +000012261 * The unset builtin command. We unset the function before we unset the
12262 * variable to allow a function to be unset when there is a readonly variable
12263 * with the same name.
12264 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012265static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012266unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012267{
12268 char **ap;
12269 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012270 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012271 int ret = 0;
12272
12273 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012274 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012275 }
Eric Andersencb57d552001-06-28 07:25:16 +000012276
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012277 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012278 if (flag != 'f') {
12279 i = unsetvar(*ap);
12280 ret |= i;
12281 if (!(i & 2))
12282 continue;
12283 }
12284 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012285 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012286 }
Eric Andersenc470f442003-07-28 09:56:35 +000012287 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012288}
12289
12290
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012291/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012292
Eric Andersenc470f442003-07-28 09:56:35 +000012293#include <sys/times.h>
12294
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012295static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012296 ' ', offsetof(struct tms, tms_utime),
12297 '\n', offsetof(struct tms, tms_stime),
12298 ' ', offsetof(struct tms, tms_cutime),
12299 '\n', offsetof(struct tms, tms_cstime),
12300 0
12301};
Eric Andersencb57d552001-06-28 07:25:16 +000012302
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012303static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012304timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012305{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012306 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012307 const unsigned char *p;
12308 struct tms buf;
12309
12310 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012311 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012312
12313 p = timescmd_str;
12314 do {
12315 t = *(clock_t *)(((char *) &buf) + p[1]);
12316 s = t / clk_tck;
12317 out1fmt("%ldm%ld.%.3lds%c",
12318 s/60, s%60,
12319 ((t - s * clk_tck) * 1000) / clk_tck,
12320 p[0]);
12321 } while (*(p += 2));
12322
Eric Andersencb57d552001-06-28 07:25:16 +000012323 return 0;
12324}
12325
Denis Vlasenko131ae172007-02-18 13:00:19 +000012326#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012327static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012328dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012329{
Eric Andersened9ecf72004-06-22 08:29:45 +000012330 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012331 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012332
Denis Vlasenkob012b102007-02-19 22:43:01 +000012333 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012334 result = arith(s, &errcode);
12335 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012336 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012337 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012338 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012339 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012340 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012341 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012342 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012343 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012344 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012345
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012346 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012347}
Eric Andersenc470f442003-07-28 09:56:35 +000012348
Eric Andersenc470f442003-07-28 09:56:35 +000012349/*
Eric Andersen90898442003-08-06 11:20:52 +000012350 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12351 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12352 *
12353 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012354 */
12355static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012356letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012357{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012358 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012359
Denis Vlasenko68404f12008-03-17 09:00:54 +000012360 argv++;
12361 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012362 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012363 do {
12364 i = dash_arith(*argv);
12365 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012366
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012367 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012368}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012369#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012370
Eric Andersenc470f442003-07-28 09:56:35 +000012371
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012372/* ============ miscbltin.c
12373 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012374 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012375 */
12376
12377#undef rflag
12378
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012379#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012380typedef enum __rlimit_resource rlim_t;
12381#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012382
Eric Andersenc470f442003-07-28 09:56:35 +000012383/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012384 * The read builtin. Options:
12385 * -r Do not interpret '\' specially
12386 * -s Turn off echo (tty only)
12387 * -n NCHARS Read NCHARS max
12388 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12389 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12390 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012391 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012392 * TODO: bash also has:
12393 * -a ARRAY Read into array[0],[1],etc
12394 * -d DELIM End on DELIM char, not newline
12395 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012396 */
Eric Andersenc470f442003-07-28 09:56:35 +000012397static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012398readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012399{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012400 static const char *const arg_REPLY[] = { "REPLY", NULL };
12401
Eric Andersenc470f442003-07-28 09:56:35 +000012402 char **ap;
12403 int backslash;
12404 char c;
12405 int rflag;
12406 char *prompt;
12407 const char *ifs;
12408 char *p;
12409 int startword;
12410 int status;
12411 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012412 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012413#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012414 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012415 int silent = 0;
12416 struct termios tty, old_tty;
12417#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012418#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012419 unsigned end_ms = 0;
12420 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012421#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012422
12423 rflag = 0;
12424 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012425 while ((i = nextopt("p:u:r"
12426 USE_ASH_READ_TIMEOUT("t:")
12427 USE_ASH_READ_NCHARS("n:s")
12428 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012429 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012430 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012431 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012432 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012433#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012434 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012435 nchars = bb_strtou(optionarg, NULL, 10);
12436 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012437 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012438 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012439 break;
12440 case 's':
12441 silent = 1;
12442 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012443#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012444#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012445 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012446 timeout = bb_strtou(optionarg, NULL, 10);
12447 if (errno || timeout > UINT_MAX / 2048)
12448 ash_msg_and_raise_error("invalid timeout");
12449 timeout *= 1000;
12450#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012451 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012452 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012453 /* EINVAL means number is ok, but not terminated by NUL */
12454 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012455 char *p2;
12456 if (*++p) {
12457 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012458 ts.tv_usec = bb_strtou(p, &p2, 10);
12459 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012460 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012461 scale = p2 - p;
12462 /* normalize to usec */
12463 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012464 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012465 while (scale++ < 6)
12466 ts.tv_usec *= 10;
12467 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012468 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012469 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012470 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012471 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012472 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012473 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012474#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012475 break;
12476#endif
12477 case 'r':
12478 rflag = 1;
12479 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012480 case 'u':
12481 fd = bb_strtou(optionarg, NULL, 10);
12482 if (fd < 0 || errno)
12483 ash_msg_and_raise_error("invalid file descriptor");
12484 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012485 default:
12486 break;
12487 }
Eric Andersenc470f442003-07-28 09:56:35 +000012488 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012489 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012490 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012491 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012492 ap = argptr;
12493 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012494 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012495 ifs = bltinlookup("IFS");
12496 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012497 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012498#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012499 tcgetattr(fd, &tty);
12500 old_tty = tty;
12501 if (nchars || silent) {
12502 if (nchars) {
12503 tty.c_lflag &= ~ICANON;
12504 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012505 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012506 if (silent) {
12507 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12508 }
12509 /* if tcgetattr failed, tcsetattr will fail too.
12510 * Ignoring, it's harmless. */
12511 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012512 }
12513#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012514
Eric Andersenc470f442003-07-28 09:56:35 +000012515 status = 0;
12516 startword = 1;
12517 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012518#if ENABLE_ASH_READ_TIMEOUT
12519 if (timeout) /* NB: ensuring end_ms is nonzero */
12520 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12521#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012522 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012523 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012524#if ENABLE_ASH_READ_TIMEOUT
12525 if (end_ms) {
12526 struct pollfd pfd[1];
12527 pfd[0].fd = fd;
12528 pfd[0].events = POLLIN;
12529 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12530 if ((int)timeout <= 0 /* already late? */
12531 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12532 ) { /* timed out! */
12533#if ENABLE_ASH_READ_NCHARS
12534 tcsetattr(fd, TCSANOW, &old_tty);
12535#endif
12536 return 1;
12537 }
12538 }
12539#endif
12540 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012541 status = 1;
12542 break;
12543 }
12544 if (c == '\0')
12545 continue;
12546 if (backslash) {
12547 backslash = 0;
12548 if (c != '\n')
12549 goto put;
12550 continue;
12551 }
12552 if (!rflag && c == '\\') {
12553 backslash++;
12554 continue;
12555 }
12556 if (c == '\n')
12557 break;
12558 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12559 continue;
12560 }
12561 startword = 0;
12562 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12563 STACKSTRNUL(p);
12564 setvar(*ap, stackblock(), 0);
12565 ap++;
12566 startword = 1;
12567 STARTSTACKSTR(p);
12568 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012569 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012570 STPUTC(c, p);
12571 }
12572 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012573/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012574#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012575 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012576#else
12577 while (1);
12578#endif
12579
12580#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012581 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012582#endif
12583
Eric Andersenc470f442003-07-28 09:56:35 +000012584 STACKSTRNUL(p);
12585 /* Remove trailing blanks */
12586 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12587 *p = '\0';
12588 setvar(*ap, stackblock(), 0);
12589 while (*++ap != NULL)
12590 setvar(*ap, nullstr, 0);
12591 return status;
12592}
12593
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012594static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012595umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012596{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012597 static const char permuser[3] ALIGN1 = "ugo";
12598 static const char permmode[3] ALIGN1 = "rwx";
12599 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012600 S_IRUSR, S_IWUSR, S_IXUSR,
12601 S_IRGRP, S_IWGRP, S_IXGRP,
12602 S_IROTH, S_IWOTH, S_IXOTH
12603 };
12604
12605 char *ap;
12606 mode_t mask;
12607 int i;
12608 int symbolic_mode = 0;
12609
12610 while (nextopt("S") != '\0') {
12611 symbolic_mode = 1;
12612 }
12613
Denis Vlasenkob012b102007-02-19 22:43:01 +000012614 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012615 mask = umask(0);
12616 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012617 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012618
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012619 ap = *argptr;
12620 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012621 if (symbolic_mode) {
12622 char buf[18];
12623 char *p = buf;
12624
12625 for (i = 0; i < 3; i++) {
12626 int j;
12627
12628 *p++ = permuser[i];
12629 *p++ = '=';
12630 for (j = 0; j < 3; j++) {
12631 if ((mask & permmask[3 * i + j]) == 0) {
12632 *p++ = permmode[j];
12633 }
12634 }
12635 *p++ = ',';
12636 }
12637 *--p = 0;
12638 puts(buf);
12639 } else {
12640 out1fmt("%.4o\n", mask);
12641 }
12642 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012643 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012644 mask = 0;
12645 do {
12646 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012647 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012648 mask = (mask << 3) + (*ap - '0');
12649 } while (*++ap != '\0');
12650 umask(mask);
12651 } else {
12652 mask = ~mask & 0777;
12653 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012654 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012655 }
12656 umask(~mask & 0777);
12657 }
12658 }
12659 return 0;
12660}
12661
12662/*
12663 * ulimit builtin
12664 *
12665 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12666 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12667 * ash by J.T. Conklin.
12668 *
12669 * Public domain.
12670 */
12671
12672struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012673 uint8_t cmd; /* RLIMIT_xxx fit into it */
12674 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012675 char option;
12676};
12677
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012678static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012679#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012680 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012681#endif
12682#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012683 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012684#endif
12685#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012686 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012687#endif
12688#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012689 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012690#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012691#ifdef RLIMIT_CORE
12692 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012693#endif
12694#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012695 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012696#endif
12697#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012698 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012699#endif
12700#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012701 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012702#endif
12703#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012704 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012705#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012706#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012707 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012708#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012709#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012710 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012711#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012712};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012713static const char limits_name[] =
12714#ifdef RLIMIT_CPU
12715 "time(seconds)" "\0"
12716#endif
12717#ifdef RLIMIT_FSIZE
12718 "file(blocks)" "\0"
12719#endif
12720#ifdef RLIMIT_DATA
12721 "data(kb)" "\0"
12722#endif
12723#ifdef RLIMIT_STACK
12724 "stack(kb)" "\0"
12725#endif
12726#ifdef RLIMIT_CORE
12727 "coredump(blocks)" "\0"
12728#endif
12729#ifdef RLIMIT_RSS
12730 "memory(kb)" "\0"
12731#endif
12732#ifdef RLIMIT_MEMLOCK
12733 "locked memory(kb)" "\0"
12734#endif
12735#ifdef RLIMIT_NPROC
12736 "process" "\0"
12737#endif
12738#ifdef RLIMIT_NOFILE
12739 "nofiles" "\0"
12740#endif
12741#ifdef RLIMIT_AS
12742 "vmemory(kb)" "\0"
12743#endif
12744#ifdef RLIMIT_LOCKS
12745 "locks" "\0"
12746#endif
12747;
Eric Andersenc470f442003-07-28 09:56:35 +000012748
Glenn L McGrath76620622004-01-13 10:19:37 +000012749enum limtype { SOFT = 0x1, HARD = 0x2 };
12750
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012751static void
12752printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012753 const struct limits *l)
12754{
12755 rlim_t val;
12756
12757 val = limit->rlim_max;
12758 if (how & SOFT)
12759 val = limit->rlim_cur;
12760
12761 if (val == RLIM_INFINITY)
12762 out1fmt("unlimited\n");
12763 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012764 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012765 out1fmt("%lld\n", (long long) val);
12766 }
12767}
12768
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012769static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012770ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012771{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012772 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012773 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012774 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012775 const struct limits *l;
12776 int set, all = 0;
12777 int optc, what;
12778 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012779
12780 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012781 while ((optc = nextopt("HSa"
12782#ifdef RLIMIT_CPU
12783 "t"
12784#endif
12785#ifdef RLIMIT_FSIZE
12786 "f"
12787#endif
12788#ifdef RLIMIT_DATA
12789 "d"
12790#endif
12791#ifdef RLIMIT_STACK
12792 "s"
12793#endif
12794#ifdef RLIMIT_CORE
12795 "c"
12796#endif
12797#ifdef RLIMIT_RSS
12798 "m"
12799#endif
12800#ifdef RLIMIT_MEMLOCK
12801 "l"
12802#endif
12803#ifdef RLIMIT_NPROC
12804 "p"
12805#endif
12806#ifdef RLIMIT_NOFILE
12807 "n"
12808#endif
12809#ifdef RLIMIT_AS
12810 "v"
12811#endif
12812#ifdef RLIMIT_LOCKS
12813 "w"
12814#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012815 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012816 switch (optc) {
12817 case 'H':
12818 how = HARD;
12819 break;
12820 case 'S':
12821 how = SOFT;
12822 break;
12823 case 'a':
12824 all = 1;
12825 break;
12826 default:
12827 what = optc;
12828 }
12829
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012830 for (l = limits_tbl; l->option != what; l++)
12831 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012832
12833 set = *argptr ? 1 : 0;
12834 if (set) {
12835 char *p = *argptr;
12836
12837 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012838 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012839 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012840 val = RLIM_INFINITY;
12841 else {
12842 val = (rlim_t) 0;
12843
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012844 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012845 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012846 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012847 if (val < (rlim_t) 0)
12848 break;
12849 }
12850 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012851 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012852 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012853 }
12854 }
12855 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012856 const char *lname = limits_name;
12857 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012858 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012859 out1fmt("%-20s ", lname);
12860 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012861 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012862 }
12863 return 0;
12864 }
12865
12866 getrlimit(l->cmd, &limit);
12867 if (set) {
12868 if (how & HARD)
12869 limit.rlim_max = val;
12870 if (how & SOFT)
12871 limit.rlim_cur = val;
12872 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012873 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012874 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012875 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012876 }
12877 return 0;
12878}
12879
Eric Andersen90898442003-08-06 11:20:52 +000012880
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012881/* ============ Math support */
12882
Denis Vlasenko131ae172007-02-18 13:00:19 +000012883#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012884
12885/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12886
12887 Permission is hereby granted, free of charge, to any person obtaining
12888 a copy of this software and associated documentation files (the
12889 "Software"), to deal in the Software without restriction, including
12890 without limitation the rights to use, copy, modify, merge, publish,
12891 distribute, sublicense, and/or sell copies of the Software, and to
12892 permit persons to whom the Software is furnished to do so, subject to
12893 the following conditions:
12894
12895 The above copyright notice and this permission notice shall be
12896 included in all copies or substantial portions of the Software.
12897
12898 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12899 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12900 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12901 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12902 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12903 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12904 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12905*/
12906
12907/* This is my infix parser/evaluator. It is optimized for size, intended
12908 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012909 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012910 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012911 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012912 * be that which POSIX specifies for shells. */
12913
12914/* The code uses a simple two-stack algorithm. See
12915 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012916 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012917 * this is based (this code differs in that it applies operators immediately
12918 * to the stack instead of adding them to a queue to end up with an
12919 * expression). */
12920
12921/* To use the routine, call it with an expression string and error return
12922 * pointer */
12923
12924/*
12925 * Aug 24, 2001 Manuel Novoa III
12926 *
12927 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12928 *
12929 * 1) In arith_apply():
12930 * a) Cached values of *numptr and &(numptr[-1]).
12931 * b) Removed redundant test for zero denominator.
12932 *
12933 * 2) In arith():
12934 * a) Eliminated redundant code for processing operator tokens by moving
12935 * to a table-based implementation. Also folded handling of parens
12936 * into the table.
12937 * b) Combined all 3 loops which called arith_apply to reduce generated
12938 * code size at the cost of speed.
12939 *
12940 * 3) The following expressions were treated as valid by the original code:
12941 * 1() , 0! , 1 ( *3 ) .
12942 * These bugs have been fixed by internally enclosing the expression in
12943 * parens and then checking that all binary ops and right parens are
12944 * preceded by a valid expression (NUM_TOKEN).
12945 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012946 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012947 * ctype's isspace() if it is used by another busybox applet or if additional
12948 * whitespace chars should be considered. Look below the "#include"s for a
12949 * precompiler test.
12950 */
12951
12952/*
12953 * Aug 26, 2001 Manuel Novoa III
12954 *
12955 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12956 *
12957 * Merge in Aaron's comments previously posted to the busybox list,
12958 * modified slightly to take account of my changes to the code.
12959 *
12960 */
12961
12962/*
12963 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12964 *
12965 * - allow access to variable,
12966 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12967 * - realize assign syntax (VAR=expr, +=, *= etc)
12968 * - realize exponentiation (** operator)
12969 * - realize comma separated - expr, expr
12970 * - realise ++expr --expr expr++ expr--
12971 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012972 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012973 * - was restored loses XOR operator
12974 * - remove one goto label, added three ;-)
12975 * - protect $((num num)) as true zero expr (Manuel`s error)
12976 * - always use special isspace(), see comment from bash ;-)
12977 */
12978
Eric Andersen90898442003-08-06 11:20:52 +000012979#define arith_isspace(arithval) \
12980 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12981
Eric Andersen90898442003-08-06 11:20:52 +000012982typedef unsigned char operator;
12983
12984/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012985 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012986 * precedence. The ID portion is so that multiple operators can have the
12987 * same precedence, ensuring that the leftmost one is evaluated first.
12988 * Consider * and /. */
12989
12990#define tok_decl(prec,id) (((id)<<5)|(prec))
12991#define PREC(op) ((op) & 0x1F)
12992
12993#define TOK_LPAREN tok_decl(0,0)
12994
12995#define TOK_COMMA tok_decl(1,0)
12996
12997#define TOK_ASSIGN tok_decl(2,0)
12998#define TOK_AND_ASSIGN tok_decl(2,1)
12999#define TOK_OR_ASSIGN tok_decl(2,2)
13000#define TOK_XOR_ASSIGN tok_decl(2,3)
13001#define TOK_PLUS_ASSIGN tok_decl(2,4)
13002#define TOK_MINUS_ASSIGN tok_decl(2,5)
13003#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13004#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13005
13006#define TOK_MUL_ASSIGN tok_decl(3,0)
13007#define TOK_DIV_ASSIGN tok_decl(3,1)
13008#define TOK_REM_ASSIGN tok_decl(3,2)
13009
13010/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000013012
13013/* conditional is right associativity too */
13014#define TOK_CONDITIONAL tok_decl(4,0)
13015#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13016
13017#define TOK_OR tok_decl(5,0)
13018
13019#define TOK_AND tok_decl(6,0)
13020
13021#define TOK_BOR tok_decl(7,0)
13022
13023#define TOK_BXOR tok_decl(8,0)
13024
13025#define TOK_BAND tok_decl(9,0)
13026
13027#define TOK_EQ tok_decl(10,0)
13028#define TOK_NE tok_decl(10,1)
13029
13030#define TOK_LT tok_decl(11,0)
13031#define TOK_GT tok_decl(11,1)
13032#define TOK_GE tok_decl(11,2)
13033#define TOK_LE tok_decl(11,3)
13034
13035#define TOK_LSHIFT tok_decl(12,0)
13036#define TOK_RSHIFT tok_decl(12,1)
13037
13038#define TOK_ADD tok_decl(13,0)
13039#define TOK_SUB tok_decl(13,1)
13040
13041#define TOK_MUL tok_decl(14,0)
13042#define TOK_DIV tok_decl(14,1)
13043#define TOK_REM tok_decl(14,2)
13044
13045/* exponent is right associativity */
13046#define TOK_EXPONENT tok_decl(15,1)
13047
13048/* For now unary operators. */
13049#define UNARYPREC 16
13050#define TOK_BNOT tok_decl(UNARYPREC,0)
13051#define TOK_NOT tok_decl(UNARYPREC,1)
13052
13053#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13054#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13055
13056#define PREC_PRE (UNARYPREC+2)
13057
13058#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13059#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13060
13061#define PREC_POST (UNARYPREC+3)
13062
13063#define TOK_POST_INC tok_decl(PREC_POST, 0)
13064#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13065
13066#define SPEC_PREC (UNARYPREC+4)
13067
13068#define TOK_NUM tok_decl(SPEC_PREC, 0)
13069#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13070
13071#define NUMPTR (*numstackptr)
13072
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013073static int
13074tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013075{
13076 operator prec = PREC(op);
13077
13078 convert_prec_is_assing(prec);
13079 return (prec == PREC(TOK_ASSIGN) ||
13080 prec == PREC_PRE || prec == PREC_POST);
13081}
13082
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013083static int
13084is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013085{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13087 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013088}
13089
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013090typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013091 arith_t val;
13092 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013093 char contidional_second_val_initialized;
13094 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013095 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013096} v_n_t;
13097
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013098typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013099 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013100 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013101} chk_var_recursive_looped_t;
13102
13103static chk_var_recursive_looped_t *prev_chk_var_recursive;
13104
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013105static int
13106arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013107{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013108 if (t->var) {
13109 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013110
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 if (p) {
13112 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013113
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013114 /* recursive try as expression */
13115 chk_var_recursive_looped_t *cur;
13116 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013117
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013118 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13119 if (strcmp(cur->var, t->var) == 0) {
13120 /* expression recursion loop detected */
13121 return -5;
13122 }
13123 }
13124 /* save current lookuped var name */
13125 cur = prev_chk_var_recursive;
13126 cur_save.var = t->var;
13127 cur_save.next = cur;
13128 prev_chk_var_recursive = &cur_save;
13129
13130 t->val = arith (p, &errcode);
13131 /* restore previous ptr after recursiving */
13132 prev_chk_var_recursive = cur;
13133 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013134 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013135 /* allow undefined var as 0 */
13136 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013137 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013138 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013139}
13140
13141/* "applying" a token means performing it on the top elements on the integer
13142 * stack. For a unary operator it will only change the top element, but a
13143 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013144static int
13145arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013146{
Eric Andersen90898442003-08-06 11:20:52 +000013147 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013148 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013149 int ret_arith_lookup_val;
13150
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013151 /* There is no operator that can work without arguments */
13152 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013153 numptr_m1 = NUMPTR - 1;
13154
13155 /* check operand is var with noninteger value */
13156 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013157 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013158 return ret_arith_lookup_val;
13159
13160 rez = numptr_m1->val;
13161 if (op == TOK_UMINUS)
13162 rez *= -1;
13163 else if (op == TOK_NOT)
13164 rez = !rez;
13165 else if (op == TOK_BNOT)
13166 rez = ~rez;
13167 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13168 rez++;
13169 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13170 rez--;
13171 else if (op != TOK_UPLUS) {
13172 /* Binary operators */
13173
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013174 /* check and binary operators need two arguments */
13175 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013176
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013177 /* ... and they pop one */
13178 --NUMPTR;
13179 numptr_val = rez;
13180 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013181 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013182 /* protect $((expr1 ? expr2)) without ": expr" */
13183 goto err;
13184 }
13185 rez = numptr_m1->contidional_second_val;
13186 } else if (numptr_m1->contidional_second_val_initialized) {
13187 /* protect $((expr1 : expr2)) without "expr ? " */
13188 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013189 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013190 numptr_m1 = NUMPTR - 1;
13191 if (op != TOK_ASSIGN) {
13192 /* check operand is var with noninteger value for not '=' */
13193 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13194 if (ret_arith_lookup_val)
13195 return ret_arith_lookup_val;
13196 }
13197 if (op == TOK_CONDITIONAL) {
13198 numptr_m1->contidional_second_val = rez;
13199 }
13200 rez = numptr_m1->val;
13201 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013202 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013203 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013204 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013206 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013207 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013208 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013209 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013210 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013211 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013212 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013213 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013214 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013215 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013216 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013217 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013218 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013219 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013220 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013222 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013223 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013224 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013225 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013226 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013227 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013228 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013229 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013230 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013231 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013232 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013233 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013234 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013235 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013236 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013237 /* protect $((expr : expr)) without "expr ? " */
13238 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013239 }
13240 numptr_m1->contidional_second_val_initialized = op;
13241 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013242 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013243 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013244 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013245 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013246 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013247 return -3; /* exponent less than 0 */
13248 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013249 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013250
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013251 if (numptr_val)
13252 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013253 c *= rez;
13254 rez = c;
13255 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013256 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013257 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013258 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013259 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013260 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013261 rez %= numptr_val;
13262 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013263 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013264 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013265
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013267 /* Hmm, 1=2 ? */
13268 goto err;
13269 }
13270 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013271#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013272 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013273#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013274 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013275#endif
Eric Andersen90898442003-08-06 11:20:52 +000013276 setvar(numptr_m1->var, buf, 0);
13277 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013278 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013279 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013280 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013281 rez++;
13282 }
13283 numptr_m1->val = rez;
13284 /* protect geting var value, is number now */
13285 numptr_m1->var = NULL;
13286 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013287 err:
13288 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013289}
13290
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013291/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013292static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013293 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13294 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13295 '<','<', 0, TOK_LSHIFT,
13296 '>','>', 0, TOK_RSHIFT,
13297 '|','|', 0, TOK_OR,
13298 '&','&', 0, TOK_AND,
13299 '!','=', 0, TOK_NE,
13300 '<','=', 0, TOK_LE,
13301 '>','=', 0, TOK_GE,
13302 '=','=', 0, TOK_EQ,
13303 '|','=', 0, TOK_OR_ASSIGN,
13304 '&','=', 0, TOK_AND_ASSIGN,
13305 '*','=', 0, TOK_MUL_ASSIGN,
13306 '/','=', 0, TOK_DIV_ASSIGN,
13307 '%','=', 0, TOK_REM_ASSIGN,
13308 '+','=', 0, TOK_PLUS_ASSIGN,
13309 '-','=', 0, TOK_MINUS_ASSIGN,
13310 '-','-', 0, TOK_POST_DEC,
13311 '^','=', 0, TOK_XOR_ASSIGN,
13312 '+','+', 0, TOK_POST_INC,
13313 '*','*', 0, TOK_EXPONENT,
13314 '!', 0, TOK_NOT,
13315 '<', 0, TOK_LT,
13316 '>', 0, TOK_GT,
13317 '=', 0, TOK_ASSIGN,
13318 '|', 0, TOK_BOR,
13319 '&', 0, TOK_BAND,
13320 '*', 0, TOK_MUL,
13321 '/', 0, TOK_DIV,
13322 '%', 0, TOK_REM,
13323 '+', 0, TOK_ADD,
13324 '-', 0, TOK_SUB,
13325 '^', 0, TOK_BXOR,
13326 /* uniq */
13327 '~', 0, TOK_BNOT,
13328 ',', 0, TOK_COMMA,
13329 '?', 0, TOK_CONDITIONAL,
13330 ':', 0, TOK_CONDITIONAL_SEP,
13331 ')', 0, TOK_RPAREN,
13332 '(', 0, TOK_LPAREN,
13333 0
13334};
13335/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013336#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013337
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013338static arith_t
13339arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013340{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013341 char arithval; /* Current character under analysis */
13342 operator lasttok, op;
13343 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013344 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013345 const char *p = endexpression;
13346 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013347 v_n_t *numstack, *numstackptr;
13348 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013349
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013350 /* Stack of integers */
13351 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13352 * in any given correct or incorrect expression is left as an exercise to
13353 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013354 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013355 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013356 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013357
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013358 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13359 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013360
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013361 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013362 arithval = *expr;
13363 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013364 if (p == endexpression) {
13365 /* Null expression. */
13366 return 0;
13367 }
13368
13369 /* This is only reached after all tokens have been extracted from the
13370 * input stream. If there are still tokens on the operator stack, they
13371 * are to be applied in order. At the end, there should be a final
13372 * result on the integer stack */
13373
13374 if (expr != endexpression + 1) {
13375 /* If we haven't done so already, */
13376 /* append a closing right paren */
13377 expr = endexpression;
13378 /* and let the loop process it. */
13379 continue;
13380 }
13381 /* At this point, we're done with the expression. */
13382 if (numstackptr != numstack+1) {
13383 /* ... but if there isn't, it's bad */
13384 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013385 *perrcode = -1;
13386 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013387 }
13388 if (numstack->var) {
13389 /* expression is $((var)) only, lookup now */
13390 errcode = arith_lookup_val(numstack);
13391 }
13392 ret:
13393 *perrcode = errcode;
13394 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013395 }
13396
Eric Andersen90898442003-08-06 11:20:52 +000013397 /* Continue processing the expression. */
13398 if (arith_isspace(arithval)) {
13399 /* Skip whitespace */
13400 goto prologue;
13401 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013402 p = endofname(expr);
13403 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013404 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013405
13406 numstackptr->var = alloca(var_name_size);
13407 safe_strncpy(numstackptr->var, expr, var_name_size);
13408 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013409 num:
Eric Andersen90898442003-08-06 11:20:52 +000013410 numstackptr->contidional_second_val_initialized = 0;
13411 numstackptr++;
13412 lasttok = TOK_NUM;
13413 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013414 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013415 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013416 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013417#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013418 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013419#else
13420 numstackptr->val = strtol(expr, (char **) &expr, 0);
13421#endif
Eric Andersen90898442003-08-06 11:20:52 +000013422 goto num;
13423 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013424 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013425 const char *o;
13426
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013427 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013428 /* strange operator not found */
13429 goto err;
13430 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013431 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013432 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013433 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013434 /* found */
13435 expr = o - 1;
13436 break;
13437 }
13438 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013439 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013440 p++;
13441 /* skip zero delim */
13442 p++;
13443 }
13444 op = p[1];
13445
13446 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013447 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13448 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013449
13450 /* Plus and minus are binary (not unary) _only_ if the last
13451 * token was as number, or a right paren (which pretends to be
13452 * a number, since it evaluates to one). Think about it.
13453 * It makes sense. */
13454 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013455 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013456 case TOK_ADD:
13457 op = TOK_UPLUS;
13458 break;
13459 case TOK_SUB:
13460 op = TOK_UMINUS;
13461 break;
13462 case TOK_POST_INC:
13463 op = TOK_PRE_INC;
13464 break;
13465 case TOK_POST_DEC:
13466 op = TOK_PRE_DEC;
13467 break;
Eric Andersen90898442003-08-06 11:20:52 +000013468 }
13469 }
13470 /* We don't want a unary operator to cause recursive descent on the
13471 * stack, because there can be many in a row and it could cause an
13472 * operator to be evaluated before its argument is pushed onto the
13473 * integer stack. */
13474 /* But for binary operators, "apply" everything on the operator
13475 * stack until we find an operator with a lesser priority than the
13476 * one we have just extracted. */
13477 /* Left paren is given the lowest priority so it will never be
13478 * "applied" in this way.
13479 * if associativity is right and priority eq, applied also skip
13480 */
13481 prec = PREC(op);
13482 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13483 /* not left paren or unary */
13484 if (lasttok != TOK_NUM) {
13485 /* binary op must be preceded by a num */
13486 goto err;
13487 }
13488 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013489 if (op == TOK_RPAREN) {
13490 /* The algorithm employed here is simple: while we don't
13491 * hit an open paren nor the bottom of the stack, pop
13492 * tokens and apply them */
13493 if (stackptr[-1] == TOK_LPAREN) {
13494 --stackptr;
13495 /* Any operator directly after a */
13496 lasttok = TOK_NUM;
13497 /* close paren should consider itself binary */
13498 goto prologue;
13499 }
13500 } else {
13501 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013502
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013503 convert_prec_is_assing(prec);
13504 convert_prec_is_assing(prev_prec);
13505 if (prev_prec < prec)
13506 break;
13507 /* check right assoc */
13508 if (prev_prec == prec && is_right_associativity(prec))
13509 break;
13510 }
13511 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13512 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013513 }
13514 if (op == TOK_RPAREN) {
13515 goto err;
13516 }
13517 }
13518
13519 /* Push this operator to the stack and remember it. */
13520 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013521 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013522 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013523 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013524}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013525#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013526
13527
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013528/* ============ main() and helpers */
13529
13530/*
13531 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013532 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013533static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013534static void
13535exitshell(void)
13536{
13537 struct jmploc loc;
13538 char *p;
13539 int status;
13540
13541 status = exitstatus;
13542 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13543 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013544 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013545/* dash bug: it just does _exit(exitstatus) here
13546 * but we have to do setjobctl(0) first!
13547 * (bug is still not fixed in dash-0.5.3 - if you run dash
13548 * under Midnight Commander, on exit from dash MC is backgrounded) */
13549 status = exitstatus;
13550 goto out;
13551 }
13552 exception_handler = &loc;
13553 p = trap[0];
13554 if (p) {
13555 trap[0] = NULL;
13556 evalstring(p, 0);
13557 }
13558 flush_stdout_stderr();
13559 out:
13560 setjobctl(0);
13561 _exit(status);
13562 /* NOTREACHED */
13563}
13564
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013565static void
13566init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013567{
13568 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013569 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013570
13571 /* from trap.c: */
13572 signal(SIGCHLD, SIG_DFL);
13573
13574 /* from var.c: */
13575 {
13576 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013577 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013578 const char *p;
13579 struct stat st1, st2;
13580
13581 initvar();
13582 for (envp = environ; envp && *envp; envp++) {
13583 if (strchr(*envp, '=')) {
13584 setvareq(*envp, VEXPORT|VTEXTFIXED);
13585 }
13586 }
13587
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013588 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013589 setvar("PPID", ppid, 0);
13590
13591 p = lookupvar("PWD");
13592 if (p)
13593 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13594 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13595 p = '\0';
13596 setpwd(p, 0);
13597 }
13598}
13599
13600/*
13601 * Process the shell command line arguments.
13602 */
13603static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013604procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013605{
13606 int i;
13607 const char *xminusc;
13608 char **xargv;
13609
13610 xargv = argv;
13611 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013612 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013613 xargv++;
13614 for (i = 0; i < NOPTS; i++)
13615 optlist[i] = 2;
13616 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013617 if (options(1)) {
13618 /* it already printed err message */
13619 raise_exception(EXERROR);
13620 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013621 xargv = argptr;
13622 xminusc = minusc;
13623 if (*xargv == NULL) {
13624 if (xminusc)
13625 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13626 sflag = 1;
13627 }
13628 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13629 iflag = 1;
13630 if (mflag == 2)
13631 mflag = iflag;
13632 for (i = 0; i < NOPTS; i++)
13633 if (optlist[i] == 2)
13634 optlist[i] = 0;
13635#if DEBUG == 2
13636 debug = 1;
13637#endif
13638 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13639 if (xminusc) {
13640 minusc = *xargv++;
13641 if (*xargv)
13642 goto setarg0;
13643 } else if (!sflag) {
13644 setinputfile(*xargv, 0);
13645 setarg0:
13646 arg0 = *xargv++;
13647 commandname = arg0;
13648 }
13649
13650 shellparam.p = xargv;
13651#if ENABLE_ASH_GETOPTS
13652 shellparam.optind = 1;
13653 shellparam.optoff = -1;
13654#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013655 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013656 while (*xargv) {
13657 shellparam.nparam++;
13658 xargv++;
13659 }
13660 optschanged();
13661}
13662
13663/*
13664 * Read /etc/profile or .profile.
13665 */
13666static void
13667read_profile(const char *name)
13668{
13669 int skip;
13670
13671 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13672 return;
13673 skip = cmdloop(0);
13674 popfile();
13675 if (skip)
13676 exitshell();
13677}
13678
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013679/*
13680 * This routine is called when an error or an interrupt occurs in an
13681 * interactive shell and control is returned to the main command loop.
13682 */
13683static void
13684reset(void)
13685{
13686 /* from eval.c: */
13687 evalskip = 0;
13688 loopnest = 0;
13689 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013690 g_parsefile->left_in_buffer = 0;
13691 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013692 popallfiles();
13693 /* from parser.c: */
13694 tokpushback = 0;
13695 checkkwd = 0;
13696 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013697 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013698}
13699
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013700#if PROFILE
13701static short profile_buf[16384];
13702extern int etext();
13703#endif
13704
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013705/*
13706 * Main routine. We initialize things, parse the arguments, execute
13707 * profiles if we're a login shell, and then call cmdloop to execute
13708 * commands. The setjmp call sets up the location to jump to when an
13709 * exception occurs. When an exception occurs the variable "state"
13710 * is used to figure out how far we had gotten.
13711 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013712int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013713int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013714{
13715 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013716 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013717 struct jmploc jmploc;
13718 struct stackmark smark;
13719
Denis Vlasenko01631112007-12-16 17:20:38 +000013720 /* Initialize global data */
13721 INIT_G_misc();
13722 INIT_G_memstack();
13723 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013724#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013725 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013726#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013727 INIT_G_cmdtable();
13728
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013729#if PROFILE
13730 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13731#endif
13732
13733#if ENABLE_FEATURE_EDITING
13734 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13735#endif
13736 state = 0;
13737 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013738 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013739 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013740
13741 reset();
13742
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013743 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013744 if (e == EXERROR)
13745 exitstatus = 2;
13746 s = state;
13747 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13748 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013749 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013750 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013751
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013752 popstackmark(&smark);
13753 FORCE_INT_ON; /* enable interrupts */
13754 if (s == 1)
13755 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013756 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013757 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013758 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013759 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013760 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013761 }
13762 exception_handler = &jmploc;
13763#if DEBUG
13764 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013765 trace_puts("Shell args: ");
13766 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013767#endif
13768 rootpid = getpid();
13769
13770#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013771 /* Can use monotonic_ns() for better randomness but for now it is
13772 * not used anywhere else in busybox... so avoid bloat */
13773 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013774#endif
13775 init();
13776 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013777 procargs(argv);
13778
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013779#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13780 if (iflag) {
13781 const char *hp = lookupvar("HISTFILE");
13782
13783 if (hp == NULL) {
13784 hp = lookupvar("HOME");
13785 if (hp != NULL) {
13786 char *defhp = concat_path_file(hp, ".ash_history");
13787 setvar("HISTFILE", defhp, 0);
13788 free(defhp);
13789 }
13790 }
13791 }
13792#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013793 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013794 isloginsh = 1;
13795 if (isloginsh) {
13796 state = 1;
13797 read_profile("/etc/profile");
13798 state1:
13799 state = 2;
13800 read_profile(".profile");
13801 }
13802 state2:
13803 state = 3;
13804 if (
13805#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013806 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013807#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013808 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013809 ) {
13810 shinit = lookupvar("ENV");
13811 if (shinit != NULL && *shinit != '\0') {
13812 read_profile(shinit);
13813 }
13814 }
13815 state3:
13816 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013817 if (minusc) {
13818 /* evalstring pushes parsefile stack.
13819 * Ensure we don't falsely claim that 0 (stdin)
13820 * is one of stacked source fds */
13821 if (!sflag)
13822 g_parsefile->fd = -1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013823 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013824 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013825
13826 if (sflag || minusc == NULL) {
13827#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013828 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013829 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013830 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013831 line_input_state->hist_file = hp;
13832 }
13833#endif
13834 state4: /* XXX ??? - why isn't this before the "if" statement */
13835 cmdloop(1);
13836 }
13837#if PROFILE
13838 monitor(0);
13839#endif
13840#ifdef GPROF
13841 {
13842 extern void _mcleanup(void);
13843 _mcleanup();
13844 }
13845#endif
13846 exitshell();
13847 /* NOTREACHED */
13848}
13849
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013850#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013851const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013852int main(int argc, char **argv)
13853{
13854 return ash_main(argc, argv);
13855}
13856#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013857
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013858
Eric Andersendf82f612001-06-28 07:46:40 +000013859/*-
13860 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013861 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013862 *
13863 * This code is derived from software contributed to Berkeley by
13864 * Kenneth Almquist.
13865 *
13866 * Redistribution and use in source and binary forms, with or without
13867 * modification, are permitted provided that the following conditions
13868 * are met:
13869 * 1. Redistributions of source code must retain the above copyright
13870 * notice, this list of conditions and the following disclaimer.
13871 * 2. Redistributions in binary form must reproduce the above copyright
13872 * notice, this list of conditions and the following disclaimer in the
13873 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013874 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013875 * may be used to endorse or promote products derived from this software
13876 * without specific prior written permission.
13877 *
13878 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13879 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13880 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13881 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13882 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13883 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13884 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13885 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13886 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13887 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13888 * SUCH DAMAGE.
13889 */